mycode-sdk 0.9.4__tar.gz → 0.13.3__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.
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/PKG-INFO +78 -35
- mycode_sdk-0.13.3/README.md +213 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/pyproject.toml +8 -7
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/__init__.py +12 -10
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/agent.py +546 -140
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/attachments.py +7 -10
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/compact.py +32 -4
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/hooks.py +12 -7
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/messages.py +51 -10
- mycode_sdk-0.13.3/src/mycode/models.py +278 -0
- mycode_sdk-0.13.3/src/mycode/models_catalog.json +11700 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/providers/__init__.py +21 -2
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/providers/anthropic_like.py +80 -58
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/providers/base.py +159 -10
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/providers/gemini.py +130 -43
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/providers/openai_chat.py +180 -68
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/providers/openai_responses.py +106 -44
- mycode_sdk-0.13.3/src/mycode/session.py +153 -0
- mycode_sdk-0.13.3/src/mycode/tools.py +375 -0
- mycode_sdk-0.9.4/README.md +0 -171
- mycode_sdk-0.9.4/src/mycode/models.py +0 -157
- mycode_sdk-0.9.4/src/mycode/models_catalog.json +0 -3294
- mycode_sdk-0.9.4/src/mycode/session.py +0 -324
- mycode_sdk-0.9.4/src/mycode/tools.py +0 -1045
- mycode_sdk-0.9.4/src/mycode/utils.py +0 -46
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/.gitignore +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/LICENSE +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.13.3}/src/mycode/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: mycode-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.3
|
|
4
4
|
Summary: Lightweight Python SDK for building AI agents.
|
|
5
5
|
Project-URL: Homepage, https://github.com/legibet/mycode
|
|
6
6
|
Project-URL: Repository, https://github.com/legibet/mycode
|
|
@@ -18,16 +18,17 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Classifier: Topic :: Software Development
|
|
20
20
|
Requires-Python: >=3.12
|
|
21
|
-
Requires-Dist: anthropic>=0.
|
|
22
|
-
Requires-Dist: google-genai>=2.
|
|
23
|
-
Requires-Dist: griffelib>=2.
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: anthropic>=0.116.0
|
|
22
|
+
Requires-Dist: google-genai>=2.15.0
|
|
23
|
+
Requires-Dist: griffelib>=2.1.0
|
|
24
|
+
Requires-Dist: httpx>=0.28.0
|
|
25
|
+
Requires-Dist: openai>=2.45.0
|
|
26
|
+
Requires-Dist: pydantic>=2.13.4
|
|
26
27
|
Description-Content-Type: text/markdown
|
|
27
28
|
|
|
28
29
|
# mycode-sdk
|
|
29
30
|
|
|
30
|
-
Lightweight Python SDK for building AI agents.
|
|
31
|
+
Lightweight Python SDK for building AI agents. Multi-turn conversations, tool calling, session persistence, and streaming events. Provider adapters for Anthropic, OpenAI, Google, and more.
|
|
31
32
|
|
|
32
33
|
## Install
|
|
33
34
|
|
|
@@ -41,15 +42,27 @@ pip install mycode-sdk
|
|
|
41
42
|
|
|
42
43
|
```python
|
|
43
44
|
import asyncio
|
|
45
|
+
from pathlib import Path
|
|
46
|
+
|
|
47
|
+
from mycode import Agent, tool
|
|
48
|
+
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
@tool
|
|
51
|
+
def read_file(path: str) -> str:
|
|
52
|
+
"""Read a UTF-8 text file.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
path: File path, relative to the working directory.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
return Path(path).read_text(encoding="utf-8")
|
|
46
59
|
|
|
47
60
|
|
|
48
61
|
async def main() -> None:
|
|
49
62
|
agent = Agent(
|
|
50
63
|
model="claude-sonnet-4-6",
|
|
51
64
|
api_key="YOUR_API_KEY",
|
|
52
|
-
tools=[
|
|
65
|
+
tools=[read_file],
|
|
53
66
|
)
|
|
54
67
|
|
|
55
68
|
async for event in agent.achat("Read pyproject.toml and tell me the project name."):
|
|
@@ -62,16 +75,36 @@ asyncio.run(main())
|
|
|
62
75
|
|
|
63
76
|
`Agent(...)` infers the provider from the model id. No tools are registered unless you pass `tools=[...]`.
|
|
64
77
|
|
|
65
|
-
For a
|
|
78
|
+
For a synchronous call, use `run()`:
|
|
66
79
|
|
|
67
80
|
```python
|
|
68
81
|
result = agent.run("Read pyproject.toml and tell me the project name.")
|
|
69
82
|
print(result.text)
|
|
70
83
|
```
|
|
71
84
|
|
|
85
|
+
## Providers
|
|
86
|
+
|
|
87
|
+
The SDK infers the provider from the model string (`claude-*` to Anthropic, `gpt-*` to OpenAI, etc.). API keys are auto-discovered from environment variables:
|
|
88
|
+
|
|
89
|
+
| Provider | id | Env var |
|
|
90
|
+
| --- | --- | --- |
|
|
91
|
+
| Anthropic | `anthropic` | `ANTHROPIC_API_KEY` |
|
|
92
|
+
| OpenAI | `openai` | `OPENAI_API_KEY` |
|
|
93
|
+
| Google Gemini | `google` | `GEMINI_API_KEY` |
|
|
94
|
+
| Moonshot | `moonshotai` | `MOONSHOT_API_KEY` |
|
|
95
|
+
| MiniMax | `minimax` | `MINIMAX_API_KEY` |
|
|
96
|
+
| DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` |
|
|
97
|
+
| Z.AI | `zai` | `ZAI_API_KEY` |
|
|
98
|
+
| OpenRouter | `openrouter` | `OPENROUTER_API_KEY` |
|
|
99
|
+
| Alibaba Cloud | `alibaba` | `DASHSCOPE_API_KEY` |
|
|
100
|
+
| xAI | `xai` | `XAI_API_KEY` |
|
|
101
|
+
| OpenAI-compatible | `openai_chat` | - |
|
|
102
|
+
|
|
103
|
+
Pass `api_key=` to override the env var, `api_base=` for a custom endpoint. Model metadata is bundled from [models.dev](https://models.dev); pass `context_window`, `supports_reasoning`, `supports_image_input`, or `supports_pdf_input` to override.
|
|
104
|
+
|
|
72
105
|
## Multi-turn conversations
|
|
73
106
|
|
|
74
|
-
Call `achat()` or `run()` again on the same `Agent`
|
|
107
|
+
Call `achat()` or `run()` again on the same `Agent` to continue:
|
|
75
108
|
|
|
76
109
|
```python
|
|
77
110
|
agent = Agent(model="claude-sonnet-4-6", api_key="...")
|
|
@@ -80,6 +113,8 @@ agent.run("What is 2 + 2?")
|
|
|
80
113
|
agent.run("Now multiply that by 10.") # remembers the earlier answer
|
|
81
114
|
```
|
|
82
115
|
|
|
116
|
+
`agent.clear()` drops in-memory history. `agent.messages` accumulates across calls.
|
|
117
|
+
|
|
83
118
|
## Attachments
|
|
84
119
|
|
|
85
120
|
Pass `attachments` to `achat()` or `run()` to add files alongside the prompt:
|
|
@@ -89,7 +124,7 @@ from mycode import Attachment
|
|
|
89
124
|
|
|
90
125
|
agent.run("Describe these.", attachments=["diagram.png", "report.pdf", "notes.txt"])
|
|
91
126
|
|
|
92
|
-
# Or build them explicitly
|
|
127
|
+
# Or build them explicitly:
|
|
93
128
|
agent.run(
|
|
94
129
|
"Review.",
|
|
95
130
|
attachments=[
|
|
@@ -100,7 +135,7 @@ agent.run(
|
|
|
100
135
|
)
|
|
101
136
|
```
|
|
102
137
|
|
|
103
|
-
Images support `image/png`, `image/jpeg`, `image/gif`, `image/webp`; documents support `application/pdf`. Sending an image or PDF to a model
|
|
138
|
+
Images support `image/png`, `image/jpeg`, `image/gif`, `image/webp`; documents support `application/pdf`. Sending an image or PDF to a model without that capability yields an `error` event. A bad path or unsupported type raises `ValueError` before the provider is called.
|
|
104
139
|
|
|
105
140
|
## Saving sessions
|
|
106
141
|
|
|
@@ -117,17 +152,9 @@ agent = Agent(
|
|
|
117
152
|
)
|
|
118
153
|
```
|
|
119
154
|
|
|
120
|
-
Construct another `Agent` with the same `(session_dir, session_id)`
|
|
155
|
+
Construct another `Agent` with the same `(session_dir, session_id)` to load the conversation history.
|
|
121
156
|
|
|
122
|
-
##
|
|
123
|
-
|
|
124
|
-
```python
|
|
125
|
-
from mycode import read_tool, write_tool, edit_tool, bash_tool
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Four tools for reading, writing, editing files, and running shell commands. Opt in by passing them to `tools=[...]`.
|
|
129
|
-
|
|
130
|
-
## Custom tools
|
|
157
|
+
## Tools
|
|
131
158
|
|
|
132
159
|
Decorate a typed function with `@tool`:
|
|
133
160
|
|
|
@@ -153,34 +180,48 @@ agent = Agent(
|
|
|
153
180
|
)
|
|
154
181
|
```
|
|
155
182
|
|
|
156
|
-
To call
|
|
183
|
+
To call another registered tool from inside a tool, type the first parameter as `ToolContext` and dispatch by name:
|
|
157
184
|
|
|
158
185
|
```python
|
|
159
186
|
from mycode import ToolContext, tool
|
|
160
187
|
|
|
161
188
|
|
|
162
189
|
@tool
|
|
163
|
-
def
|
|
164
|
-
"""
|
|
190
|
+
def greet_team(ctx: ToolContext, names: list[str]) -> str:
|
|
191
|
+
"""Greet several people at once."""
|
|
165
192
|
|
|
166
|
-
|
|
167
|
-
return result.output.splitlines()[0] if result.output else ""
|
|
193
|
+
return "\n".join(ctx.call("greet", {"name": name}).output for name in names)
|
|
168
194
|
```
|
|
169
195
|
|
|
196
|
+
Async tools use `await ctx.acall(name, args)` instead.
|
|
197
|
+
|
|
170
198
|
## Tool hooks
|
|
171
199
|
|
|
172
200
|
Inspect or replace tool calls before they run. Return `None` from `before_tool` to let the tool execute, or a `ToolExecutionResult` to skip it:
|
|
173
201
|
|
|
174
202
|
```python
|
|
175
|
-
|
|
203
|
+
import os
|
|
204
|
+
|
|
205
|
+
from mycode import Agent, Hooks, ToolExecutionResult, tool
|
|
176
206
|
|
|
177
207
|
hooks = Hooks()
|
|
178
208
|
|
|
179
209
|
|
|
210
|
+
@tool
|
|
211
|
+
def delete_file(path: str) -> str:
|
|
212
|
+
"""Delete a file.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
path: Path of the file to delete.
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
os.remove(path)
|
|
219
|
+
return f"deleted {path}"
|
|
220
|
+
|
|
221
|
+
|
|
180
222
|
@hooks.before_tool
|
|
181
|
-
async def
|
|
182
|
-
|
|
183
|
-
if ctx.tool_name == "bash" and "rm -rf" in cmd:
|
|
223
|
+
async def protect_dotfiles(ctx):
|
|
224
|
+
if ctx.tool_name == "delete_file" and str(ctx.tool_input.get("path") or "").startswith("."):
|
|
184
225
|
return ToolExecutionResult(output="error: blocked", is_error=True)
|
|
185
226
|
return None
|
|
186
227
|
|
|
@@ -188,11 +229,13 @@ async def block_rm(ctx):
|
|
|
188
229
|
agent = Agent(
|
|
189
230
|
model="claude-sonnet-4-6",
|
|
190
231
|
api_key="...",
|
|
191
|
-
tools=[
|
|
232
|
+
tools=[delete_file],
|
|
192
233
|
hooks=hooks,
|
|
193
234
|
)
|
|
194
235
|
```
|
|
195
236
|
|
|
196
237
|
`@hooks.after_tool` runs after the tool and can replace the result (audit, redact, etc.).
|
|
197
238
|
|
|
198
|
-
|
|
239
|
+
## Further reading
|
|
240
|
+
|
|
241
|
+
See [docs/sdk.md](https://github.com/legibet/mycode/blob/main/docs/sdk.md) for the streaming event API, cancellation, retries, compaction, session internals, and the full `Agent` / `@tool` reference.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# mycode-sdk
|
|
2
|
+
|
|
3
|
+
Lightweight Python SDK for building AI agents. Multi-turn conversations, tool calling, session persistence, and streaming events. Provider adapters for Anthropic, OpenAI, Google, and more.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uv add mycode-sdk
|
|
9
|
+
# or
|
|
10
|
+
pip install mycode-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import asyncio
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from mycode import Agent, tool
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@tool
|
|
23
|
+
def read_file(path: str) -> str:
|
|
24
|
+
"""Read a UTF-8 text file.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
path: File path, relative to the working directory.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
return Path(path).read_text(encoding="utf-8")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async def main() -> None:
|
|
34
|
+
agent = Agent(
|
|
35
|
+
model="claude-sonnet-4-6",
|
|
36
|
+
api_key="YOUR_API_KEY",
|
|
37
|
+
tools=[read_file],
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
async for event in agent.achat("Read pyproject.toml and tell me the project name."):
|
|
41
|
+
if event.type == "text":
|
|
42
|
+
print(event.data["delta"], end="", flush=True)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
asyncio.run(main())
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`Agent(...)` infers the provider from the model id. No tools are registered unless you pass `tools=[...]`.
|
|
49
|
+
|
|
50
|
+
For a synchronous call, use `run()`:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
result = agent.run("Read pyproject.toml and tell me the project name.")
|
|
54
|
+
print(result.text)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Providers
|
|
58
|
+
|
|
59
|
+
The SDK infers the provider from the model string (`claude-*` to Anthropic, `gpt-*` to OpenAI, etc.). API keys are auto-discovered from environment variables:
|
|
60
|
+
|
|
61
|
+
| Provider | id | Env var |
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
| Anthropic | `anthropic` | `ANTHROPIC_API_KEY` |
|
|
64
|
+
| OpenAI | `openai` | `OPENAI_API_KEY` |
|
|
65
|
+
| Google Gemini | `google` | `GEMINI_API_KEY` |
|
|
66
|
+
| Moonshot | `moonshotai` | `MOONSHOT_API_KEY` |
|
|
67
|
+
| MiniMax | `minimax` | `MINIMAX_API_KEY` |
|
|
68
|
+
| DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` |
|
|
69
|
+
| Z.AI | `zai` | `ZAI_API_KEY` |
|
|
70
|
+
| OpenRouter | `openrouter` | `OPENROUTER_API_KEY` |
|
|
71
|
+
| Alibaba Cloud | `alibaba` | `DASHSCOPE_API_KEY` |
|
|
72
|
+
| xAI | `xai` | `XAI_API_KEY` |
|
|
73
|
+
| OpenAI-compatible | `openai_chat` | - |
|
|
74
|
+
|
|
75
|
+
Pass `api_key=` to override the env var, `api_base=` for a custom endpoint. Model metadata is bundled from [models.dev](https://models.dev); pass `context_window`, `supports_reasoning`, `supports_image_input`, or `supports_pdf_input` to override.
|
|
76
|
+
|
|
77
|
+
## Multi-turn conversations
|
|
78
|
+
|
|
79
|
+
Call `achat()` or `run()` again on the same `Agent` to continue:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
agent = Agent(model="claude-sonnet-4-6", api_key="...")
|
|
83
|
+
|
|
84
|
+
agent.run("What is 2 + 2?")
|
|
85
|
+
agent.run("Now multiply that by 10.") # remembers the earlier answer
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`agent.clear()` drops in-memory history. `agent.messages` accumulates across calls.
|
|
89
|
+
|
|
90
|
+
## Attachments
|
|
91
|
+
|
|
92
|
+
Pass `attachments` to `achat()` or `run()` to add files alongside the prompt:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from mycode import Attachment
|
|
96
|
+
|
|
97
|
+
agent.run("Describe these.", attachments=["diagram.png", "report.pdf", "notes.txt"])
|
|
98
|
+
|
|
99
|
+
# Or build them explicitly:
|
|
100
|
+
agent.run(
|
|
101
|
+
"Review.",
|
|
102
|
+
attachments=[
|
|
103
|
+
Attachment.path("diagram.png"),
|
|
104
|
+
Attachment.bytes(png_data, media_type="image/png"),
|
|
105
|
+
Attachment.text("TODO: ship it", name="note.md"),
|
|
106
|
+
],
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Images support `image/png`, `image/jpeg`, `image/gif`, `image/webp`; documents support `application/pdf`. Sending an image or PDF to a model without that capability yields an `error` event. A bad path or unsupported type raises `ValueError` before the provider is called.
|
|
111
|
+
|
|
112
|
+
## Saving sessions
|
|
113
|
+
|
|
114
|
+
Pass `session_dir` to persist the conversation to disk. Each session lives in a subdirectory named by `session_id`:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from pathlib import Path
|
|
118
|
+
|
|
119
|
+
agent = Agent(
|
|
120
|
+
model="claude-sonnet-4-6",
|
|
121
|
+
api_key="...",
|
|
122
|
+
session_dir=Path("./chats"),
|
|
123
|
+
session_id="my-chat",
|
|
124
|
+
)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Construct another `Agent` with the same `(session_dir, session_id)` to load the conversation history.
|
|
128
|
+
|
|
129
|
+
## Tools
|
|
130
|
+
|
|
131
|
+
Decorate a typed function with `@tool`:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from mycode import Agent, tool
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@tool
|
|
138
|
+
def greet(name: str) -> str:
|
|
139
|
+
"""Return a friendly greeting.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
name: Person name.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
return f"hello, {name}"
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
agent = Agent(
|
|
149
|
+
model="claude-sonnet-4-6",
|
|
150
|
+
api_key="...",
|
|
151
|
+
tools=[greet],
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
To call another registered tool from inside a tool, type the first parameter as `ToolContext` and dispatch by name:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from mycode import ToolContext, tool
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@tool
|
|
162
|
+
def greet_team(ctx: ToolContext, names: list[str]) -> str:
|
|
163
|
+
"""Greet several people at once."""
|
|
164
|
+
|
|
165
|
+
return "\n".join(ctx.call("greet", {"name": name}).output for name in names)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Async tools use `await ctx.acall(name, args)` instead.
|
|
169
|
+
|
|
170
|
+
## Tool hooks
|
|
171
|
+
|
|
172
|
+
Inspect or replace tool calls before they run. Return `None` from `before_tool` to let the tool execute, or a `ToolExecutionResult` to skip it:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
import os
|
|
176
|
+
|
|
177
|
+
from mycode import Agent, Hooks, ToolExecutionResult, tool
|
|
178
|
+
|
|
179
|
+
hooks = Hooks()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@tool
|
|
183
|
+
def delete_file(path: str) -> str:
|
|
184
|
+
"""Delete a file.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
path: Path of the file to delete.
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
os.remove(path)
|
|
191
|
+
return f"deleted {path}"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@hooks.before_tool
|
|
195
|
+
async def protect_dotfiles(ctx):
|
|
196
|
+
if ctx.tool_name == "delete_file" and str(ctx.tool_input.get("path") or "").startswith("."):
|
|
197
|
+
return ToolExecutionResult(output="error: blocked", is_error=True)
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
agent = Agent(
|
|
202
|
+
model="claude-sonnet-4-6",
|
|
203
|
+
api_key="...",
|
|
204
|
+
tools=[delete_file],
|
|
205
|
+
hooks=hooks,
|
|
206
|
+
)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
`@hooks.after_tool` runs after the tool and can replace the result (audit, redact, etc.).
|
|
210
|
+
|
|
211
|
+
## Further reading
|
|
212
|
+
|
|
213
|
+
See [docs/sdk.md](https://github.com/legibet/mycode/blob/main/docs/sdk.md) for the streaming event API, cancellation, retries, compaction, session internals, and the full `Agent` / `@tool` reference.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["hatchling"]
|
|
2
|
+
requires = ["hatchling>=1.31.0"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mycode-sdk"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.13.3"
|
|
8
8
|
description = "Lightweight Python SDK for building AI agents."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -23,11 +23,12 @@ classifiers = [
|
|
|
23
23
|
]
|
|
24
24
|
keywords = ["agent", "llm", "anthropic", "openai", "gemini", "sdk"]
|
|
25
25
|
dependencies = [
|
|
26
|
-
"anthropic>=0.
|
|
27
|
-
"google-genai>=2.
|
|
28
|
-
"griffelib>=2.
|
|
29
|
-
"
|
|
30
|
-
"
|
|
26
|
+
"anthropic>=0.116.0",
|
|
27
|
+
"google-genai>=2.15.0",
|
|
28
|
+
"griffelib>=2.1.0",
|
|
29
|
+
"httpx>=0.28.0",
|
|
30
|
+
"openai>=2.45.0",
|
|
31
|
+
"pydantic>=2.13.4",
|
|
31
32
|
]
|
|
32
33
|
|
|
33
34
|
[project.urls]
|
|
@@ -4,12 +4,14 @@ from importlib import metadata
|
|
|
4
4
|
|
|
5
5
|
from mycode.agent import Agent, Event, PersistCallback, RunResult
|
|
6
6
|
from mycode.attachments import Attachment
|
|
7
|
+
from mycode.compact import NothingToCompactError
|
|
7
8
|
from mycode.hooks import AfterToolHook, BeforeToolHook, HookResult, Hooks, ToolHookContext
|
|
8
9
|
from mycode.messages import (
|
|
9
10
|
ContentBlock,
|
|
10
11
|
ConversationMessage,
|
|
11
12
|
assistant_message,
|
|
12
13
|
build_message,
|
|
14
|
+
build_usage,
|
|
13
15
|
document_block,
|
|
14
16
|
flatten_message_text,
|
|
15
17
|
image_block,
|
|
@@ -19,18 +21,15 @@ from mycode.messages import (
|
|
|
19
21
|
tool_use_block,
|
|
20
22
|
user_text_message,
|
|
21
23
|
)
|
|
24
|
+
from mycode.models import Cost, ModelMetadata, estimate_cost, resolve_model_metadata
|
|
25
|
+
from mycode.providers.base import ProviderError, StreamStartTimeoutError
|
|
22
26
|
from mycode.session import SessionStore
|
|
23
27
|
from mycode.tools import (
|
|
24
28
|
ToolContext,
|
|
25
29
|
ToolExecutionResult,
|
|
26
30
|
ToolExecutor,
|
|
27
31
|
ToolSpec,
|
|
28
|
-
bash_tool,
|
|
29
|
-
cancel_all_tools,
|
|
30
|
-
edit_tool,
|
|
31
|
-
read_tool,
|
|
32
32
|
tool,
|
|
33
|
-
write_tool,
|
|
34
33
|
)
|
|
35
34
|
|
|
36
35
|
__version__ = metadata.version("mycode-sdk")
|
|
@@ -40,14 +39,19 @@ __all__ = [
|
|
|
40
39
|
"Attachment",
|
|
41
40
|
"ContentBlock",
|
|
42
41
|
"ConversationMessage",
|
|
42
|
+
"Cost",
|
|
43
43
|
"Event",
|
|
44
44
|
"AfterToolHook",
|
|
45
45
|
"BeforeToolHook",
|
|
46
46
|
"HookResult",
|
|
47
47
|
"Hooks",
|
|
48
|
+
"ModelMetadata",
|
|
49
|
+
"NothingToCompactError",
|
|
48
50
|
"PersistCallback",
|
|
51
|
+
"ProviderError",
|
|
49
52
|
"RunResult",
|
|
50
53
|
"SessionStore",
|
|
54
|
+
"StreamStartTimeoutError",
|
|
51
55
|
"ToolContext",
|
|
52
56
|
"ToolExecutionResult",
|
|
53
57
|
"ToolExecutor",
|
|
@@ -55,19 +59,17 @@ __all__ = [
|
|
|
55
59
|
"ToolSpec",
|
|
56
60
|
"__version__",
|
|
57
61
|
"assistant_message",
|
|
58
|
-
"bash_tool",
|
|
59
62
|
"build_message",
|
|
60
|
-
"
|
|
63
|
+
"build_usage",
|
|
61
64
|
"document_block",
|
|
62
|
-
"
|
|
65
|
+
"estimate_cost",
|
|
63
66
|
"flatten_message_text",
|
|
64
67
|
"image_block",
|
|
65
|
-
"
|
|
68
|
+
"resolve_model_metadata",
|
|
66
69
|
"text_block",
|
|
67
70
|
"thinking_block",
|
|
68
71
|
"tool",
|
|
69
72
|
"tool_result_block",
|
|
70
73
|
"tool_use_block",
|
|
71
74
|
"user_text_message",
|
|
72
|
-
"write_tool",
|
|
73
75
|
]
|