supyagent 0.2.0__tar.gz → 0.2.2__tar.gz
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.
Potentially problematic release.
This version of supyagent might be problematic. Click here for more details.
- {supyagent-0.2.0 → supyagent-0.2.2}/.gitignore +1 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/PKG-INFO +27 -3
- {supyagent-0.2.0 → supyagent-0.2.2}/README.md +26 -2
- supyagent-0.2.2/agents/tester.yaml +23 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/pyproject.toml +1 -1
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/cli/main.py +83 -15
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/llm.py +4 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/tools.py +43 -25
- supyagent-0.2.2/supyagent/default_tools/__init__.py +74 -0
- supyagent-0.2.2/supyagent/default_tools/files.py +439 -0
- supyagent-0.2.2/supyagent/default_tools/shell.py +217 -0
- supyagent-0.2.2/supypowers/__init__.py +1 -0
- supyagent-0.2.2/supypowers/files.py +439 -0
- supyagent-0.2.2/supypowers/shell.py +217 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/LICENSE +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/agents/assistant.yaml +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/agents/coder.yaml +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/agents/planner.yaml +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/agents/researcher.yaml +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/agents/summarizer.yaml +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/agents/writer.yaml +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/plans/initial_plan.md +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/scripts/release.sh +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/sprints/README.md +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_1_foundation.md +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_2_sessions.md +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_3_repl.md +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_4_execution.md +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_5_multiagent.md +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/sprints/sprint_6_polish.md +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/__init__.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/__main__.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/cli/__init__.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/__init__.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/agent.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/config.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/context.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/credentials.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/delegation.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/executor.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/registry.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/core/session_manager.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/models/__init__.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/models/agent_config.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/models/session.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/utils/__init__.py +0 -0
- {supyagent-0.2.0 → supyagent-0.2.2}/supyagent/utils/paths.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: supyagent
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: LLM agents powered by supypowers - build AI agents with tool use, multi-agent orchestration, and secure credential management
|
|
5
5
|
Project-URL: Homepage, https://github.com/ergodic-ai/supyagent
|
|
6
6
|
Project-URL: Documentation, https://github.com/ergodic-ai/supyagent#readme
|
|
@@ -68,6 +68,9 @@ uv pip install supyagent
|
|
|
68
68
|
## Quick Start
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
|
+
# Initialize supyagent (sets up default tools)
|
|
72
|
+
supyagent init
|
|
73
|
+
|
|
71
74
|
# Set up your API key (stored securely)
|
|
72
75
|
supyagent config set ANTHROPIC_API_KEY
|
|
73
76
|
|
|
@@ -229,6 +232,7 @@ delegates:
|
|
|
229
232
|
|
|
230
233
|
| Command | Description |
|
|
231
234
|
|---------|-------------|
|
|
235
|
+
| `supyagent init` | Initialize project with default tools |
|
|
232
236
|
| `supyagent new <name>` | Create a new agent |
|
|
233
237
|
| `supyagent list` | List all agents |
|
|
234
238
|
| `supyagent show <name>` | Show agent details |
|
|
@@ -281,6 +285,25 @@ While chatting, use these commands:
|
|
|
281
285
|
| `/clear` | Clear conversation history |
|
|
282
286
|
| `/quit` | Exit the chat |
|
|
283
287
|
|
|
288
|
+
## Bundled Tools
|
|
289
|
+
|
|
290
|
+
Running `supyagent init` installs these default tools:
|
|
291
|
+
|
|
292
|
+
### Shell (`shell.py`)
|
|
293
|
+
- `run_command` - Execute shell commands
|
|
294
|
+
- `run_script` - Run multi-line bash scripts
|
|
295
|
+
- `which` - Find executable paths
|
|
296
|
+
- `get_env` - Get environment variables
|
|
297
|
+
|
|
298
|
+
### Files (`files.py`)
|
|
299
|
+
- `read_file` / `write_file` - File I/O
|
|
300
|
+
- `list_directory` - List files with glob patterns
|
|
301
|
+
- `copy_file` / `move_file` / `delete_file` - File operations
|
|
302
|
+
- `create_directory` - Create directories
|
|
303
|
+
- `file_info` - Get file metadata
|
|
304
|
+
|
|
305
|
+
You can add your own tools by creating Python files in `supypowers/`.
|
|
306
|
+
|
|
284
307
|
## Project Structure
|
|
285
308
|
|
|
286
309
|
```
|
|
@@ -290,8 +313,9 @@ your-project/
|
|
|
290
313
|
│ ├── planner.yaml
|
|
291
314
|
│ └── researcher.yaml
|
|
292
315
|
├── supypowers/ # Tool definitions (Python)
|
|
293
|
-
│ ├──
|
|
294
|
-
│
|
|
316
|
+
│ ├── shell.py # Shell commands (bundled)
|
|
317
|
+
│ ├── files.py # File operations (bundled)
|
|
318
|
+
│ └── my_tools.py # Your custom tools
|
|
295
319
|
└── .supyagent/ # Runtime data (gitignore this)
|
|
296
320
|
├── sessions/ # Conversation history
|
|
297
321
|
├── credentials/ # Encrypted secrets
|
|
@@ -32,6 +32,9 @@ uv pip install supyagent
|
|
|
32
32
|
## Quick Start
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
+
# Initialize supyagent (sets up default tools)
|
|
36
|
+
supyagent init
|
|
37
|
+
|
|
35
38
|
# Set up your API key (stored securely)
|
|
36
39
|
supyagent config set ANTHROPIC_API_KEY
|
|
37
40
|
|
|
@@ -193,6 +196,7 @@ delegates:
|
|
|
193
196
|
|
|
194
197
|
| Command | Description |
|
|
195
198
|
|---------|-------------|
|
|
199
|
+
| `supyagent init` | Initialize project with default tools |
|
|
196
200
|
| `supyagent new <name>` | Create a new agent |
|
|
197
201
|
| `supyagent list` | List all agents |
|
|
198
202
|
| `supyagent show <name>` | Show agent details |
|
|
@@ -245,6 +249,25 @@ While chatting, use these commands:
|
|
|
245
249
|
| `/clear` | Clear conversation history |
|
|
246
250
|
| `/quit` | Exit the chat |
|
|
247
251
|
|
|
252
|
+
## Bundled Tools
|
|
253
|
+
|
|
254
|
+
Running `supyagent init` installs these default tools:
|
|
255
|
+
|
|
256
|
+
### Shell (`shell.py`)
|
|
257
|
+
- `run_command` - Execute shell commands
|
|
258
|
+
- `run_script` - Run multi-line bash scripts
|
|
259
|
+
- `which` - Find executable paths
|
|
260
|
+
- `get_env` - Get environment variables
|
|
261
|
+
|
|
262
|
+
### Files (`files.py`)
|
|
263
|
+
- `read_file` / `write_file` - File I/O
|
|
264
|
+
- `list_directory` - List files with glob patterns
|
|
265
|
+
- `copy_file` / `move_file` / `delete_file` - File operations
|
|
266
|
+
- `create_directory` - Create directories
|
|
267
|
+
- `file_info` - Get file metadata
|
|
268
|
+
|
|
269
|
+
You can add your own tools by creating Python files in `supypowers/`.
|
|
270
|
+
|
|
248
271
|
## Project Structure
|
|
249
272
|
|
|
250
273
|
```
|
|
@@ -254,8 +277,9 @@ your-project/
|
|
|
254
277
|
│ ├── planner.yaml
|
|
255
278
|
│ └── researcher.yaml
|
|
256
279
|
├── supypowers/ # Tool definitions (Python)
|
|
257
|
-
│ ├──
|
|
258
|
-
│
|
|
280
|
+
│ ├── shell.py # Shell commands (bundled)
|
|
281
|
+
│ ├── files.py # File operations (bundled)
|
|
282
|
+
│ └── my_tools.py # Your custom tools
|
|
259
283
|
└── .supyagent/ # Runtime data (gitignore this)
|
|
260
284
|
├── sessions/ # Conversation history
|
|
261
285
|
├── credentials/ # Encrypted secrets
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: tester
|
|
2
|
+
description: A test agent to verify supyagent works
|
|
3
|
+
version: "1.0"
|
|
4
|
+
type: interactive
|
|
5
|
+
|
|
6
|
+
model:
|
|
7
|
+
provider: openrouter/moonshotai/kimi-k2.5
|
|
8
|
+
temperature: 0.7
|
|
9
|
+
max_tokens: 4096
|
|
10
|
+
|
|
11
|
+
system_prompt: |
|
|
12
|
+
You are a helpful AI assistant for testing supyagent.
|
|
13
|
+
You have access to shell commands and file operations.
|
|
14
|
+
|
|
15
|
+
When asked to do something, use the available tools.
|
|
16
|
+
Be concise in your responses.
|
|
17
|
+
|
|
18
|
+
tools:
|
|
19
|
+
allow:
|
|
20
|
+
- "*"
|
|
21
|
+
|
|
22
|
+
limits:
|
|
23
|
+
max_tool_calls_per_turn: 10
|
|
@@ -21,13 +21,15 @@ from supyagent.core.config import ConfigManager, load_config
|
|
|
21
21
|
from supyagent.core.executor import ExecutionRunner
|
|
22
22
|
from supyagent.core.registry import AgentRegistry
|
|
23
23
|
from supyagent.core.session_manager import SessionManager
|
|
24
|
+
from supyagent.default_tools import install_default_tools, list_default_tools
|
|
24
25
|
from supyagent.models.agent_config import AgentNotFoundError, load_agent_config
|
|
25
26
|
|
|
26
27
|
console = Console()
|
|
28
|
+
console_err = Console(stderr=True)
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
@click.group()
|
|
30
|
-
@click.version_option(version="0.2.
|
|
32
|
+
@click.version_option(version="0.2.2", prog_name="supyagent")
|
|
31
33
|
def cli():
|
|
32
34
|
"""
|
|
33
35
|
Supyagent - LLM agents powered by supypowers.
|
|
@@ -37,12 +39,82 @@ def cli():
|
|
|
37
39
|
Quick start:
|
|
38
40
|
|
|
39
41
|
\b
|
|
42
|
+
supyagent init # Set up default tools
|
|
43
|
+
supyagent config set # Configure API keys
|
|
40
44
|
supyagent new myagent # Create an agent
|
|
41
45
|
supyagent chat myagent # Start chatting
|
|
42
46
|
"""
|
|
43
47
|
pass
|
|
44
48
|
|
|
45
49
|
|
|
50
|
+
@cli.command()
|
|
51
|
+
@click.option(
|
|
52
|
+
"--tools-dir",
|
|
53
|
+
"-t",
|
|
54
|
+
default="supypowers",
|
|
55
|
+
help="Directory for tools (default: supypowers/)",
|
|
56
|
+
)
|
|
57
|
+
@click.option(
|
|
58
|
+
"--force",
|
|
59
|
+
"-f",
|
|
60
|
+
is_flag=True,
|
|
61
|
+
help="Overwrite existing files",
|
|
62
|
+
)
|
|
63
|
+
def init(tools_dir: str, force: bool):
|
|
64
|
+
"""
|
|
65
|
+
Initialize supyagent in the current directory.
|
|
66
|
+
|
|
67
|
+
This sets up:
|
|
68
|
+
- Default tools in supypowers/ (shell commands, file operations)
|
|
69
|
+
- agents/ directory for agent configurations
|
|
70
|
+
|
|
71
|
+
\b
|
|
72
|
+
Examples:
|
|
73
|
+
supyagent init
|
|
74
|
+
supyagent init --tools-dir my_tools
|
|
75
|
+
"""
|
|
76
|
+
console.print("[bold]Initializing supyagent...[/bold]")
|
|
77
|
+
console.print()
|
|
78
|
+
|
|
79
|
+
# Create agents directory
|
|
80
|
+
agents_dir = Path("agents")
|
|
81
|
+
if not agents_dir.exists():
|
|
82
|
+
agents_dir.mkdir(parents=True)
|
|
83
|
+
console.print(f" [green]✓[/green] Created {agents_dir}/")
|
|
84
|
+
else:
|
|
85
|
+
console.print(f" [dim]○[/dim] {agents_dir}/ already exists")
|
|
86
|
+
|
|
87
|
+
# Install default tools
|
|
88
|
+
tools_path = Path(tools_dir)
|
|
89
|
+
|
|
90
|
+
if force:
|
|
91
|
+
# Remove and reinstall
|
|
92
|
+
import shutil
|
|
93
|
+
|
|
94
|
+
if tools_path.exists():
|
|
95
|
+
shutil.rmtree(tools_path)
|
|
96
|
+
|
|
97
|
+
if tools_path.exists() and any(tools_path.glob("*.py")):
|
|
98
|
+
console.print(f" [dim]○[/dim] {tools_dir}/ already has tools")
|
|
99
|
+
else:
|
|
100
|
+
count = install_default_tools(tools_path)
|
|
101
|
+
console.print(
|
|
102
|
+
f" [green]✓[/green] Installed {count} default tools to {tools_dir}/"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Show available tools
|
|
106
|
+
console.print()
|
|
107
|
+
console.print("[bold]Available tools:[/bold]")
|
|
108
|
+
for tool in list_default_tools():
|
|
109
|
+
console.print(f" • [cyan]{tool['name']}[/cyan]: {tool['description']}")
|
|
110
|
+
|
|
111
|
+
console.print()
|
|
112
|
+
console.print("[bold]Next steps:[/bold]")
|
|
113
|
+
console.print(" 1. Configure your API key: [cyan]supyagent config set[/cyan]")
|
|
114
|
+
console.print(" 2. Create an agent: [cyan]supyagent new myagent[/cyan]")
|
|
115
|
+
console.print(" 3. Start chatting: [cyan]supyagent chat myagent[/cyan]")
|
|
116
|
+
|
|
117
|
+
|
|
46
118
|
@cli.command()
|
|
47
119
|
@click.argument("name")
|
|
48
120
|
@click.option(
|
|
@@ -636,15 +708,14 @@ def run(
|
|
|
636
708
|
try:
|
|
637
709
|
config = load_agent_config(agent_name)
|
|
638
710
|
except AgentNotFoundError as e:
|
|
639
|
-
|
|
711
|
+
console_err.print(f"[red]Error:[/red] {e}")
|
|
640
712
|
sys.exit(1)
|
|
641
713
|
|
|
642
714
|
# Warn if using interactive agent in execution mode
|
|
643
715
|
if config.type != "execution" and not quiet:
|
|
644
|
-
|
|
716
|
+
console_err.print(
|
|
645
717
|
f"[yellow]Note:[/yellow] '{agent_name}' is an interactive agent. "
|
|
646
|
-
"Consider using 'chat' for interactive use."
|
|
647
|
-
err=True,
|
|
718
|
+
"Consider using 'chat' for interactive use."
|
|
648
719
|
)
|
|
649
720
|
|
|
650
721
|
# Parse secrets
|
|
@@ -659,9 +730,7 @@ def run(
|
|
|
659
730
|
else:
|
|
660
731
|
input_path = Path(input_file)
|
|
661
732
|
if not input_path.exists():
|
|
662
|
-
|
|
663
|
-
f"[red]Error:[/red] File not found: {input_file}", err=True
|
|
664
|
-
)
|
|
733
|
+
console_err.print(f"[red]Error:[/red] File not found: {input_file}")
|
|
665
734
|
sys.exit(1)
|
|
666
735
|
task_content = input_path.read_text().strip()
|
|
667
736
|
elif task:
|
|
@@ -675,22 +744,21 @@ def run(
|
|
|
675
744
|
if not sys.stdin.isatty():
|
|
676
745
|
task_content = sys.stdin.read().strip()
|
|
677
746
|
else:
|
|
678
|
-
|
|
747
|
+
console_err.print(
|
|
679
748
|
"[red]Error:[/red] No task provided. "
|
|
680
|
-
"Use positional argument, --input, or pipe to stdin."
|
|
681
|
-
err=True,
|
|
749
|
+
"Use positional argument, --input, or pipe to stdin."
|
|
682
750
|
)
|
|
683
751
|
sys.exit(1)
|
|
684
752
|
|
|
685
753
|
if not task_content:
|
|
686
|
-
|
|
754
|
+
console_err.print("[red]Error:[/red] Empty task")
|
|
687
755
|
sys.exit(1)
|
|
688
756
|
|
|
689
757
|
# Run the agent
|
|
690
758
|
runner = ExecutionRunner(config)
|
|
691
759
|
|
|
692
760
|
if not quiet:
|
|
693
|
-
|
|
761
|
+
console_err.print(f"[dim]Running {agent_name}...[/dim]")
|
|
694
762
|
|
|
695
763
|
result = runner.run(task_content, secrets=secrets_dict, output_format=output_format)
|
|
696
764
|
|
|
@@ -700,7 +768,7 @@ def run(
|
|
|
700
768
|
elif result["ok"]:
|
|
701
769
|
click.echo(result["data"])
|
|
702
770
|
else:
|
|
703
|
-
|
|
771
|
+
console_err.print(f"[red]Error:[/red] {result['error']}")
|
|
704
772
|
sys.exit(1)
|
|
705
773
|
|
|
706
774
|
|
|
@@ -756,7 +824,7 @@ def batch(
|
|
|
756
824
|
try:
|
|
757
825
|
config = load_agent_config(agent_name)
|
|
758
826
|
except AgentNotFoundError as e:
|
|
759
|
-
|
|
827
|
+
console_err.print(f"[red]Error:[/red] {e}")
|
|
760
828
|
sys.exit(1)
|
|
761
829
|
|
|
762
830
|
# Parse secrets
|
|
@@ -4,9 +4,13 @@ LiteLLM wrapper for unified LLM access.
|
|
|
4
4
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
+
import litellm
|
|
7
8
|
from litellm import completion
|
|
8
9
|
from litellm.types.utils import ModelResponse
|
|
9
10
|
|
|
11
|
+
# Suppress LiteLLM debug messages (e.g., "Provider List: ...")
|
|
12
|
+
litellm.suppress_debug_info = True
|
|
13
|
+
|
|
10
14
|
|
|
11
15
|
class LLMClient:
|
|
12
16
|
"""
|
|
@@ -193,43 +193,61 @@ def supypowers_to_openai_tools(sp_tools: list[dict[str, Any]]) -> list[dict[str,
|
|
|
193
193
|
Convert supypowers tool definitions to OpenAI function calling format.
|
|
194
194
|
|
|
195
195
|
Supypowers docs output format:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
[
|
|
197
|
+
{
|
|
198
|
+
"script": "/path/to/script.py",
|
|
199
|
+
"functions": [
|
|
200
|
+
{
|
|
201
|
+
"name": "function_name",
|
|
202
|
+
"description": "...",
|
|
203
|
+
"input_schema": {...}
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
]
|
|
202
208
|
|
|
203
209
|
OpenAI format:
|
|
204
210
|
{
|
|
205
211
|
"type": "function",
|
|
206
212
|
"function": {
|
|
207
|
-
"name": "
|
|
213
|
+
"name": "script__function_name",
|
|
208
214
|
"description": "...",
|
|
209
215
|
"parameters": {...}
|
|
210
216
|
}
|
|
211
217
|
}
|
|
212
218
|
"""
|
|
219
|
+
import os
|
|
220
|
+
|
|
213
221
|
openai_tools = []
|
|
214
222
|
|
|
215
|
-
for
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
223
|
+
for script_entry in sp_tools:
|
|
224
|
+
script_path = script_entry.get("script", "")
|
|
225
|
+
functions = script_entry.get("functions", [])
|
|
226
|
+
|
|
227
|
+
# Extract script name from path (e.g., "files" from "/path/to/files.py")
|
|
228
|
+
script_name = os.path.splitext(os.path.basename(script_path))[0]
|
|
229
|
+
|
|
230
|
+
# Skip __init__ files with no functions
|
|
231
|
+
if script_name == "__init__" and not functions:
|
|
232
|
+
continue
|
|
233
|
+
|
|
234
|
+
for func_def in functions:
|
|
235
|
+
func_name = func_def.get("name", "")
|
|
236
|
+
description = func_def.get("description", "No description")
|
|
237
|
+
input_schema = func_def.get("input_schema", {"type": "object", "properties": {}})
|
|
238
|
+
|
|
239
|
+
# Use double underscore to join script:function (since : isn't allowed in function names)
|
|
240
|
+
name = f"{script_name}__{func_name}"
|
|
241
|
+
|
|
242
|
+
openai_tool = {
|
|
243
|
+
"type": "function",
|
|
244
|
+
"function": {
|
|
245
|
+
"name": name,
|
|
246
|
+
"description": description,
|
|
247
|
+
"parameters": input_schema,
|
|
248
|
+
},
|
|
249
|
+
}
|
|
232
250
|
|
|
233
|
-
|
|
251
|
+
openai_tools.append(openai_tool)
|
|
234
252
|
|
|
235
253
|
return openai_tools
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Default supypowers tools bundled with supyagent.
|
|
3
|
+
|
|
4
|
+
These tools are copied to the user's project when running `supyagent init`.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import shutil
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
# Path to the bundled default tools
|
|
11
|
+
TOOLS_DIR = Path(__file__).parent
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_bundled_tools() -> list[Path]:
|
|
15
|
+
"""Get list of bundled tool files."""
|
|
16
|
+
return [f for f in TOOLS_DIR.glob("*.py") if f.name != "__init__.py"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def install_default_tools(target_dir: Path | str = "supypowers") -> int:
|
|
20
|
+
"""
|
|
21
|
+
Install default tools to a target directory.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
target_dir: Directory to install tools to (default: supypowers/)
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Number of files installed
|
|
28
|
+
"""
|
|
29
|
+
target = Path(target_dir)
|
|
30
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
|
|
32
|
+
installed = 0
|
|
33
|
+
for tool_file in get_bundled_tools():
|
|
34
|
+
dest = target / tool_file.name
|
|
35
|
+
if not dest.exists():
|
|
36
|
+
shutil.copy(tool_file, dest)
|
|
37
|
+
installed += 1
|
|
38
|
+
|
|
39
|
+
# Create __init__.py if not exists
|
|
40
|
+
init_file = target / "__init__.py"
|
|
41
|
+
if not init_file.exists():
|
|
42
|
+
init_file.write_text('"""Supypowers tools for this project."""\n')
|
|
43
|
+
installed += 1
|
|
44
|
+
|
|
45
|
+
return installed
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def list_default_tools() -> list[dict]:
|
|
49
|
+
"""
|
|
50
|
+
List available default tools.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
List of tool info dicts
|
|
54
|
+
"""
|
|
55
|
+
tools = []
|
|
56
|
+
for tool_file in get_bundled_tools():
|
|
57
|
+
# Read first docstring
|
|
58
|
+
content = tool_file.read_text()
|
|
59
|
+
description = ""
|
|
60
|
+
if '"""' in content:
|
|
61
|
+
start = content.find('"""') + 3
|
|
62
|
+
end = content.find('"""', start)
|
|
63
|
+
if end > start:
|
|
64
|
+
description = content[start:end].strip().split("\n")[0]
|
|
65
|
+
|
|
66
|
+
tools.append(
|
|
67
|
+
{
|
|
68
|
+
"name": tool_file.stem,
|
|
69
|
+
"file": tool_file.name,
|
|
70
|
+
"description": description,
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return tools
|