agent-runtime-core 0.6.0__py3-none-any.whl → 0.7.1__py3-none-any.whl
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.
- agent_runtime_core/__init__.py +118 -2
- agent_runtime_core/agentic_loop.py +254 -0
- agent_runtime_core/config.py +54 -4
- agent_runtime_core/config_schema.py +307 -0
- agent_runtime_core/contexts.py +348 -0
- agent_runtime_core/interfaces.py +106 -0
- agent_runtime_core/json_runtime.py +509 -0
- agent_runtime_core/llm/__init__.py +80 -7
- agent_runtime_core/llm/anthropic.py +133 -12
- agent_runtime_core/llm/models_config.py +180 -0
- agent_runtime_core/memory/__init__.py +70 -0
- agent_runtime_core/memory/manager.py +554 -0
- agent_runtime_core/memory/mixin.py +294 -0
- agent_runtime_core/multi_agent.py +569 -0
- agent_runtime_core/persistence/__init__.py +2 -0
- agent_runtime_core/persistence/file.py +277 -0
- agent_runtime_core/rag/__init__.py +65 -0
- agent_runtime_core/rag/chunking.py +224 -0
- agent_runtime_core/rag/indexer.py +253 -0
- agent_runtime_core/rag/retriever.py +261 -0
- agent_runtime_core/runner.py +193 -15
- agent_runtime_core/tool_calling_agent.py +88 -130
- agent_runtime_core/tools.py +179 -0
- agent_runtime_core/vectorstore/__init__.py +193 -0
- agent_runtime_core/vectorstore/base.py +138 -0
- agent_runtime_core/vectorstore/embeddings.py +242 -0
- agent_runtime_core/vectorstore/sqlite_vec.py +328 -0
- agent_runtime_core/vectorstore/vertex.py +295 -0
- {agent_runtime_core-0.6.0.dist-info → agent_runtime_core-0.7.1.dist-info}/METADATA +202 -1
- agent_runtime_core-0.7.1.dist-info/RECORD +57 -0
- agent_runtime_core-0.6.0.dist-info/RECORD +0 -38
- {agent_runtime_core-0.6.0.dist-info → agent_runtime_core-0.7.1.dist-info}/WHEEL +0 -0
- {agent_runtime_core-0.6.0.dist-info → agent_runtime_core-0.7.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Vertex AI Vector Search implementation.
|
|
3
|
+
|
|
4
|
+
Uses Google Cloud Vertex AI Vector Search for enterprise-scale vector similarity search.
|
|
5
|
+
Ideal for production deployments requiring managed infrastructure and high scalability.
|
|
6
|
+
|
|
7
|
+
Requires: pip install google-cloud-aiplatform
|
|
8
|
+
|
|
9
|
+
Note: Vertex AI Vector Search requires pre-created index and endpoint resources.
|
|
10
|
+
See: https://cloud.google.com/vertex-ai/docs/vector-search/overview
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
from agent_runtime_core.vectorstore.base import (
|
|
17
|
+
VectorStore,
|
|
18
|
+
VectorRecord,
|
|
19
|
+
VectorSearchResult,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class VertexVectorStore(VectorStore):
|
|
24
|
+
"""
|
|
25
|
+
Vector store using Google Vertex AI Vector Search.
|
|
26
|
+
|
|
27
|
+
This implementation uses Vertex AI's managed vector search service for
|
|
28
|
+
enterprise-scale similarity search. It's ideal for:
|
|
29
|
+
- Production deployments requiring high availability
|
|
30
|
+
- Large-scale datasets (billions of vectors)
|
|
31
|
+
- Applications already using Google Cloud
|
|
32
|
+
|
|
33
|
+
Important: This store requires pre-created Vertex AI resources:
|
|
34
|
+
1. A Vector Search Index
|
|
35
|
+
2. An Index Endpoint with the index deployed
|
|
36
|
+
|
|
37
|
+
The store maintains a local cache of content/metadata since Vertex AI
|
|
38
|
+
Vector Search only stores vectors and IDs. For production use, consider
|
|
39
|
+
using a separate database for content storage.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
project_id: str,
|
|
45
|
+
location: str,
|
|
46
|
+
index_endpoint_id: str,
|
|
47
|
+
deployed_index_id: str,
|
|
48
|
+
index_id: Optional[str] = None,
|
|
49
|
+
):
|
|
50
|
+
"""
|
|
51
|
+
Initialize Vertex AI Vector Search store.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
project_id: Google Cloud project ID
|
|
55
|
+
location: Google Cloud region (e.g., "us-central1")
|
|
56
|
+
index_endpoint_id: The ID of the index endpoint
|
|
57
|
+
deployed_index_id: The ID of the deployed index
|
|
58
|
+
index_id: Optional index ID for batch updates
|
|
59
|
+
"""
|
|
60
|
+
self._project_id = project_id
|
|
61
|
+
self._location = location
|
|
62
|
+
self._index_endpoint_id = index_endpoint_id
|
|
63
|
+
self._deployed_index_id = deployed_index_id
|
|
64
|
+
self._index_id = index_id
|
|
65
|
+
self._endpoint = None
|
|
66
|
+
self._initialized = False
|
|
67
|
+
|
|
68
|
+
# Local cache for content and metadata
|
|
69
|
+
# In production, use a proper database
|
|
70
|
+
self._content_cache: dict[str, tuple[str, dict]] = {}
|
|
71
|
+
|
|
72
|
+
def _ensure_initialized(self) -> None:
|
|
73
|
+
"""Initialize Vertex AI SDK and get endpoint."""
|
|
74
|
+
if self._initialized:
|
|
75
|
+
return
|
|
76
|
+
try:
|
|
77
|
+
from google.cloud import aiplatform
|
|
78
|
+
from google.cloud.aiplatform.matching_engine import MatchingEngineIndexEndpoint
|
|
79
|
+
except ImportError:
|
|
80
|
+
raise ImportError(
|
|
81
|
+
"Google Cloud AI Platform package not installed. "
|
|
82
|
+
"Install with: pip install google-cloud-aiplatform"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
aiplatform.init(project=self._project_id, location=self._location)
|
|
86
|
+
|
|
87
|
+
# Get the index endpoint
|
|
88
|
+
endpoint_resource_name = (
|
|
89
|
+
f"projects/{self._project_id}/locations/{self._location}/"
|
|
90
|
+
f"indexEndpoints/{self._index_endpoint_id}"
|
|
91
|
+
)
|
|
92
|
+
self._endpoint = MatchingEngineIndexEndpoint(endpoint_resource_name)
|
|
93
|
+
self._initialized = True
|
|
94
|
+
|
|
95
|
+
async def add(
|
|
96
|
+
self,
|
|
97
|
+
id: str,
|
|
98
|
+
vector: list[float],
|
|
99
|
+
content: str,
|
|
100
|
+
metadata: Optional[dict] = None,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""
|
|
103
|
+
Add a vector with its content and metadata.
|
|
104
|
+
|
|
105
|
+
Note: For Vertex AI, vectors are typically added through batch operations
|
|
106
|
+
to the index. This method adds to a local cache and should be followed
|
|
107
|
+
by a batch update to the index.
|
|
108
|
+
"""
|
|
109
|
+
self._ensure_initialized()
|
|
110
|
+
|
|
111
|
+
# Store content and metadata locally
|
|
112
|
+
self._content_cache[id] = (content, metadata or {})
|
|
113
|
+
|
|
114
|
+
# For single adds, we use the streaming update API if available
|
|
115
|
+
# This requires the index to support streaming updates
|
|
116
|
+
if self._index_id:
|
|
117
|
+
import asyncio
|
|
118
|
+
from google.cloud import aiplatform
|
|
119
|
+
|
|
120
|
+
index_resource_name = (
|
|
121
|
+
f"projects/{self._project_id}/locations/{self._location}/"
|
|
122
|
+
f"indexes/{self._index_id}"
|
|
123
|
+
)
|
|
124
|
+
index = aiplatform.MatchingEngineIndex(index_resource_name)
|
|
125
|
+
|
|
126
|
+
# Prepare datapoint
|
|
127
|
+
datapoint = {
|
|
128
|
+
"datapoint_id": id,
|
|
129
|
+
"feature_vector": vector,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Run in executor since SDK is synchronous
|
|
133
|
+
loop = asyncio.get_event_loop()
|
|
134
|
+
await loop.run_in_executor(
|
|
135
|
+
None,
|
|
136
|
+
lambda: index.upsert_datapoints(datapoints=[datapoint]),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
async def add_batch(
|
|
140
|
+
self,
|
|
141
|
+
items: list[tuple[str, list[float], str, Optional[dict]]],
|
|
142
|
+
) -> None:
|
|
143
|
+
"""Add multiple vectors efficiently."""
|
|
144
|
+
if not items:
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
self._ensure_initialized()
|
|
148
|
+
|
|
149
|
+
# Store content and metadata locally
|
|
150
|
+
for id, _, content, metadata in items:
|
|
151
|
+
self._content_cache[id] = (content, metadata or {})
|
|
152
|
+
|
|
153
|
+
if self._index_id:
|
|
154
|
+
import asyncio
|
|
155
|
+
from google.cloud import aiplatform
|
|
156
|
+
|
|
157
|
+
index_resource_name = (
|
|
158
|
+
f"projects/{self._project_id}/locations/{self._location}/"
|
|
159
|
+
f"indexes/{self._index_id}"
|
|
160
|
+
)
|
|
161
|
+
index = aiplatform.MatchingEngineIndex(index_resource_name)
|
|
162
|
+
|
|
163
|
+
# Prepare datapoints
|
|
164
|
+
datapoints = [
|
|
165
|
+
{"datapoint_id": id, "feature_vector": vector}
|
|
166
|
+
for id, vector, _, _ in items
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
# Run in executor since SDK is synchronous
|
|
170
|
+
loop = asyncio.get_event_loop()
|
|
171
|
+
await loop.run_in_executor(
|
|
172
|
+
None,
|
|
173
|
+
lambda: index.upsert_datapoints(datapoints=datapoints),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
async def search(
|
|
177
|
+
self,
|
|
178
|
+
query_vector: list[float],
|
|
179
|
+
limit: int = 10,
|
|
180
|
+
filter: Optional[dict] = None,
|
|
181
|
+
) -> list[VectorSearchResult]:
|
|
182
|
+
"""Search for similar vectors."""
|
|
183
|
+
self._ensure_initialized()
|
|
184
|
+
import asyncio
|
|
185
|
+
|
|
186
|
+
# Vertex AI Vector Search uses find_neighbors for queries
|
|
187
|
+
loop = asyncio.get_event_loop()
|
|
188
|
+
|
|
189
|
+
# Build the query
|
|
190
|
+
queries = [query_vector]
|
|
191
|
+
|
|
192
|
+
# Execute search
|
|
193
|
+
response = await loop.run_in_executor(
|
|
194
|
+
None,
|
|
195
|
+
lambda: self._endpoint.find_neighbors(
|
|
196
|
+
deployed_index_id=self._deployed_index_id,
|
|
197
|
+
queries=queries,
|
|
198
|
+
num_neighbors=limit,
|
|
199
|
+
),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
results = []
|
|
203
|
+
if response and len(response) > 0:
|
|
204
|
+
for neighbor in response[0]:
|
|
205
|
+
id = neighbor.id
|
|
206
|
+
# Vertex AI returns distance, convert to similarity
|
|
207
|
+
# Using cosine similarity: score = 1 - distance for normalized vectors
|
|
208
|
+
score = 1.0 - neighbor.distance if hasattr(neighbor, "distance") else 0.0
|
|
209
|
+
|
|
210
|
+
# Get content and metadata from cache
|
|
211
|
+
content, metadata = self._content_cache.get(id, ("", {}))
|
|
212
|
+
|
|
213
|
+
# Apply filter if provided
|
|
214
|
+
if filter:
|
|
215
|
+
match = all(
|
|
216
|
+
metadata.get(k) == v for k, v in filter.items()
|
|
217
|
+
)
|
|
218
|
+
if not match:
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
results.append(
|
|
222
|
+
VectorSearchResult(
|
|
223
|
+
id=id,
|
|
224
|
+
content=content,
|
|
225
|
+
score=score,
|
|
226
|
+
metadata=metadata,
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
return results
|
|
231
|
+
|
|
232
|
+
async def delete(self, id: str) -> bool:
|
|
233
|
+
"""Delete a vector by ID."""
|
|
234
|
+
self._ensure_initialized()
|
|
235
|
+
|
|
236
|
+
# Remove from local cache
|
|
237
|
+
existed = id in self._content_cache
|
|
238
|
+
self._content_cache.pop(id, None)
|
|
239
|
+
|
|
240
|
+
# Delete from index if index_id is provided
|
|
241
|
+
if self._index_id:
|
|
242
|
+
import asyncio
|
|
243
|
+
from google.cloud import aiplatform
|
|
244
|
+
|
|
245
|
+
index_resource_name = (
|
|
246
|
+
f"projects/{self._project_id}/locations/{self._location}/"
|
|
247
|
+
f"indexes/{self._index_id}"
|
|
248
|
+
)
|
|
249
|
+
index = aiplatform.MatchingEngineIndex(index_resource_name)
|
|
250
|
+
|
|
251
|
+
loop = asyncio.get_event_loop()
|
|
252
|
+
await loop.run_in_executor(
|
|
253
|
+
None,
|
|
254
|
+
lambda: index.remove_datapoints(datapoint_ids=[id]),
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
return existed
|
|
258
|
+
|
|
259
|
+
async def delete_by_filter(self, filter: dict) -> int:
|
|
260
|
+
"""Delete vectors matching filter."""
|
|
261
|
+
# Find matching IDs from cache
|
|
262
|
+
ids_to_delete = []
|
|
263
|
+
for id, (_, metadata) in self._content_cache.items():
|
|
264
|
+
if all(metadata.get(k) == v for k, v in filter.items()):
|
|
265
|
+
ids_to_delete.append(id)
|
|
266
|
+
|
|
267
|
+
# Delete each matching ID
|
|
268
|
+
for id in ids_to_delete:
|
|
269
|
+
await self.delete(id)
|
|
270
|
+
|
|
271
|
+
return len(ids_to_delete)
|
|
272
|
+
|
|
273
|
+
async def get(self, id: str) -> Optional[VectorRecord]:
|
|
274
|
+
"""
|
|
275
|
+
Get a vector by ID.
|
|
276
|
+
|
|
277
|
+
Note: Vertex AI Vector Search doesn't support direct vector retrieval.
|
|
278
|
+
This returns cached content/metadata with an empty vector.
|
|
279
|
+
"""
|
|
280
|
+
if id not in self._content_cache:
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
content, metadata = self._content_cache[id]
|
|
284
|
+
return VectorRecord(
|
|
285
|
+
id=id,
|
|
286
|
+
vector=[], # Vertex AI doesn't support vector retrieval
|
|
287
|
+
content=content,
|
|
288
|
+
metadata=metadata,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
async def close(self) -> None:
|
|
292
|
+
"""Close connections."""
|
|
293
|
+
self._endpoint = None
|
|
294
|
+
self._initialized = False
|
|
295
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-runtime-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Framework-agnostic Python library for executing AI agents with consistent patterns
|
|
5
5
|
Project-URL: Homepage, https://github.com/makemore/agent-runtime-core
|
|
6
6
|
Project-URL: Repository, https://github.com/makemore/agent-runtime-core
|
|
@@ -19,10 +19,12 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
19
19
|
Requires-Python: >=3.11
|
|
20
20
|
Provides-Extra: all
|
|
21
21
|
Requires-Dist: anthropic>=0.18.0; extra == 'all'
|
|
22
|
+
Requires-Dist: google-cloud-aiplatform>=1.38.0; extra == 'all'
|
|
22
23
|
Requires-Dist: langfuse>=2.0.0; extra == 'all'
|
|
23
24
|
Requires-Dist: litellm>=1.0.0; extra == 'all'
|
|
24
25
|
Requires-Dist: openai>=1.0.0; extra == 'all'
|
|
25
26
|
Requires-Dist: redis>=5.0.0; extra == 'all'
|
|
27
|
+
Requires-Dist: sqlite-vec>=0.1.0; extra == 'all'
|
|
26
28
|
Provides-Extra: anthropic
|
|
27
29
|
Requires-Dist: anthropic>=0.18.0; extra == 'anthropic'
|
|
28
30
|
Provides-Extra: dev
|
|
@@ -39,6 +41,10 @@ Provides-Extra: openai
|
|
|
39
41
|
Requires-Dist: openai>=1.0.0; extra == 'openai'
|
|
40
42
|
Provides-Extra: redis
|
|
41
43
|
Requires-Dist: redis>=5.0.0; extra == 'redis'
|
|
44
|
+
Provides-Extra: sqlite-vec
|
|
45
|
+
Requires-Dist: sqlite-vec>=0.1.0; extra == 'sqlite-vec'
|
|
46
|
+
Provides-Extra: vertex
|
|
47
|
+
Requires-Dist: google-cloud-aiplatform>=1.38.0; extra == 'vertex'
|
|
42
48
|
Description-Content-Type: text/markdown
|
|
43
49
|
|
|
44
50
|
# agent-runtime-core
|
|
@@ -53,6 +59,7 @@ A lightweight, framework-agnostic Python library for building AI agent systems.
|
|
|
53
59
|
|
|
54
60
|
| Version | Date | Changes |
|
|
55
61
|
|---------|------|---------|
|
|
62
|
+
| **0.7.1** | 2026-01-24 | RAG module, vector stores (sqlite-vec, Vertex AI), memory system, multi-agent support, agentic loop, JSON runtime |
|
|
56
63
|
| **0.6.0** | 2025-01-23 | Enhanced registry with factory functions and class registration |
|
|
57
64
|
| **0.5.2** | 2025-01-14 | Add ToolCallingAgent base class, execute_with_events helper |
|
|
58
65
|
| **0.5.1** | 2025-01-13 | Bug fixes and improvements |
|
|
@@ -820,6 +827,200 @@ The executor emits events for observability:
|
|
|
820
827
|
- `EventType.STEP_SKIPPED` - Step skipped (already completed)
|
|
821
828
|
- `EventType.PROGRESS_UPDATE` - Progress percentage update
|
|
822
829
|
|
|
830
|
+
## Multi-Agent Systems
|
|
831
|
+
|
|
832
|
+
The `multi_agent` module provides the "agent-as-tool" pattern, allowing agents to invoke other agents as tools. This enables router/dispatcher patterns, hierarchical agent systems, and specialist delegation.
|
|
833
|
+
|
|
834
|
+
### Core Concepts
|
|
835
|
+
|
|
836
|
+
**Invocation Modes:**
|
|
837
|
+
- `DELEGATE`: Sub-agent runs and returns result to parent (parent continues). Good for "get me an answer".
|
|
838
|
+
- `HANDOFF`: Control transfers completely to sub-agent (parent exits). Good for "transfer this customer to billing".
|
|
839
|
+
|
|
840
|
+
**Context Modes:**
|
|
841
|
+
- `FULL`: Complete conversation history passed to sub-agent (default)
|
|
842
|
+
- `SUMMARY`: Summarized context + current message (more efficient)
|
|
843
|
+
- `MESSAGE_ONLY`: Only the invocation message (clean isolation)
|
|
844
|
+
|
|
845
|
+
### Creating Agent Tools
|
|
846
|
+
|
|
847
|
+
Wrap any agent as a tool that can be called by other agents:
|
|
848
|
+
|
|
849
|
+
```python
|
|
850
|
+
from agent_runtime_core.multi_agent import (
|
|
851
|
+
AgentTool,
|
|
852
|
+
InvocationMode,
|
|
853
|
+
ContextMode,
|
|
854
|
+
invoke_agent,
|
|
855
|
+
register_agent_tools,
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
# Define specialist agents
|
|
859
|
+
class BillingAgent(AgentRuntime):
|
|
860
|
+
@property
|
|
861
|
+
def key(self) -> str:
|
|
862
|
+
return "billing-specialist"
|
|
863
|
+
|
|
864
|
+
async def run(self, ctx: RunContext) -> RunResult:
|
|
865
|
+
# Handle billing questions, refunds, payments
|
|
866
|
+
...
|
|
867
|
+
|
|
868
|
+
class TechSupportAgent(AgentRuntime):
|
|
869
|
+
@property
|
|
870
|
+
def key(self) -> str:
|
|
871
|
+
return "tech-support"
|
|
872
|
+
|
|
873
|
+
async def run(self, ctx: RunContext) -> RunResult:
|
|
874
|
+
# Handle technical issues
|
|
875
|
+
...
|
|
876
|
+
|
|
877
|
+
# Wrap agents as tools
|
|
878
|
+
billing_tool = AgentTool(
|
|
879
|
+
agent=BillingAgent(),
|
|
880
|
+
name="billing_specialist",
|
|
881
|
+
description="Handles billing questions, refunds, and payment issues. Use when customer has billing-related questions.",
|
|
882
|
+
invocation_mode=InvocationMode.DELEGATE,
|
|
883
|
+
context_mode=ContextMode.FULL,
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
tech_support_tool = AgentTool(
|
|
887
|
+
agent=TechSupportAgent(),
|
|
888
|
+
name="tech_support",
|
|
889
|
+
description="Handles technical issues and troubleshooting. Use for technical problems.",
|
|
890
|
+
invocation_mode=InvocationMode.HANDOFF, # Transfer control completely
|
|
891
|
+
)
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
### Router Agent Pattern
|
|
895
|
+
|
|
896
|
+
Create a router agent that delegates to specialists:
|
|
897
|
+
|
|
898
|
+
```python
|
|
899
|
+
class RouterAgent(AgentRuntime):
|
|
900
|
+
"""Routes customer requests to appropriate specialist agents."""
|
|
901
|
+
|
|
902
|
+
def __init__(self):
|
|
903
|
+
self.agent_tools = [billing_tool, tech_support_tool]
|
|
904
|
+
|
|
905
|
+
@property
|
|
906
|
+
def key(self) -> str:
|
|
907
|
+
return "customer-router"
|
|
908
|
+
|
|
909
|
+
async def run(self, ctx: RunContext) -> RunResult:
|
|
910
|
+
from agent_runtime_core.llm import get_llm_client
|
|
911
|
+
llm = get_llm_client()
|
|
912
|
+
|
|
913
|
+
# Create tool registry with agent-tools
|
|
914
|
+
tools = ToolRegistry()
|
|
915
|
+
messages = list(ctx.input_messages)
|
|
916
|
+
|
|
917
|
+
# Register agent-tools as callable tools
|
|
918
|
+
register_agent_tools(
|
|
919
|
+
registry=tools,
|
|
920
|
+
agent_tools=self.agent_tools,
|
|
921
|
+
get_conversation_history=lambda: messages,
|
|
922
|
+
parent_ctx=ctx,
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
# Add system prompt for routing
|
|
926
|
+
system_message = {
|
|
927
|
+
"role": "system",
|
|
928
|
+
"content": """You are a customer service router. Analyze the customer's
|
|
929
|
+
request and delegate to the appropriate specialist:
|
|
930
|
+
- billing_specialist: For billing, payments, refunds
|
|
931
|
+
- tech_support: For technical issues (this will transfer the customer)
|
|
932
|
+
|
|
933
|
+
If you can answer directly, do so. Otherwise, use the appropriate tool."""
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
while True:
|
|
937
|
+
response = await llm.generate(
|
|
938
|
+
[system_message] + messages,
|
|
939
|
+
tools=tools.to_openai_format(),
|
|
940
|
+
)
|
|
941
|
+
|
|
942
|
+
messages.append(response.message)
|
|
943
|
+
|
|
944
|
+
if not response.tool_calls:
|
|
945
|
+
# No tool call - respond directly
|
|
946
|
+
await ctx.emit(EventType.ASSISTANT_MESSAGE, {
|
|
947
|
+
"content": response.content,
|
|
948
|
+
})
|
|
949
|
+
break
|
|
950
|
+
|
|
951
|
+
# Execute tool calls (which may invoke sub-agents)
|
|
952
|
+
for tool_call in response.tool_calls:
|
|
953
|
+
result = await tools.execute_with_events(tool_call, ctx)
|
|
954
|
+
|
|
955
|
+
# Check for handoff
|
|
956
|
+
if isinstance(result, dict) and result.get("handoff"):
|
|
957
|
+
# Sub-agent took over - we're done
|
|
958
|
+
return RunResult(
|
|
959
|
+
final_output=result.get("final_output", {}),
|
|
960
|
+
final_messages=messages,
|
|
961
|
+
)
|
|
962
|
+
|
|
963
|
+
# Add result to conversation
|
|
964
|
+
messages.append({
|
|
965
|
+
"role": "tool",
|
|
966
|
+
"tool_call_id": tool_call.id,
|
|
967
|
+
"content": str(result.get("response", result)),
|
|
968
|
+
})
|
|
969
|
+
|
|
970
|
+
return RunResult(
|
|
971
|
+
final_output={"response": response.content},
|
|
972
|
+
final_messages=messages,
|
|
973
|
+
)
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
### Direct Invocation
|
|
977
|
+
|
|
978
|
+
You can also invoke agents directly without the tool pattern:
|
|
979
|
+
|
|
980
|
+
```python
|
|
981
|
+
from agent_runtime_core.multi_agent import invoke_agent
|
|
982
|
+
|
|
983
|
+
# Invoke a sub-agent directly
|
|
984
|
+
result = await invoke_agent(
|
|
985
|
+
agent_tool=billing_tool,
|
|
986
|
+
message="Customer wants a refund for order #123",
|
|
987
|
+
parent_ctx=ctx,
|
|
988
|
+
conversation_history=messages,
|
|
989
|
+
additional_context="Customer has been waiting 2 weeks",
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
if result.handoff:
|
|
993
|
+
# Sub-agent took over
|
|
994
|
+
return RunResult(final_output=result.run_result.final_output)
|
|
995
|
+
else:
|
|
996
|
+
# Use the response
|
|
997
|
+
print(result.response)
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
### Events
|
|
1001
|
+
|
|
1002
|
+
Multi-agent invocations emit events for observability:
|
|
1003
|
+
|
|
1004
|
+
- `sub_agent.start` - Sub-agent invocation started
|
|
1005
|
+
- `sub_agent.end` - Sub-agent invocation completed
|
|
1006
|
+
- `tool.call` with `is_agent_tool: true` - Agent-tool was called
|
|
1007
|
+
- `tool.result` with `is_agent_tool: true` - Agent-tool returned
|
|
1008
|
+
|
|
1009
|
+
### AgentTool Options
|
|
1010
|
+
|
|
1011
|
+
```python
|
|
1012
|
+
AgentTool(
|
|
1013
|
+
agent=my_agent, # Required: The agent to wrap
|
|
1014
|
+
name="specialist", # Required: Tool name
|
|
1015
|
+
description="When to use this agent", # Required: Shown to parent LLM
|
|
1016
|
+
invocation_mode=InvocationMode.DELEGATE, # DELEGATE or HANDOFF
|
|
1017
|
+
context_mode=ContextMode.FULL, # FULL, SUMMARY, or MESSAGE_ONLY
|
|
1018
|
+
max_turns=10, # Optional: Limit sub-agent turns
|
|
1019
|
+
input_schema={...}, # Optional: Custom input schema
|
|
1020
|
+
metadata={"category": "billing"}, # Optional: Additional metadata
|
|
1021
|
+
)
|
|
1022
|
+
```
|
|
1023
|
+
|
|
823
1024
|
## API Reference
|
|
824
1025
|
|
|
825
1026
|
### Configuration
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
agent_runtime_core/__init__.py,sha256=fNQv_lSufnwrfGYKj6HduUOHzPtAZXqh5dUFShR9kGQ,7117
|
|
2
|
+
agent_runtime_core/agentic_loop.py,sha256=DJ3RXaQLFlHMf-icQjpTJkAvdfjkPBSGN1S_iy3mf64,9635
|
|
3
|
+
agent_runtime_core/config.py,sha256=WVfcIluFvf2zC_NKk2WuJhhPIGQNcuxmZV9TCg4t13o,7297
|
|
4
|
+
agent_runtime_core/config_schema.py,sha256=1t2ZF5Rk2oRbeYpRATMdFgbvgTPtedIQO6xej4cELh0,10576
|
|
5
|
+
agent_runtime_core/contexts.py,sha256=41UVkSqRJ3E9yIxfCQ-BQ7-wetUfSnDNGdr5-dQOWiY,11349
|
|
6
|
+
agent_runtime_core/interfaces.py,sha256=QvRFPRc2Fwqv_sv-3nExCoaiqwBf_HsQo9yzCYE_Hm0,15498
|
|
7
|
+
agent_runtime_core/json_runtime.py,sha256=b1BnGAQMORB4YULsKcCR0hEaCvln3Bz2dP228yq5BIQ,17358
|
|
8
|
+
agent_runtime_core/multi_agent.py,sha256=UleIFocGW0GZFeoa_bPfYGLZZH-NYiEsiG2BBTGdKyA,18409
|
|
9
|
+
agent_runtime_core/registry.py,sha256=QmazCAcHTsPt236Z_xEBJjdppm6jUuufE-gfvcGMUCk,3959
|
|
10
|
+
agent_runtime_core/runner.py,sha256=ydzyGJHCyz1wjs-sN5dx4RlJsYK0U-Or12BL_Fg8-aQ,19595
|
|
11
|
+
agent_runtime_core/steps.py,sha256=XpVFK7P-ZOpr7NwaP7XFygduIpjrKld-OIig7dHNMKE,11994
|
|
12
|
+
agent_runtime_core/testing.py,sha256=ordECGprBappLBMWxlETvuf2AoIPNomJFeSedXaY30E,11131
|
|
13
|
+
agent_runtime_core/tool_calling_agent.py,sha256=kyPTwHlYGaqUo0HJ7ycYCCQMFHD_AmPZMsyjxaKpJVc,5967
|
|
14
|
+
agent_runtime_core/tools.py,sha256=SSbiViOJe7raSijwV_TSHI60F5ReHy43Vd_QrAkKgAk,5620
|
|
15
|
+
agent_runtime_core/events/__init__.py,sha256=Gg7cMQHWfLTQ4Xik09KSg7cWbQDmW_MuF5_jl-yZkHU,1575
|
|
16
|
+
agent_runtime_core/events/base.py,sha256=NfHYyoczxr40Er5emROi_aY_07m5hDrKsn31pdWY2DY,1950
|
|
17
|
+
agent_runtime_core/events/memory.py,sha256=9z4tY8XB8xDg3ybHsIwilOcRo7HY-vB-8vxiz6O54BE,2491
|
|
18
|
+
agent_runtime_core/events/redis.py,sha256=7PsUO2-iqrdGCJZUOq1IdzwDdNhqT5mwEnH5xy2Fklo,5874
|
|
19
|
+
agent_runtime_core/events/sqlite.py,sha256=ZpGgeuQujYT8pkDsiXDoFXTcBf2KqzoWX4D4J9xkmeE,5097
|
|
20
|
+
agent_runtime_core/llm/__init__.py,sha256=7-tA1FmPkhY1l-lHhzd84MQf2bjs26bcgb8oAlglc58,4605
|
|
21
|
+
agent_runtime_core/llm/anthropic.py,sha256=AX2QJoQf-4uLDCxWFs71XfD11IMN6iI3gMPA25F3KrM,12239
|
|
22
|
+
agent_runtime_core/llm/litellm_client.py,sha256=c-O-lE08cT3ne0xSOvSDezPL6hCiA69p3HnB451Ipe4,5193
|
|
23
|
+
agent_runtime_core/llm/models_config.py,sha256=i275wtsWeR-LHdFrObPI19PtCIGvMDKTEKpue5TuYIk,5342
|
|
24
|
+
agent_runtime_core/llm/openai.py,sha256=qBZkkndDgYQ6LG-9bHS2za5KJTGSgL-c_7h0bD3_5lg,6862
|
|
25
|
+
agent_runtime_core/memory/__init__.py,sha256=7dgoXMr3IIjKsKKbxVgQBrrhZHqmJRIx64mcbF8jgUM,2178
|
|
26
|
+
agent_runtime_core/memory/manager.py,sha256=ba8ZKb1aF9Pc-zZB4O_22b6zb6ELpvo_LhpSs959L5U,18474
|
|
27
|
+
agent_runtime_core/memory/mixin.py,sha256=Afq7rhW1SmZgii6b3gUbM3peDzMwfMxQ0DstHzGfuDM,9683
|
|
28
|
+
agent_runtime_core/persistence/__init__.py,sha256=HdkGW5hnLjBRE0QcUWVjL2xsIkMyKPpqJKpamPbFh0s,2822
|
|
29
|
+
agent_runtime_core/persistence/base.py,sha256=k0wuzTCffPJ609dj9hIhnaqnNI1Qr3pCzJ-6E1YkSRU,21616
|
|
30
|
+
agent_runtime_core/persistence/file.py,sha256=uZxl3W405jeX1H6f0Xj_pZOQFTpZOYbpIdJ98rZabBI,28712
|
|
31
|
+
agent_runtime_core/persistence/manager.py,sha256=UL_eFsFM28nXM6O9PTHdzKX9Qxh9v2gBGd1m9Bs0vog,14309
|
|
32
|
+
agent_runtime_core/queue/__init__.py,sha256=m8gapXACPGApLj0RIDpVe5cQYuvKq1QsY2_mXzZcULQ,1527
|
|
33
|
+
agent_runtime_core/queue/base.py,sha256=QW1eWbwBX_tmVD8yJobFJtlxLd_RtUWHTuXGessuxy8,3959
|
|
34
|
+
agent_runtime_core/queue/memory.py,sha256=G65NJ2QU8sB2WQ7myHXc8LzSFowEzBXtCt78WmhvxO8,5416
|
|
35
|
+
agent_runtime_core/queue/redis.py,sha256=x9BEoeh6cVcWdaziFSQZHUvq_bHN1WYHWw17HIEWi5o,15622
|
|
36
|
+
agent_runtime_core/queue/sqlite.py,sha256=3YwkU0QOcmAxYBpbfEB9xk8f-e2C3QcHK_RnPZvdick,14338
|
|
37
|
+
agent_runtime_core/rag/__init__.py,sha256=ilsbzkcycLlhLa0BCCpdMb0EGnLaC-l52EH3xIyN6vw,1845
|
|
38
|
+
agent_runtime_core/rag/chunking.py,sha256=wlFa_K4G1Mbkyy-g-JR67e4Xwb3-ysCmf8s1ar7s5Ls,6364
|
|
39
|
+
agent_runtime_core/rag/indexer.py,sha256=BWq_BysZD1rMkAD_30mLe6qk6cDkut05DpfmrWMSLQc,8003
|
|
40
|
+
agent_runtime_core/rag/retriever.py,sha256=-svCtkTL5n65KALssWM1MwxsTa4wIiUQKRf3j9GPrTM,8304
|
|
41
|
+
agent_runtime_core/state/__init__.py,sha256=RK16Sj1QPW0SNxtmESlLRMpFBY_hZbMGGNnvcLbdcWw,1564
|
|
42
|
+
agent_runtime_core/state/base.py,sha256=NqE3B0ySa-U2jkelgmkBbkmkaIQxfu4pDryoxkZTMrc,1593
|
|
43
|
+
agent_runtime_core/state/memory.py,sha256=yEGwoR25zWcoxL79_gEu38P_dHvjJOTGij2wxqZ7X9A,1566
|
|
44
|
+
agent_runtime_core/state/redis.py,sha256=VXY6ULEphehHVq4zSw5Y4AMCibm9Ghvzk7PqCrgStDg,3430
|
|
45
|
+
agent_runtime_core/state/sqlite.py,sha256=HKZwDiC_7F1W8Z_Pz8roEs91XhQ9rUHfGpuQ7WWt_NQ,4873
|
|
46
|
+
agent_runtime_core/tracing/__init__.py,sha256=u1QicGc39e30gWyQD4cQWxGGjITnkwoOPUhNrG6aNyI,1266
|
|
47
|
+
agent_runtime_core/tracing/langfuse.py,sha256=Rj2sUlatk5sFro0y68tw5X6fQcSwWxcBOSOjB0F7JTU,3660
|
|
48
|
+
agent_runtime_core/tracing/noop.py,sha256=SpsbpsUcNG6C3xZG3uyiNPUHY8etloISx3w56Q8D3KE,751
|
|
49
|
+
agent_runtime_core/vectorstore/__init__.py,sha256=HO6B2TsHTPMkHvRf-774IgVtVcrOf_5P-yUPRpYIUj4,5688
|
|
50
|
+
agent_runtime_core/vectorstore/base.py,sha256=y1ZZCbAMkFkbHwOqzaK0JUBAVjbnNwwdSnrxokzzvjQ,3426
|
|
51
|
+
agent_runtime_core/vectorstore/embeddings.py,sha256=WTTiId9Q6I7cxHuiInYHEV8EWS2HyRYRW1YDPALGUVY,7749
|
|
52
|
+
agent_runtime_core/vectorstore/sqlite_vec.py,sha256=B4KPYdSkm-af-uqltqMga1uXslC19Uaeuy1sXfKOjyo,10552
|
|
53
|
+
agent_runtime_core/vectorstore/vertex.py,sha256=a1Ps_gMYqTlWUkNALpgMVEX6r6Zw_8gN5pZ59-vH7Ww,9695
|
|
54
|
+
agent_runtime_core-0.7.1.dist-info/METADATA,sha256=AXI1AH7ZFAVmiwpI7pCP3pq0fdJUzvzbES_G7QjpSLA,30560
|
|
55
|
+
agent_runtime_core-0.7.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
56
|
+
agent_runtime_core-0.7.1.dist-info/licenses/LICENSE,sha256=fDlWep3_mUrj8KHV_jk275tHVEW7_9sJRhkNuGCZ_TA,1068
|
|
57
|
+
agent_runtime_core-0.7.1.dist-info/RECORD,,
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
agent_runtime_core/__init__.py,sha256=qPJb-oJ424TMygK4OxUpLqoOu8e4I68MWok1ufxOAzQ,4139
|
|
2
|
-
agent_runtime_core/config.py,sha256=e3_uB5brAuQcWU36sOhWF9R6RoJrngtCS-xEB3n2fas,4986
|
|
3
|
-
agent_runtime_core/interfaces.py,sha256=T74pgS229tvarQD-_o25oflylUR7jq_jbgUjnvVs6IA,12191
|
|
4
|
-
agent_runtime_core/registry.py,sha256=QmazCAcHTsPt236Z_xEBJjdppm6jUuufE-gfvcGMUCk,3959
|
|
5
|
-
agent_runtime_core/runner.py,sha256=M3It72UhfmLt17jVnSvObiSfQ1_RN4JVUIJsjnRd2Ps,12771
|
|
6
|
-
agent_runtime_core/steps.py,sha256=XpVFK7P-ZOpr7NwaP7XFygduIpjrKld-OIig7dHNMKE,11994
|
|
7
|
-
agent_runtime_core/testing.py,sha256=ordECGprBappLBMWxlETvuf2AoIPNomJFeSedXaY30E,11131
|
|
8
|
-
agent_runtime_core/tool_calling_agent.py,sha256=LY0lSI9GZbWoIKd-7NXFSvByAhvK6L625eOp01VbTrw,8525
|
|
9
|
-
agent_runtime_core/events/__init__.py,sha256=Gg7cMQHWfLTQ4Xik09KSg7cWbQDmW_MuF5_jl-yZkHU,1575
|
|
10
|
-
agent_runtime_core/events/base.py,sha256=NfHYyoczxr40Er5emROi_aY_07m5hDrKsn31pdWY2DY,1950
|
|
11
|
-
agent_runtime_core/events/memory.py,sha256=9z4tY8XB8xDg3ybHsIwilOcRo7HY-vB-8vxiz6O54BE,2491
|
|
12
|
-
agent_runtime_core/events/redis.py,sha256=7PsUO2-iqrdGCJZUOq1IdzwDdNhqT5mwEnH5xy2Fklo,5874
|
|
13
|
-
agent_runtime_core/events/sqlite.py,sha256=ZpGgeuQujYT8pkDsiXDoFXTcBf2KqzoWX4D4J9xkmeE,5097
|
|
14
|
-
agent_runtime_core/llm/__init__.py,sha256=LyFFDtk4HhvUXct0nTeKuYuWzVmVqLDSVRpnPArbGqY,2461
|
|
15
|
-
agent_runtime_core/llm/anthropic.py,sha256=pt9QAjrv2dIPSAY3Pv6N_BzxL1tbhL-kPWsQ-DcHMLI,7516
|
|
16
|
-
agent_runtime_core/llm/litellm_client.py,sha256=c-O-lE08cT3ne0xSOvSDezPL6hCiA69p3HnB451Ipe4,5193
|
|
17
|
-
agent_runtime_core/llm/openai.py,sha256=qBZkkndDgYQ6LG-9bHS2za5KJTGSgL-c_7h0bD3_5lg,6862
|
|
18
|
-
agent_runtime_core/persistence/__init__.py,sha256=l9_1Mzhy9_Y-IIKuBKTR3Z8r2TPyr0a6b1HEdyZf1_I,2772
|
|
19
|
-
agent_runtime_core/persistence/base.py,sha256=k0wuzTCffPJ609dj9hIhnaqnNI1Qr3pCzJ-6E1YkSRU,21616
|
|
20
|
-
agent_runtime_core/persistence/file.py,sha256=oDB4_ZQkwHTCT1uoqpw5jOleK69YXCQwlTPsW86Yb-I,17785
|
|
21
|
-
agent_runtime_core/persistence/manager.py,sha256=UL_eFsFM28nXM6O9PTHdzKX9Qxh9v2gBGd1m9Bs0vog,14309
|
|
22
|
-
agent_runtime_core/queue/__init__.py,sha256=m8gapXACPGApLj0RIDpVe5cQYuvKq1QsY2_mXzZcULQ,1527
|
|
23
|
-
agent_runtime_core/queue/base.py,sha256=QW1eWbwBX_tmVD8yJobFJtlxLd_RtUWHTuXGessuxy8,3959
|
|
24
|
-
agent_runtime_core/queue/memory.py,sha256=G65NJ2QU8sB2WQ7myHXc8LzSFowEzBXtCt78WmhvxO8,5416
|
|
25
|
-
agent_runtime_core/queue/redis.py,sha256=x9BEoeh6cVcWdaziFSQZHUvq_bHN1WYHWw17HIEWi5o,15622
|
|
26
|
-
agent_runtime_core/queue/sqlite.py,sha256=3YwkU0QOcmAxYBpbfEB9xk8f-e2C3QcHK_RnPZvdick,14338
|
|
27
|
-
agent_runtime_core/state/__init__.py,sha256=RK16Sj1QPW0SNxtmESlLRMpFBY_hZbMGGNnvcLbdcWw,1564
|
|
28
|
-
agent_runtime_core/state/base.py,sha256=NqE3B0ySa-U2jkelgmkBbkmkaIQxfu4pDryoxkZTMrc,1593
|
|
29
|
-
agent_runtime_core/state/memory.py,sha256=yEGwoR25zWcoxL79_gEu38P_dHvjJOTGij2wxqZ7X9A,1566
|
|
30
|
-
agent_runtime_core/state/redis.py,sha256=VXY6ULEphehHVq4zSw5Y4AMCibm9Ghvzk7PqCrgStDg,3430
|
|
31
|
-
agent_runtime_core/state/sqlite.py,sha256=HKZwDiC_7F1W8Z_Pz8roEs91XhQ9rUHfGpuQ7WWt_NQ,4873
|
|
32
|
-
agent_runtime_core/tracing/__init__.py,sha256=u1QicGc39e30gWyQD4cQWxGGjITnkwoOPUhNrG6aNyI,1266
|
|
33
|
-
agent_runtime_core/tracing/langfuse.py,sha256=Rj2sUlatk5sFro0y68tw5X6fQcSwWxcBOSOjB0F7JTU,3660
|
|
34
|
-
agent_runtime_core/tracing/noop.py,sha256=SpsbpsUcNG6C3xZG3uyiNPUHY8etloISx3w56Q8D3KE,751
|
|
35
|
-
agent_runtime_core-0.6.0.dist-info/METADATA,sha256=Q9egtbhBSMcgmeA9TM4gy_8P5pUcN0WFMCNaAOii12w,23858
|
|
36
|
-
agent_runtime_core-0.6.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
37
|
-
agent_runtime_core-0.6.0.dist-info/licenses/LICENSE,sha256=fDlWep3_mUrj8KHV_jk275tHVEW7_9sJRhkNuGCZ_TA,1068
|
|
38
|
-
agent_runtime_core-0.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|