EvoScientist 0.0.1.dev4__py3-none-any.whl → 0.1.0rc2__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 (37) hide show
  1. EvoScientist/EvoScientist.py +25 -61
  2. EvoScientist/__init__.py +0 -19
  3. EvoScientist/backends.py +0 -26
  4. EvoScientist/cli.py +1365 -480
  5. EvoScientist/middleware.py +7 -56
  6. EvoScientist/skills/clip/SKILL.md +253 -0
  7. EvoScientist/skills/clip/references/applications.md +207 -0
  8. EvoScientist/skills/langgraph-docs/SKILL.md +36 -0
  9. EvoScientist/skills/tensorboard/SKILL.md +629 -0
  10. EvoScientist/skills/tensorboard/references/integrations.md +638 -0
  11. EvoScientist/skills/tensorboard/references/profiling.md +545 -0
  12. EvoScientist/skills/tensorboard/references/visualization.md +620 -0
  13. EvoScientist/skills/vllm/SKILL.md +364 -0
  14. EvoScientist/skills/vllm/references/optimization.md +226 -0
  15. EvoScientist/skills/vllm/references/quantization.md +284 -0
  16. EvoScientist/skills/vllm/references/server-deployment.md +255 -0
  17. EvoScientist/skills/vllm/references/troubleshooting.md +447 -0
  18. EvoScientist/stream/__init__.py +0 -25
  19. EvoScientist/stream/utils.py +16 -23
  20. EvoScientist/tools.py +2 -75
  21. {evoscientist-0.0.1.dev4.dist-info → evoscientist-0.1.0rc2.dist-info}/METADATA +8 -153
  22. {evoscientist-0.0.1.dev4.dist-info → evoscientist-0.1.0rc2.dist-info}/RECORD +26 -24
  23. evoscientist-0.1.0rc2.dist-info/entry_points.txt +2 -0
  24. EvoScientist/config.py +0 -274
  25. EvoScientist/llm/__init__.py +0 -21
  26. EvoScientist/llm/models.py +0 -99
  27. EvoScientist/memory.py +0 -715
  28. EvoScientist/onboard.py +0 -725
  29. EvoScientist/paths.py +0 -44
  30. EvoScientist/skills_manager.py +0 -391
  31. EvoScientist/stream/display.py +0 -604
  32. EvoScientist/stream/events.py +0 -415
  33. EvoScientist/stream/state.py +0 -343
  34. evoscientist-0.0.1.dev4.dist-info/entry_points.txt +0 -5
  35. {evoscientist-0.0.1.dev4.dist-info → evoscientist-0.1.0rc2.dist-info}/WHEEL +0 -0
  36. {evoscientist-0.0.1.dev4.dist-info → evoscientist-0.1.0rc2.dist-info}/licenses/LICENSE +0 -0
  37. {evoscientist-0.0.1.dev4.dist-info → evoscientist-0.1.0rc2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,447 @@
1
+ # Troubleshooting Guide
2
+
3
+ ## Contents
4
+ - Out of memory (OOM) errors
5
+ - Performance issues
6
+ - Model loading errors
7
+ - Network and connection issues
8
+ - Quantization problems
9
+ - Distributed serving issues
10
+ - Debugging tools and commands
11
+
12
+ ## Out of memory (OOM) errors
13
+
14
+ ### Symptom: `torch.cuda.OutOfMemoryError` during model loading
15
+
16
+ **Cause**: Model + KV cache exceeds available VRAM
17
+
18
+ **Solutions (try in order)**:
19
+
20
+ 1. **Reduce GPU memory utilization**:
21
+ ```bash
22
+ vllm serve MODEL --gpu-memory-utilization 0.7 # Try 0.7, 0.75, 0.8
23
+ ```
24
+
25
+ 2. **Reduce max sequence length**:
26
+ ```bash
27
+ vllm serve MODEL --max-model-len 4096 # Instead of 8192
28
+ ```
29
+
30
+ 3. **Enable quantization**:
31
+ ```bash
32
+ vllm serve MODEL --quantization awq # 4x memory reduction
33
+ ```
34
+
35
+ 4. **Use tensor parallelism** (multiple GPUs):
36
+ ```bash
37
+ vllm serve MODEL --tensor-parallel-size 2 # Split across 2 GPUs
38
+ ```
39
+
40
+ 5. **Reduce max concurrent sequences**:
41
+ ```bash
42
+ vllm serve MODEL --max-num-seqs 128 # Default is 256
43
+ ```
44
+
45
+ ### Symptom: OOM during inference (not model loading)
46
+
47
+ **Cause**: KV cache fills up during generation
48
+
49
+ **Solutions**:
50
+
51
+ ```bash
52
+ # Reduce KV cache allocation
53
+ vllm serve MODEL --gpu-memory-utilization 0.85
54
+
55
+ # Reduce batch size
56
+ vllm serve MODEL --max-num-seqs 64
57
+
58
+ # Reduce max tokens per request
59
+ # Set in client request: max_tokens=512
60
+ ```
61
+
62
+ ### Symptom: OOM with quantized model
63
+
64
+ **Cause**: Quantization overhead or incorrect configuration
65
+
66
+ **Solution**:
67
+ ```bash
68
+ # Ensure quantization flag matches model
69
+ vllm serve TheBloke/Llama-2-70B-AWQ --quantization awq # Must specify
70
+
71
+ # Try different dtype
72
+ vllm serve MODEL --quantization awq --dtype float16
73
+ ```
74
+
75
+ ## Performance issues
76
+
77
+ ### Symptom: Low throughput (<50 req/sec expected >100)
78
+
79
+ **Diagnostic steps**:
80
+
81
+ 1. **Check GPU utilization**:
82
+ ```bash
83
+ watch -n 1 nvidia-smi
84
+ # GPU utilization should be >80%
85
+ ```
86
+
87
+ If <80%, increase concurrent requests:
88
+ ```bash
89
+ vllm serve MODEL --max-num-seqs 512 # Increase from 256
90
+ ```
91
+
92
+ 2. **Check if memory-bound**:
93
+ ```bash
94
+ # If memory at 100% but GPU <80%, reduce sequence length
95
+ vllm serve MODEL --max-model-len 4096
96
+ ```
97
+
98
+ 3. **Enable optimizations**:
99
+ ```bash
100
+ vllm serve MODEL \
101
+ --enable-prefix-caching \
102
+ --enable-chunked-prefill \
103
+ --max-num-seqs 512
104
+ ```
105
+
106
+ 4. **Check tensor parallelism settings**:
107
+ ```bash
108
+ # Must use power-of-2 GPUs
109
+ vllm serve MODEL --tensor-parallel-size 4 # Not 3 or 5
110
+ ```
111
+
112
+ ### Symptom: High TTFT (time to first token >1 second)
113
+
114
+ **Causes and solutions**:
115
+
116
+ **Long prompts**:
117
+ ```bash
118
+ vllm serve MODEL --enable-chunked-prefill
119
+ ```
120
+
121
+ **No prefix caching**:
122
+ ```bash
123
+ vllm serve MODEL --enable-prefix-caching # For repeated prompts
124
+ ```
125
+
126
+ **Too many concurrent requests**:
127
+ ```bash
128
+ vllm serve MODEL --max-num-seqs 64 # Reduce to prioritize latency
129
+ ```
130
+
131
+ **Model too large for single GPU**:
132
+ ```bash
133
+ vllm serve MODEL --tensor-parallel-size 2 # Parallelize prefill
134
+ ```
135
+
136
+ ### Symptom: Slow token generation (low tokens/sec)
137
+
138
+ **Diagnostic**:
139
+ ```bash
140
+ # Check if model is correct size
141
+ vllm serve MODEL # Should see model size in logs
142
+
143
+ # Check speculative decoding
144
+ vllm serve MODEL --speculative-model DRAFT_MODEL
145
+ ```
146
+
147
+ **For H100 GPUs**, enable FP8:
148
+ ```bash
149
+ vllm serve MODEL --quantization fp8
150
+ ```
151
+
152
+ ## Model loading errors
153
+
154
+ ### Symptom: `OSError: MODEL not found`
155
+
156
+ **Causes**:
157
+
158
+ 1. **Model name typo**:
159
+ ```bash
160
+ # Check exact model name on HuggingFace
161
+ vllm serve meta-llama/Llama-3-8B-Instruct # Correct capitalization
162
+ ```
163
+
164
+ 2. **Private/gated model**:
165
+ ```bash
166
+ # Login to HuggingFace first
167
+ huggingface-cli login
168
+ # Then run vLLM
169
+ vllm serve meta-llama/Llama-3-70B-Instruct
170
+ ```
171
+
172
+ 3. **Custom model needs trust flag**:
173
+ ```bash
174
+ vllm serve MODEL --trust-remote-code
175
+ ```
176
+
177
+ ### Symptom: `ValueError: Tokenizer not found`
178
+
179
+ **Solution**:
180
+ ```bash
181
+ # Download model manually first
182
+ python -c "from transformers import AutoTokenizer; AutoTokenizer.from_pretrained('MODEL')"
183
+
184
+ # Then launch vLLM
185
+ vllm serve MODEL
186
+ ```
187
+
188
+ ### Symptom: `ImportError: No module named 'flash_attn'`
189
+
190
+ **Solution**:
191
+ ```bash
192
+ # Install flash attention
193
+ pip install flash-attn --no-build-isolation
194
+
195
+ # Or disable flash attention
196
+ vllm serve MODEL --disable-flash-attn
197
+ ```
198
+
199
+ ## Network and connection issues
200
+
201
+ ### Symptom: `Connection refused` when querying server
202
+
203
+ **Diagnostic**:
204
+
205
+ 1. **Check server is running**:
206
+ ```bash
207
+ curl http://localhost:8000/health
208
+ ```
209
+
210
+ 2. **Check port binding**:
211
+ ```bash
212
+ # Bind to all interfaces for remote access
213
+ vllm serve MODEL --host 0.0.0.0 --port 8000
214
+
215
+ # Check if port is in use
216
+ lsof -i :8000
217
+ ```
218
+
219
+ 3. **Check firewall**:
220
+ ```bash
221
+ # Allow port through firewall
222
+ sudo ufw allow 8000
223
+ ```
224
+
225
+ ### Symptom: Slow response times over network
226
+
227
+ **Solutions**:
228
+
229
+ 1. **Increase timeout**:
230
+ ```python
231
+ from openai import OpenAI
232
+
233
+ client = OpenAI(
234
+ base_url="http://localhost:8000/v1",
235
+ api_key="EMPTY",
236
+ timeout=300.0 # 5 minute timeout
237
+ )
238
+ ```
239
+
240
+ 2. **Check network latency**:
241
+ ```bash
242
+ ping SERVER_IP # Should be <10ms for local network
243
+ ```
244
+
245
+ 3. **Use connection pooling**:
246
+ ```python
247
+ import requests
248
+ from requests.adapters import HTTPAdapter
249
+ from urllib3.util.retry import Retry
250
+
251
+ session = requests.Session()
252
+ retries = Retry(total=3, backoff_factor=1)
253
+ session.mount('http://', HTTPAdapter(max_retries=retries))
254
+ ```
255
+
256
+ ## Quantization problems
257
+
258
+ ### Symptom: `RuntimeError: Quantization format not supported`
259
+
260
+ **Solution**:
261
+ ```bash
262
+ # Ensure correct quantization method
263
+ vllm serve MODEL --quantization awq # For AWQ models
264
+ vllm serve MODEL --quantization gptq # For GPTQ models
265
+
266
+ # Check model card for quantization type
267
+ ```
268
+
269
+ ### Symptom: Poor quality outputs after quantization
270
+
271
+ **Diagnostic**:
272
+
273
+ 1. **Verify model is correctly quantized**:
274
+ ```bash
275
+ # Check model config.json for quantization_config
276
+ cat ~/.cache/huggingface/hub/models--MODEL/config.json
277
+ ```
278
+
279
+ 2. **Try different quantization method**:
280
+ ```bash
281
+ # If AWQ quality issues, try FP8 (H100 only)
282
+ vllm serve MODEL --quantization fp8
283
+
284
+ # Or use less aggressive quantization
285
+ vllm serve MODEL # No quantization
286
+ ```
287
+
288
+ 3. **Increase temperature for better diversity**:
289
+ ```python
290
+ sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
291
+ ```
292
+
293
+ ## Distributed serving issues
294
+
295
+ ### Symptom: `RuntimeError: Distributed init failed`
296
+
297
+ **Diagnostic**:
298
+
299
+ 1. **Check environment variables**:
300
+ ```bash
301
+ # On all nodes
302
+ echo $MASTER_ADDR # Should be same
303
+ echo $MASTER_PORT # Should be same
304
+ echo $RANK # Should be unique per node (0, 1, 2, ...)
305
+ echo $WORLD_SIZE # Should be same (total nodes)
306
+ ```
307
+
308
+ 2. **Check network connectivity**:
309
+ ```bash
310
+ # From node 1 to node 2
311
+ ping NODE2_IP
312
+ nc -zv NODE2_IP 29500 # Check port accessibility
313
+ ```
314
+
315
+ 3. **Check NCCL settings**:
316
+ ```bash
317
+ export NCCL_DEBUG=INFO
318
+ export NCCL_SOCKET_IFNAME=eth0 # Or your network interface
319
+ vllm serve MODEL --tensor-parallel-size 8
320
+ ```
321
+
322
+ ### Symptom: `NCCL error: unhandled cuda error`
323
+
324
+ **Solutions**:
325
+
326
+ ```bash
327
+ # Set NCCL to use correct network interface
328
+ export NCCL_SOCKET_IFNAME=eth0 # Replace with your interface
329
+
330
+ # Increase timeout
331
+ export NCCL_TIMEOUT=1800 # 30 minutes
332
+
333
+ # Force P2P for debugging
334
+ export NCCL_P2P_DISABLE=1
335
+ ```
336
+
337
+ ## Debugging tools and commands
338
+
339
+ ### Enable debug logging
340
+
341
+ ```bash
342
+ export VLLM_LOGGING_LEVEL=DEBUG
343
+ vllm serve MODEL
344
+ ```
345
+
346
+ ### Monitor GPU usage
347
+
348
+ ```bash
349
+ # Real-time GPU monitoring
350
+ watch -n 1 nvidia-smi
351
+
352
+ # Memory breakdown
353
+ nvidia-smi --query-gpu=memory.used,memory.free --format=csv -l 1
354
+ ```
355
+
356
+ ### Profile performance
357
+
358
+ ```bash
359
+ # Built-in benchmarking
360
+ vllm bench throughput \
361
+ --model MODEL \
362
+ --input-tokens 128 \
363
+ --output-tokens 256 \
364
+ --num-prompts 100
365
+
366
+ vllm bench latency \
367
+ --model MODEL \
368
+ --input-tokens 128 \
369
+ --output-tokens 256 \
370
+ --batch-size 8
371
+ ```
372
+
373
+ ### Check metrics
374
+
375
+ ```bash
376
+ # Prometheus metrics
377
+ curl http://localhost:9090/metrics
378
+
379
+ # Filter for specific metrics
380
+ curl http://localhost:9090/metrics | grep vllm_time_to_first_token
381
+
382
+ # Key metrics to monitor:
383
+ # - vllm_time_to_first_token_seconds
384
+ # - vllm_time_per_output_token_seconds
385
+ # - vllm_num_requests_running
386
+ # - vllm_gpu_cache_usage_perc
387
+ # - vllm_request_success_total
388
+ ```
389
+
390
+ ### Test server health
391
+
392
+ ```bash
393
+ # Health check
394
+ curl http://localhost:8000/health
395
+
396
+ # Model info
397
+ curl http://localhost:8000/v1/models
398
+
399
+ # Test completion
400
+ curl http://localhost:8000/v1/completions \
401
+ -H "Content-Type: application/json" \
402
+ -d '{
403
+ "model": "MODEL",
404
+ "prompt": "Hello",
405
+ "max_tokens": 10
406
+ }'
407
+ ```
408
+
409
+ ### Common environment variables
410
+
411
+ ```bash
412
+ # CUDA settings
413
+ export CUDA_VISIBLE_DEVICES=0,1,2,3 # Limit to specific GPUs
414
+
415
+ # vLLM settings
416
+ export VLLM_LOGGING_LEVEL=DEBUG
417
+ export VLLM_TRACE_FUNCTION=1 # Profile functions
418
+ export VLLM_USE_V1=1 # Use v1.0 engine (faster)
419
+
420
+ # NCCL settings (distributed)
421
+ export NCCL_DEBUG=INFO
422
+ export NCCL_SOCKET_IFNAME=eth0
423
+ export NCCL_IB_DISABLE=0 # Enable InfiniBand
424
+ ```
425
+
426
+ ### Collect diagnostic info for bug reports
427
+
428
+ ```bash
429
+ # System info
430
+ nvidia-smi
431
+ python --version
432
+ pip show vllm
433
+
434
+ # vLLM version and config
435
+ vllm --version
436
+ python -c "import vllm; print(vllm.__version__)"
437
+
438
+ # Run with debug logging
439
+ export VLLM_LOGGING_LEVEL=DEBUG
440
+ vllm serve MODEL 2>&1 | tee vllm_debug.log
441
+
442
+ # Include in bug report:
443
+ # - vllm_debug.log
444
+ # - nvidia-smi output
445
+ # - Full command used
446
+ # - Expected vs actual behavior
447
+ ```
@@ -6,9 +6,6 @@ Provides:
6
6
  - ToolCallTracker: Incremental JSON parsing for tool parameters
7
7
  - ToolResultFormatter: Content-aware result formatting with Rich
8
8
  - Utility functions and constants
9
- - SubAgentState / StreamState: Stream state tracking
10
- - stream_agent_events: Async event generator
11
- - Display functions: Rich rendering for streaming and final output
12
9
  """
13
10
 
14
11
  from .emitter import StreamEventEmitter, StreamEvent
@@ -28,15 +25,6 @@ from .utils import (
28
25
  truncate_with_line_hint,
29
26
  get_status_symbol,
30
27
  )
31
- from .state import SubAgentState, StreamState, _parse_todo_items, _build_todo_stats
32
- from .events import stream_agent_events
33
- from .display import (
34
- console,
35
- formatter,
36
- format_tool_result_compact,
37
- create_streaming_display,
38
- display_final_results,
39
- )
40
28
 
41
29
  __all__ = [
42
30
  # Emitter
@@ -62,17 +50,4 @@ __all__ = [
62
50
  "count_lines",
63
51
  "truncate_with_line_hint",
64
52
  "get_status_symbol",
65
- # State
66
- "SubAgentState",
67
- "StreamState",
68
- "_parse_todo_items",
69
- "_build_todo_stats",
70
- # Events
71
- "stream_agent_events",
72
- # Display
73
- "console",
74
- "formatter",
75
- "format_tool_result_compact",
76
- "create_streaming_display",
77
- "display_final_results",
78
53
  ]
@@ -114,40 +114,34 @@ def format_tool_compact(name: str, args: dict | None) -> str:
114
114
  if name_lower == "execute":
115
115
  cmd = args.get("command", "")
116
116
  if len(cmd) > 50:
117
- cmd = cmd[:47] + "\u2026"
117
+ cmd = cmd[:47] + "..."
118
118
  return f"execute({cmd})"
119
119
 
120
- # File operations (with special case for memory files)
120
+ # File operations
121
121
  if name_lower == "read_file":
122
- path = args.get("path", "")
123
- if path.endswith("/MEMORY.md") or path == "/MEMORY.md":
124
- return "Reading memory"
125
- return f"read_file({_shorten_path(path)})"
122
+ path = _shorten_path(args.get("path", ""))
123
+ return f"read_file({path})"
126
124
 
127
125
  if name_lower == "write_file":
128
- path = args.get("path", "")
129
- if path.endswith("/MEMORY.md") or path == "/MEMORY.md":
130
- return "Updating memory"
131
- return f"write_file({_shorten_path(path)})"
126
+ path = _shorten_path(args.get("path", ""))
127
+ return f"write_file({path})"
132
128
 
133
129
  if name_lower == "edit_file":
134
- path = args.get("path", "")
135
- if path.endswith("/MEMORY.md") or path == "/MEMORY.md":
136
- return "Updating memory"
137
- return f"edit_file({_shorten_path(path)})"
130
+ path = _shorten_path(args.get("path", ""))
131
+ return f"edit_file({path})"
138
132
 
139
133
  # Search operations
140
134
  if name_lower == "glob":
141
135
  pattern = args.get("pattern", "")
142
136
  if len(pattern) > 40:
143
- pattern = pattern[:37] + "\u2026"
137
+ pattern = pattern[:37] + "..."
144
138
  return f"glob({pattern})"
145
139
 
146
140
  if name_lower == "grep":
147
141
  pattern = args.get("pattern", "")
148
142
  path = args.get("path", ".")
149
143
  if len(pattern) > 30:
150
- pattern = pattern[:27] + "\u2026"
144
+ pattern = pattern[:27] + "..."
151
145
  return f"grep({pattern}, {path})"
152
146
 
153
147
  # Directory listing
@@ -169,17 +163,16 @@ def format_tool_compact(name: str, args: dict | None) -> str:
169
163
  if name_lower == "task":
170
164
  sa_type = args.get("subagent_type", "").strip()
171
165
  task_desc = args.get("description", args.get("task", "")).strip()
172
- task_desc = task_desc.split("\n")[0].strip() if task_desc else ""
173
166
  if sa_type:
174
167
  if task_desc:
175
168
  if len(task_desc) > 50:
176
- task_desc = task_desc[:47] + "\u2026"
169
+ task_desc = task_desc[:47] + "..."
177
170
  return f"Cooking with {sa_type} — {task_desc}"
178
171
  return f"Cooking with {sa_type}"
179
172
  # Fallback if no subagent_type
180
173
  if task_desc:
181
174
  if len(task_desc) > 50:
182
- task_desc = task_desc[:47] + "\u2026"
175
+ task_desc = task_desc[:47] + "..."
183
176
  return f"Cooking with sub-agent — {task_desc}"
184
177
  return "Cooking with sub-agent"
185
178
 
@@ -192,14 +185,14 @@ def format_tool_compact(name: str, args: dict | None) -> str:
192
185
  if name_lower in ("tavily_search", "internet_search"):
193
186
  query = args.get("query", "")
194
187
  if len(query) > 40:
195
- query = query[:37] + "\u2026"
188
+ query = query[:37] + "..."
196
189
  return f"{name}({query})"
197
190
 
198
191
  # Think/reflection
199
192
  if name_lower == "think_tool":
200
193
  reflection = args.get("reflection", "")
201
194
  if len(reflection) > 40:
202
- reflection = reflection[:37] + "\u2026"
195
+ reflection = reflection[:37] + "..."
203
196
  return f"think_tool({reflection})"
204
197
 
205
198
  # Default: show first few params
@@ -207,12 +200,12 @@ def format_tool_compact(name: str, args: dict | None) -> str:
207
200
  for k, v in list(args.items())[:2]:
208
201
  v_str = str(v)
209
202
  if len(v_str) > 20:
210
- v_str = v_str[:17] + "\u2026"
203
+ v_str = v_str[:17] + "..."
211
204
  params.append(f"{k}={v_str}")
212
205
 
213
206
  params_str = ", ".join(params)
214
207
  if len(params_str) > 50:
215
- params_str = params_str[:47] + "\u2026"
208
+ params_str = params_str[:47] + "..."
216
209
 
217
210
  return f"{name}({params_str})"
218
211
 
EvoScientist/tools.py CHANGED
@@ -16,16 +16,7 @@ from typing_extensions import Annotated
16
16
 
17
17
  load_dotenv(override=True)
18
18
 
19
- # Lazy initialization - only create client when needed
20
- _tavily_client = None
21
-
22
-
23
- def _get_tavily_client() -> TavilyClient:
24
- """Get or create the Tavily client (lazy initialization)."""
25
- global _tavily_client
26
- if _tavily_client is None:
27
- _tavily_client = TavilyClient()
28
- return _tavily_client
19
+ tavily_client = TavilyClient()
29
20
 
30
21
 
31
22
  async def fetch_webpage_content(url: str, timeout: float = 10.0) -> str:
@@ -76,7 +67,7 @@ async def tavily_search(
76
67
  """
77
68
 
78
69
  def _sync_search() -> dict:
79
- return _get_tavily_client().search(
70
+ return tavily_client.search(
80
71
  query,
81
72
  max_results=max_results,
82
73
  topic=topic,
@@ -115,70 +106,6 @@ async def tavily_search(
115
106
  return f"Search failed: {str(e)}"
116
107
 
117
108
 
118
- @tool(parse_docstring=True)
119
- def skill_manager(
120
- action: Literal["install", "list", "uninstall"],
121
- source: str = "",
122
- name: str = "",
123
- ) -> str:
124
- """Manage user skills: install, list, or uninstall.
125
-
126
- Use this tool when the user asks to:
127
- - Install a skill (action="install", source required)
128
- - List installed skills (action="list")
129
- - Uninstall a skill (action="uninstall", name required)
130
-
131
- Supported sources for install:
132
- - Local path: "./my-skill" or "/path/to/skill"
133
- - GitHub URL: "https://github.com/owner/repo/tree/main/skill-name"
134
- - GitHub shorthand: "owner/repo@skill-name"
135
-
136
- Args:
137
- action: One of "install", "list", or "uninstall"
138
- source: For install - local path or GitHub URL/shorthand
139
- name: For uninstall - skill name to remove
140
-
141
- Returns:
142
- Result message
143
- """
144
- from .skills_manager import install_skill, list_skills, uninstall_skill
145
-
146
- if action == "install":
147
- if not source:
148
- return "Error: 'source' is required for install action"
149
- result = install_skill(source)
150
- if result["success"]:
151
- return (
152
- f"Successfully installed skill: {result['name']}\n"
153
- f"Description: {result.get('description', '(none)')}\n"
154
- f"Path: {result['path']}\n\n"
155
- f"Use load_skill to activate it."
156
- )
157
- else:
158
- return f"Failed to install skill: {result['error']}"
159
-
160
- elif action == "list":
161
- skills = list_skills(include_system=False)
162
- if not skills:
163
- return "No user skills installed. Use action='install' to add skills."
164
- lines = [f"Installed User Skills ({len(skills)}):"]
165
- for skill in skills:
166
- lines.append(f" - {skill.name}: {skill.description}")
167
- return "\n".join(lines)
168
-
169
- elif action == "uninstall":
170
- if not name:
171
- return "Error: 'name' is required for uninstall action"
172
- result = uninstall_skill(name)
173
- if result["success"]:
174
- return f"Successfully uninstalled skill: {name}"
175
- else:
176
- return f"Failed to uninstall skill: {result['error']}"
177
-
178
- else:
179
- return f"Unknown action: {action}. Use 'install', 'list', or 'uninstall'."
180
-
181
-
182
109
  @tool(parse_docstring=True)
183
110
  def think_tool(reflection: str) -> str:
184
111
  """Tool for strategic reflection on research progress and decision-making.