agentx-dev 3.0.5__tar.gz → 3.1.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.
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/PKG-INFO +233 -6
- agentx_dev-3.0.5/agentx_dev.egg-info/PKG-INFO → agentx_dev-3.1.1/README.md +1049 -886
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/ChatModel.py +1530 -1174
- agentx_dev-3.1.1/agentx_dev/Compiler.py +308 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/DefaultTools.py +271 -31
- agentx_dev-3.1.1/agentx_dev/Embeddings.py +649 -0
- agentx_dev-3.1.1/agentx_dev/Evals.py +508 -0
- agentx_dev-3.1.1/agentx_dev/Handoffs.py +420 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Memory.py +27 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Runner/AgentRun.py +273 -7
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Runner/AsyncAgentRun.py +14 -6
- agentx_dev-3.1.1/agentx_dev/Splitters.py +306 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Supervisor.py +1117 -1026
- agentx_dev-3.1.1/agentx_dev/VectorStores/__init__.py +43 -0
- agentx_dev-3.1.1/agentx_dev/VectorStores/chroma_store.py +171 -0
- agentx_dev-3.1.1/agentx_dev/VectorStores/pg_store.py +237 -0
- agentx_dev-3.1.1/agentx_dev/VectorStores/qdrant_store.py +198 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/WebTools.py +84 -2
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/__init__.py +60 -1
- agentx_dev-3.0.5/README.md → agentx_dev-3.1.1/agentx_dev.egg-info/PKG-INFO +1113 -835
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev.egg-info/SOURCES.txt +14 -1
- agentx_dev-3.1.1/agentx_dev.egg-info/requires.txt +37 -0
- agentx_dev-3.1.1/examples/agentic_rag_demo.py +270 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/supervisor_codebase_analysis_demo.py +57 -38
- agentx_dev-3.1.1/examples/v3_1_1_features_demo.py +246 -0
- agentx_dev-3.1.1/examples/v3_1_comprehensive_demo.py +446 -0
- agentx_dev-3.1.1/examples/v3_1_features_demo.py +260 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/pyproject.toml +19 -4
- agentx_dev-3.0.5/agentx_dev.egg-info/requires.txt +0 -20
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/AGENTX.md +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/LICENSE +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/MANIFEST.in +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Agents/Agent.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Agents/__init__.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/AsyncTools.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/AutoSetup.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Cache.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Config.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Loader.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/MCP.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Observability.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Planner.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Runner/__init__.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Runner/promptTemplate.yaml +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Session.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Streaming.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/Tools.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/resources/__init__.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev/resources/promptTemplate.yaml +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev.egg-info/dependency_links.txt +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/agentx_dev.egg-info/top_level.txt +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/README.md +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/async_example.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/async_quickstart.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/auto_features_example.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/caching_example.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/chatbot_example.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/complete_example.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/concurrent_example.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/concurrent_tool_example.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/file_agent_demo.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/function_calling_demo.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/mcp_demo.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/observability_example.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/orchestration_demo.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/planner_example.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/supervisor_example.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/examples/sync_quickstart.py +0 -0
- {agentx_dev-3.0.5 → agentx_dev-3.1.1}/setup.cfg +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentx-dev
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary: A
|
|
3
|
+
Version: 3.1.1
|
|
4
|
+
Summary: A production-grade Python framework for building LLM agents. Multi-provider chat, permission-gated tools, RAG (in-mem + Chroma/Qdrant/pgvector), semantic memory, Supervisor + Handoffs multi-agent orchestration with streaming, evals harness, prompt caching, Batch API, prompt-optimizer, MCP integration.
|
|
5
5
|
Author-email: Bruce-Arhin Shadrach <brucearhin098@gmail.com>
|
|
6
6
|
License: MIT License
|
|
7
7
|
|
|
@@ -36,23 +36,36 @@ Requires-Dist: pydantic>=2.0
|
|
|
36
36
|
Requires-Dist: PyYAML>=5.3.1
|
|
37
37
|
Requires-Dist: rich>=10.0
|
|
38
38
|
Provides-Extra: anthropic
|
|
39
|
-
Requires-Dist: anthropic>=0.
|
|
39
|
+
Requires-Dist: anthropic>=0.36; extra == "anthropic"
|
|
40
40
|
Provides-Extra: mcp
|
|
41
41
|
Requires-Dist: mcp>=0.1; extra == "mcp"
|
|
42
42
|
Provides-Extra: otel
|
|
43
43
|
Requires-Dist: opentelemetry-api>=1.20; extra == "otel"
|
|
44
44
|
Requires-Dist: opentelemetry-sdk>=1.20; extra == "otel"
|
|
45
|
+
Provides-Extra: chroma
|
|
46
|
+
Requires-Dist: chromadb>=0.4; extra == "chroma"
|
|
47
|
+
Provides-Extra: qdrant
|
|
48
|
+
Requires-Dist: qdrant-client>=1.6; extra == "qdrant"
|
|
49
|
+
Provides-Extra: pgvector
|
|
50
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == "pgvector"
|
|
51
|
+
Provides-Extra: dev
|
|
52
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
53
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
54
|
+
Requires-Dist: numpy>=1.24; extra == "dev"
|
|
45
55
|
Provides-Extra: all
|
|
46
|
-
Requires-Dist: anthropic>=0.
|
|
56
|
+
Requires-Dist: anthropic>=0.36; extra == "all"
|
|
47
57
|
Requires-Dist: mcp>=0.1; extra == "all"
|
|
48
58
|
Requires-Dist: opentelemetry-api>=1.20; extra == "all"
|
|
49
59
|
Requires-Dist: opentelemetry-sdk>=1.20; extra == "all"
|
|
60
|
+
Requires-Dist: chromadb>=0.4; extra == "all"
|
|
61
|
+
Requires-Dist: qdrant-client>=1.6; extra == "all"
|
|
62
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == "all"
|
|
50
63
|
Dynamic: license-file
|
|
51
64
|
|
|
52
65
|
# agentx_dev
|
|
53
66
|
|
|
54
|
-
A
|
|
55
|
-
|
|
67
|
+
A production-grade Python framework for building LLM agents.
|
|
68
|
+
Nothing sprawls.
|
|
56
69
|
|
|
57
70
|
This README walks you through real usage — start an agent, add tools,
|
|
58
71
|
connect MCP servers, ship to production. Every code block is something
|
|
@@ -60,6 +73,220 @@ you can paste and run.
|
|
|
60
73
|
|
|
61
74
|
---
|
|
62
75
|
|
|
76
|
+
## What's new in 3.1 — power features
|
|
77
|
+
|
|
78
|
+
Six force-multipliers land in one release. All additive; existing code
|
|
79
|
+
keeps working.
|
|
80
|
+
|
|
81
|
+
| Feature | What you get |
|
|
82
|
+
|---|---|
|
|
83
|
+
| **Anthropic prompt caching** | `Claude(enable_prompt_cache=True)` marks the system prompt, tool schemas, and (for long chats) stable history segments with `cache_control: ephemeral`. Repeat calls that share those blocks read them from Anthropic's cache -- typically ~90% cheaper on cached input. `TokenUsage` now surfaces `cache_hit_ratio` so you can verify hits. |
|
|
84
|
+
| **Parallel tool calls per turn (sync)** | `AgentRunner(bind_tools_natively=True)` binds your tools as native FC tools. When the LLM emits multiple `tool_use` blocks in one turn, they dispatch concurrently on a `ThreadPoolExecutor` (`parallel_tool_workers=8` by default). Async runner already had this; sync now matches. |
|
|
85
|
+
| **Semantic long-term memory** | `SemanticMemory(embeddings=...)` embeds every message and retrieves the top-K most relevant older turns for the current query -- keeps recent tail verbatim, injects retrieved older context as a synthetic system message. Beats sliding-window on multi-topic conversations. |
|
|
86
|
+
| **`vector_search` RAG tool** | `vector_search_tool(store)` gives your agent a native RAG tool. Backed by `VectorStore` -- in-memory cosine (numpy-accelerated when available), save/load to JSON. `HashEmbeddings` runs offline for dev + tests; `OpenAIEmbeddings` for production. |
|
|
87
|
+
| **Agent-to-agent handoffs** | `handoff_tool("researcher")` + `HandoffCoordinator({...}, entry="triage")` let one agent transfer control to a peer mid-run (Swarm-style). Bounded by `max_hops`; conversation history propagates so the target agent starts fully informed. |
|
|
88
|
+
| **Evals harness** | `EvalCase` + `EvalRunner` run declarative test cases against any runner, collect per-case latency + tokens + tool-error counts, and produce a pass/fail report. `contains`, `called_tool`, `matches_regex`, `llm_judge`, and more assertion helpers included. CLI: `python -m agentx_dev.Evals run tests/evals/ --config agent.yaml`. |
|
|
89
|
+
|
|
90
|
+
### 3.1 API reference — everything new at a glance
|
|
91
|
+
|
|
92
|
+
Grouped by module. All symbols below are exported at the package top level
|
|
93
|
+
(`from agentx_dev import X`) unless marked `agentx_dev.X.Y`.
|
|
94
|
+
|
|
95
|
+
**1. Prompt caching (`agentx_dev.ChatModel`)**
|
|
96
|
+
|
|
97
|
+
New parameters on `Claude.__init__`:
|
|
98
|
+
|
|
99
|
+
| Param | Type | Default | What it does |
|
|
100
|
+
|---|---|---|---|
|
|
101
|
+
| `enable_prompt_cache` | `bool` | `False` | Turn on Anthropic prompt caching. System prompt + tool schemas get `cache_control: ephemeral`. Second call reads them from cache. |
|
|
102
|
+
| `cache_history_after` | `int` | `4` | When history has more than N turns, add a third cache breakpoint on the last stable assistant message so long chats also benefit. Max 3 breakpoints used (Anthropic allows 4). |
|
|
103
|
+
|
|
104
|
+
New `TokenUsage` attributes + property:
|
|
105
|
+
|
|
106
|
+
| Attribute | What it tracks |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `total_cache_read_tokens` | Cumulative tokens served from cache (discounted rate). |
|
|
109
|
+
| `total_cache_creation_tokens` | Cumulative tokens written into cache (small premium). |
|
|
110
|
+
| `last_cache_read_tokens` | Cache read tokens from the most recent call. |
|
|
111
|
+
| `last_cache_creation_tokens` | Cache creation tokens from the most recent call. |
|
|
112
|
+
| `cache_hit_ratio` *(property)* | `total_cache_read_tokens / total_input_tokens`. Zero when no cache. |
|
|
113
|
+
|
|
114
|
+
`TokenUsage.record()` gained two keyword args: `cache_read_tokens`, `cache_creation_tokens` (default `0` — providers that don't cache don't need to pass them).
|
|
115
|
+
|
|
116
|
+
**2. Parallel per-turn tool dispatch (`agentx_dev.Runner.AgentRun`)**
|
|
117
|
+
|
|
118
|
+
New parameters on `AgentRunner.__init__`:
|
|
119
|
+
|
|
120
|
+
| Param | Type | Default | What it does |
|
|
121
|
+
|---|---|---|---|
|
|
122
|
+
| `bind_tools_natively` | `bool` | `False` | Skip the AgentType parser indirection; bind your tools directly as native FC tools so the LLM can call them (and multiple at once per turn). Mutually exclusive with `use_function_calling`. Mirrors the async runner's mode of the same name. |
|
|
123
|
+
| `parallel_tool_workers` | `int` | `8` | Max threads in the pool that runs concurrent tool calls when native binding is on. Bounded by `min(workers, num_calls)` per turn. |
|
|
124
|
+
|
|
125
|
+
Auto-registered when `bind_tools_natively=True`: a synthetic `respond` tool the LLM calls to end the loop with its final answer.
|
|
126
|
+
|
|
127
|
+
**3. Embeddings + vector store + RAG (`agentx_dev.Embeddings`)**
|
|
128
|
+
|
|
129
|
+
New module. Exports:
|
|
130
|
+
|
|
131
|
+
| Symbol | Kind | What it is |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| `Embeddings` | ABC | Base class. `embed(texts) -> list[list[float]]`, `embed_one(text)`, `dim: int`. |
|
|
134
|
+
| `OpenAIEmbeddings(model="text-embedding-3-small", api_key=None, base_url=None, batch_size=100)` | class | OpenAI-backed. Auto-batches up to 100 texts per request. |
|
|
135
|
+
| `HashEmbeddings(dim=256)` | class | Zero-dep fallback. Deterministic char-trigram hash. For tests + dev. |
|
|
136
|
+
| `VectorHit(id, text, score, metadata)` | dataclass | One search result. `score` is cosine similarity in `[-1, 1]`. |
|
|
137
|
+
| `VectorStore(embeddings)` | class | In-memory store. Methods: `add(texts, ids=None, metadata=None) -> ids`, `search(query, top_k=5, min_score=0.0) -> [VectorHit]`, `delete(ids) -> count`, `clear()`, `save(path)`, `VectorStore.load(path, embeddings=...)`, `len(store)`. numpy-accelerated when available. |
|
|
138
|
+
| `SemanticMemory(embeddings, recent_tail=6, top_k=4, min_score=0.15, preserve_system=True)` | class | `BaseMemory` implementation. Every message goes into a `VectorStore`; `get_messages()` returns system + retrieved-context + recent-tail. Call `set_query(text)` before each turn to focus retrieval. Access underlying store via `.store`. |
|
|
139
|
+
| `vector_search_tool(store, name="vector_search", default_top_k=5, max_top_k=20, description=None)` | factory | Returns a `StructuredTool` the agent can call. Args schema: `{query, top_k, min_score}`. |
|
|
140
|
+
|
|
141
|
+
`create_semantic_memory(embeddings=None, recent_tail=6, top_k=4)` — factory in `agentx_dev.Memory`. When `embeddings=None`, defaults to `HashEmbeddings(256)` so tests work with no keys.
|
|
142
|
+
|
|
143
|
+
**4. Agent-to-agent handoffs (`agentx_dev.Handoffs`)**
|
|
144
|
+
|
|
145
|
+
New module. Exports:
|
|
146
|
+
|
|
147
|
+
| Symbol | Kind | What it is |
|
|
148
|
+
|---|---|---|
|
|
149
|
+
| `HandoffRequest(target, task="", context={})` | dataclass | Sentinel returned by a handoff tool. String form: `"HANDOFF -> <target>: <task>"` — coordinator detects this in `ToolCall.result`. |
|
|
150
|
+
| `handoff_tool(target, description=None, tool_name=None)` | factory | Returns a `StructuredTool` named `handoff_to_<target>`. Args schema: `{task, rationale}`. |
|
|
151
|
+
| `HandoffCoordinator(agents: dict, entry: str, max_hops=8)` | class | Runs the routing loop. `.run(query, chat_history=None) -> HandoffResult`, `.arun(...)` (async). Chat history propagates across hops. |
|
|
152
|
+
| `HandoffResult(completion, hops)` | dataclass | Wraps the final `AgentCompletion` + ordered list of routing hops (`[{from, to, task, hop}, ...]`). Convenience props: `.content`, `.tool_calls`. |
|
|
153
|
+
| `MAX_HANDOFF_HOPS = 8` | constant | Default hop cap. |
|
|
154
|
+
|
|
155
|
+
**5. Evals harness (`agentx_dev.Evals`)**
|
|
156
|
+
|
|
157
|
+
New module. Exports:
|
|
158
|
+
|
|
159
|
+
| Symbol | Kind | What it is |
|
|
160
|
+
|---|---|---|
|
|
161
|
+
| `EvalCase(name, input, assertions=[], tags=[], metadata={})` | dataclass | One test case. |
|
|
162
|
+
| `EvalResult(case, completion, passed, duration_sec, ...)` | dataclass | Outcome of one case. Also tracks `input_tokens`, `output_tokens`, `cache_read_tokens`, `tool_error_count`. |
|
|
163
|
+
| `EvalReport(results)` | dataclass | Aggregate. Props: `passed`, `failed`, `total`, `pass_rate`, `total_duration_sec`, `total_input_tokens`, `total_output_tokens`, `total_cache_read_tokens`. Methods: `.summary() -> str`, `.to_dict()` for CI. |
|
|
164
|
+
| `EvalRunner(runner_factory, verbose=True)` | class | `.run(cases) -> EvalReport`. Builds a FRESH runner per case via `runner_factory` so state doesn't leak. |
|
|
165
|
+
|
|
166
|
+
Assertion helpers (all return an `(AgentCompletion) -> (passed, message)` callable):
|
|
167
|
+
|
|
168
|
+
| Helper | Signature | Checks |
|
|
169
|
+
|---|---|---|
|
|
170
|
+
| `contains(needle, case_sensitive=False)` | | Final answer contains substring. |
|
|
171
|
+
| `not_contains(needle, case_sensitive=False)` | | Final answer does NOT contain substring. |
|
|
172
|
+
| `matches_regex(pattern, flags=re.IGNORECASE)` | | Final answer matches `re.search`. |
|
|
173
|
+
| `called_tool(name)` | | Tool with that name was called at least once. |
|
|
174
|
+
| `tool_count(*, min=0, max=None)` | | Total tool calls within bounds. |
|
|
175
|
+
| `max_iterations(limit)` | | Steps taken `<= limit`. |
|
|
176
|
+
| `llm_judge(judge_model, criterion)` | | Uses another chat model to grade yes/no. |
|
|
177
|
+
|
|
178
|
+
`AssertionFn` type alias exported for typing custom assertions.
|
|
179
|
+
|
|
180
|
+
Case loaders (declarative JSON files):
|
|
181
|
+
|
|
182
|
+
| Function | What it loads |
|
|
183
|
+
|---|---|
|
|
184
|
+
| `load_case_from_dict(data: dict) -> EvalCase` | Recognizes `expected_substrings`, `forbidden_substrings`, `matches_regex`, `must_call_tools`, `max_iterations`. |
|
|
185
|
+
| `load_cases_from_dir(path, pattern="*.eval.json") -> list[EvalCase]` | Walks a directory. Each file is one case dict, a list of dicts, or `{"cases": [...]}`. |
|
|
186
|
+
|
|
187
|
+
CLI:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
python -m agentx_dev.Evals run tests/evals/ --config agent.yaml [--pattern '*.eval.json'] [--json-out report.json]
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Exits `0` on all-pass, `1` on any failure. Prints `EvalReport.summary()` to stdout.
|
|
194
|
+
|
|
195
|
+
**6. Package exports (`agentx_dev`)**
|
|
196
|
+
|
|
197
|
+
Everything above is re-exported at the top level. New entries in `__all__`:
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
create_semantic_memory,
|
|
201
|
+
Embeddings, OpenAIEmbeddings, HashEmbeddings,
|
|
202
|
+
VectorStore, VectorHit, SemanticMemory, vector_search_tool,
|
|
203
|
+
HandoffRequest, HandoffResult, HandoffCoordinator, handoff_tool,
|
|
204
|
+
EvalCase, EvalResult, EvalReport, EvalRunner,
|
|
205
|
+
contains, not_contains, matches_regex,
|
|
206
|
+
called_tool, tool_count, max_iterations, llm_judge,
|
|
207
|
+
load_case_from_dict, load_cases_from_dir,
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**7. Runnable demo**
|
|
211
|
+
|
|
212
|
+
`examples/v3_1_features_demo.py` walks every 3.1 feature. Runs offline via
|
|
213
|
+
`HashEmbeddings` + mock runners for the RAG / memory / handoffs / evals
|
|
214
|
+
sections. Live sections (prompt caching, parallel tools) fall back to GPT
|
|
215
|
+
when only `OPENAI_API_KEY` is set; use Claude when `ANTHROPIC_API_KEY` is
|
|
216
|
+
set (prompt caching stays Anthropic-only).
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
Quick tour:
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from agentx_dev import (
|
|
224
|
+
Claude, AgentRunner, AgentType, Permissions,
|
|
225
|
+
VectorStore, HashEmbeddings, vector_search_tool,
|
|
226
|
+
SemanticMemory, HandoffCoordinator, handoff_tool,
|
|
227
|
+
EvalCase, EvalRunner, contains, called_tool,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# Prompt caching -- opt in on the model.
|
|
231
|
+
llm = Claude(model="claude-sonnet-4-6", enable_prompt_cache=True)
|
|
232
|
+
|
|
233
|
+
# RAG -- build a private store and hand the search tool to the agent.
|
|
234
|
+
store = VectorStore(embeddings=HashEmbeddings())
|
|
235
|
+
store.add(["Postgres uses MVCC for isolation.",
|
|
236
|
+
"SQLite stores the whole DB in one file."])
|
|
237
|
+
runner = AgentRunner(
|
|
238
|
+
model=llm, agent=AgentType.ReAct,
|
|
239
|
+
tools=[vector_search_tool(store)],
|
|
240
|
+
permissions=Permissions.read_only(["./"]),
|
|
241
|
+
)
|
|
242
|
+
result = runner.invoke("How does Postgres handle concurrent writes?")
|
|
243
|
+
|
|
244
|
+
# Parallel tool calls -- native binding + multi-worker dispatch.
|
|
245
|
+
fast = AgentRunner(
|
|
246
|
+
model=llm, agent=AgentType.ReAct,
|
|
247
|
+
tools=[weather_tool, stock_tool, news_tool],
|
|
248
|
+
bind_tools_natively=True, parallel_tool_workers=4,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Handoffs -- specialists that can hand back and forth.
|
|
252
|
+
coord = HandoffCoordinator({
|
|
253
|
+
"triage": triage_runner,
|
|
254
|
+
"researcher": researcher_runner,
|
|
255
|
+
"writer": writer_runner,
|
|
256
|
+
}, entry="triage")
|
|
257
|
+
answer = coord.run("Write a 200-word summary of MVCC.")
|
|
258
|
+
|
|
259
|
+
# Evals -- catch regressions before shipping.
|
|
260
|
+
cases = [
|
|
261
|
+
EvalCase("capital", "What's the capital of France?",
|
|
262
|
+
assertions=[contains("Paris")]),
|
|
263
|
+
EvalCase("math", "137 * 91?",
|
|
264
|
+
assertions=[called_tool("calculator"), contains("12467")]),
|
|
265
|
+
]
|
|
266
|
+
report = EvalRunner(lambda: build_runner()).run(cases)
|
|
267
|
+
print(report.summary())
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## What's new in 3.0.6 — security hardening release
|
|
273
|
+
|
|
274
|
+
All seven findings from the third-party framework audit are now fixed.
|
|
275
|
+
Every change is backwards-compatible: existing code keeps working; the
|
|
276
|
+
new defaults are strictly safer.
|
|
277
|
+
|
|
278
|
+
| Fix | What changed |
|
|
279
|
+
|---|---|
|
|
280
|
+
| **SSRF guard on `web_fetch`** | Rejects non-public destinations (loopback, RFC1918, 169.254.169.254 cloud-metadata, link-local, multicast) *and* re-validates every redirect hop. `WebTools.py` |
|
|
281
|
+
| **`SpawnConfig.auto_spawn_allowed_caps`** | New allow-list gates which capabilities `auto_spawn=True` will silently grant. Prompt-injected tasks can no longer talk the planner into a `["code"]` or `["delete"]` spawn. Recommended: `{"web"}`. |
|
|
282
|
+
| **Scrubbed subprocess env** | `run_python` / `run_shell` no longer inherit the full parent env. `print(os.environ)` inside `execute_python` cannot exfiltrate `OPENAI_API_KEY`, `AWS_*`, etc. Opt vars back in with `Permissions(subprocess_env_passthrough=[...])`; use `["*"]` for the pre-3.0.6 behaviour. |
|
|
283
|
+
| **HMAC-signed persistent state** | Each `run_python` build generates a per-run HMAC key, kept in parent memory. A tampered or attacker-dropped `.run_python_state.pkl` fails verification and the header starts fresh instead of unpickling attacker `__reduce__` payloads. |
|
|
284
|
+
| **`session_id` sanitizer** | `mint_session_dir` / `Permissions.new_session` now reject path separators, `..`, control chars, leading dots. `session_id="../../../tmp/pwn"` no longer escapes `base`. |
|
|
285
|
+
| **`permissions.json` mode 0o600** | Config file is now written via `os.open(..., 0o600)`, blocking multi-user hijack where another local user pre-creates the config with `execute_python=True`. |
|
|
286
|
+
| **ReDoS guard on `grep`** | 500-char pattern cap + 10s total-scan wall-clock budget. A catastrophic-backtracking regex from an untrusted planner can no longer freeze the agent. |
|
|
287
|
+
| **`invoke` / `ainvoke` accept a bare string** | `BaseChatModel.invoke("hi")` now wraps to `[{"role":"user","content":"hi"}]` — same behaviour `with_structured_output(...).invoke("hi")` has always had. Silently forwarding a string to the provider used to produce an OpenAI `Invalid type for 'messages'` 400 that read like a caller-payload problem. Lists and `{"messages": [...]}` dicts pass through unchanged. Fully additive. |
|
|
288
|
+
| **`AgentRunner.invoke` / `AsyncAgentRunner.ainvoke` accept a message list** | Parallel fix for the runner. Callers thinking in chat-model shapes can now pass `runner.invoke([{"role":"user","content":"prior"}, {"role":"assistant","content":"ok"}, {"role":"user","content":"NOW"}])` — the last user turn becomes the query and earlier non-system turns become the chat history (merged with any explicit `chat_history=`). Bare strings still work (classic path). `system` messages in the list are dropped because the runner assembles its own from the AgentType template + `system_addendum` + sandbox hint. `stream` / `astream` accept the same shapes. |
|
|
289
|
+
|
|
63
290
|
## What's new in 3.0
|
|
64
291
|
|
|
65
292
|
- **Multi-agent orchestration** — `Supervisor` decomposes a task into
|