adk-code-mode 0.2.0__tar.gz → 0.2.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.
Files changed (73) hide show
  1. adk_code_mode-0.2.1/.release-please-manifest.json +3 -0
  2. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/CHANGELOG.md +12 -0
  3. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/PKG-INFO +64 -11
  4. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/README.md +63 -10
  5. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/sandbox-wheel/src/adk_code_mode_sandbox/__about__.py +1 -1
  6. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/__about__.py +1 -1
  7. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/callback.py +3 -3
  8. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/executor.py +23 -3
  9. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_agent_with_fake_llm.py +1 -1
  10. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_callback.py +6 -6
  11. adk_code_mode-0.2.0/.release-please-manifest.json +0 -3
  12. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/.githooks/pre-commit +0 -0
  13. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/.githooks/pre-push +0 -0
  14. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/.github/workflows/ci.yml +0 -0
  15. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/.github/workflows/release-please.yml +0 -0
  16. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/.gitignore +0 -0
  17. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/.prettierrc +0 -0
  18. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/.vscode/settings.json +0 -0
  19. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/.zed/settings.json +0 -0
  20. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/AGENTS.md +0 -0
  21. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/CLAUDE.md +0 -0
  22. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/LICENSE +0 -0
  23. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/Makefile +0 -0
  24. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/docker/Dockerfile +0 -0
  25. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/pyproject.toml +0 -0
  26. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/release-please-config.json +0 -0
  27. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/sandbox-wheel/README.md +0 -0
  28. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/sandbox-wheel/pyproject.toml +0 -0
  29. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/sandbox-wheel/src/adk_code_mode_sandbox/__init__.py +0 -0
  30. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/sandbox-wheel/src/adk_code_mode_sandbox/__main__.py +0 -0
  31. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/sandbox-wheel/src/adk_code_mode_sandbox/_entry.py +0 -0
  32. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/sandbox-wheel/src/adk_code_mode_sandbox/_http_server.py +0 -0
  33. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/sandbox-wheel/src/adk_code_mode_sandbox/_rpc_client.py +0 -0
  34. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/sandbox-wheel/src/adk_code_mode_sandbox/protocol.py +0 -0
  35. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/scripts/install-git-hooks.sh +0 -0
  36. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/__init__.py +0 -0
  37. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/_artifact_tools.py +0 -0
  38. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/output.py +0 -0
  39. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/py.typed +0 -0
  40. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/runtime/__init__.py +0 -0
  41. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/runtime/base.py +0 -0
  42. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/runtime/docker.py +0 -0
  43. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/runtime/protocol.py +0 -0
  44. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/runtime/remote.py +0 -0
  45. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/tools/__init__.py +0 -0
  46. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/tools/catalog.py +0 -0
  47. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/tools/dispatcher.py +0 -0
  48. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/tools/namespacing.py +0 -0
  49. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/tools/normaliser.py +0 -0
  50. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/tools/stubs.py +0 -0
  51. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/workspace/__init__.py +0 -0
  52. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/src/adk_code_mode/workspace/files.py +0 -0
  53. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/__init__.py +0 -0
  54. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/_docker_helpers.py +0 -0
  55. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/_fake_runtime.py +0 -0
  56. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_artifact_tools.py +0 -0
  57. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_catalog.py +0 -0
  58. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_dispatcher.py +0 -0
  59. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_docker_image.py +0 -0
  60. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_docker_integration.py +0 -0
  61. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_host_loop.py +0 -0
  62. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_integration.py +0 -0
  63. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_namespacing.py +0 -0
  64. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_normaliser.py +0 -0
  65. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_output.py +0 -0
  66. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_protocol.py +0 -0
  67. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_remote_integration.py +0 -0
  68. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_runtime_docker.py +0 -0
  69. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_sandbox_rpc_client.py +0 -0
  70. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_stubs.py +0 -0
  71. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_version.py +0 -0
  72. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/tests/test_workspace_files.py +0 -0
  73. {adk_code_mode-0.2.0 → adk_code_mode-0.2.1}/uv.lock +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.2.1"
3
+ }
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.1](https://github.com/a2anet/adk-code-mode/compare/adk-code-mode-v0.2.0...adk-code-mode-v0.2.1) (2026-05-05)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * rename `<tools>` tag to `<code-mode>` and update `CODE_MODE_SYSTEM_INSTRUCTION` ([0a06bfb](https://github.com/a2anet/adk-code-mode/commit/0a06bfbdf22db6e0226e477d0ed074a6cc4d1515))
9
+
10
+
11
+ ### Documentation
12
+
13
+ * update `README.md` with deployment instructions ([1dd7a95](https://github.com/a2anet/adk-code-mode/commit/1dd7a95197fd56231ba97182526a825baf18508a))
14
+
3
15
  ## [0.2.0](https://github.com/a2anet/adk-code-mode/compare/adk-code-mode-v0.1.0...adk-code-mode-v0.2.0) (2026-05-03)
4
16
 
5
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: adk-code-mode
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: A "Code Mode" Code Executor for ADK for agents to interact with tools, files, and custom packages with Python
5
5
  Project-URL: Documentation, https://github.com/A2ANet/adk-code-mode#readme
6
6
  Project-URL: Issues, https://github.com/A2ANet/adk-code-mode/issues
@@ -78,12 +78,17 @@ Requires Python 3.10+. Local development requires [Docker](https://docs.docker.c
78
78
 
79
79
  ## 🚀 Usage
80
80
 
81
- Build a `CodeModeCodeExecutor`, wire `code_mode_before_model_callback` into the agent, and put `CODE_MODE_SYSTEM_INSTRUCTION` in the agent's `instruction`. The callback injects the tool catalog into the system prompt — skip it and the model has no idea what tools exist.
81
+ Build a `CodeModeCodeExecutor`, then wire three things into the agent:
82
+
83
+ - **`CODE_MODE_SYSTEM_INSTRUCTION`** — append to the agent's `instruction`. Teaches the model how to write code blocks and use artifacts.
84
+ - **`code_mode_before_model_callback`** — set as `before_model_callback`. Injects the tool catalog (`<code-mode>` block) into the system prompt on every model turn.
85
+ - **`generate_content_config`** with `function_calling_config.mode="NONE"` — disables native function calling so the model writes Python instead of attempting tool calls that fail with `MALFORMED_FUNCTION_CALL` (since `tools=[]`).
82
86
 
83
87
  ### Production (remote sandbox)
84
88
 
85
89
  ```python
86
90
  from google.adk.agents import LlmAgent
91
+ from google.genai import types as genai_types
87
92
  from adk_code_mode import (
88
93
  CODE_MODE_SYSTEM_INSTRUCTION,
89
94
  CodeModeCodeExecutor,
@@ -105,6 +110,11 @@ root_agent = LlmAgent(
105
110
  instruction=f"You are a helpful assistant.\n\n{CODE_MODE_SYSTEM_INSTRUCTION}",
106
111
  tools=[], # do NOT also bind tools here; the executor owns them.
107
112
  code_executor=executor,
113
+ generate_content_config=genai_types.GenerateContentConfig(
114
+ tool_config=genai_types.ToolConfig(
115
+ function_calling_config=genai_types.FunctionCallingConfig(mode="NONE"),
116
+ ),
117
+ ),
108
118
  before_model_callback=code_mode_before_model_callback(executor),
109
119
  )
110
120
  ```
@@ -153,10 +163,10 @@ Setting `ADK_CODE_MODE_CONTROL_HTTP=1` activates HTTP mode. The container:
153
163
  ```bash
154
164
  # Push the sandbox image to Artifact Registry
155
165
  gcloud auth configure-docker <region>-docker.pkg.dev
156
- docker pull ghcr.io/a2anet/adk-code-mode:latest
166
+ docker pull --platform linux/amd64 ghcr.io/a2anet/adk-code-mode:latest
157
167
  docker tag ghcr.io/a2anet/adk-code-mode:latest \
158
- <region>-docker.pkg.dev/<project>/adk-code-mode/sandbox:latest
159
- docker push <region>-docker.pkg.dev/<project>/adk-code-mode/sandbox:latest
168
+ <region>-docker.pkg.dev/<project>/<repository>/adk-code-mode-sandbox:latest
169
+ docker push <region>-docker.pkg.dev/<project>/<repository>/adk-code-mode-sandbox:latest
160
170
 
161
171
  # Create a VPC connector with no egress routes (blocks outbound network from sandbox)
162
172
  gcloud compute networks create adk-sandbox-vpc --subnet-mode=custom
@@ -176,16 +186,17 @@ gcloud compute networks vpc-access connectors create adk-sandbox-connector \
176
186
 
177
187
  # Deploy — note --concurrency 1 and --vpc-egress=all-traffic
178
188
  gcloud run deploy adk-code-mode-sandbox \
179
- --image <region>-docker.pkg.dev/<project>/adk-code-mode/sandbox:latest \
189
+ --image <region>-docker.pkg.dev/<project>/<repository>/adk-code-mode-sandbox:latest \
180
190
  --region <region> \
181
191
  --port 8080 \
182
192
  --cpu 1 \
183
193
  --memory 1Gi \
184
194
  --concurrency 1 \
185
- --no-allow-unauthenticated \
195
+ --allow-unauthenticated \
186
196
  --vpc-connector=adk-sandbox-connector \
187
197
  --vpc-egress=all-traffic \
188
- --set-env-vars "ADK_CODE_MODE_CONTROL_HTTP=1,ADK_CODE_MODE_AUTH_TOKEN=<your-secret>"
198
+ --set-env-vars "ADK_CODE_MODE_CONTROL_HTTP=1" \
199
+ --set-secrets "ADK_CODE_MODE_AUTH_TOKEN=<your-secret-name>:latest"
189
200
  ```
190
201
 
191
202
  Then in your agent:
@@ -328,12 +339,41 @@ The only things crossing the boundary are: code, tool call arguments, tool retur
328
339
 
329
340
  ### What the model sees
330
341
 
331
- Your `instruction` (containing `CODE_MODE_SYSTEM_INSTRUCTION`) followed by a `<tools>` block appended by the callback:
342
+ Your `instruction` (containing `CODE_MODE_SYSTEM_INSTRUCTION`) followed by a `<code-mode>` block appended by the callback:
332
343
 
333
344
  ```
334
345
  …your instruction…
335
346
 
336
- <tools>
347
+ # How to execute code and use tools
348
+ Code you write in a fenced Python block (i.e. ```python) will be executed in a sandbox.
349
+ The Python Standard Library and a custom set of tools are available to you.
350
+ To see the result of your code, you need to print it.
351
+
352
+ For example, if you had the following tool:
353
+
354
+ ```
355
+ from tools.slack import send_message
356
+
357
+ def send_message(*, channel: str, text: str, thread_ts: str | None = ...) -> Any:
358
+ """Send a message to a Slack channel."""
359
+ ...
360
+ ```
361
+
362
+ To call the tool, you should write:
363
+
364
+ """
365
+ ```python
366
+ from tools.slack import send_message
367
+
368
+ print(send_message(channel="C123", text="hi"))
369
+ ```
370
+ """
371
+
372
+ # How to use files and variables in between executions
373
+ Code is executed in a new environment each time.
374
+ To list available Artifacts, use the `list_artifacts` tool. To save an Artifact, use the `save_artifact` tool, and to load an Artifact, use the `load_artifact` tool.
375
+
376
+ <code-mode>
337
377
 
338
378
  # tools.slack
339
379
 
@@ -352,7 +392,20 @@ def send_message(*, channel: str, text: str, thread_ts: str | None = ...) -> Any
352
392
  from tools import save_artifact, load_artifact, list_artifacts
353
393
 
354
394
 
355
- </tools>
395
+ </code-mode>
396
+ ```
397
+
398
+ When the rendered catalog exceeds `max_catalog_chars`, the per-tool sections are replaced with:
399
+
400
+ ```
401
+ <code-mode>
402
+ A `tools` package is available in the sandbox. List `/tools/` with
403
+ `pathlib.Path('/tools').iterdir()`. Each entry is either a `.py` file
404
+ (a top-level tool, importable as `from tools import <name>`) or a
405
+ subdirectory (a namespace, with tools importable as
406
+ `from tools.<namespace> import <name>`). To see a tool's signature and
407
+ docstring, read its `.py` file with `open(...).read()`.
408
+ </code-mode>
356
409
  ```
357
410
 
358
411
  Text and JSON-like MIME types travel as plain strings in artifact tools; binary content is base64-encoded. `load_artifact` returns `{"kind": "text" | "bytes", "data": str, "mime_type": str | None}`.
@@ -51,12 +51,17 @@ Requires Python 3.10+. Local development requires [Docker](https://docs.docker.c
51
51
 
52
52
  ## 🚀 Usage
53
53
 
54
- Build a `CodeModeCodeExecutor`, wire `code_mode_before_model_callback` into the agent, and put `CODE_MODE_SYSTEM_INSTRUCTION` in the agent's `instruction`. The callback injects the tool catalog into the system prompt — skip it and the model has no idea what tools exist.
54
+ Build a `CodeModeCodeExecutor`, then wire three things into the agent:
55
+
56
+ - **`CODE_MODE_SYSTEM_INSTRUCTION`** — append to the agent's `instruction`. Teaches the model how to write code blocks and use artifacts.
57
+ - **`code_mode_before_model_callback`** — set as `before_model_callback`. Injects the tool catalog (`<code-mode>` block) into the system prompt on every model turn.
58
+ - **`generate_content_config`** with `function_calling_config.mode="NONE"` — disables native function calling so the model writes Python instead of attempting tool calls that fail with `MALFORMED_FUNCTION_CALL` (since `tools=[]`).
55
59
 
56
60
  ### Production (remote sandbox)
57
61
 
58
62
  ```python
59
63
  from google.adk.agents import LlmAgent
64
+ from google.genai import types as genai_types
60
65
  from adk_code_mode import (
61
66
  CODE_MODE_SYSTEM_INSTRUCTION,
62
67
  CodeModeCodeExecutor,
@@ -78,6 +83,11 @@ root_agent = LlmAgent(
78
83
  instruction=f"You are a helpful assistant.\n\n{CODE_MODE_SYSTEM_INSTRUCTION}",
79
84
  tools=[], # do NOT also bind tools here; the executor owns them.
80
85
  code_executor=executor,
86
+ generate_content_config=genai_types.GenerateContentConfig(
87
+ tool_config=genai_types.ToolConfig(
88
+ function_calling_config=genai_types.FunctionCallingConfig(mode="NONE"),
89
+ ),
90
+ ),
81
91
  before_model_callback=code_mode_before_model_callback(executor),
82
92
  )
83
93
  ```
@@ -126,10 +136,10 @@ Setting `ADK_CODE_MODE_CONTROL_HTTP=1` activates HTTP mode. The container:
126
136
  ```bash
127
137
  # Push the sandbox image to Artifact Registry
128
138
  gcloud auth configure-docker <region>-docker.pkg.dev
129
- docker pull ghcr.io/a2anet/adk-code-mode:latest
139
+ docker pull --platform linux/amd64 ghcr.io/a2anet/adk-code-mode:latest
130
140
  docker tag ghcr.io/a2anet/adk-code-mode:latest \
131
- <region>-docker.pkg.dev/<project>/adk-code-mode/sandbox:latest
132
- docker push <region>-docker.pkg.dev/<project>/adk-code-mode/sandbox:latest
141
+ <region>-docker.pkg.dev/<project>/<repository>/adk-code-mode-sandbox:latest
142
+ docker push <region>-docker.pkg.dev/<project>/<repository>/adk-code-mode-sandbox:latest
133
143
 
134
144
  # Create a VPC connector with no egress routes (blocks outbound network from sandbox)
135
145
  gcloud compute networks create adk-sandbox-vpc --subnet-mode=custom
@@ -149,16 +159,17 @@ gcloud compute networks vpc-access connectors create adk-sandbox-connector \
149
159
 
150
160
  # Deploy — note --concurrency 1 and --vpc-egress=all-traffic
151
161
  gcloud run deploy adk-code-mode-sandbox \
152
- --image <region>-docker.pkg.dev/<project>/adk-code-mode/sandbox:latest \
162
+ --image <region>-docker.pkg.dev/<project>/<repository>/adk-code-mode-sandbox:latest \
153
163
  --region <region> \
154
164
  --port 8080 \
155
165
  --cpu 1 \
156
166
  --memory 1Gi \
157
167
  --concurrency 1 \
158
- --no-allow-unauthenticated \
168
+ --allow-unauthenticated \
159
169
  --vpc-connector=adk-sandbox-connector \
160
170
  --vpc-egress=all-traffic \
161
- --set-env-vars "ADK_CODE_MODE_CONTROL_HTTP=1,ADK_CODE_MODE_AUTH_TOKEN=<your-secret>"
171
+ --set-env-vars "ADK_CODE_MODE_CONTROL_HTTP=1" \
172
+ --set-secrets "ADK_CODE_MODE_AUTH_TOKEN=<your-secret-name>:latest"
162
173
  ```
163
174
 
164
175
  Then in your agent:
@@ -301,12 +312,41 @@ The only things crossing the boundary are: code, tool call arguments, tool retur
301
312
 
302
313
  ### What the model sees
303
314
 
304
- Your `instruction` (containing `CODE_MODE_SYSTEM_INSTRUCTION`) followed by a `<tools>` block appended by the callback:
315
+ Your `instruction` (containing `CODE_MODE_SYSTEM_INSTRUCTION`) followed by a `<code-mode>` block appended by the callback:
305
316
 
306
317
  ```
307
318
  …your instruction…
308
319
 
309
- <tools>
320
+ # How to execute code and use tools
321
+ Code you write in a fenced Python block (i.e. ```python) will be executed in a sandbox.
322
+ The Python Standard Library and a custom set of tools are available to you.
323
+ To see the result of your code, you need to print it.
324
+
325
+ For example, if you had the following tool:
326
+
327
+ ```
328
+ from tools.slack import send_message
329
+
330
+ def send_message(*, channel: str, text: str, thread_ts: str | None = ...) -> Any:
331
+ """Send a message to a Slack channel."""
332
+ ...
333
+ ```
334
+
335
+ To call the tool, you should write:
336
+
337
+ """
338
+ ```python
339
+ from tools.slack import send_message
340
+
341
+ print(send_message(channel="C123", text="hi"))
342
+ ```
343
+ """
344
+
345
+ # How to use files and variables in between executions
346
+ Code is executed in a new environment each time.
347
+ To list available Artifacts, use the `list_artifacts` tool. To save an Artifact, use the `save_artifact` tool, and to load an Artifact, use the `load_artifact` tool.
348
+
349
+ <code-mode>
310
350
 
311
351
  # tools.slack
312
352
 
@@ -325,7 +365,20 @@ def send_message(*, channel: str, text: str, thread_ts: str | None = ...) -> Any
325
365
  from tools import save_artifact, load_artifact, list_artifacts
326
366
 
327
367
 
328
- </tools>
368
+ </code-mode>
369
+ ```
370
+
371
+ When the rendered catalog exceeds `max_catalog_chars`, the per-tool sections are replaced with:
372
+
373
+ ```
374
+ <code-mode>
375
+ A `tools` package is available in the sandbox. List `/tools/` with
376
+ `pathlib.Path('/tools').iterdir()`. Each entry is either a `.py` file
377
+ (a top-level tool, importable as `from tools import <name>`) or a
378
+ subdirectory (a namespace, with tools importable as
379
+ `from tools.<namespace> import <name>`). To see a tool's signature and
380
+ docstring, read its `.py` file with `open(...).read()`.
381
+ </code-mode>
329
382
  ```
330
383
 
331
384
  Text and JSON-like MIME types travel as plain strings in artifact tools; binary content is base64-encoded. `load_artifact` returns `{"kind": "text" | "bytes", "data": str, "mime_type": str | None}`.
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2025-present A2A Net <hello@a2anet.com>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
- __version__ = "0.2.0" # x-release-please-version
4
+ __version__ = "0.2.1" # x-release-please-version
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2025-present A2A Net <hello@a2anet.com>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
- __version__ = "0.2.0" # x-release-please-version
4
+ __version__ = "0.2.1" # x-release-please-version
@@ -12,7 +12,7 @@ callback:
12
12
  2. Renders the catalog — signatures + docstrings for every tool —
13
13
  falling back to a short overflow message when the rendered catalog
14
14
  would exceed ``max_catalog_chars``.
15
- 3. Appends ``\\n\\n<tools>\\n…\\n</tools>`` to
15
+ 3. Appends ``\\n\\n<code-mode>\\n…\\n</code-mode>`` to
16
16
  ``llm_request.config.system_instruction``.
17
17
  4. Caches the resolved tools on the executor so the follow-up
18
18
  ``execute_code`` call reuses them instead of re-resolving toolsets.
@@ -31,8 +31,8 @@ if TYPE_CHECKING:
31
31
 
32
32
  logger = logging.getLogger("adk_code_mode.callback")
33
33
 
34
- _TOOLS_OPEN = "<tools>"
35
- _TOOLS_CLOSE = "</tools>"
34
+ _TOOLS_OPEN = "<code-mode>"
35
+ _TOOLS_CLOSE = "</code-mode>"
36
36
 
37
37
 
38
38
  def code_mode_before_model_callback(
@@ -40,7 +40,7 @@ from pydantic import ConfigDict, Field, PrivateAttr
40
40
 
41
41
  from adk_code_mode._artifact_tools import ARTIFACT_TOOLS
42
42
  from adk_code_mode.output import truncate
43
- from adk_code_mode.runtime.base import SandboxResult, SandboxBackend, SandboxSession
43
+ from adk_code_mode.runtime.base import SandboxBackend, SandboxResult, SandboxSession
44
44
  from adk_code_mode.runtime.protocol import (
45
45
  PROTOCOL_VERSION,
46
46
  DoneFrame,
@@ -68,10 +68,30 @@ CODE_MODE_SYSTEM_INSTRUCTION = """\
68
68
  # How to execute code and use tools
69
69
  Code you write in a fenced Python block (i.e. ```python) will be executed in a sandbox.
70
70
  The Python Standard Library and a custom set of tools are available to you.
71
- To see the result of your code, you need to print it. Don't make assumptions about the tool result format.
71
+ To see the result of your code, you need to print it.
72
+
73
+ For example, if you had the following tool:
74
+
75
+ ```
76
+ from tools.slack import send_message
77
+
78
+ def send_message(*, channel: str, text: str, thread_ts: str | None = ...) -> Any:
79
+ \"\"\"Send a message to a Slack channel.\"\"\"
80
+ ...
81
+ ```
82
+
83
+ To call the tool, you should write:
84
+
85
+ \"\"\"
86
+ ```python
87
+ from tools.slack import send_message
88
+
89
+ print(send_message(channel="C123", text="hi"))
90
+ ```
91
+ \"\"\"
72
92
 
73
93
  # How to use files and variables in between executions
74
- Code is executed in a new environment each time. Tool results are automatically saved as Artifacts.
94
+ Code is executed in a new environment each time.
75
95
  To list available Artifacts, use the `list_artifacts` tool. To save an Artifact, use the `save_artifact` tool, and to load an Artifact, use the `load_artifact` tool.
76
96
  """
77
97
 
@@ -172,7 +172,7 @@ async def test_agent_runs_code_block_through_executor() -> None:
172
172
  else:
173
173
  rendered = "\n".join(p for p in system_instruction if isinstance(p, str))
174
174
  assert CODE_MODE_SYSTEM_INSTRUCTION.split("\n", 1)[0] in rendered
175
- assert "<tools>" in rendered and "</tools>" in rendered
175
+ assert "<code-mode>" in rendered and "</code-mode>" in rendered
176
176
  assert "def echo" in rendered
177
177
 
178
178
 
@@ -57,8 +57,8 @@ async def test_callback_appends_block_when_system_instruction_is_none() -> None:
57
57
 
58
58
  instruction = request.config.system_instruction
59
59
  assert isinstance(instruction, str)
60
- assert instruction.startswith("<tools>")
61
- assert instruction.rstrip().endswith("</tools>")
60
+ assert instruction.startswith("<code-mode>")
61
+ assert instruction.rstrip().endswith("</code-mode>")
62
62
  assert "def ping" in instruction
63
63
 
64
64
 
@@ -73,8 +73,8 @@ async def test_callback_appends_block_to_string_system_instruction() -> None:
73
73
  instruction = request.config.system_instruction
74
74
  assert isinstance(instruction, str)
75
75
  assert instruction.startswith("You are an assistant.")
76
- assert "<tools>" in instruction
77
- assert instruction.rstrip().endswith("</tools>")
76
+ assert "<code-mode>" in instruction
77
+ assert instruction.rstrip().endswith("</code-mode>")
78
78
 
79
79
 
80
80
  @pytest.mark.asyncio
@@ -88,8 +88,8 @@ async def test_callback_appends_block_to_list_system_instruction() -> None:
88
88
  instruction = request.config.system_instruction
89
89
  assert isinstance(instruction, list)
90
90
  assert instruction[:2] == ["First part.", "Second part."]
91
- assert instruction[-1].startswith("<tools>")
92
- assert instruction[-1].rstrip().endswith("</tools>")
91
+ assert instruction[-1].startswith("<code-mode>")
92
+ assert instruction[-1].rstrip().endswith("</code-mode>")
93
93
 
94
94
 
95
95
  @pytest.mark.asyncio
@@ -1,3 +0,0 @@
1
- {
2
- ".": "0.2.0"
3
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes