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,485 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Description: Semantic search for relevant code or documentation using text similarity.
4
+
5
+ This tool performs semantic search across code and documentation files using
6
+ text similarity algorithms to find relevant content based on natural language queries.
7
+
8
+ Parameters:
9
+ query (string, required): The search query in natural language.
10
+ max_results (integer, optional): Maximum number of results to return (default: 10).
11
+ file_types (array, optional): File types to search in (default: common code files).
12
+ similarity_threshold (float, optional): Minimum similarity score to include (default: 0.1).
13
+ context_size (integer, optional): Number of lines of context around matches (default: 3).
14
+ search_path (string, optional): Directory to search in (default: current directory).
15
+ """
16
+
17
+ import argparse
18
+ import math
19
+ import os
20
+ import re
21
+ import sys
22
+ from collections import Counter, defaultdict
23
+ from pathlib import Path
24
+ from typing import Any, Dict, List, Set
25
+
26
+
27
+ def semantic_search(
28
+ query: str,
29
+ max_results: int = 10,
30
+ file_types: List[str] = None,
31
+ similarity_threshold: float = 0.1,
32
+ context_size: int = 3,
33
+ search_path: str = None,
34
+ ) -> List[Dict[str, Any]]:
35
+ """
36
+ Perform semantic search for relevant code or documentation.
37
+
38
+ Args:
39
+ query: The search query in natural language
40
+ max_results: Maximum number of results to return
41
+ file_types: File types to search in
42
+ similarity_threshold: Minimum similarity score to include
43
+ context_size: Number of lines of context around matches
44
+ search_path: Directory to search in
45
+
46
+ Returns:
47
+ List of search results with similarity scores
48
+ """
49
+ if search_path is None:
50
+ search_path = os.getcwd()
51
+
52
+ if file_types is None:
53
+ file_types = [
54
+ ".py",
55
+ ".js",
56
+ ".ts",
57
+ ".jsx",
58
+ ".tsx",
59
+ ".java",
60
+ ".cpp",
61
+ ".c",
62
+ ".h",
63
+ ".cs",
64
+ ".php",
65
+ ".rb",
66
+ ".go",
67
+ ".rs",
68
+ ".swift",
69
+ ".kt",
70
+ ".scala",
71
+ ".html",
72
+ ".css",
73
+ ".scss",
74
+ ".md",
75
+ ".rst",
76
+ ".txt",
77
+ ".json",
78
+ ".yaml",
79
+ ".yml",
80
+ ".xml",
81
+ ".sql",
82
+ ".sh",
83
+ ".bash",
84
+ ".ps1",
85
+ ".bat",
86
+ ]
87
+
88
+ # Get all files to search
89
+ files_to_search = get_searchable_files(search_path, file_types)
90
+
91
+ # Process query
92
+ query_terms = preprocess_text(query)
93
+
94
+ # Search each file
95
+ results = []
96
+
97
+ for file_path in files_to_search:
98
+ try:
99
+ file_results = search_file(file_path, query_terms, context_size)
100
+
101
+ # Calculate similarity scores
102
+ for result in file_results:
103
+ score = calculate_similarity(query_terms, result["content_tokens"])
104
+
105
+ if score >= similarity_threshold:
106
+ result["similarity_score"] = score
107
+ result["query"] = query
108
+ results.append(result)
109
+
110
+ except Exception as e:
111
+ # Skip files that can't be processed
112
+ continue
113
+
114
+ # Sort by similarity score
115
+ results.sort(key=lambda x: x["similarity_score"], reverse=True)
116
+
117
+ # Return top results
118
+ return results[:max_results]
119
+
120
+
121
+ def get_searchable_files(search_path: str, file_types: List[str]) -> List[str]:
122
+ """Get all files that match the specified file types."""
123
+ files = []
124
+
125
+ for root, dirs, filenames in os.walk(search_path):
126
+ # Skip hidden directories
127
+ dirs[:] = [d for d in dirs if not d.startswith(".")]
128
+
129
+ for filename in filenames:
130
+ if filename.startswith("."):
131
+ continue
132
+
133
+ file_path = Path(root) / filename
134
+
135
+ # Check if file type is in our list
136
+ if any(filename.lower().endswith(ext) for ext in file_types):
137
+ files.append(str(file_path.resolve()))
138
+
139
+ return files
140
+
141
+
142
+ def search_file(
143
+ file_path: str, query_terms: Set[str], context_size: int
144
+ ) -> List[Dict[str, Any]]:
145
+ """Search for relevant content in a single file."""
146
+ try:
147
+ with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
148
+ lines = f.readlines()
149
+ except Exception:
150
+ return []
151
+
152
+ results = []
153
+
154
+ # Search for matches in each line
155
+ for line_num, line in enumerate(lines):
156
+ line_tokens = preprocess_text(line.strip())
157
+
158
+ # Check if line contains any query terms
159
+ if query_terms.intersection(line_tokens):
160
+ # Get context around the match
161
+ start_line = max(0, line_num - context_size)
162
+ end_line = min(len(lines), line_num + context_size + 1)
163
+
164
+ context_lines = []
165
+ for i in range(start_line, end_line):
166
+ context_lines.append(
167
+ {
168
+ "line_number": i + 1,
169
+ "content": lines[i].rstrip("\n\r"),
170
+ "is_match": i == line_num,
171
+ }
172
+ )
173
+
174
+ # Get all tokens from the context
175
+ context_text = " ".join(cl["content"] for cl in context_lines)
176
+ content_tokens = preprocess_text(context_text)
177
+
178
+ result = {
179
+ "file_path": file_path,
180
+ "line_number": line_num + 1,
181
+ "matched_line": line.strip(),
182
+ "context": context_lines,
183
+ "content_tokens": content_tokens,
184
+ "file_type": Path(file_path).suffix.lower(),
185
+ }
186
+
187
+ results.append(result)
188
+
189
+ # Also search in function/class definitions and comments
190
+ function_results = extract_functions_and_classes(
191
+ file_path, lines, query_terms, context_size
192
+ )
193
+ results.extend(function_results)
194
+
195
+ # Remove duplicates based on line number
196
+ seen_lines = set()
197
+ unique_results = []
198
+ for result in results:
199
+ if result["line_number"] not in seen_lines:
200
+ seen_lines.add(result["line_number"])
201
+ unique_results.append(result)
202
+
203
+ return unique_results
204
+
205
+
206
+ def extract_functions_and_classes(
207
+ file_path: str, lines: List[str], query_terms: Set[str], context_size: int
208
+ ) -> List[Dict[str, Any]]:
209
+ """Extract functions and classes that might be relevant."""
210
+ results = []
211
+ file_ext = Path(file_path).suffix.lower()
212
+
213
+ # Define patterns for different file types
214
+ patterns = {
215
+ ".py": [r"^\s*def\s+(\w+)", r"^\s*class\s+(\w+)", r"^\s*async\s+def\s+(\w+)"],
216
+ ".js": [
217
+ r"^\s*function\s+(\w+)",
218
+ r"^\s*const\s+(\w+)\s*=\s*\(",
219
+ r"^\s*let\s+(\w+)\s*=\s*\(",
220
+ r"^\s*var\s+(\w+)\s*=\s*\(",
221
+ r"^\s*(\w+)\s*:\s*function",
222
+ r"^\s*class\s+(\w+)",
223
+ ],
224
+ ".ts": [
225
+ r"^\s*function\s+(\w+)",
226
+ r"^\s*const\s+(\w+)\s*=\s*\(",
227
+ r"^\s*let\s+(\w+)\s*=\s*\(",
228
+ r"^\s*class\s+(\w+)",
229
+ r"^\s*interface\s+(\w+)",
230
+ r"^\s*type\s+(\w+)",
231
+ ],
232
+ ".java": [
233
+ r"^\s*public\s+.*\s+(\w+)\s*\(",
234
+ r"^\s*private\s+.*\s+(\w+)\s*\(",
235
+ r"^\s*protected\s+.*\s+(\w+)\s*\(",
236
+ r"^\s*class\s+(\w+)",
237
+ r"^\s*interface\s+(\w+)",
238
+ ],
239
+ ".cpp": [r"^\s*.*\s+(\w+)\s*\(", r"^\s*class\s+(\w+)", r"^\s*struct\s+(\w+)"],
240
+ ".c": [r"^\s*.*\s+(\w+)\s*\(", r"^\s*struct\s+(\w+)"],
241
+ }
242
+
243
+ if file_ext not in patterns:
244
+ return results
245
+
246
+ for line_num, line in enumerate(lines):
247
+ for pattern in patterns[file_ext]:
248
+ match = re.search(pattern, line)
249
+ if match:
250
+ function_name = match.group(1)
251
+
252
+ # Check if function name or line content matches query
253
+ name_tokens = preprocess_text(function_name)
254
+ line_tokens = preprocess_text(line.strip())
255
+
256
+ if query_terms.intersection(name_tokens) or query_terms.intersection(
257
+ line_tokens
258
+ ):
259
+ # Get context around the function
260
+ start_line = max(0, line_num - context_size)
261
+ end_line = min(len(lines), line_num + context_size + 1)
262
+
263
+ context_lines = []
264
+ for i in range(start_line, end_line):
265
+ context_lines.append(
266
+ {
267
+ "line_number": i + 1,
268
+ "content": lines[i].rstrip("\n\r"),
269
+ "is_match": i == line_num,
270
+ }
271
+ )
272
+
273
+ context_text = " ".join(cl["content"] for cl in context_lines)
274
+ content_tokens = preprocess_text(context_text)
275
+
276
+ result = {
277
+ "file_path": file_path,
278
+ "line_number": line_num + 1,
279
+ "matched_line": line.strip(),
280
+ "context": context_lines,
281
+ "content_tokens": content_tokens,
282
+ "file_type": file_ext,
283
+ "match_type": "function_definition",
284
+ "function_name": function_name,
285
+ }
286
+
287
+ results.append(result)
288
+
289
+ return results
290
+
291
+
292
+ def preprocess_text(text: str) -> Set[str]:
293
+ """Preprocess text for similarity comparison."""
294
+ # Convert to lowercase
295
+ text = text.lower()
296
+
297
+ # Remove special characters and split into words
298
+ words = re.findall(r"\b\w+\b", text)
299
+
300
+ # Remove common stop words
301
+ stop_words = {
302
+ "the",
303
+ "a",
304
+ "an",
305
+ "and",
306
+ "or",
307
+ "but",
308
+ "in",
309
+ "on",
310
+ "at",
311
+ "to",
312
+ "for",
313
+ "of",
314
+ "with",
315
+ "by",
316
+ "from",
317
+ "is",
318
+ "are",
319
+ "was",
320
+ "were",
321
+ "be",
322
+ "been",
323
+ "have",
324
+ "has",
325
+ "had",
326
+ "do",
327
+ "does",
328
+ "did",
329
+ "will",
330
+ "would",
331
+ "could",
332
+ "should",
333
+ "may",
334
+ "might",
335
+ "can",
336
+ "this",
337
+ "that",
338
+ "these",
339
+ "those",
340
+ "i",
341
+ "you",
342
+ "he",
343
+ "she",
344
+ "it",
345
+ "we",
346
+ "they",
347
+ "me",
348
+ "him",
349
+ "her",
350
+ "us",
351
+ "them",
352
+ "my",
353
+ "your",
354
+ "his",
355
+ "her",
356
+ "its",
357
+ "our",
358
+ "their",
359
+ }
360
+
361
+ # Filter out stop words and short words
362
+ filtered_words = [
363
+ word for word in words if word not in stop_words and len(word) > 2
364
+ ]
365
+
366
+ return set(filtered_words)
367
+
368
+
369
+ def calculate_similarity(query_terms: Set[str], content_tokens: Set[str]) -> float:
370
+ """Calculate similarity between query terms and content tokens."""
371
+ if not query_terms or not content_tokens:
372
+ return 0.0
373
+
374
+ # Jaccard similarity: intersection / union
375
+ intersection = len(query_terms.intersection(content_tokens))
376
+ union = len(query_terms.union(content_tokens))
377
+
378
+ if union == 0:
379
+ return 0.0
380
+
381
+ jaccard_score = intersection / union
382
+
383
+ # Boost score if many query terms are present
384
+ query_coverage = intersection / len(query_terms)
385
+
386
+ # Combined score
387
+ similarity_score = (jaccard_score * 0.6) + (query_coverage * 0.4)
388
+
389
+ return similarity_score
390
+
391
+
392
+ def main():
393
+ parser = argparse.ArgumentParser(
394
+ description="Semantic search for relevant code or documentation using text similarity."
395
+ )
396
+ parser.add_argument("query", type=str, help="The search query in natural language")
397
+ parser.add_argument(
398
+ "--max_results",
399
+ type=int,
400
+ default=10,
401
+ help="Maximum number of results to return (default: 10)",
402
+ )
403
+ parser.add_argument(
404
+ "--file_types", nargs="+", help="File types to search in (e.g., .py .js .md)"
405
+ )
406
+ parser.add_argument(
407
+ "--similarity_threshold",
408
+ type=float,
409
+ default=0.1,
410
+ help="Minimum similarity score to include (default: 0.1)",
411
+ )
412
+ parser.add_argument(
413
+ "--context_size",
414
+ type=int,
415
+ default=3,
416
+ help="Number of lines of context around matches (default: 3)",
417
+ )
418
+ parser.add_argument(
419
+ "--search_path",
420
+ type=str,
421
+ help="Directory to search in (default: current directory)",
422
+ )
423
+ parser.add_argument(
424
+ "--output_format",
425
+ choices=["default", "json", "compact"],
426
+ default="default",
427
+ help="Output format (default: default)",
428
+ )
429
+
430
+ args = parser.parse_args()
431
+
432
+ results = semantic_search(
433
+ args.query,
434
+ max_results=args.max_results,
435
+ file_types=args.file_types,
436
+ similarity_threshold=args.similarity_threshold,
437
+ context_size=args.context_size,
438
+ search_path=args.search_path,
439
+ )
440
+
441
+ if not results:
442
+ print(f"No results found for query: {args.query}")
443
+ sys.exit(0)
444
+
445
+ if args.output_format == "json":
446
+ import json
447
+
448
+ print(json.dumps(results, indent=2))
449
+
450
+ elif args.output_format == "compact":
451
+ print(f"Found {len(results)} results for: {args.query}")
452
+ print("-" * 60)
453
+
454
+ for i, result in enumerate(results, 1):
455
+ print(
456
+ f"{i}. {result['file_path']}:{result['line_number']} (score: {result['similarity_score']:.3f})"
457
+ )
458
+ print(f" {result['matched_line'][:100]}...")
459
+ print()
460
+
461
+ else: # default format
462
+ print(f"Semantic search results for: {args.query}")
463
+ print(f"Found {len(results)} results")
464
+ print("=" * 80)
465
+
466
+ for i, result in enumerate(results, 1):
467
+ print(f"\nResult {i}: {result['file_path']}:{result['line_number']}")
468
+ print(f"Similarity Score: {result['similarity_score']:.3f}")
469
+ print(f"File Type: {result['file_type']}")
470
+
471
+ if result.get("match_type") == "function_definition":
472
+ print(f"Function: {result.get('function_name', 'unknown')}")
473
+
474
+ print("Context:")
475
+ for context_line in result["context"]:
476
+ marker = ">>>" if context_line["is_match"] else " "
477
+ print(
478
+ f"{marker} {context_line['line_number']:4d}: {context_line['content']}"
479
+ )
480
+
481
+ print("-" * 80)
482
+
483
+
484
+ if __name__ == "__main__":
485
+ main()