toolplane-python-client 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. toolplane/__init__.py +106 -0
  2. toolplane/common/__init__.py +93 -0
  3. toolplane/common/base_config.py +129 -0
  4. toolplane/common/base_connection_manager.py +171 -0
  5. toolplane/common/base_session_manager.py +321 -0
  6. toolplane/common/base_tool_manager.py +347 -0
  7. toolplane/common/constants.py +47 -0
  8. toolplane/common/utils.py +310 -0
  9. toolplane/core/__init__.py +67 -0
  10. toolplane/core/config.py +107 -0
  11. toolplane/core/connection.py +285 -0
  12. toolplane/core/errors.py +298 -0
  13. toolplane/core/machine.py +480 -0
  14. toolplane/core/request.py +775 -0
  15. toolplane/core/session.py +332 -0
  16. toolplane/core/session_context.py +514 -0
  17. toolplane/core/task.py +130 -0
  18. toolplane/core/tool.py +329 -0
  19. toolplane/http_core/__init__.py +37 -0
  20. toolplane/http_core/http_config.py +97 -0
  21. toolplane/http_core/http_connection.py +409 -0
  22. toolplane/http_core/http_machine.py +298 -0
  23. toolplane/http_core/http_request.py +748 -0
  24. toolplane/http_core/http_session.py +348 -0
  25. toolplane/http_core/http_session_context.py +491 -0
  26. toolplane/http_core/http_task.py +101 -0
  27. toolplane/http_core/http_tool.py +400 -0
  28. toolplane/interfaces/__init__.py +27 -0
  29. toolplane/interfaces/client_interface.py +122 -0
  30. toolplane/interfaces/connection_interface.py +193 -0
  31. toolplane/interfaces/event_interface.py +290 -0
  32. toolplane/interfaces/request_interface.py +439 -0
  33. toolplane/interfaces/session_interface.py +288 -0
  34. toolplane/interfaces/tool_interface.py +441 -0
  35. toolplane/proto/__init__.py +0 -0
  36. toolplane/proto/service_pb2.py +315 -0
  37. toolplane/proto/service_pb2_grpc.py +2240 -0
  38. toolplane/provider_cli.py +268 -0
  39. toolplane/provider_registry.py +77 -0
  40. toolplane/provider_runtime.py +302 -0
  41. toolplane/toolkits/__init__.py +0 -0
  42. toolplane/toolkits/standalone_tools/__init__.py +0 -0
  43. toolplane/toolkits/standalone_tools/create_directory.py +94 -0
  44. toolplane/toolkits/standalone_tools/create_file.py +124 -0
  45. toolplane/toolkits/standalone_tools/file_search.py +229 -0
  46. toolplane/toolkits/standalone_tools/grep_search.py +372 -0
  47. toolplane/toolkits/standalone_tools/launcher.py +146 -0
  48. toolplane/toolkits/standalone_tools/list_dir.py +395 -0
  49. toolplane/toolkits/standalone_tools/read_file.py +346 -0
  50. toolplane/toolkits/standalone_tools/replace_string_in_file.py +407 -0
  51. toolplane/toolkits/standalone_tools/run_tests.py +66 -0
  52. toolplane/toolkits/standalone_tools/semantic_search.py +485 -0
  53. toolplane/toolkits/standalone_tools/standalone_toolkit.py +979 -0
  54. toolplane/toolkits/standalone_tools/test_failure_analysis.py +618 -0
  55. toolplane/toolkits/standalone_tools/test_standalone_toolkit.py +517 -0
  56. toolplane/toolkits/swe/__init__.py +35 -0
  57. toolplane/toolkits/swe/create_directory.py +15 -0
  58. toolplane/toolkits/swe/create_file.py +15 -0
  59. toolplane/toolkits/swe/descriptions.py +273 -0
  60. toolplane/toolkits/swe/execute_bash.py +93 -0
  61. toolplane/toolkits/swe/file_editor.py +775 -0
  62. toolplane/toolkits/swe/file_search.py +16 -0
  63. toolplane/toolkits/swe/finish.py +50 -0
  64. toolplane/toolkits/swe/grep_search.py +19 -0
  65. toolplane/toolkits/swe/list_dir.py +407 -0
  66. toolplane/toolkits/swe/read_file.py +18 -0
  67. toolplane/toolkits/swe/replace_string_in_file.py +17 -0
  68. toolplane/toolkits/swe/search.py +260 -0
  69. toolplane/toolkits/swe/semantic_search.py +20 -0
  70. toolplane/toolkits/swe/str_replace_editor.py +647 -0
  71. toolplane/toolkits/swe/submit.py +29 -0
  72. toolplane/toolkits/swe/swe_toolkit.py +1296 -0
  73. toolplane/toolplane_client.py +686 -0
  74. toolplane/toolplane_http_client.py +681 -0
  75. toolplane/utils/__init__.py +3 -0
  76. toolplane/utils/schema.py +146 -0
  77. toolplane_python_client-0.1.0.dist-info/METADATA +543 -0
  78. toolplane_python_client-0.1.0.dist-info/RECORD +81 -0
  79. toolplane_python_client-0.1.0.dist-info/WHEEL +5 -0
  80. toolplane_python_client-0.1.0.dist-info/entry_points.txt +2 -0
  81. toolplane_python_client-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,439 @@
1
+ """Request management interface definitions."""
2
+
3
+ import uuid
4
+ from abc import ABC, abstractmethod
5
+ from dataclasses import dataclass
6
+ from datetime import datetime
7
+ from enum import Enum
8
+ from typing import Any, Dict, List, Optional, Protocol, runtime_checkable
9
+
10
+
11
+ class RequestStatus(Enum):
12
+ """Request status enumeration."""
13
+
14
+ PENDING = "pending"
15
+ RUNNING = "running"
16
+ DONE = "done"
17
+ FAILURE = "failure"
18
+ PROCESSING = "running"
19
+ COMPLETED = "done"
20
+ FAILED = "failure"
21
+ TIMEOUT = "timeout"
22
+ CANCELLED = "cancelled"
23
+
24
+
25
+ class RequestPriority(Enum):
26
+ """Request priority levels."""
27
+
28
+ LOW = "low"
29
+ NORMAL = "normal"
30
+ HIGH = "high"
31
+ URGENT = "urgent"
32
+
33
+
34
+ @dataclass
35
+ class RequestInfo:
36
+ """Information about a request."""
37
+
38
+ request_id: str
39
+ session_id: str
40
+ tool_name: str
41
+ parameters: Dict[str, Any]
42
+ status: RequestStatus
43
+ priority: RequestPriority = RequestPriority.NORMAL
44
+ created_at: datetime = None
45
+ started_at: Optional[datetime] = None
46
+ completed_at: Optional[datetime] = None
47
+ result: Any = None
48
+ error: Optional[str] = None
49
+ metadata: Dict[str, Any] = None
50
+ retry_count: int = 0
51
+ max_retries: int = 3
52
+
53
+ def __post_init__(self):
54
+ if self.created_at is None:
55
+ self.created_at = datetime.now()
56
+ if self.metadata is None:
57
+ self.metadata = {}
58
+ if not self.request_id:
59
+ self.request_id = str(uuid.uuid4())
60
+
61
+ @property
62
+ def execution_time(self) -> Optional[float]:
63
+ """Get execution time in seconds."""
64
+ if self.started_at and self.completed_at:
65
+ return (self.completed_at - self.started_at).total_seconds()
66
+ return None
67
+
68
+ @property
69
+ def wait_time(self) -> Optional[float]:
70
+ """Get wait time in seconds."""
71
+ if self.started_at:
72
+ return (self.started_at - self.created_at).total_seconds()
73
+ return None
74
+
75
+ def to_dict(self) -> Dict[str, Any]:
76
+ """Convert to dictionary."""
77
+ return {
78
+ "request_id": self.request_id,
79
+ "session_id": self.session_id,
80
+ "tool_name": self.tool_name,
81
+ "parameters": self.parameters,
82
+ "status": self.status.value,
83
+ "priority": self.priority.value,
84
+ "created_at": self.created_at.isoformat() if self.created_at else None,
85
+ "started_at": self.started_at.isoformat() if self.started_at else None,
86
+ "completed_at": (
87
+ self.completed_at.isoformat() if self.completed_at else None
88
+ ),
89
+ "result": self.result,
90
+ "error": self.error,
91
+ "metadata": self.metadata,
92
+ "retry_count": self.retry_count,
93
+ "max_retries": self.max_retries,
94
+ "execution_time": self.execution_time,
95
+ "wait_time": self.wait_time,
96
+ }
97
+
98
+
99
+ @runtime_checkable
100
+ class IRequestProcessor(Protocol):
101
+ """Protocol interface for request processing."""
102
+
103
+ def process_request(self, request: RequestInfo) -> RequestInfo:
104
+ """Process a single request."""
105
+ ...
106
+
107
+ def can_process(self, request: RequestInfo) -> bool:
108
+ """Check if processor can handle this request."""
109
+ ...
110
+
111
+ def get_processing_capacity(self) -> int:
112
+ """Get current processing capacity."""
113
+ ...
114
+
115
+
116
+ @runtime_checkable
117
+ class IRequestManager(Protocol):
118
+ """Protocol interface for request management."""
119
+
120
+ def submit_request(
121
+ self,
122
+ session_id: str,
123
+ tool_name: str,
124
+ parameters: Dict[str, Any],
125
+ priority: RequestPriority = RequestPriority.NORMAL,
126
+ metadata: Optional[Dict[str, Any]] = None,
127
+ ) -> str:
128
+ """Submit a new request."""
129
+ ...
130
+
131
+ def get_request_status(self, request_id: str) -> Optional[RequestInfo]:
132
+ """Get request status."""
133
+ ...
134
+
135
+ def cancel_request(self, request_id: str) -> bool:
136
+ """Cancel a request."""
137
+ ...
138
+
139
+ def list_requests(
140
+ self, session_id: Optional[str] = None, status: Optional[RequestStatus] = None
141
+ ) -> List[RequestInfo]:
142
+ """List requests with optional filtering."""
143
+ ...
144
+
145
+ def start_polling(self, interval: float) -> None:
146
+ """Start request polling."""
147
+ ...
148
+
149
+ def stop_polling(self) -> None:
150
+ """Stop request polling."""
151
+ ...
152
+
153
+
154
+ class IRequestQueue(ABC):
155
+ """Abstract interface for request queuing."""
156
+
157
+ @abstractmethod
158
+ def enqueue(self, request: RequestInfo) -> None:
159
+ """Add request to queue."""
160
+ pass
161
+
162
+ @abstractmethod
163
+ def dequeue(self) -> Optional[RequestInfo]:
164
+ """Remove and return next request from queue."""
165
+ pass
166
+
167
+ @abstractmethod
168
+ def peek(self) -> Optional[RequestInfo]:
169
+ """Look at next request without removing it."""
170
+ pass
171
+
172
+ @abstractmethod
173
+ def size(self) -> int:
174
+ """Get queue size."""
175
+ pass
176
+
177
+ @abstractmethod
178
+ def is_empty(self) -> bool:
179
+ """Check if queue is empty."""
180
+ pass
181
+
182
+ @abstractmethod
183
+ def clear(self) -> None:
184
+ """Clear all requests from queue."""
185
+ pass
186
+
187
+ @abstractmethod
188
+ def remove_request(self, request_id: str) -> bool:
189
+ """Remove specific request from queue."""
190
+ pass
191
+
192
+
193
+ class PriorityRequestQueue(IRequestQueue):
194
+ """Priority-based request queue implementation."""
195
+
196
+ def __init__(self):
197
+ import heapq
198
+
199
+ self._queue = []
200
+ self._entry_finder = {} # Maps request_id to entry
201
+ self._counter = 0 # Unique sequence count
202
+ self.heapq = heapq
203
+
204
+ def enqueue(self, request: RequestInfo) -> None:
205
+ """Add request to priority queue."""
206
+ # Lower number = higher priority
207
+ priority_map = {
208
+ RequestPriority.URGENT: 0,
209
+ RequestPriority.HIGH: 1,
210
+ RequestPriority.NORMAL: 2,
211
+ RequestPriority.LOW: 3,
212
+ }
213
+
214
+ priority = priority_map.get(request.priority, 2)
215
+
216
+ # Remove existing entry if updating
217
+ if request.request_id in self._entry_finder:
218
+ self.remove_request(request.request_id)
219
+
220
+ count = self._counter
221
+ self._counter += 1
222
+
223
+ # Entry format: [priority, count, request]
224
+ entry = [priority, count, request]
225
+ self._entry_finder[request.request_id] = entry
226
+ self.heapq.heappush(self._queue, entry)
227
+
228
+ def dequeue(self) -> Optional[RequestInfo]:
229
+ """Remove and return highest priority request."""
230
+ while self._queue:
231
+ priority, count, request = self.heapq.heappop(self._queue)
232
+
233
+ # Check if this entry was marked as removed
234
+ if request.request_id not in self._entry_finder:
235
+ continue
236
+
237
+ del self._entry_finder[request.request_id]
238
+ return request
239
+
240
+ return None
241
+
242
+ def peek(self) -> Optional[RequestInfo]:
243
+ """Look at highest priority request without removing it."""
244
+ while self._queue:
245
+ priority, count, request = self._queue[0]
246
+
247
+ # Check if this entry was marked as removed
248
+ if request.request_id not in self._entry_finder:
249
+ self.heapq.heappop(self._queue)
250
+ continue
251
+
252
+ return request
253
+
254
+ return None
255
+
256
+ def size(self) -> int:
257
+ """Get queue size."""
258
+ return len(self._entry_finder)
259
+
260
+ def is_empty(self) -> bool:
261
+ """Check if queue is empty."""
262
+ return len(self._entry_finder) == 0
263
+
264
+ def clear(self) -> None:
265
+ """Clear all requests from queue."""
266
+ self._queue.clear()
267
+ self._entry_finder.clear()
268
+ self._counter = 0
269
+
270
+ def remove_request(self, request_id: str) -> bool:
271
+ """Remove specific request from queue."""
272
+ if request_id in self._entry_finder:
273
+ self._entry_finder.pop(request_id)
274
+ # Mark as removed by clearing the request_id
275
+ # The actual removal happens during dequeue
276
+ return True
277
+ return False
278
+
279
+
280
+ class FIFORequestQueue(IRequestQueue):
281
+ """First-in-first-out request queue implementation."""
282
+
283
+ def __init__(self):
284
+ from collections import deque
285
+
286
+ self._queue = deque()
287
+ self._request_ids = set()
288
+
289
+ def enqueue(self, request: RequestInfo) -> None:
290
+ """Add request to end of queue."""
291
+ if request.request_id not in self._request_ids:
292
+ self._queue.append(request)
293
+ self._request_ids.add(request.request_id)
294
+
295
+ def dequeue(self) -> Optional[RequestInfo]:
296
+ """Remove and return first request from queue."""
297
+ if self._queue:
298
+ request = self._queue.popleft()
299
+ self._request_ids.remove(request.request_id)
300
+ return request
301
+ return None
302
+
303
+ def peek(self) -> Optional[RequestInfo]:
304
+ """Look at first request without removing it."""
305
+ return self._queue[0] if self._queue else None
306
+
307
+ def size(self) -> int:
308
+ """Get queue size."""
309
+ return len(self._queue)
310
+
311
+ def is_empty(self) -> bool:
312
+ """Check if queue is empty."""
313
+ return len(self._queue) == 0
314
+
315
+ def clear(self) -> None:
316
+ """Clear all requests from queue."""
317
+ self._queue.clear()
318
+ self._request_ids.clear()
319
+
320
+ def remove_request(self, request_id: str) -> bool:
321
+ """Remove specific request from queue."""
322
+ if request_id in self._request_ids:
323
+ # Find and remove the request
324
+ for i, request in enumerate(self._queue):
325
+ if request.request_id == request_id:
326
+ del self._queue[i]
327
+ self._request_ids.remove(request_id)
328
+ return True
329
+ return False
330
+
331
+
332
+ class RequestQueueFactory:
333
+ """Factory for creating request queues."""
334
+
335
+ _queue_types = {
336
+ "priority": PriorityRequestQueue,
337
+ "fifo": FIFORequestQueue,
338
+ }
339
+
340
+ @classmethod
341
+ def create_queue(cls, queue_type: str = "priority") -> IRequestQueue:
342
+ """Create a request queue of specified type."""
343
+ if queue_type not in cls._queue_types:
344
+ raise ValueError(f"Unknown queue type: {queue_type}")
345
+
346
+ queue_class = cls._queue_types[queue_type]
347
+ return queue_class()
348
+
349
+ @classmethod
350
+ def register_queue_type(cls, name: str, queue_class: type) -> None:
351
+ """Register a new queue type."""
352
+ cls._queue_types[name] = queue_class
353
+
354
+
355
+ class RequestMetrics:
356
+ """Metrics collection for request processing."""
357
+
358
+ def __init__(self):
359
+ self.reset()
360
+
361
+ def reset(self) -> None:
362
+ """Reset all metrics."""
363
+ self.total_requests = 0
364
+ self.completed_requests = 0
365
+ self.failed_requests = 0
366
+ self.cancelled_requests = 0
367
+ self.timeout_requests = 0
368
+ self.total_execution_time = 0.0
369
+ self.total_wait_time = 0.0
370
+ self.requests_by_tool = {}
371
+ self.requests_by_session = {}
372
+ self.requests_by_priority = {p: 0 for p in RequestPriority}
373
+
374
+ def record_request(self, request: RequestInfo) -> None:
375
+ """Record request metrics."""
376
+ self.total_requests += 1
377
+
378
+ # Count by status
379
+ if request.status == RequestStatus.COMPLETED:
380
+ self.completed_requests += 1
381
+ elif request.status == RequestStatus.FAILED:
382
+ self.failed_requests += 1
383
+ elif request.status == RequestStatus.CANCELLED:
384
+ self.cancelled_requests += 1
385
+ elif request.status == RequestStatus.TIMEOUT:
386
+ self.timeout_requests += 1
387
+
388
+ # Record timing
389
+ if request.execution_time:
390
+ self.total_execution_time += request.execution_time
391
+ if request.wait_time:
392
+ self.total_wait_time += request.wait_time
393
+
394
+ # Count by tool
395
+ tool_name = request.tool_name
396
+ self.requests_by_tool[tool_name] = self.requests_by_tool.get(tool_name, 0) + 1
397
+
398
+ # Count by session
399
+ session_id = request.session_id
400
+ self.requests_by_session[session_id] = (
401
+ self.requests_by_session.get(session_id, 0) + 1
402
+ )
403
+
404
+ # Count by priority
405
+ self.requests_by_priority[request.priority] += 1
406
+
407
+ def get_metrics(self) -> Dict[str, Any]:
408
+ """Get all metrics."""
409
+ success_rate = (
410
+ self.completed_requests / self.total_requests
411
+ if self.total_requests > 0
412
+ else 0
413
+ )
414
+
415
+ avg_execution_time = (
416
+ self.total_execution_time / self.completed_requests
417
+ if self.completed_requests > 0
418
+ else 0
419
+ )
420
+
421
+ avg_wait_time = (
422
+ self.total_wait_time / self.total_requests if self.total_requests > 0 else 0
423
+ )
424
+
425
+ return {
426
+ "total_requests": self.total_requests,
427
+ "completed_requests": self.completed_requests,
428
+ "failed_requests": self.failed_requests,
429
+ "cancelled_requests": self.cancelled_requests,
430
+ "timeout_requests": self.timeout_requests,
431
+ "success_rate": success_rate,
432
+ "avg_execution_time": avg_execution_time,
433
+ "avg_wait_time": avg_wait_time,
434
+ "requests_by_tool": self.requests_by_tool.copy(),
435
+ "requests_by_session": self.requests_by_session.copy(),
436
+ "requests_by_priority": {
437
+ p.value: count for p, count in self.requests_by_priority.items()
438
+ },
439
+ }