swytchcode-runtime 0.1.0__tar.gz → 1.0.1__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.
- swytchcode_runtime-1.0.1/LICENSE.md +21 -0
- swytchcode_runtime-1.0.1/PKG-INFO +236 -0
- swytchcode_runtime-1.0.1/README.md +218 -0
- {swytchcode_runtime-0.1.0 → swytchcode_runtime-1.0.1}/pyproject.toml +2 -2
- swytchcode_runtime-1.0.1/swytchcode_runtime/__init__.py +30 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime/cli.py +58 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime/client.py +243 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime/discover.py +29 -0
- {swytchcode_runtime-0.1.0 → swytchcode_runtime-1.0.1}/swytchcode_runtime/exec.py +57 -3
- swytchcode_runtime-1.0.1/swytchcode_runtime/manage.py +30 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime/providers/__init__.py +27 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime/providers/anthropic.py +13 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime/providers/base.py +31 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime/providers/crewai.py +22 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime/providers/langgraph.py +17 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime/providers/openai_agents.py +30 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime/providers/vercel.py +30 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime/schema.py +115 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime.egg-info/PKG-INFO +236 -0
- swytchcode_runtime-1.0.1/swytchcode_runtime.egg-info/SOURCES.txt +24 -0
- swytchcode_runtime-1.0.1/tests/test_alias.py +42 -0
- swytchcode_runtime-1.0.1/tests/test_schema_routing.py +62 -0
- swytchcode_runtime-0.1.0/PKG-INFO +0 -133
- swytchcode_runtime-0.1.0/README.md +0 -117
- swytchcode_runtime-0.1.0/swytchcode_runtime/__init__.py +0 -6
- swytchcode_runtime-0.1.0/swytchcode_runtime.egg-info/PKG-INFO +0 -133
- swytchcode_runtime-0.1.0/swytchcode_runtime.egg-info/SOURCES.txt +0 -9
- {swytchcode_runtime-0.1.0 → swytchcode_runtime-1.0.1}/setup.cfg +0 -0
- {swytchcode_runtime-0.1.0 → swytchcode_runtime-1.0.1}/swytchcode_runtime/errors.py +0 -0
- {swytchcode_runtime-0.1.0 → swytchcode_runtime-1.0.1}/swytchcode_runtime.egg-info/dependency_links.txt +0 -0
- {swytchcode_runtime-0.1.0 → swytchcode_runtime-1.0.1}/swytchcode_runtime.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Swytchcode Technologies Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: swytchcode-runtime
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Thin runtime wrapper around the Swytchcode CLI
|
|
5
|
+
Author: Swytchcode
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: repository, https://github.com/swytchcodehq/runtime-py
|
|
8
|
+
Keywords: swytchcode,cli,runtime,exec
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE.md
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# swytchcode-runtime (Python)
|
|
20
|
+
|
|
21
|
+
Thin runtime wrapper around the Swytchcode CLI. Calls `swytchcode exec` for you so you can stay in Python without shell boilerplate.
|
|
22
|
+
|
|
23
|
+
**Requires:** The `swytchcode` CLI must be installed. The binary is located automatically — no configuration needed in most environments. Resolution order:
|
|
24
|
+
|
|
25
|
+
1. `SWYTCHCODE_BIN` env var — explicit override.
|
|
26
|
+
2. `$PATH` lookup via `shutil.which` — the standard system resolution.
|
|
27
|
+
3. Common install paths — `~/.local/bin`, `/usr/local/bin` (Unix) or `%LOCALAPPDATA%\Programs\swytchcode\bin` (Windows).
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install swytchcode-runtime
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or from the repo:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install /path/to/runtime-libraries/python-runtime
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Use
|
|
42
|
+
|
|
43
|
+
### JSON mode (default)
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from swytchcode_runtime import exec
|
|
47
|
+
|
|
48
|
+
result = exec("api.account.create", {"email": "test@example.com"})
|
|
49
|
+
# result is parsed JSON (any)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Equivalent to: `swytchcode exec api.account.create --json` with args on stdin.
|
|
53
|
+
|
|
54
|
+
**Request input (args):** The second argument is the kernel **args** object (sent as JSON on stdin). Use this shape so the kernel builds the request correctly:
|
|
55
|
+
- **body** — Request body (dict).
|
|
56
|
+
- **params** — Query/path params (e.g. `{"id": "cluster-123"}`).
|
|
57
|
+
- **Authorization** — Auth header value (e.g. `"Bearer token123"`).
|
|
58
|
+
- **headers** — Additional request headers (e.g. `{"X-Request-Id": "abc-123"}`).
|
|
59
|
+
- Other top-level keys are passed as query params.
|
|
60
|
+
|
|
61
|
+
Example with body, params, and headers:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
exec("api.cluster.get", {
|
|
65
|
+
"params": {"id": "cluster-123"},
|
|
66
|
+
"Authorization": "Bearer token123",
|
|
67
|
+
"headers": {"X-Request-Id": "abc-123"},
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Raw mode
|
|
72
|
+
|
|
73
|
+
Get stdout as a string instead of parsing JSON:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from swytchcode_runtime import exec
|
|
77
|
+
|
|
78
|
+
output = exec("api.report.export", {"id": "123"}, raw=True)
|
|
79
|
+
# output is the raw stdout string
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Options
|
|
83
|
+
|
|
84
|
+
- **cwd** – Working directory for the process (default: current directory).
|
|
85
|
+
- **env** – Extra environment variables (merged with `os.environ`).
|
|
86
|
+
- **raw** – If `True`, use `--raw` and return stdout as a string.
|
|
87
|
+
- **dry_run** – If `True`, pass `--dry-run` to the CLI; request details (method, url, headers, body) are output instead of calling the server.
|
|
88
|
+
- **allow_raw** – If `True`, pass `--allow-raw` to the CLI; required for executing raw methods (kernel has this disabled by default).
|
|
89
|
+
|
|
90
|
+
This runtime invokes `swytchcode exec [canonical_id]` with the flags above. For full exec behavior, see the Swytchcode kernel documentation.
|
|
91
|
+
|
|
92
|
+
### Environment variables
|
|
93
|
+
|
|
94
|
+
| Variable | Description |
|
|
95
|
+
|----------|-------------|
|
|
96
|
+
| `SWYTCHCODE_BIN` | Override the resolved binary path. Set this only when automatic resolution does not find the correct binary (e.g. non-standard install locations or virtualised environments). |
|
|
97
|
+
| `SWYTCHCODE_TOKEN` | Auth token passed to the CLI via the process environment. |
|
|
98
|
+
|
|
99
|
+
### Error handling
|
|
100
|
+
|
|
101
|
+
Failures raise `SwytchcodeError` (spawn error, non-zero exit, invalid JSON). Use the helper to narrow:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from swytchcode_runtime import exec, SwytchcodeError, is_swytchcode_error
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
exec("api.account.create", {"email": "test@example.com"})
|
|
108
|
+
except Exception as e:
|
|
109
|
+
if is_swytchcode_error(e):
|
|
110
|
+
print(e.message, e.cause)
|
|
111
|
+
raise
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## What this library is
|
|
115
|
+
|
|
116
|
+
- A thin process wrapper over the Swytchcode shell.
|
|
117
|
+
- A stable call surface: `exec(canonical_id, input=None, *, cwd=..., env=..., raw=...)`.
|
|
118
|
+
- A convenience for developers and IDE agents.
|
|
119
|
+
|
|
120
|
+
## What this library is not
|
|
121
|
+
|
|
122
|
+
- An HTTP SDK or API client.
|
|
123
|
+
- A replacement for the shell. All authority stays in the Swytchcode CLI.
|
|
124
|
+
|
|
125
|
+
This library **must not** and **does not**:
|
|
126
|
+
|
|
127
|
+
- Read `.swytchcode/*` or parse `tooling.json`.
|
|
128
|
+
- Validate schemas or add retries/timeouts.
|
|
129
|
+
- Call the registry or embed auth logic.
|
|
130
|
+
|
|
131
|
+
It is a pipe, not a brain.
|
|
132
|
+
|
|
133
|
+
## For Cursor / IDE agents
|
|
134
|
+
|
|
135
|
+
Generate code that uses this runtime instead of subprocess boilerplate.
|
|
136
|
+
|
|
137
|
+
**Preferred:**
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from swytchcode_runtime import exec
|
|
141
|
+
|
|
142
|
+
def create_account(input: dict) -> any:
|
|
143
|
+
return exec("api.account.create", input)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Avoid:** subprocess boilerplate, HTTP calls, or config parsing. Use `exec(...)` and let the CLI handle execution and policy.
|
|
147
|
+
|
|
148
|
+
## Agentic workflows (framework integrations)
|
|
149
|
+
|
|
150
|
+
For full, production-ready examples across all major frameworks, check out the [Swytchcode Examples Repository](https://github.com/swytchcodehq/swytchcode-examples).
|
|
151
|
+
|
|
152
|
+
On top of `exec`, the runtime exposes a small agentic surface that turns Swytchcode tools into the native tool objects each agent framework expects.
|
|
153
|
+
|
|
154
|
+
### Quickstart: Anthropic SDK
|
|
155
|
+
|
|
156
|
+
Here is a clean example of building a simple agent using the Anthropic SDK. We use `python-dotenv` to load environment variables (like `ANTHROPIC_API_KEY`).
|
|
157
|
+
|
|
158
|
+
**Installation:**
|
|
159
|
+
```bash
|
|
160
|
+
pip install swytchcode-runtime anthropic python-dotenv
|
|
161
|
+
```
|
|
162
|
+
*(Note: You only need to install the SDK for the framework you are actually using. You **do not** need to install `openai-agents` or `langchain` if you are only using Anthropic. The `swytchcode-runtime` isolates these dependencies via lazy loading.)*
|
|
163
|
+
|
|
164
|
+
**Example:**
|
|
165
|
+
```python
|
|
166
|
+
import os
|
|
167
|
+
from dotenv import load_dotenv
|
|
168
|
+
import anthropic
|
|
169
|
+
from swytchcode_runtime import Swytchcode
|
|
170
|
+
from swytchcode_runtime.providers.anthropic import AnthropicProvider
|
|
171
|
+
|
|
172
|
+
load_dotenv() # Loads .env automatically
|
|
173
|
+
|
|
174
|
+
def run_agent():
|
|
175
|
+
client = anthropic.Anthropic()
|
|
176
|
+
|
|
177
|
+
# 1. Initialize Swytchcode with the Anthropic provider
|
|
178
|
+
swx = Swytchcode(provider=AnthropicProvider())
|
|
179
|
+
|
|
180
|
+
# 2. Fetch the tools you want your agent to use (e.g., Stripe tools)
|
|
181
|
+
tools = swx.tools.get(toolkits=["stripe"])
|
|
182
|
+
|
|
183
|
+
# 3. Pass them to Claude
|
|
184
|
+
response = client.messages.create(
|
|
185
|
+
model="claude-3-5-sonnet-latest",
|
|
186
|
+
max_tokens=1024,
|
|
187
|
+
tools=tools,
|
|
188
|
+
messages=[{"role": "user", "content": "Refund charge ch_123 for $20."}],
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
print(response)
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
run_agent()
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Selecting tools - `swx.tools.get(...)`
|
|
198
|
+
|
|
199
|
+
Pass exactly one selector; IDs resolve against your local Swytchcode state and remote search:
|
|
200
|
+
|
|
201
|
+
- `toolkits=["stripe"]` - every enabled tool whose integration matches a toolkit.
|
|
202
|
+
- `tools=["charges.charge.create"]` - explicit canonical IDs.
|
|
203
|
+
- `search="refund a charge"` - natural-language discovery (via `swytchcode discover`).
|
|
204
|
+
|
|
205
|
+
Each returned tool carries a **full input schema** - every field is surfaced to the model, with
|
|
206
|
+
only the truly-required ones marked `required` - and an `execute` callback that runs `swytchcode
|
|
207
|
+
exec` for you (empty optional values are stripped before the call so APIs like Stripe don't reject them).
|
|
208
|
+
|
|
209
|
+
### Supported providers
|
|
210
|
+
|
|
211
|
+
| Framework | Import | Who runs the tool loop |
|
|
212
|
+
|-----------|--------|------------------------|
|
|
213
|
+
| Anthropic Claude | `from swytchcode_runtime.providers.anthropic import AnthropicProvider` | you (Messages API + `swx.handle_tool_calls`) |
|
|
214
|
+
| OpenAI Agents SDK | `from swytchcode_runtime.providers.openai_agents import OpenAIAgentsProvider` | the SDK |
|
|
215
|
+
| Vercel AI SDK | `from swytchcode_runtime.providers.vercel import VercelProvider` | the SDK |
|
|
216
|
+
| LangGraph | `from swytchcode_runtime.providers.langgraph import LangGraphProvider` | the prebuilt agent |
|
|
217
|
+
| CrewAI | `from swytchcode_runtime.providers.crewai import CrewAIProvider` | the crew |
|
|
218
|
+
|
|
219
|
+
### Non-agentic APIs (Anthropic Messages)
|
|
220
|
+
|
|
221
|
+
When you run the tool loop yourself, `handle_tool_calls` executes each `tool_use` block and
|
|
222
|
+
returns the `tool_result` blocks to send back:
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
import anthropic
|
|
226
|
+
client = anthropic.Anthropic()
|
|
227
|
+
msg = client.messages.create(
|
|
228
|
+
model="claude-sonnet-5", max_tokens=1024, tools=tools,
|
|
229
|
+
messages=[{"role": "user", "content": "Refund charge ch_123 for $20"}],
|
|
230
|
+
)
|
|
231
|
+
results = swx.handle_tool_calls(msg) # runs the tool calls, returns tool_result blocks
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
One runnable file per framework lives in `sdk-examples/`. Install the matching framework SDK
|
|
235
|
+
(`pip install openai-agents` / `anthropic` / `ai` / `langgraph` / `crewai`) alongside the
|
|
236
|
+
`swytchcode` CLI.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# swytchcode-runtime (Python)
|
|
2
|
+
|
|
3
|
+
Thin runtime wrapper around the Swytchcode CLI. Calls `swytchcode exec` for you so you can stay in Python without shell boilerplate.
|
|
4
|
+
|
|
5
|
+
**Requires:** The `swytchcode` CLI must be installed. The binary is located automatically — no configuration needed in most environments. Resolution order:
|
|
6
|
+
|
|
7
|
+
1. `SWYTCHCODE_BIN` env var — explicit override.
|
|
8
|
+
2. `$PATH` lookup via `shutil.which` — the standard system resolution.
|
|
9
|
+
3. Common install paths — `~/.local/bin`, `/usr/local/bin` (Unix) or `%LOCALAPPDATA%\Programs\swytchcode\bin` (Windows).
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install swytchcode-runtime
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or from the repo:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install /path/to/runtime-libraries/python-runtime
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Use
|
|
24
|
+
|
|
25
|
+
### JSON mode (default)
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from swytchcode_runtime import exec
|
|
29
|
+
|
|
30
|
+
result = exec("api.account.create", {"email": "test@example.com"})
|
|
31
|
+
# result is parsed JSON (any)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Equivalent to: `swytchcode exec api.account.create --json` with args on stdin.
|
|
35
|
+
|
|
36
|
+
**Request input (args):** The second argument is the kernel **args** object (sent as JSON on stdin). Use this shape so the kernel builds the request correctly:
|
|
37
|
+
- **body** — Request body (dict).
|
|
38
|
+
- **params** — Query/path params (e.g. `{"id": "cluster-123"}`).
|
|
39
|
+
- **Authorization** — Auth header value (e.g. `"Bearer token123"`).
|
|
40
|
+
- **headers** — Additional request headers (e.g. `{"X-Request-Id": "abc-123"}`).
|
|
41
|
+
- Other top-level keys are passed as query params.
|
|
42
|
+
|
|
43
|
+
Example with body, params, and headers:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
exec("api.cluster.get", {
|
|
47
|
+
"params": {"id": "cluster-123"},
|
|
48
|
+
"Authorization": "Bearer token123",
|
|
49
|
+
"headers": {"X-Request-Id": "abc-123"},
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Raw mode
|
|
54
|
+
|
|
55
|
+
Get stdout as a string instead of parsing JSON:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from swytchcode_runtime import exec
|
|
59
|
+
|
|
60
|
+
output = exec("api.report.export", {"id": "123"}, raw=True)
|
|
61
|
+
# output is the raw stdout string
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Options
|
|
65
|
+
|
|
66
|
+
- **cwd** – Working directory for the process (default: current directory).
|
|
67
|
+
- **env** – Extra environment variables (merged with `os.environ`).
|
|
68
|
+
- **raw** – If `True`, use `--raw` and return stdout as a string.
|
|
69
|
+
- **dry_run** – If `True`, pass `--dry-run` to the CLI; request details (method, url, headers, body) are output instead of calling the server.
|
|
70
|
+
- **allow_raw** – If `True`, pass `--allow-raw` to the CLI; required for executing raw methods (kernel has this disabled by default).
|
|
71
|
+
|
|
72
|
+
This runtime invokes `swytchcode exec [canonical_id]` with the flags above. For full exec behavior, see the Swytchcode kernel documentation.
|
|
73
|
+
|
|
74
|
+
### Environment variables
|
|
75
|
+
|
|
76
|
+
| Variable | Description |
|
|
77
|
+
|----------|-------------|
|
|
78
|
+
| `SWYTCHCODE_BIN` | Override the resolved binary path. Set this only when automatic resolution does not find the correct binary (e.g. non-standard install locations or virtualised environments). |
|
|
79
|
+
| `SWYTCHCODE_TOKEN` | Auth token passed to the CLI via the process environment. |
|
|
80
|
+
|
|
81
|
+
### Error handling
|
|
82
|
+
|
|
83
|
+
Failures raise `SwytchcodeError` (spawn error, non-zero exit, invalid JSON). Use the helper to narrow:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from swytchcode_runtime import exec, SwytchcodeError, is_swytchcode_error
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
exec("api.account.create", {"email": "test@example.com"})
|
|
90
|
+
except Exception as e:
|
|
91
|
+
if is_swytchcode_error(e):
|
|
92
|
+
print(e.message, e.cause)
|
|
93
|
+
raise
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## What this library is
|
|
97
|
+
|
|
98
|
+
- A thin process wrapper over the Swytchcode shell.
|
|
99
|
+
- A stable call surface: `exec(canonical_id, input=None, *, cwd=..., env=..., raw=...)`.
|
|
100
|
+
- A convenience for developers and IDE agents.
|
|
101
|
+
|
|
102
|
+
## What this library is not
|
|
103
|
+
|
|
104
|
+
- An HTTP SDK or API client.
|
|
105
|
+
- A replacement for the shell. All authority stays in the Swytchcode CLI.
|
|
106
|
+
|
|
107
|
+
This library **must not** and **does not**:
|
|
108
|
+
|
|
109
|
+
- Read `.swytchcode/*` or parse `tooling.json`.
|
|
110
|
+
- Validate schemas or add retries/timeouts.
|
|
111
|
+
- Call the registry or embed auth logic.
|
|
112
|
+
|
|
113
|
+
It is a pipe, not a brain.
|
|
114
|
+
|
|
115
|
+
## For Cursor / IDE agents
|
|
116
|
+
|
|
117
|
+
Generate code that uses this runtime instead of subprocess boilerplate.
|
|
118
|
+
|
|
119
|
+
**Preferred:**
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from swytchcode_runtime import exec
|
|
123
|
+
|
|
124
|
+
def create_account(input: dict) -> any:
|
|
125
|
+
return exec("api.account.create", input)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Avoid:** subprocess boilerplate, HTTP calls, or config parsing. Use `exec(...)` and let the CLI handle execution and policy.
|
|
129
|
+
|
|
130
|
+
## Agentic workflows (framework integrations)
|
|
131
|
+
|
|
132
|
+
For full, production-ready examples across all major frameworks, check out the [Swytchcode Examples Repository](https://github.com/swytchcodehq/swytchcode-examples).
|
|
133
|
+
|
|
134
|
+
On top of `exec`, the runtime exposes a small agentic surface that turns Swytchcode tools into the native tool objects each agent framework expects.
|
|
135
|
+
|
|
136
|
+
### Quickstart: Anthropic SDK
|
|
137
|
+
|
|
138
|
+
Here is a clean example of building a simple agent using the Anthropic SDK. We use `python-dotenv` to load environment variables (like `ANTHROPIC_API_KEY`).
|
|
139
|
+
|
|
140
|
+
**Installation:**
|
|
141
|
+
```bash
|
|
142
|
+
pip install swytchcode-runtime anthropic python-dotenv
|
|
143
|
+
```
|
|
144
|
+
*(Note: You only need to install the SDK for the framework you are actually using. You **do not** need to install `openai-agents` or `langchain` if you are only using Anthropic. The `swytchcode-runtime` isolates these dependencies via lazy loading.)*
|
|
145
|
+
|
|
146
|
+
**Example:**
|
|
147
|
+
```python
|
|
148
|
+
import os
|
|
149
|
+
from dotenv import load_dotenv
|
|
150
|
+
import anthropic
|
|
151
|
+
from swytchcode_runtime import Swytchcode
|
|
152
|
+
from swytchcode_runtime.providers.anthropic import AnthropicProvider
|
|
153
|
+
|
|
154
|
+
load_dotenv() # Loads .env automatically
|
|
155
|
+
|
|
156
|
+
def run_agent():
|
|
157
|
+
client = anthropic.Anthropic()
|
|
158
|
+
|
|
159
|
+
# 1. Initialize Swytchcode with the Anthropic provider
|
|
160
|
+
swx = Swytchcode(provider=AnthropicProvider())
|
|
161
|
+
|
|
162
|
+
# 2. Fetch the tools you want your agent to use (e.g., Stripe tools)
|
|
163
|
+
tools = swx.tools.get(toolkits=["stripe"])
|
|
164
|
+
|
|
165
|
+
# 3. Pass them to Claude
|
|
166
|
+
response = client.messages.create(
|
|
167
|
+
model="claude-3-5-sonnet-latest",
|
|
168
|
+
max_tokens=1024,
|
|
169
|
+
tools=tools,
|
|
170
|
+
messages=[{"role": "user", "content": "Refund charge ch_123 for $20."}],
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
print(response)
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
run_agent()
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Selecting tools - `swx.tools.get(...)`
|
|
180
|
+
|
|
181
|
+
Pass exactly one selector; IDs resolve against your local Swytchcode state and remote search:
|
|
182
|
+
|
|
183
|
+
- `toolkits=["stripe"]` - every enabled tool whose integration matches a toolkit.
|
|
184
|
+
- `tools=["charges.charge.create"]` - explicit canonical IDs.
|
|
185
|
+
- `search="refund a charge"` - natural-language discovery (via `swytchcode discover`).
|
|
186
|
+
|
|
187
|
+
Each returned tool carries a **full input schema** - every field is surfaced to the model, with
|
|
188
|
+
only the truly-required ones marked `required` - and an `execute` callback that runs `swytchcode
|
|
189
|
+
exec` for you (empty optional values are stripped before the call so APIs like Stripe don't reject them).
|
|
190
|
+
|
|
191
|
+
### Supported providers
|
|
192
|
+
|
|
193
|
+
| Framework | Import | Who runs the tool loop |
|
|
194
|
+
|-----------|--------|------------------------|
|
|
195
|
+
| Anthropic Claude | `from swytchcode_runtime.providers.anthropic import AnthropicProvider` | you (Messages API + `swx.handle_tool_calls`) |
|
|
196
|
+
| OpenAI Agents SDK | `from swytchcode_runtime.providers.openai_agents import OpenAIAgentsProvider` | the SDK |
|
|
197
|
+
| Vercel AI SDK | `from swytchcode_runtime.providers.vercel import VercelProvider` | the SDK |
|
|
198
|
+
| LangGraph | `from swytchcode_runtime.providers.langgraph import LangGraphProvider` | the prebuilt agent |
|
|
199
|
+
| CrewAI | `from swytchcode_runtime.providers.crewai import CrewAIProvider` | the crew |
|
|
200
|
+
|
|
201
|
+
### Non-agentic APIs (Anthropic Messages)
|
|
202
|
+
|
|
203
|
+
When you run the tool loop yourself, `handle_tool_calls` executes each `tool_use` block and
|
|
204
|
+
returns the `tool_result` blocks to send back:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
import anthropic
|
|
208
|
+
client = anthropic.Anthropic()
|
|
209
|
+
msg = client.messages.create(
|
|
210
|
+
model="claude-sonnet-5", max_tokens=1024, tools=tools,
|
|
211
|
+
messages=[{"role": "user", "content": "Refund charge ch_123 for $20"}],
|
|
212
|
+
)
|
|
213
|
+
results = swx.handle_tool_calls(msg) # runs the tool calls, returns tool_result blocks
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
One runnable file per framework lives in `sdk-examples/`. Install the matching framework SDK
|
|
217
|
+
(`pip install openai-agents` / `anthropic` / `ai` / `langgraph` / `crewai`) alongside the
|
|
218
|
+
`swytchcode` CLI.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swytchcode-runtime"
|
|
7
|
-
version = "0.1
|
|
7
|
+
version = "1.0.1"
|
|
8
8
|
description = "Thin runtime wrapper around the Swytchcode CLI"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
]
|
|
21
21
|
|
|
22
22
|
[project.urls]
|
|
23
|
-
repository = "https://
|
|
23
|
+
repository = "https://github.com/swytchcodehq/runtime-py"
|
|
24
24
|
|
|
25
25
|
[tool.setuptools.packages.find]
|
|
26
26
|
where = ["."]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Thin runtime wrapper around the Swytchcode CLI."""
|
|
2
|
+
|
|
3
|
+
from .errors import SwytchcodeError, is_swytchcode_error
|
|
4
|
+
from .exec import exec_ as exec
|
|
5
|
+
from .client import Swytchcode
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"exec",
|
|
9
|
+
"Swytchcode",
|
|
10
|
+
"SwytchcodeError",
|
|
11
|
+
"is_swytchcode_error",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def __getattr__(name):
|
|
16
|
+
"""Lazy provider imports for backward compatibility."""
|
|
17
|
+
_provider_map = {
|
|
18
|
+
"AnthropicProvider": ("providers.anthropic", "AnthropicProvider"),
|
|
19
|
+
"OpenAIAgentsProvider": ("providers.openai_agents", "OpenAIAgentsProvider"),
|
|
20
|
+
"VercelProvider": ("providers.vercel", "VercelProvider"),
|
|
21
|
+
"LangGraphProvider": ("providers.langgraph", "LangGraphProvider"),
|
|
22
|
+
"CrewAIProvider": ("providers.crewai", "CrewAIProvider"),
|
|
23
|
+
}
|
|
24
|
+
if name in _provider_map:
|
|
25
|
+
module_path, attr = _provider_map[name]
|
|
26
|
+
import importlib
|
|
27
|
+
|
|
28
|
+
mod = importlib.import_module(f".{module_path}", __package__)
|
|
29
|
+
return getattr(mod, attr)
|
|
30
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Run a swytchcode CLI subcommand with --json and parse the output."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from .exec import _resolve_bin
|
|
10
|
+
from .errors import SwytchcodeError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run_cli(
|
|
14
|
+
args: list[str],
|
|
15
|
+
*,
|
|
16
|
+
cwd: str | None = None,
|
|
17
|
+
env: dict | None = None,
|
|
18
|
+
timeout: float | None = 60,
|
|
19
|
+
) -> Any:
|
|
20
|
+
cmd = [_resolve_bin(), *args]
|
|
21
|
+
if "--json" not in args:
|
|
22
|
+
cmd.append("--json")
|
|
23
|
+
|
|
24
|
+
run_env = os.environ.copy()
|
|
25
|
+
if env:
|
|
26
|
+
run_env.update(env)
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
r = subprocess.run(
|
|
30
|
+
cmd,
|
|
31
|
+
capture_output=True,
|
|
32
|
+
cwd=cwd or os.getcwd(),
|
|
33
|
+
env=run_env,
|
|
34
|
+
timeout=timeout,
|
|
35
|
+
)
|
|
36
|
+
except FileNotFoundError as e:
|
|
37
|
+
raise SwytchcodeError(
|
|
38
|
+
"Failed to spawn swytchcode; is the CLI installed?", e
|
|
39
|
+
) from e
|
|
40
|
+
except subprocess.TimeoutExpired as e:
|
|
41
|
+
raise SwytchcodeError(
|
|
42
|
+
f"swytchcode command timed out after {timeout}s", e
|
|
43
|
+
) from e
|
|
44
|
+
|
|
45
|
+
if r.returncode != 0:
|
|
46
|
+
raise SwytchcodeError(
|
|
47
|
+
r.stderr.decode("utf-8", "replace").strip() or "command failed",
|
|
48
|
+
r.returncode,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
out = r.stdout.decode("utf-8", "replace").strip()
|
|
52
|
+
if not out:
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
return json.loads(out)
|
|
57
|
+
except json.JSONDecodeError as e:
|
|
58
|
+
raise SwytchcodeError("Invalid JSON from swytchcode", out) from e
|