agentbyte 0.2.1__tar.gz → 0.2.4__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.
- {agentbyte-0.2.1 → agentbyte-0.2.4}/CHANGELOG.md +18 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/PKG-INFO +6 -1
- {agentbyte-0.2.1 → agentbyte-0.2.4}/README.md +4 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docs/progress.md +37 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docs/study/topic_agents.md +119 -2
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docs/study/topic_tools.md +186 -3
- agentbyte-0.2.4/logo/agent-byte-avatar-low.png +0 -0
- {agentbyte-0.2.1/notebooks → agentbyte-0.2.4/notebooks/concepts/agent}/01-tools-example.ipynb +36 -20
- agentbyte-0.2.4/notebooks/concepts/agent/02a-azure-openai-wrapper.ipynb +688 -0
- {agentbyte-0.2.1/notebooks → agentbyte-0.2.4/notebooks/concepts/agent}/02b-openai-wrapper.ipynb +138 -175
- {agentbyte-0.2.1/notebooks → agentbyte-0.2.4/notebooks/concepts/agent}/03a-simple-agent-no-tool.ipynb +59 -112
- {agentbyte-0.2.1/notebooks → agentbyte-0.2.4/notebooks/concepts/agent}/03b-agent-with-tool.ipynb +55 -108
- agentbyte-0.2.1/notebooks/03d-multi-tool-agent.ipynb → agentbyte-0.2.4/notebooks/concepts/agent/03c-multi-tool-agent.ipynb +53 -103
- agentbyte-0.2.1/notebooks/03e-agent-with-context.ipynb → agentbyte-0.2.4/notebooks/concepts/agent/04-agent-with-context.ipynb +304 -89
- agentbyte-0.2.1/notebooks/03f-agent-with-middleware.ipynb → agentbyte-0.2.4/notebooks/concepts/agent/05a-agent-with-middleware.ipynb +5 -80
- agentbyte-0.2.1/notebooks/03h-agent-with-otel.ipynb → agentbyte-0.2.4/notebooks/concepts/agent/05b-agent-with-otel-middleware.ipynb +21 -1
- agentbyte-0.2.1/notebooks/03g-agent-with-memory.ipynb → agentbyte-0.2.4/notebooks/concepts/agent/06-agent-with-memory.ipynb +22 -78
- agentbyte-0.2.4/notebooks/concepts/agent/07-agent-as-tool-coordinator.ipynb +700 -0
- agentbyte-0.2.4/notebooks/concepts/agent/sample-data/contracts_asmd_seed_rows.json +92 -0
- agentbyte-0.2.4/notebooks/concepts/agent/sample-data/contracts_esmd_seed_rows.json +112 -0
- agentbyte-0.2.1/notebooks/03c-multi-turn-agent.ipynb → agentbyte-0.2.4/notebooks/usecases/04.1-multi-turn-agent.ipynb +71 -119
- agentbyte-0.2.4/notebooks/usecases/04.2-metadata-extraction-large-doc.ipynb +662 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/pyproject.toml +1 -0
- agentbyte-0.2.4/src/agentbyte/__about__.py +2 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/__init__.py +11 -1
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/agents/agent.py +3 -2
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/agents/agent_as_tool.py +12 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/agents/base.py +18 -1
- agentbyte-0.2.4/src/agentbyte/entity.py +27 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/llm/__init__.py +5 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/llm/azure_openai.py +383 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/llm/openai.py +75 -0
- agentbyte-0.2.4/src/agentbyte/llm/settings.py +220 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/middleware/__init__.py +4 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/middleware/base.py +106 -0
- agentbyte-0.2.4/src/agentbyte/session.py +121 -0
- agentbyte-0.2.4/src/agentbyte/tools/memory_tool.py +282 -0
- agentbyte-0.2.4/tests/agents/test_agent_as_tool.py +270 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/middleware/test_middleware_chain.py +134 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/test_package_api.py +20 -0
- agentbyte-0.2.4/tests/test_session.py +156 -0
- agentbyte-0.2.4/tests/tools/test_memory_tool.py +251 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/uv.lock +2 -0
- agentbyte-0.2.1/notebooks/02a-azure-openai-wrapper.ipynb +0 -877
- agentbyte-0.2.1/notebooks/pico-init-agent-test.ipynb +0 -135
- agentbyte-0.2.1/src/agentbyte/__about__.py +0 -2
- agentbyte-0.2.1/src/agentbyte/tools/memory_tool.py +0 -158
- agentbyte-0.2.1/tests/agents/test_agent_as_tool.py +0 -119
- agentbyte-0.2.1/tests/tools/test_memory_tool.py +0 -109
- {agentbyte-0.2.1 → agentbyte-0.2.4}/.github/copilot-instructions.md +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/.gitignore +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/.gitlab-ci.yml +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/.gitmodules +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/.python-version +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/LICENSE +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docker/jaeger-compose.yml +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docs/book-designing-multi-agent-system.md +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docs/implementation_plan.md +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docs/study/topic_approval.md +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docs/study/topic_memory.md +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docs/study/topic_middleware.md +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docs/study/topic_model_client.md +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docs/study/topic_otel.md +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/docs/study_plan.md +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/examples/azure_openai_usage_example.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/examples/function_tool_example.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/examples/llm_client_dependency_injection.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/examples/memory/memory_usage_example.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/examples/message_types_example.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/examples/openai_client_example.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/examples/otel/agent_with_content_capture.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/examples/otel/agent_with_telemetry.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/notebooks/agent-knowledge-building.ipynb +0 -0
- {agentbyte-0.2.1/notebooks → agentbyte-0.2.4/notebooks/auth}/00-Authentication-and-Identity-of-AI-clients.ipynb +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/agents/__init__.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/agents/types.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/cancellation_token.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/context.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/llm/auth.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/llm/base.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/llm/types.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/memory/__init__.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/memory/base.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/messages.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/middleware/otel.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/tools/__init__.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/tools/base.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/tools/core_tools.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/tools/decorator.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/src/agentbyte/types.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/agents/test_agent_basic.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/agents/test_agent_event_types.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/agents/test_agent_memory_integration.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/agents/test_agent_middleware_integration.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/agents/test_agent_stream_events.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/agents/test_tool_approval.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/llm/test_azure_client.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/llm/test_llm_types.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/llm/test_openai_client.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/memory/test_memory.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/middleware/test_otel.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/test_cancellation_token.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/test_context.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/test_messages.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/test_types.py +0 -0
- {agentbyte-0.2.1 → agentbyte-0.2.4}/tests/tools/test_tools.py +0 -0
|
@@ -4,6 +4,24 @@ All notable changes to Agentbyte are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format follows Keep a Changelog principles and semantic versioning.
|
|
6
6
|
|
|
7
|
+
## [0.2.3] - 2026-03-02
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Unified `Conversation` and `Workspace` into a single `Session` model. Both were structurally identical; domain semantics now live in `Session.metadata` (e.g. `{"name": "Blog Post"}`).
|
|
11
|
+
- `session.py` now contains `Session(Entity)`, `BaseSession(ABC)`, and `InMemorySession` — flat pattern mirroring `memory/base.py`.
|
|
12
|
+
- `AgentContext.conversation_id` field removed. `session_id: Optional[str]` (already present) is the sole context-level session link.
|
|
13
|
+
|
|
14
|
+
### Removed
|
|
15
|
+
- `src/agentbyte/conversation/` module and all exports (`Conversation`, `BaseConversation`, `InMemoryConversation`).
|
|
16
|
+
- `src/agentbyte/workspace/` module and all exports (`Workspace`, `BaseWorkspace`, `InMemoryWorkspace`).
|
|
17
|
+
- `tests/test_conversation.py`, `tests/test_workspace.py`, `tests/agents/test_agent_conversation_integration.py`.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- `tests/test_session.py` — 19 tests covering `Session` identity/equality, `to_context()`/`from_context()`, `InMemorySession` CRUD, copy-safety, and ABC contract.
|
|
21
|
+
|
|
22
|
+
### Reorganized
|
|
23
|
+
- Notebooks moved from flat layout into `notebooks/agent/`, `notebooks/auth/`, and `notebooks/usecase/` subdirectories.
|
|
24
|
+
|
|
7
25
|
## [0.2.1] - 2026-02-27
|
|
8
26
|
|
|
9
27
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentbyte
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: A toolkit for designing multiagent systems
|
|
5
5
|
Project-URL: Homepage, https://gitlab.com/pyninja/aiengineering/agentbyte
|
|
6
6
|
Project-URL: Repository, https://gitlab.com/pyninja/aiengineering/agentbyte
|
|
@@ -10,6 +10,7 @@ Author-email: MrDataPsycho <mr.data.psycho@gmail.com>
|
|
|
10
10
|
License-Expression: MIT
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Requires-Python: >=3.12
|
|
13
|
+
Requires-Dist: pydantic-settings>=2.13.0
|
|
13
14
|
Requires-Dist: pydantic>=2.12.5
|
|
14
15
|
Provides-Extra: all
|
|
15
16
|
Requires-Dist: azure-identity>=1.25.1; extra == 'all'
|
|
@@ -28,6 +29,10 @@ Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.39.1; extra == 'otel'
|
|
|
28
29
|
Requires-Dist: opentelemetry-sdk>=1.39.1; extra == 'otel'
|
|
29
30
|
Description-Content-Type: text/markdown
|
|
30
31
|
|
|
32
|
+
<p align="center">
|
|
33
|
+
<img src="logo/agent-byte-avatar-low.png" alt="Agentbyte" width="200"/>
|
|
34
|
+
</p>
|
|
35
|
+
|
|
31
36
|
# Agentbyte
|
|
32
37
|
|
|
33
38
|
Agentbyte is a Python toolkit for building and studying multiagent systems with a learning-first, implementation-oriented workflow.
|
|
@@ -268,3 +268,40 @@ Middleware parity with picoagents (Chapter 4.9), plus Agent integration.
|
|
|
268
268
|
- `git push origin v0.2.0`
|
|
269
269
|
5. Publish (if desired):
|
|
270
270
|
- `uv publish`
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Update (2026-03-02, Session Unification — Conversation/Workspace Removed)
|
|
275
|
+
|
|
276
|
+
### Design Decision
|
|
277
|
+
|
|
278
|
+
`Conversation` and `Workspace` were structurally identical (`name` was the only difference and
|
|
279
|
+
belongs in `metadata`). Introducing two separate types, two ABCs, two in-memory adapters,
|
|
280
|
+
and a `conversation_id: Optional[UUID]` field on `AgentContext` was over-engineering.
|
|
281
|
+
|
|
282
|
+
Consolidated into a single unified model:
|
|
283
|
+
|
|
284
|
+
- `session.py` now contains `Session`, `BaseSession`, `InMemorySession` (flat, mirrors `memory/base.py`)
|
|
285
|
+
- `AgentContext.conversation_id` removed — `session_id: str` (already present) is sufficient
|
|
286
|
+
- Callers use `metadata` for domain semantics (e.g. `metadata={"name": "Blog Post"}` for workspace-like sessions)
|
|
287
|
+
|
|
288
|
+
### Source Changes
|
|
289
|
+
|
|
290
|
+
- `src/agentbyte/session.py` — rewritten: `Session(Entity)` + `BaseSession(ABC)` + `InMemorySession`
|
|
291
|
+
- `src/agentbyte/conversation/` — **deleted**
|
|
292
|
+
- `src/agentbyte/workspace/` — **deleted**
|
|
293
|
+
- `src/agentbyte/context.py` — `conversation_id` field and `UUID` import removed
|
|
294
|
+
- `src/agentbyte/__init__.py` — exports updated: `Session`, `BaseSession`, `InMemorySession`
|
|
295
|
+
|
|
296
|
+
### Tests
|
|
297
|
+
|
|
298
|
+
- `tests/test_conversation.py` — **deleted**
|
|
299
|
+
- `tests/test_workspace.py` — **deleted**
|
|
300
|
+
- `tests/agents/test_agent_conversation_integration.py` — **deleted**
|
|
301
|
+
- `tests/test_package_api.py` — `test_conversation_import_paths` replaced with `test_session_import_paths`
|
|
302
|
+
- `tests/test_session.py` — **new**: 15 tests covering `Session` model, `to_context()`/`from_context()`, `InMemorySession` CRUD, copy-safety, and ABC contract
|
|
303
|
+
|
|
304
|
+
### Validation Run
|
|
305
|
+
|
|
306
|
+
- Ruff: `All checks passed!`
|
|
307
|
+
- Pytest: **253 passed** (`uv run pytest tests/ -q`)
|
|
@@ -12,6 +12,7 @@ This file now contains **agents-only** study material.
|
|
|
12
12
|
- Memory content moved to [docs/study/topic_memory.md](../study/topic_memory.md)
|
|
13
13
|
- Middleware content moved to [docs/study/topic_middleware.md](../study/topic_middleware.md)
|
|
14
14
|
- Approval deep dive moved to [docs/study/topic_approval.md](../study/topic_approval.md)
|
|
15
|
+
- Session persistence (unified) → `src/agentbyte/session.py` (`Session`, `BaseSession`, `InMemorySession`)
|
|
15
16
|
- Study gaps are tracked at the end of this same document.
|
|
16
17
|
|
|
17
18
|
---
|
|
@@ -78,6 +79,32 @@ Common checkpoints:
|
|
|
78
79
|
3. Before tool execution
|
|
79
80
|
4. During long-running operations linked to futures/tasks
|
|
80
81
|
|
|
82
|
+
### 4.12 Context Engineering
|
|
83
|
+
|
|
84
|
+
> "If the LLM is the CPU, then the context window is RAM." — Andrej Karpathy
|
|
85
|
+
|
|
86
|
+
As agents execute multi-step tasks, context grows with every tool call, memory retrieval, and conversation turn. Without management this causes two problems:
|
|
87
|
+
|
|
88
|
+
- **Context rot** — performance degrades as token count approaches the model's attention limit (empirically around 32K tokens).
|
|
89
|
+
- **Context explosion** — a long tool-heavy run can accumulate 20K+ tokens, pushing system instructions out of the attention window.
|
|
90
|
+
|
|
91
|
+
**Four management strategies (Chapter 4.12.3):**
|
|
92
|
+
|
|
93
|
+
| Strategy | Mechanism | Where in Agentbyte |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| Compaction | Summarise + trim old messages at model-call time | `ContextCompactionMiddleware` |
|
|
96
|
+
| Active memory management | Agent explicitly writes/deletes knowledge via tool | `MemoryTool` (6-command CRUD) |
|
|
97
|
+
| Context isolation | Specialist agents run in their own context bubble | `AgentAsTool` / `result_strategy` |
|
|
98
|
+
| Tool result filtering | Truncate large tool outputs before appending to context | Future: agent `_execute_tool_call` hook |
|
|
99
|
+
|
|
100
|
+
**Empirical token reduction (from the book):**
|
|
101
|
+
|
|
102
|
+
- Baseline (no management): 21,633 tokens
|
|
103
|
+
- Compaction middleware: 7,276 tokens (−66%)
|
|
104
|
+
- Isolation via agents-as-tools: 5,892 tokens (−73%)
|
|
105
|
+
|
|
106
|
+
Key takeaway: architectural prevention (isolation) outperforms reactive trimming (compaction).
|
|
107
|
+
|
|
81
108
|
---
|
|
82
109
|
|
|
83
110
|
## Part 2: Agentbyte Implementation Mapping
|
|
@@ -90,6 +117,10 @@ Common checkpoints:
|
|
|
90
117
|
- `src/agentbyte/agents/types.py`
|
|
91
118
|
- `src/agentbyte/context.py`
|
|
92
119
|
- `src/agentbyte/cancellation_token.py`
|
|
120
|
+
- `src/agentbyte/session.py` — `Session`, `BaseSession`, `InMemorySession` unified session persistence
|
|
121
|
+
|
|
122
|
+
- `src/agentbyte/middleware/base.py` — `ContextCompactionMiddleware`, `OperationType`
|
|
123
|
+
- `src/agentbyte/tools/memory_tool.py` — `MemoryBackend`, `MemoryTool` (6 commands)
|
|
93
124
|
|
|
94
125
|
### Current Behavior Highlights
|
|
95
126
|
|
|
@@ -100,6 +131,81 @@ Common checkpoints:
|
|
|
100
131
|
- Middleware-integrated model/tool execution now uses event-aware `execute_stream(...)`
|
|
101
132
|
- Middleware events can flow through `run_stream(...)` and pause execution via `ToolApprovalEvent`
|
|
102
133
|
- Token-level streaming is auto-disabled when middleware is configured (picoagents-aligned fallback)
|
|
134
|
+
- Agent operation strings are constants via `OperationType` enum (`MODEL_CALL`, `TOOL_CALL`, etc.)
|
|
135
|
+
|
|
136
|
+
### ContextCompactionMiddleware — How It Works
|
|
137
|
+
|
|
138
|
+
**File:** `src/agentbyte/middleware/base.py`
|
|
139
|
+
|
|
140
|
+
The middleware hooks into the `process_request` phase of every `model_call` operation. It does nothing for `tool_call` or other operations.
|
|
141
|
+
|
|
142
|
+
**Decision point:**
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
estimated_tokens = total_chars_of_all_message_content ÷ 4
|
|
146
|
+
if estimated_tokens > max_tokens:
|
|
147
|
+
apply compaction
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The `÷ 4` heuristic avoids a hard tiktoken dependency. It can be swapped for an exact tokeniser in production.
|
|
151
|
+
|
|
152
|
+
**Compaction output structure:**
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
[messages[0], SystemMessage("[Previous context summarized...]"), *messages[-keep_last:]]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
- `messages[0]` — the original system/first message is always preserved.
|
|
159
|
+
- `SystemMessage(...)` — a placeholder that signals to the model that prior context was trimmed.
|
|
160
|
+
- `messages[-keep_last:]` — the most recent `keep_last` messages (default 10) are retained verbatim.
|
|
161
|
+
|
|
162
|
+
**Constructor:**
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
ContextCompactionMiddleware(
|
|
166
|
+
max_tokens: int = 32_000, # trigger threshold
|
|
167
|
+
keep_last: int = 10, # messages to retain from tail
|
|
168
|
+
)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Metadata written on compaction:**
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
context.metadata["compaction_applied"] = True
|
|
175
|
+
context.metadata["original_message_count"] = len(messages)
|
|
176
|
+
context.metadata["compacted_message_count"] = len(compacted)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Usage:**
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from agentbyte.middleware import ContextCompactionMiddleware
|
|
183
|
+
|
|
184
|
+
agent = Agent(
|
|
185
|
+
name="researcher",
|
|
186
|
+
model_client=llm,
|
|
187
|
+
tools=[...],
|
|
188
|
+
middlewares=[ContextCompactionMiddleware(max_tokens=16_000, keep_last=8)],
|
|
189
|
+
)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Combine with `AgentAsTool` for maximum reduction: the specialist compacts internally while the coordinator only ever sees the `result_strategy`-filtered summary.
|
|
193
|
+
|
|
194
|
+
### OperationType Enum
|
|
195
|
+
|
|
196
|
+
**File:** `src/agentbyte/middleware/base.py`
|
|
197
|
+
|
|
198
|
+
`OperationType(str, Enum)` replaces bare string literals for the `operation` field on `MiddlewareContext`. Values remain plain strings so existing middleware that checks `context.operation == "model_call"` continues to work unchanged.
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
class OperationType(str, Enum):
|
|
202
|
+
MODEL_CALL = "model_call"
|
|
203
|
+
TOOL_CALL = "tool_call"
|
|
204
|
+
MEMORY_ACCESS = "memory_access"
|
|
205
|
+
APPROVAL = "approval"
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The agent now passes `OperationType.MODEL_CALL` and `OperationType.TOOL_CALL` into `middleware_chain.execute_stream(...)`. Middleware can branch on either the enum or the string — both compare equal.
|
|
103
209
|
|
|
104
210
|
---
|
|
105
211
|
|
|
@@ -133,6 +239,14 @@ Common checkpoints:
|
|
|
133
239
|
- [x] Resume after `approve_tool(...)`
|
|
134
240
|
- [x] Handle denial after `deny_tool(...)`
|
|
135
241
|
|
|
242
|
+
### ✅ Context Engineering (Chapter 4.12)
|
|
243
|
+
|
|
244
|
+
- [x] `ContextCompactionMiddleware` — model-call intercept with token estimation and tail-keep strategy
|
|
245
|
+
- [x] `OperationType` enum — canonical operation constants, backward-compatible with plain strings
|
|
246
|
+
- [x] Context isolation via `AgentAsTool` + `result_strategy` (Chapter 4.11)
|
|
247
|
+
- [x] Agent-managed memory via `MemoryTool` 6-command CRUD (Chapter 4.8)
|
|
248
|
+
- [ ] Tool result filtering hook inside `_execute_tool_call` (optional, future)
|
|
249
|
+
|
|
136
250
|
---
|
|
137
251
|
|
|
138
252
|
## Summary
|
|
@@ -164,12 +278,13 @@ For memory and middleware deep dives, use the dedicated topic files listed at th
|
|
|
164
278
|
- base memory abstractions and models
|
|
165
279
|
- list/file memory implementations
|
|
166
280
|
- tests and examples
|
|
281
|
+
- `MemoryTool` / `MemoryBackend` with picoagents 6-command set (`view`, `create`, `str_replace`, `insert`, `delete`, `rename`)
|
|
282
|
+
- path-traversal sandbox and security validation
|
|
167
283
|
|
|
168
284
|
#### Remaining Gaps
|
|
169
285
|
|
|
170
286
|
- [ ] component serialization parity for memory classes/configs
|
|
171
287
|
- [ ] vector memory backend(s)
|
|
172
|
-
- [ ] agent-managed memory tool (`MemoryTool`) and security-tested file operations
|
|
173
288
|
|
|
174
289
|
### Middleware
|
|
175
290
|
|
|
@@ -181,12 +296,14 @@ For memory and middleware deep dives, use the dedicated topic files listed at th
|
|
|
181
296
|
- middleware `execute_stream(...)` parity baseline
|
|
182
297
|
- middleware event propagation in agent streaming path
|
|
183
298
|
- middleware pause handling (`ToolApprovalEvent`) in agent loop
|
|
299
|
+
- `OperationType(str, Enum)` — canonical operation constants with backward compat
|
|
300
|
+
- `ContextCompactionMiddleware` — model-call token budget trimming (Chapter 4.12.3)
|
|
184
301
|
|
|
185
302
|
#### Remaining Gaps
|
|
186
303
|
|
|
187
304
|
- [ ] approval middleware (optional centralization)
|
|
188
305
|
- [ ] caching middleware (optional)
|
|
189
|
-
- [ ]
|
|
306
|
+
- [ ] tool result filtering hook inside agent execution loop (Chapter 4.12.3)
|
|
190
307
|
|
|
191
308
|
### Observability / Production
|
|
192
309
|
|
|
@@ -1571,6 +1571,176 @@ if __name__ == "__main__":
|
|
|
1571
1571
|
|
|
1572
1572
|
---
|
|
1573
1573
|
|
|
1574
|
+
## Part 6: MemoryTool — Agent-Managed Persistent Memory
|
|
1575
|
+
|
|
1576
|
+
### Motivation
|
|
1577
|
+
|
|
1578
|
+
Agents often need to remember information across sessions or across many turns within a single session. There are two memory models:
|
|
1579
|
+
|
|
1580
|
+
- **Application-managed memory** (`BaseMemory` subclasses): the framework decides what to store and retrieve — transparent to the agent.
|
|
1581
|
+
- **Agent-managed memory** (`MemoryTool`): the agent explicitly calls a tool to read, write, and organise its own knowledge files — the agent is in full control.
|
|
1582
|
+
|
|
1583
|
+
`MemoryTool` implements the second model. It mirrors the six file-system-style commands from Chapter 4.8.3 / picoagents `_memory_tool.py`.
|
|
1584
|
+
|
|
1585
|
+
### Architecture: Two Classes
|
|
1586
|
+
|
|
1587
|
+
```
|
|
1588
|
+
MemoryBackend ← pure Python, no async, operates on the real filesystem
|
|
1589
|
+
↑
|
|
1590
|
+
MemoryTool ← BaseTool subclass, wraps MemoryBackend, async execute()
|
|
1591
|
+
```
|
|
1592
|
+
|
|
1593
|
+
Separating storage logic (`MemoryBackend`) from the tool interface (`MemoryTool`) makes each class easy to test independently and allows future backends (e.g. in-memory, remote) without changing the tool layer.
|
|
1594
|
+
|
|
1595
|
+
### The Six Commands
|
|
1596
|
+
|
|
1597
|
+
| Command | Required params | Optional params | Purpose |
|
|
1598
|
+
|---|---|---|---|
|
|
1599
|
+
| `view` | `path` | — | Read file content; list directory; `"/"` lists entire root |
|
|
1600
|
+
| `create` | `path`, `file_text` | — | Write (or overwrite) a file; creates parent dirs automatically |
|
|
1601
|
+
| `str_replace` | `path`, `old_str`, `new_str` | — | Replace **first occurrence** of `old_str` with `new_str` |
|
|
1602
|
+
| `insert` | `path`, `insert_line`, `insert_text` | — | Insert text after 1-based line number |
|
|
1603
|
+
| `delete` | `path` | — | Remove a file |
|
|
1604
|
+
| `rename` | `path`, `new_path` | — | Move / rename a file within the sandbox |
|
|
1605
|
+
|
|
1606
|
+
### Security: Sandbox Model
|
|
1607
|
+
|
|
1608
|
+
All paths are validated by `MemoryBackend._validate_path()`:
|
|
1609
|
+
|
|
1610
|
+
```python
|
|
1611
|
+
def _validate_path(self, path: str) -> Path:
|
|
1612
|
+
normalized = path.strip().lstrip("/") # strip leading slashes
|
|
1613
|
+
if not normalized:
|
|
1614
|
+
raise ValueError("path cannot be empty")
|
|
1615
|
+
full_path = (self.base_path / normalized).resolve()
|
|
1616
|
+
try:
|
|
1617
|
+
full_path.relative_to(self.base_path) # raises if outside
|
|
1618
|
+
except ValueError as exc:
|
|
1619
|
+
raise ValueError("Access denied: path is outside memory directory") from exc
|
|
1620
|
+
return full_path
|
|
1621
|
+
```
|
|
1622
|
+
|
|
1623
|
+
**Key points:**
|
|
1624
|
+
- Leading `/` is stripped — `"/etc/passwd"` becomes `"etc/passwd"` inside the sandbox, not the real `/etc/passwd`.
|
|
1625
|
+
- `Path.resolve()` expands `..` — `"../../etc/passwd"` resolves outside `base_path` and raises `ValueError`.
|
|
1626
|
+
- `view("/")` is special-cased **before** `_validate_path` is called, so it lists the root without hitting the empty-path guard.
|
|
1627
|
+
|
|
1628
|
+
### MemoryBackend Key Design Decisions
|
|
1629
|
+
|
|
1630
|
+
**`create` (auto-mkdir):**
|
|
1631
|
+
```python
|
|
1632
|
+
def create(self, path: str, file_text: str) -> str:
|
|
1633
|
+
target = self._validate_path(path)
|
|
1634
|
+
target.parent.mkdir(parents=True, exist_ok=True) # ← auto-create subdirs
|
|
1635
|
+
target.write_text(file_text, encoding="utf-8")
|
|
1636
|
+
return f"Created: {path}"
|
|
1637
|
+
```
|
|
1638
|
+
|
|
1639
|
+
**`str_replace` (first occurrence only):**
|
|
1640
|
+
```python
|
|
1641
|
+
def str_replace(self, path: str, old_str: str, new_str: str) -> str:
|
|
1642
|
+
content = target.read_text(encoding="utf-8")
|
|
1643
|
+
if old_str not in content:
|
|
1644
|
+
raise ValueError(f"String not found in file: {old_str!r}")
|
|
1645
|
+
updated = content.replace(old_str, new_str, 1) # ← count=1
|
|
1646
|
+
target.write_text(updated, encoding="utf-8")
|
|
1647
|
+
return f"Replaced in: {path}"
|
|
1648
|
+
```
|
|
1649
|
+
Replacing only the *first* occurrence is intentional: it makes the edit deterministic and avoids unintended bulk replacements — the same design choice picoagents makes.
|
|
1650
|
+
|
|
1651
|
+
**`insert` (1-based line numbers):**
|
|
1652
|
+
```python
|
|
1653
|
+
def insert(self, path: str, insert_line: int, insert_text: str) -> str:
|
|
1654
|
+
lines = target.read_text(encoding="utf-8").splitlines(keepends=True)
|
|
1655
|
+
# insert_line=0 → prepend; insert_line=len(lines) → append
|
|
1656
|
+
lines.insert(insert_line, text)
|
|
1657
|
+
target.write_text("".join(lines), encoding="utf-8")
|
|
1658
|
+
```
|
|
1659
|
+
Python's `list.insert(i, v)` treats `i` as the index *before* which to insert, so `insert_line=3` places text after line 3 (0-indexed slot 3 = after the 3rd line). The guard `0 <= insert_line <= len(lines)` keeps the range valid.
|
|
1660
|
+
|
|
1661
|
+
### MemoryTool Parameters Schema
|
|
1662
|
+
|
|
1663
|
+
The tool exposes all six commands as a single `command` enum plus optional per-command fields:
|
|
1664
|
+
|
|
1665
|
+
```python
|
|
1666
|
+
@property
|
|
1667
|
+
def parameters(self) -> Dict[str, Any]:
|
|
1668
|
+
return {
|
|
1669
|
+
"type": "object",
|
|
1670
|
+
"properties": {
|
|
1671
|
+
"command": {
|
|
1672
|
+
"type": "string",
|
|
1673
|
+
"enum": ["view", "create", "str_replace", "insert", "delete", "rename"],
|
|
1674
|
+
},
|
|
1675
|
+
"path": {"type": "string"},
|
|
1676
|
+
"file_text": {"type": "string"}, # create
|
|
1677
|
+
"old_str": {"type": "string"}, # str_replace
|
|
1678
|
+
"new_str": {"type": "string"}, # str_replace
|
|
1679
|
+
"insert_line": {"type": "integer"}, # insert
|
|
1680
|
+
"insert_text": {"type": "string"}, # insert
|
|
1681
|
+
"new_path": {"type": "string"}, # rename
|
|
1682
|
+
},
|
|
1683
|
+
"required": ["command"], # ← only command is always required
|
|
1684
|
+
}
|
|
1685
|
+
```
|
|
1686
|
+
|
|
1687
|
+
Only `command` is globally required; per-command fields are validated at runtime inside `execute()` via `_require_str()` / `isinstance(insert_line, int)`.
|
|
1688
|
+
|
|
1689
|
+
### Usage Example
|
|
1690
|
+
|
|
1691
|
+
```python
|
|
1692
|
+
import asyncio
|
|
1693
|
+
from agentbyte.tools import MemoryTool
|
|
1694
|
+
|
|
1695
|
+
async def demo():
|
|
1696
|
+
mem = MemoryTool(base_path="/tmp/agent_memory")
|
|
1697
|
+
|
|
1698
|
+
# Create a file
|
|
1699
|
+
r = await mem.execute({"command": "create", "path": "notes.md",
|
|
1700
|
+
"file_text": "# Notes\nFirst note."})
|
|
1701
|
+
print(r.result) # Created: notes.md
|
|
1702
|
+
|
|
1703
|
+
# Read it back
|
|
1704
|
+
r = await mem.execute({"command": "view", "path": "notes.md"})
|
|
1705
|
+
print(r.result) # # Notes\nFirst note.
|
|
1706
|
+
|
|
1707
|
+
# Patch a line
|
|
1708
|
+
r = await mem.execute({"command": "str_replace", "path": "notes.md",
|
|
1709
|
+
"old_str": "First note.", "new_str": "Updated note."})
|
|
1710
|
+
print(r.result) # Replaced in: notes.md
|
|
1711
|
+
|
|
1712
|
+
# Insert a new line after line 1
|
|
1713
|
+
r = await mem.execute({"command": "insert", "path": "notes.md",
|
|
1714
|
+
"insert_line": 1, "insert_text": "Second note."})
|
|
1715
|
+
|
|
1716
|
+
# List all memory files
|
|
1717
|
+
r = await mem.execute({"command": "view", "path": "/"})
|
|
1718
|
+
print(r.result) # ['notes.md']
|
|
1719
|
+
|
|
1720
|
+
# Rename
|
|
1721
|
+
r = await mem.execute({"command": "rename", "path": "notes.md",
|
|
1722
|
+
"new_path": "archive/notes.md"})
|
|
1723
|
+
|
|
1724
|
+
# Delete
|
|
1725
|
+
r = await mem.execute({"command": "delete", "path": "archive/notes.md"})
|
|
1726
|
+
|
|
1727
|
+
asyncio.run(demo())
|
|
1728
|
+
```
|
|
1729
|
+
|
|
1730
|
+
### Difference from Old (4-Command) MemoryTool
|
|
1731
|
+
|
|
1732
|
+
| Old command | New equivalent |
|
|
1733
|
+
|---|---|
|
|
1734
|
+
| `read_knowledge` | `view` |
|
|
1735
|
+
| `write_knowledge` | `create` |
|
|
1736
|
+
| `list_knowledge` | `view` on directory path (or `view("/")`) |
|
|
1737
|
+
| `delete_knowledge` | `delete` |
|
|
1738
|
+
| *(missing)* | `str_replace` |
|
|
1739
|
+
| *(missing)* | `insert` |
|
|
1740
|
+
| *(missing)* | `rename` |
|
|
1741
|
+
|
|
1742
|
+
---
|
|
1743
|
+
|
|
1574
1744
|
## Part 5: Parity Checklist with picoagents
|
|
1575
1745
|
|
|
1576
1746
|
### ✅ BaseTool Interface
|
|
@@ -1615,6 +1785,20 @@ if __name__ == "__main__":
|
|
|
1615
1785
|
- [x] Streaming tool outputs are forwarded and finalized into `ToolMessage` via `ToolResult`
|
|
1616
1786
|
- [x] Covered by agent tests (`tests/agents/test_agent_basic.py`)
|
|
1617
1787
|
|
|
1788
|
+
### ✅ MemoryTool (Agent-Managed Memory)
|
|
1789
|
+
- [x] `MemoryBackend` class — pure Python, no async, real filesystem operations
|
|
1790
|
+
- [x] `MemoryTool` class — `BaseTool` subclass, async `execute()` dispatch
|
|
1791
|
+
- [x] `view` — file content OR directory listing; `view("/")` lists full root
|
|
1792
|
+
- [x] `create` — write/overwrite file, parent dirs auto-created
|
|
1793
|
+
- [x] `str_replace` — replaces **first occurrence** only (deterministic)
|
|
1794
|
+
- [x] `insert` — insert text after 1-based line number (0 = prepend, `len` = append)
|
|
1795
|
+
- [x] `delete` — remove file (directory deletion not allowed via this command)
|
|
1796
|
+
- [x] `rename` — move/rename within sandbox, validates both src and dst
|
|
1797
|
+
- [x] `_validate_path` sandbox — strips leading `/`, `resolve()` + `relative_to()` check
|
|
1798
|
+
- [x] `_require_str` helper — uniform required-field validation in `execute()`
|
|
1799
|
+
- [x] Parameters schema has `command` as only globally-required field
|
|
1800
|
+
- [x] 30+ tests in `tests/tools/test_memory_tool.py` covering all commands and security edge cases
|
|
1801
|
+
|
|
1618
1802
|
---
|
|
1619
1803
|
|
|
1620
1804
|
## Summary
|
|
@@ -1627,7 +1811,6 @@ This study document provides:
|
|
|
1627
1811
|
4. **Usage Examples**: Practical demonstrations of all features
|
|
1628
1812
|
5. **Validation**: Test script to verify correctness
|
|
1629
1813
|
6. **Parity Checklist**: Verification that Agentbyte matches picoagents capabilities
|
|
1814
|
+
7. **MemoryTool**: Agent-managed persistent file memory with 6-command picoagents-parity set, sandbox security, and full test coverage
|
|
1630
1815
|
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
Tool system is implemented in `src/agentbyte/tools/` and integrated with `Agent` execution paths.
|
|
1816
|
+
Tool system is fully implemented in `src/agentbyte/tools/` and integrated with `Agent` execution paths.
|
|
Binary file
|
{agentbyte-0.2.1/notebooks → agentbyte-0.2.4/notebooks/concepts/agent}/01-tools-example.ipynb
RENAMED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"cell_type": "code",
|
|
19
|
-
"execution_count":
|
|
19
|
+
"execution_count": 1,
|
|
20
20
|
"id": "f7df8759",
|
|
21
21
|
"metadata": {},
|
|
22
22
|
"outputs": [],
|
|
@@ -38,14 +38,14 @@
|
|
|
38
38
|
"id": "406d7dd7",
|
|
39
39
|
"metadata": {},
|
|
40
40
|
"source": [
|
|
41
|
-
"## Understanding ToolResult\n",
|
|
41
|
+
"## Example 1a: Understanding ToolResult\n",
|
|
42
42
|
"\n",
|
|
43
|
-
"`ToolResult` is the standardized structure for all tool execution outcomes. It's a Pydantic BaseModel with immutable fields
|
|
43
|
+
"`ToolResult` is the standardized structure for all tool execution outcomes. It's a Pydantic BaseModel with immutable fields."
|
|
44
44
|
]
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"cell_type": "code",
|
|
48
|
-
"execution_count":
|
|
48
|
+
"execution_count": 2,
|
|
49
49
|
"id": "17c1e655",
|
|
50
50
|
"metadata": {},
|
|
51
51
|
"outputs": [
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
},
|
|
113
113
|
{
|
|
114
114
|
"cell_type": "code",
|
|
115
|
-
"execution_count":
|
|
115
|
+
"execution_count": 3,
|
|
116
116
|
"id": "f414f5ac",
|
|
117
117
|
"metadata": {},
|
|
118
118
|
"outputs": [
|
|
@@ -149,20 +149,12 @@
|
|
|
149
149
|
"id": "ba737a65",
|
|
150
150
|
"metadata": {},
|
|
151
151
|
"source": [
|
|
152
|
-
"## Example
|
|
153
|
-
]
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
"cell_type": "markdown",
|
|
157
|
-
"id": "28cf2bc4",
|
|
158
|
-
"metadata": {},
|
|
159
|
-
"source": [
|
|
160
|
-
"## Example 1b: ToolResult and Error Handling"
|
|
152
|
+
"## Example 1b: Error Handling with FunctionTool"
|
|
161
153
|
]
|
|
162
154
|
},
|
|
163
155
|
{
|
|
164
156
|
"cell_type": "code",
|
|
165
|
-
"execution_count":
|
|
157
|
+
"execution_count": 4,
|
|
166
158
|
"id": "df267ff3",
|
|
167
159
|
"metadata": {},
|
|
168
160
|
"outputs": [
|
|
@@ -200,9 +192,17 @@
|
|
|
200
192
|
"await test_error_handling()"
|
|
201
193
|
]
|
|
202
194
|
},
|
|
195
|
+
{
|
|
196
|
+
"cell_type": "markdown",
|
|
197
|
+
"id": "fc9a8a3b",
|
|
198
|
+
"metadata": {},
|
|
199
|
+
"source": [
|
|
200
|
+
"## Example 2a: @tool Decorator (No Parentheses)"
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
203
|
{
|
|
204
204
|
"cell_type": "code",
|
|
205
|
-
"execution_count":
|
|
205
|
+
"execution_count": 5,
|
|
206
206
|
"id": "e1f229c0",
|
|
207
207
|
"metadata": {},
|
|
208
208
|
"outputs": [
|
|
@@ -234,9 +234,17 @@
|
|
|
234
234
|
"print(f\"\\nDirect call: multiply_numbers(4, 5) = {direct_result}\")"
|
|
235
235
|
]
|
|
236
236
|
},
|
|
237
|
+
{
|
|
238
|
+
"cell_type": "markdown",
|
|
239
|
+
"id": "bc29534f",
|
|
240
|
+
"metadata": {},
|
|
241
|
+
"source": [
|
|
242
|
+
"## Example 2b: Executing @tool Decorator"
|
|
243
|
+
]
|
|
244
|
+
},
|
|
237
245
|
{
|
|
238
246
|
"cell_type": "code",
|
|
239
|
-
"execution_count":
|
|
247
|
+
"execution_count": 6,
|
|
240
248
|
"id": "cf74f858",
|
|
241
249
|
"metadata": {},
|
|
242
250
|
"outputs": [
|
|
@@ -262,12 +270,12 @@
|
|
|
262
270
|
"id": "366fb111",
|
|
263
271
|
"metadata": {},
|
|
264
272
|
"source": [
|
|
265
|
-
"## Example
|
|
273
|
+
"## Example 3a: @tool Decorator (With Parentheses)"
|
|
266
274
|
]
|
|
267
275
|
},
|
|
268
276
|
{
|
|
269
277
|
"cell_type": "code",
|
|
270
|
-
"execution_count":
|
|
278
|
+
"execution_count": 7,
|
|
271
279
|
"id": "56a451e5",
|
|
272
280
|
"metadata": {},
|
|
273
281
|
"outputs": [
|
|
@@ -322,9 +330,17 @@
|
|
|
322
330
|
"print(f\"Approval Mode is ALWAYS: {reset_admin.approval_mode == ApprovalMode.ALWAYS}\")"
|
|
323
331
|
]
|
|
324
332
|
},
|
|
333
|
+
{
|
|
334
|
+
"cell_type": "markdown",
|
|
335
|
+
"id": "91525edb",
|
|
336
|
+
"metadata": {},
|
|
337
|
+
"source": [
|
|
338
|
+
"## Example 3b: Executing Tools with Approval"
|
|
339
|
+
]
|
|
340
|
+
},
|
|
325
341
|
{
|
|
326
342
|
"cell_type": "code",
|
|
327
|
-
"execution_count":
|
|
343
|
+
"execution_count": 8,
|
|
328
344
|
"id": "1ca85e41",
|
|
329
345
|
"metadata": {},
|
|
330
346
|
"outputs": [
|