runagents 0.2.0__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.
- runagents-0.2.0/PKG-INFO +150 -0
- runagents-0.2.0/README.md +123 -0
- runagents-0.2.0/pyproject.toml +50 -0
- runagents-0.2.0/runagents/__init__.py +8 -0
- runagents-0.2.0/runagents/agent.py +143 -0
- runagents-0.2.0/runagents/cli/__init__.py +1 -0
- runagents-0.2.0/runagents/cli/binary.py +121 -0
- runagents-0.2.0/runagents/cli/dev_cmd.py +308 -0
- runagents-0.2.0/runagents/cli/init_cmd.py +57 -0
- runagents-0.2.0/runagents/cli/main.py +71 -0
- runagents-0.2.0/runagents/cli/templates/agent.py.tmpl +21 -0
- runagents-0.2.0/runagents/cli/templates/agents_md.tmpl +22 -0
- runagents-0.2.0/runagents/cli/templates/claude_md.tmpl +37 -0
- runagents-0.2.0/runagents/cli/templates/cursorrules.tmpl +14 -0
- runagents-0.2.0/runagents/cli/templates/gitignore.tmpl +8 -0
- runagents-0.2.0/runagents/cli/templates/mcp_json.tmpl +9 -0
- runagents-0.2.0/runagents/cli/templates/requirements.txt.tmpl +1 -0
- runagents-0.2.0/runagents/cli/templates/runagents.yaml.tmpl +7 -0
- runagents-0.2.0/runagents/client.py +253 -0
- runagents-0.2.0/runagents/config.py +70 -0
- runagents-0.2.0/runagents/mcp/__init__.py +5 -0
- runagents-0.2.0/runagents/mcp/__main__.py +5 -0
- runagents-0.2.0/runagents/mcp/server.py +254 -0
- runagents-0.2.0/runagents/runtime.py +1163 -0
- runagents-0.2.0/runagents/types.py +166 -0
- runagents-0.2.0/runagents.egg-info/PKG-INFO +150 -0
- runagents-0.2.0/runagents.egg-info/SOURCES.txt +36 -0
- runagents-0.2.0/runagents.egg-info/dependency_links.txt +1 -0
- runagents-0.2.0/runagents.egg-info/entry_points.txt +3 -0
- runagents-0.2.0/runagents.egg-info/requires.txt +6 -0
- runagents-0.2.0/runagents.egg-info/top_level.txt +2 -0
- runagents-0.2.0/runagents_runtime.py +13 -0
- runagents-0.2.0/setup.cfg +4 -0
- runagents-0.2.0/tests/test_agent.py +69 -0
- runagents-0.2.0/tests/test_cli_init.py +47 -0
- runagents-0.2.0/tests/test_client.py +63 -0
- runagents-0.2.0/tests/test_config.py +87 -0
- runagents-0.2.0/tests/test_types.py +77 -0
runagents-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: runagents
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: RunAgents Python SDK — deploy, manage, and build AI agents
|
|
5
|
+
Author-email: RunAgents <try@runagents.io>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://runagents.io
|
|
8
|
+
Project-URL: Documentation, https://docs.runagents.io
|
|
9
|
+
Project-URL: Repository, https://github.com/runagents-io/runagents
|
|
10
|
+
Project-URL: Issues, https://github.com/runagents-io/runagents/issues
|
|
11
|
+
Keywords: ai,agents,runagents,llm,tool-calling
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Provides-Extra: mcp
|
|
24
|
+
Requires-Dist: mcp>=1.0; extra == "mcp"
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: watchdog>=3.0; extra == "dev"
|
|
27
|
+
|
|
28
|
+
# RunAgents Python SDK
|
|
29
|
+
|
|
30
|
+
Everything you need to build, test, and deploy AI agents on [RunAgents](https://runagents.io) — in one package.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install runagents # Core SDK + CLI + runtime (zero deps)
|
|
36
|
+
pip install runagents[mcp] # + MCP server for AI coding assistants
|
|
37
|
+
pip install runagents[dev] # + hot-reload for local dev
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
runagents init my-agent # Scaffold a project
|
|
44
|
+
cd my-agent
|
|
45
|
+
runagents dev # Local dev server with mock tools
|
|
46
|
+
runagents deploy # Ship to the platform
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Python API
|
|
50
|
+
|
|
51
|
+
### Client — manage platform resources
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from runagents import Client
|
|
55
|
+
|
|
56
|
+
client = Client() # reads ~/.runagents/config.json + env vars
|
|
57
|
+
|
|
58
|
+
agents = client.agents.list()
|
|
59
|
+
tools = client.tools.list()
|
|
60
|
+
runs = client.runs.list(agent="payment-agent")
|
|
61
|
+
|
|
62
|
+
result = client.agents.deploy(
|
|
63
|
+
name="my-agent",
|
|
64
|
+
source_files={"agent.py": open("agent.py").read()},
|
|
65
|
+
required_tools=["stripe-api"],
|
|
66
|
+
llm_configs=[{"provider": "openai", "model": "gpt-4o-mini", "role": "default"}],
|
|
67
|
+
)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Agent — write agent code
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from runagents import Agent, tool
|
|
74
|
+
|
|
75
|
+
agent = Agent() # reads TOOL_URL_*, LLM_GATEWAY_URL, LLM_MODEL from env
|
|
76
|
+
|
|
77
|
+
# Call a platform tool (mesh handles auth)
|
|
78
|
+
result = agent.call_tool("echo-tool", "/echo", {"message": "hello"})
|
|
79
|
+
|
|
80
|
+
# Chat via LLM gateway
|
|
81
|
+
response = agent.chat("What is 2+2?", tools=[...])
|
|
82
|
+
|
|
83
|
+
# Custom handler (Tier 2)
|
|
84
|
+
def handler(request, ctx):
|
|
85
|
+
message = request["message"]
|
|
86
|
+
result = agent.chat(message)
|
|
87
|
+
return result["choices"][0]["message"]["content"]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### @tool decorator
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from runagents import tool
|
|
94
|
+
|
|
95
|
+
@tool(name="calculator", description="Evaluate math expressions")
|
|
96
|
+
def calculate(expression: str) -> str:
|
|
97
|
+
return str(eval(expression))
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## CLI Commands
|
|
101
|
+
|
|
102
|
+
| Command | Description |
|
|
103
|
+
|---------|-------------|
|
|
104
|
+
| `runagents init [name]` | Scaffold a new agent project |
|
|
105
|
+
| `runagents dev` | Start local dev server with mock tools |
|
|
106
|
+
| `runagents deploy` | Deploy an agent (delegates to Go CLI) |
|
|
107
|
+
| `runagents agents list` | List agents |
|
|
108
|
+
| `runagents tools list` | List tools |
|
|
109
|
+
| `runagents runs list` | List runs |
|
|
110
|
+
| `runagents config` | Manage configuration |
|
|
111
|
+
|
|
112
|
+
## Runtime
|
|
113
|
+
|
|
114
|
+
The runtime provides the HTTP server for deployed agents — tool calling loop, SSE streaming, health checks, OAuth consent, and JIT approvals. It runs automatically inside the platform; for local development use `runagents dev`.
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
# Backward compatible — still works
|
|
118
|
+
import runagents_runtime
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## MCP Server
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install runagents[mcp]
|
|
125
|
+
runagents-mcp # starts on stdio
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
14 tools for AI coding assistants (Claude Code, Cursor, Codex). See [AI Assistant Setup](https://docs.runagents.io/cli/ai-assistant-setup/).
|
|
129
|
+
|
|
130
|
+
## Configuration
|
|
131
|
+
|
|
132
|
+
Reads from `~/.runagents/config.json` with env var overrides:
|
|
133
|
+
|
|
134
|
+
| Variable | Description | Default |
|
|
135
|
+
|----------|-------------|---------|
|
|
136
|
+
| `RUNAGENTS_ENDPOINT` | Platform API URL | `http://localhost:8092` |
|
|
137
|
+
| `RUNAGENTS_API_KEY` | API key or workspace key | — |
|
|
138
|
+
| `RUNAGENTS_NAMESPACE` | Target namespace | `default` |
|
|
139
|
+
|
|
140
|
+
## Documentation
|
|
141
|
+
|
|
142
|
+
- [RunAgents Docs](https://docs.runagents.io)
|
|
143
|
+
- [Writing Agents](https://docs.runagents.io/getting-started/writing-agents/)
|
|
144
|
+
- [Agent Runtime](https://docs.runagents.io/platform/agent-runtime/)
|
|
145
|
+
- [AI Assistant Setup](https://docs.runagents.io/cli/ai-assistant-setup/)
|
|
146
|
+
- [CLI Commands](https://docs.runagents.io/cli/commands/)
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
Apache-2.0
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# RunAgents Python SDK
|
|
2
|
+
|
|
3
|
+
Everything you need to build, test, and deploy AI agents on [RunAgents](https://runagents.io) — in one package.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install runagents # Core SDK + CLI + runtime (zero deps)
|
|
9
|
+
pip install runagents[mcp] # + MCP server for AI coding assistants
|
|
10
|
+
pip install runagents[dev] # + hot-reload for local dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
runagents init my-agent # Scaffold a project
|
|
17
|
+
cd my-agent
|
|
18
|
+
runagents dev # Local dev server with mock tools
|
|
19
|
+
runagents deploy # Ship to the platform
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Python API
|
|
23
|
+
|
|
24
|
+
### Client — manage platform resources
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from runagents import Client
|
|
28
|
+
|
|
29
|
+
client = Client() # reads ~/.runagents/config.json + env vars
|
|
30
|
+
|
|
31
|
+
agents = client.agents.list()
|
|
32
|
+
tools = client.tools.list()
|
|
33
|
+
runs = client.runs.list(agent="payment-agent")
|
|
34
|
+
|
|
35
|
+
result = client.agents.deploy(
|
|
36
|
+
name="my-agent",
|
|
37
|
+
source_files={"agent.py": open("agent.py").read()},
|
|
38
|
+
required_tools=["stripe-api"],
|
|
39
|
+
llm_configs=[{"provider": "openai", "model": "gpt-4o-mini", "role": "default"}],
|
|
40
|
+
)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Agent — write agent code
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from runagents import Agent, tool
|
|
47
|
+
|
|
48
|
+
agent = Agent() # reads TOOL_URL_*, LLM_GATEWAY_URL, LLM_MODEL from env
|
|
49
|
+
|
|
50
|
+
# Call a platform tool (mesh handles auth)
|
|
51
|
+
result = agent.call_tool("echo-tool", "/echo", {"message": "hello"})
|
|
52
|
+
|
|
53
|
+
# Chat via LLM gateway
|
|
54
|
+
response = agent.chat("What is 2+2?", tools=[...])
|
|
55
|
+
|
|
56
|
+
# Custom handler (Tier 2)
|
|
57
|
+
def handler(request, ctx):
|
|
58
|
+
message = request["message"]
|
|
59
|
+
result = agent.chat(message)
|
|
60
|
+
return result["choices"][0]["message"]["content"]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### @tool decorator
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from runagents import tool
|
|
67
|
+
|
|
68
|
+
@tool(name="calculator", description="Evaluate math expressions")
|
|
69
|
+
def calculate(expression: str) -> str:
|
|
70
|
+
return str(eval(expression))
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## CLI Commands
|
|
74
|
+
|
|
75
|
+
| Command | Description |
|
|
76
|
+
|---------|-------------|
|
|
77
|
+
| `runagents init [name]` | Scaffold a new agent project |
|
|
78
|
+
| `runagents dev` | Start local dev server with mock tools |
|
|
79
|
+
| `runagents deploy` | Deploy an agent (delegates to Go CLI) |
|
|
80
|
+
| `runagents agents list` | List agents |
|
|
81
|
+
| `runagents tools list` | List tools |
|
|
82
|
+
| `runagents runs list` | List runs |
|
|
83
|
+
| `runagents config` | Manage configuration |
|
|
84
|
+
|
|
85
|
+
## Runtime
|
|
86
|
+
|
|
87
|
+
The runtime provides the HTTP server for deployed agents — tool calling loop, SSE streaming, health checks, OAuth consent, and JIT approvals. It runs automatically inside the platform; for local development use `runagents dev`.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
# Backward compatible — still works
|
|
91
|
+
import runagents_runtime
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## MCP Server
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install runagents[mcp]
|
|
98
|
+
runagents-mcp # starts on stdio
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
14 tools for AI coding assistants (Claude Code, Cursor, Codex). See [AI Assistant Setup](https://docs.runagents.io/cli/ai-assistant-setup/).
|
|
102
|
+
|
|
103
|
+
## Configuration
|
|
104
|
+
|
|
105
|
+
Reads from `~/.runagents/config.json` with env var overrides:
|
|
106
|
+
|
|
107
|
+
| Variable | Description | Default |
|
|
108
|
+
|----------|-------------|---------|
|
|
109
|
+
| `RUNAGENTS_ENDPOINT` | Platform API URL | `http://localhost:8092` |
|
|
110
|
+
| `RUNAGENTS_API_KEY` | API key or workspace key | — |
|
|
111
|
+
| `RUNAGENTS_NAMESPACE` | Target namespace | `default` |
|
|
112
|
+
|
|
113
|
+
## Documentation
|
|
114
|
+
|
|
115
|
+
- [RunAgents Docs](https://docs.runagents.io)
|
|
116
|
+
- [Writing Agents](https://docs.runagents.io/getting-started/writing-agents/)
|
|
117
|
+
- [Agent Runtime](https://docs.runagents.io/platform/agent-runtime/)
|
|
118
|
+
- [AI Assistant Setup](https://docs.runagents.io/cli/ai-assistant-setup/)
|
|
119
|
+
- [CLI Commands](https://docs.runagents.io/cli/commands/)
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
Apache-2.0
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "runagents"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "RunAgents Python SDK — deploy, manage, and build AI agents"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "Apache-2.0"}
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "RunAgents", email = "try@runagents.io"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["ai", "agents", "runagents", "llm", "tool-calling"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: Apache Software License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
26
|
+
]
|
|
27
|
+
dependencies = []
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
mcp = ["mcp>=1.0"]
|
|
31
|
+
dev = ["watchdog>=3.0"]
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
runagents = "runagents.cli.main:main"
|
|
35
|
+
runagents-mcp = "runagents.mcp:main"
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://runagents.io"
|
|
39
|
+
Documentation = "https://docs.runagents.io"
|
|
40
|
+
Repository = "https://github.com/runagents-io/runagents"
|
|
41
|
+
Issues = "https://github.com/runagents-io/runagents/issues"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.packages.find]
|
|
44
|
+
include = ["runagents*"]
|
|
45
|
+
|
|
46
|
+
[tool.setuptools]
|
|
47
|
+
py-modules = ["runagents_runtime"]
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.package-data]
|
|
50
|
+
"runagents.cli" = ["templates/*"]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""RunAgents — Python SDK for the RunAgents AI agent platform."""
|
|
2
|
+
|
|
3
|
+
from runagents.client import Client
|
|
4
|
+
from runagents.agent import Agent, tool
|
|
5
|
+
from runagents.runtime import RunContext
|
|
6
|
+
|
|
7
|
+
__version__ = "0.2.0"
|
|
8
|
+
__all__ = ["Client", "Agent", "tool", "RunContext"]
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Agent SDK for writing RunAgents agents.
|
|
2
|
+
|
|
3
|
+
Provides a high-level Agent class that reads operator-injected env vars
|
|
4
|
+
and exposes ``call_tool()`` and ``chat()`` helpers. Stdlib only.
|
|
5
|
+
|
|
6
|
+
Usage::
|
|
7
|
+
|
|
8
|
+
from runagents import Agent
|
|
9
|
+
|
|
10
|
+
agent = Agent()
|
|
11
|
+
result = agent.chat("What is 2+2?", tools=[...])
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import functools
|
|
19
|
+
from typing import Any, Callable
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Agent:
|
|
23
|
+
"""High-level agent helper that reads operator-injected env vars.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
system_prompt: The agent's system prompt.
|
|
27
|
+
model: LLM model name (e.g. ``gpt-4o-mini``).
|
|
28
|
+
llm_gateway_url: URL for the LLM gateway chat completions endpoint.
|
|
29
|
+
tool_urls: Mapping of tool name → base URL.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
self.system_prompt = os.environ.get("SYSTEM_PROMPT", "You are a helpful assistant.")
|
|
34
|
+
self.model = os.environ.get("LLM_MODEL", "gpt-4o-mini")
|
|
35
|
+
self.llm_gateway_url = os.environ.get(
|
|
36
|
+
"LLM_GATEWAY_URL",
|
|
37
|
+
"http://llm-gateway.agent-system.svc:8080/v1/chat/completions",
|
|
38
|
+
)
|
|
39
|
+
self.tool_urls: dict[str, str] = {}
|
|
40
|
+
for key, val in os.environ.items():
|
|
41
|
+
if key.startswith("TOOL_URL_"):
|
|
42
|
+
name = key[len("TOOL_URL_"):].lower().replace("_", "-")
|
|
43
|
+
self.tool_urls[name] = val
|
|
44
|
+
|
|
45
|
+
def call_tool(
|
|
46
|
+
self,
|
|
47
|
+
name: str,
|
|
48
|
+
path: str = "/",
|
|
49
|
+
payload: dict | None = None,
|
|
50
|
+
method: str = "POST",
|
|
51
|
+
) -> dict:
|
|
52
|
+
"""Call a platform tool by name.
|
|
53
|
+
|
|
54
|
+
The Istio mesh handles auth and policy — this just makes the HTTP call.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
name: Tool name (must be in ``self.tool_urls``).
|
|
58
|
+
path: HTTP path on the tool (default ``/``).
|
|
59
|
+
payload: JSON body (for POST/PUT/PATCH).
|
|
60
|
+
method: HTTP method.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Parsed JSON response.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
KeyError: If tool name is not found in env vars.
|
|
67
|
+
"""
|
|
68
|
+
from runagents.runtime import execute_tool_call
|
|
69
|
+
|
|
70
|
+
base = self.tool_urls.get(name)
|
|
71
|
+
if base is None:
|
|
72
|
+
env_key = "TOOL_URL_" + name.upper().replace("-", "_")
|
|
73
|
+
raise KeyError(f"Tool {name!r} not found. Set {env_key} or check requiredTools.")
|
|
74
|
+
|
|
75
|
+
url = base.rstrip("/") + path
|
|
76
|
+
body = json.dumps(payload) if payload else None
|
|
77
|
+
result_str = execute_tool_call(method, url, body=body)
|
|
78
|
+
try:
|
|
79
|
+
return json.loads(result_str)
|
|
80
|
+
except (json.JSONDecodeError, TypeError):
|
|
81
|
+
return {"raw": result_str}
|
|
82
|
+
|
|
83
|
+
def chat(
|
|
84
|
+
self,
|
|
85
|
+
message: str,
|
|
86
|
+
tools: list[dict] | None = None,
|
|
87
|
+
history: list[dict] | None = None,
|
|
88
|
+
) -> dict:
|
|
89
|
+
"""Send a chat completion request through the LLM gateway.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
message: User message.
|
|
93
|
+
tools: OpenAI-format tool definitions (optional).
|
|
94
|
+
history: Prior conversation messages (optional).
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Full OpenAI-format response dict.
|
|
98
|
+
"""
|
|
99
|
+
from runagents.runtime import call_llm
|
|
100
|
+
|
|
101
|
+
messages = list(history) if history else []
|
|
102
|
+
messages.append({"role": "user", "content": message})
|
|
103
|
+
|
|
104
|
+
return call_llm(
|
|
105
|
+
messages,
|
|
106
|
+
gateway_url=self.llm_gateway_url,
|
|
107
|
+
model=self.model,
|
|
108
|
+
system_prompt=self.system_prompt if not history else None,
|
|
109
|
+
tools=tools,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def tool(fn: Callable | None = None, *, name: str | None = None, description: str = "") -> Any:
|
|
114
|
+
"""Decorator to mark a function as a tool handler.
|
|
115
|
+
|
|
116
|
+
The decorated function gains ``.tool_name`` and ``.tool_description``
|
|
117
|
+
attributes for discovery by frameworks.
|
|
118
|
+
|
|
119
|
+
Usage::
|
|
120
|
+
|
|
121
|
+
@tool
|
|
122
|
+
def calculator(expression: str) -> str:
|
|
123
|
+
return str(eval(expression))
|
|
124
|
+
|
|
125
|
+
@tool(name="weather-lookup", description="Get current weather")
|
|
126
|
+
def weather(city: str) -> dict:
|
|
127
|
+
...
|
|
128
|
+
"""
|
|
129
|
+
def decorator(f: Callable) -> Callable:
|
|
130
|
+
f.tool_name = name or f.__name__.replace("_", "-") # type: ignore[attr-defined]
|
|
131
|
+
f.tool_description = description or (f.__doc__ or "").strip().split("\n")[0] # type: ignore[attr-defined]
|
|
132
|
+
|
|
133
|
+
@functools.wraps(f)
|
|
134
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
135
|
+
return f(*args, **kwargs)
|
|
136
|
+
|
|
137
|
+
wrapper.tool_name = f.tool_name # type: ignore[attr-defined]
|
|
138
|
+
wrapper.tool_description = f.tool_description # type: ignore[attr-defined]
|
|
139
|
+
return wrapper
|
|
140
|
+
|
|
141
|
+
if fn is not None:
|
|
142
|
+
return decorator(fn)
|
|
143
|
+
return decorator
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""RunAgents CLI — Python entry point that delegates to Go binary."""
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Go CLI binary downloader — mirrors cli/npm/install.js logic.
|
|
2
|
+
|
|
3
|
+
Downloads from S3, verifies SHA256, caches at ~/.runagents/bin/.
|
|
4
|
+
Stdlib only: urllib.request, tarfile, hashlib, platform.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import hashlib
|
|
8
|
+
import os
|
|
9
|
+
import platform
|
|
10
|
+
import shutil
|
|
11
|
+
import stat
|
|
12
|
+
import sys
|
|
13
|
+
import tarfile
|
|
14
|
+
import tempfile
|
|
15
|
+
import urllib.error
|
|
16
|
+
import urllib.request
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
CLI_VERSION = "1.0.6"
|
|
20
|
+
S3_BASE = "https://runagents-releases.s3.amazonaws.com/cli"
|
|
21
|
+
|
|
22
|
+
PLATFORM_MAP = {"Darwin": "darwin", "Linux": "linux", "Windows": "windows"}
|
|
23
|
+
ARCH_MAP = {"x86_64": "amd64", "AMD64": "amd64", "arm64": "arm64", "aarch64": "arm64"}
|
|
24
|
+
|
|
25
|
+
_BIN_DIR = Path.home() / ".runagents" / "bin"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def ensure_binary(version: str = CLI_VERSION) -> Path | None:
|
|
29
|
+
"""Return path to Go binary, downloading if needed. Returns None on failure."""
|
|
30
|
+
# 1. Check cached binary
|
|
31
|
+
cached = _BIN_DIR / f"runagents-{version}"
|
|
32
|
+
if cached.exists() and os.access(cached, os.X_OK):
|
|
33
|
+
return cached
|
|
34
|
+
|
|
35
|
+
# 2. Check PATH
|
|
36
|
+
on_path = shutil.which("runagents")
|
|
37
|
+
if on_path:
|
|
38
|
+
return Path(on_path)
|
|
39
|
+
|
|
40
|
+
# 3. Download
|
|
41
|
+
try:
|
|
42
|
+
return _download(version)
|
|
43
|
+
except Exception as e:
|
|
44
|
+
print(f"Warning: could not download CLI binary: {e}", file=sys.stderr)
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _download(version: str) -> Path:
|
|
49
|
+
plat = PLATFORM_MAP.get(platform.system())
|
|
50
|
+
arch = ARCH_MAP.get(platform.machine())
|
|
51
|
+
if not plat or not arch:
|
|
52
|
+
raise RuntimeError(f"Unsupported platform: {platform.system()}/{platform.machine()}")
|
|
53
|
+
|
|
54
|
+
ext = ".zip" if plat == "windows" else ".tar.gz"
|
|
55
|
+
asset = f"runagents_{plat}_{arch}{ext}"
|
|
56
|
+
url = f"{S3_BASE}/v{version}/{asset}"
|
|
57
|
+
checksums_url = f"{S3_BASE}/v{version}/checksums.txt"
|
|
58
|
+
|
|
59
|
+
_BIN_DIR.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
|
|
61
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
62
|
+
archive_path = Path(tmpdir) / asset
|
|
63
|
+
|
|
64
|
+
# Download archive
|
|
65
|
+
print(f"Downloading runagents v{version} for {plat}/{arch}...")
|
|
66
|
+
urllib.request.urlretrieve(url, archive_path)
|
|
67
|
+
|
|
68
|
+
# Verify checksum
|
|
69
|
+
try:
|
|
70
|
+
with urllib.request.urlopen(checksums_url, timeout=10) as resp:
|
|
71
|
+
checksums_text = resp.read().decode()
|
|
72
|
+
expected_hash = _find_hash(checksums_text, asset)
|
|
73
|
+
if expected_hash:
|
|
74
|
+
actual_hash = _sha256(archive_path)
|
|
75
|
+
if actual_hash != expected_hash:
|
|
76
|
+
raise RuntimeError(
|
|
77
|
+
f"SHA256 mismatch for {asset}: expected {expected_hash}, got {actual_hash}"
|
|
78
|
+
)
|
|
79
|
+
except urllib.error.URLError:
|
|
80
|
+
pass # Skip verification if checksums unavailable
|
|
81
|
+
|
|
82
|
+
# Extract
|
|
83
|
+
bin_name = "runagents.exe" if plat == "windows" else "runagents"
|
|
84
|
+
if ext == ".tar.gz":
|
|
85
|
+
with tarfile.open(archive_path, "r:gz") as tar:
|
|
86
|
+
# Find the binary in the archive
|
|
87
|
+
for member in tar.getmembers():
|
|
88
|
+
if member.name.endswith(bin_name):
|
|
89
|
+
member.name = bin_name
|
|
90
|
+
tar.extract(member, tmpdir)
|
|
91
|
+
break
|
|
92
|
+
else:
|
|
93
|
+
import zipfile
|
|
94
|
+
with zipfile.ZipFile(archive_path) as zf:
|
|
95
|
+
for name in zf.namelist():
|
|
96
|
+
if name.endswith(bin_name):
|
|
97
|
+
zf.extract(name, tmpdir)
|
|
98
|
+
break
|
|
99
|
+
|
|
100
|
+
src = Path(tmpdir) / bin_name
|
|
101
|
+
dst = _BIN_DIR / f"runagents-{version}"
|
|
102
|
+
shutil.move(str(src), str(dst))
|
|
103
|
+
dst.chmod(dst.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
104
|
+
print(f"Installed runagents v{version} to {dst}")
|
|
105
|
+
return dst
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _sha256(path: Path) -> str:
|
|
109
|
+
h = hashlib.sha256()
|
|
110
|
+
with open(path, "rb") as f:
|
|
111
|
+
for chunk in iter(lambda: f.read(8192), b""):
|
|
112
|
+
h.update(chunk)
|
|
113
|
+
return h.hexdigest()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _find_hash(checksums_text: str, asset_name: str) -> str | None:
|
|
117
|
+
for line in checksums_text.strip().splitlines():
|
|
118
|
+
parts = line.split()
|
|
119
|
+
if len(parts) == 2 and parts[1] == asset_name:
|
|
120
|
+
return parts[0]
|
|
121
|
+
return None
|