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
agent_runtime_core/__init__.py
CHANGED
|
@@ -34,16 +34,18 @@ Example usage:
|
|
|
34
34
|
return RunResult(final_output={"message": "Hello!"})
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
-
__version__ = "0.
|
|
37
|
+
__version__ = "0.7.0"
|
|
38
38
|
|
|
39
39
|
# Core interfaces
|
|
40
40
|
from agent_runtime_core.interfaces import (
|
|
41
41
|
AgentRuntime,
|
|
42
42
|
EventType,
|
|
43
|
+
EventVisibility,
|
|
43
44
|
ErrorInfo,
|
|
44
45
|
LLMClient,
|
|
45
46
|
LLMResponse,
|
|
46
47
|
LLMStreamChunk,
|
|
48
|
+
LLMToolCall,
|
|
47
49
|
Message,
|
|
48
50
|
RunContext,
|
|
49
51
|
RunResult,
|
|
@@ -57,6 +59,12 @@ from agent_runtime_core.interfaces import (
|
|
|
57
59
|
# Tool Calling Agent base class
|
|
58
60
|
from agent_runtime_core.tool_calling_agent import ToolCallingAgent
|
|
59
61
|
|
|
62
|
+
# Agentic loop helper
|
|
63
|
+
from agent_runtime_core.agentic_loop import (
|
|
64
|
+
run_agentic_loop,
|
|
65
|
+
AgenticLoopResult,
|
|
66
|
+
)
|
|
67
|
+
|
|
60
68
|
# Configuration
|
|
61
69
|
from agent_runtime_core.config import (
|
|
62
70
|
RuntimeConfig,
|
|
@@ -91,6 +99,12 @@ from agent_runtime_core.steps import (
|
|
|
91
99
|
StepCancelledError,
|
|
92
100
|
)
|
|
93
101
|
|
|
102
|
+
# Concrete RunContext implementations for different use cases
|
|
103
|
+
from agent_runtime_core.contexts import (
|
|
104
|
+
InMemoryRunContext,
|
|
105
|
+
FileRunContext,
|
|
106
|
+
)
|
|
107
|
+
|
|
94
108
|
# Testing utilities
|
|
95
109
|
from agent_runtime_core.testing import (
|
|
96
110
|
MockRunContext,
|
|
@@ -122,6 +136,7 @@ from agent_runtime_core.persistence import (
|
|
|
122
136
|
FileConversationStore,
|
|
123
137
|
FileTaskStore,
|
|
124
138
|
FilePreferencesStore,
|
|
139
|
+
FileKnowledgeStore,
|
|
125
140
|
# Manager
|
|
126
141
|
PersistenceManager,
|
|
127
142
|
PersistenceConfig,
|
|
@@ -129,6 +144,69 @@ from agent_runtime_core.persistence import (
|
|
|
129
144
|
configure_persistence,
|
|
130
145
|
)
|
|
131
146
|
|
|
147
|
+
# Agent configuration schema (portable JSON format)
|
|
148
|
+
from agent_runtime_core.config_schema import (
|
|
149
|
+
AgentConfig,
|
|
150
|
+
ToolConfig,
|
|
151
|
+
KnowledgeConfig,
|
|
152
|
+
SubAgentToolConfig,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# JSON-based runtime (loads from AgentConfig)
|
|
156
|
+
from agent_runtime_core.json_runtime import (
|
|
157
|
+
JsonAgentRuntime,
|
|
158
|
+
ConfiguredTool,
|
|
159
|
+
SubAgentTool,
|
|
160
|
+
resolve_function,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Vector store (optional - requires additional dependencies)
|
|
164
|
+
# Import these directly from agent_runtime_core.vectorstore when needed:
|
|
165
|
+
# from agent_runtime_core.vectorstore import (
|
|
166
|
+
# VectorStore, VectorRecord, VectorSearchResult,
|
|
167
|
+
# EmbeddingClient, OpenAIEmbeddings, VertexAIEmbeddings,
|
|
168
|
+
# get_vector_store, get_embedding_client,
|
|
169
|
+
# )
|
|
170
|
+
|
|
171
|
+
# RAG (Retrieval Augmented Generation)
|
|
172
|
+
from agent_runtime_core.rag import (
|
|
173
|
+
chunk_text,
|
|
174
|
+
ChunkingConfig,
|
|
175
|
+
TextChunk,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# RAG services are imported lazily to avoid circular dependencies
|
|
179
|
+
# Import directly when needed:
|
|
180
|
+
# from agent_runtime_core.rag import KnowledgeIndexer, KnowledgeRetriever
|
|
181
|
+
|
|
182
|
+
# Tool schema builder utilities
|
|
183
|
+
from agent_runtime_core.tools import (
|
|
184
|
+
ToolSchema,
|
|
185
|
+
ToolSchemaBuilder,
|
|
186
|
+
ToolParameter,
|
|
187
|
+
schemas_to_openai_format,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# Multi-agent support (agent-as-tool pattern)
|
|
191
|
+
from agent_runtime_core.multi_agent import (
|
|
192
|
+
AgentTool,
|
|
193
|
+
AgentInvocationResult,
|
|
194
|
+
InvocationMode,
|
|
195
|
+
ContextMode,
|
|
196
|
+
SubAgentContext,
|
|
197
|
+
invoke_agent,
|
|
198
|
+
create_agent_tool_handler,
|
|
199
|
+
register_agent_tools,
|
|
200
|
+
build_sub_agent_messages,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Cross-conversation memory
|
|
204
|
+
# Import directly when needed for full functionality:
|
|
205
|
+
# from agent_runtime_core.memory import (
|
|
206
|
+
# MemoryManager, MemoryConfig, MemoryEnabledAgent,
|
|
207
|
+
# ExtractedMemory, RecalledMemory, with_memory,
|
|
208
|
+
# )
|
|
209
|
+
|
|
132
210
|
__all__ = [
|
|
133
211
|
# Version
|
|
134
212
|
"__version__",
|
|
@@ -137,6 +215,7 @@ __all__ = [
|
|
|
137
215
|
"LLMClient",
|
|
138
216
|
"LLMResponse",
|
|
139
217
|
"LLMStreamChunk",
|
|
218
|
+
"LLMToolCall",
|
|
140
219
|
"Message",
|
|
141
220
|
"RunContext",
|
|
142
221
|
"RunResult",
|
|
@@ -145,8 +224,12 @@ __all__ = [
|
|
|
145
224
|
"ToolDefinition",
|
|
146
225
|
"TraceSink",
|
|
147
226
|
"EventType",
|
|
227
|
+
"EventVisibility",
|
|
148
228
|
"ErrorInfo",
|
|
149
|
-
|
|
229
|
+
# Tool calling
|
|
230
|
+
"ToolCallingAgent",
|
|
231
|
+
"run_agentic_loop",
|
|
232
|
+
"AgenticLoopResult",
|
|
150
233
|
# Configuration
|
|
151
234
|
"RuntimeConfig",
|
|
152
235
|
"configure",
|
|
@@ -169,6 +252,9 @@ __all__ = [
|
|
|
169
252
|
"ExecutionState",
|
|
170
253
|
"StepExecutionError",
|
|
171
254
|
"StepCancelledError",
|
|
255
|
+
# Concrete RunContext implementations
|
|
256
|
+
"InMemoryRunContext",
|
|
257
|
+
"FileRunContext",
|
|
172
258
|
# Testing
|
|
173
259
|
"MockRunContext",
|
|
174
260
|
"MockLLMClient",
|
|
@@ -195,9 +281,39 @@ __all__ = [
|
|
|
195
281
|
"FileConversationStore",
|
|
196
282
|
"FileTaskStore",
|
|
197
283
|
"FilePreferencesStore",
|
|
284
|
+
"FileKnowledgeStore",
|
|
198
285
|
# Persistence - Manager
|
|
199
286
|
"PersistenceManager",
|
|
200
287
|
"PersistenceConfig",
|
|
201
288
|
"get_persistence_manager",
|
|
202
289
|
"configure_persistence",
|
|
290
|
+
# Agent configuration schema
|
|
291
|
+
"AgentConfig",
|
|
292
|
+
"ToolConfig",
|
|
293
|
+
"KnowledgeConfig",
|
|
294
|
+
"SubAgentToolConfig",
|
|
295
|
+
# JSON-based runtime
|
|
296
|
+
"JsonAgentRuntime",
|
|
297
|
+
"ConfiguredTool",
|
|
298
|
+
"SubAgentTool",
|
|
299
|
+
"resolve_function",
|
|
300
|
+
# RAG (Retrieval Augmented Generation)
|
|
301
|
+
"chunk_text",
|
|
302
|
+
"ChunkingConfig",
|
|
303
|
+
"TextChunk",
|
|
304
|
+
# Tool schema builder
|
|
305
|
+
"ToolSchema",
|
|
306
|
+
"ToolSchemaBuilder",
|
|
307
|
+
"ToolParameter",
|
|
308
|
+
"schemas_to_openai_format",
|
|
309
|
+
# Multi-agent support
|
|
310
|
+
"AgentTool",
|
|
311
|
+
"AgentInvocationResult",
|
|
312
|
+
"InvocationMode",
|
|
313
|
+
"ContextMode",
|
|
314
|
+
"SubAgentContext",
|
|
315
|
+
"invoke_agent",
|
|
316
|
+
"create_agent_tool_handler",
|
|
317
|
+
"register_agent_tools",
|
|
318
|
+
"build_sub_agent_messages",
|
|
203
319
|
]
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Reusable agentic loop for tool-calling agents.
|
|
3
|
+
|
|
4
|
+
This module provides a flexible `run_agentic_loop` function that handles
|
|
5
|
+
the standard tool-calling pattern:
|
|
6
|
+
1. Call LLM with tools
|
|
7
|
+
2. If tool calls, execute them and loop back
|
|
8
|
+
3. If no tool calls, return final response
|
|
9
|
+
|
|
10
|
+
This can be used by any agent implementation without requiring inheritance.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from typing import Any, Callable, Optional, Awaitable, Union
|
|
17
|
+
|
|
18
|
+
from agent_runtime_core.interfaces import (
|
|
19
|
+
RunContext,
|
|
20
|
+
EventType,
|
|
21
|
+
LLMClient,
|
|
22
|
+
LLMResponse,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
# Type alias for tool executor function
|
|
28
|
+
ToolExecutor = Callable[[str, dict], Awaitable[Any]]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class AgenticLoopResult:
|
|
33
|
+
"""Result from running the agentic loop."""
|
|
34
|
+
|
|
35
|
+
final_content: str
|
|
36
|
+
"""The final text response from the LLM."""
|
|
37
|
+
|
|
38
|
+
messages: list[dict]
|
|
39
|
+
"""All messages including tool calls and results."""
|
|
40
|
+
|
|
41
|
+
iterations: int
|
|
42
|
+
"""Number of iterations the loop ran."""
|
|
43
|
+
|
|
44
|
+
usage: dict
|
|
45
|
+
"""Token usage from the final LLM call."""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def run_agentic_loop(
|
|
49
|
+
llm: LLMClient,
|
|
50
|
+
messages: list[dict],
|
|
51
|
+
tools: Optional[list[dict]],
|
|
52
|
+
execute_tool: ToolExecutor,
|
|
53
|
+
ctx: RunContext,
|
|
54
|
+
*,
|
|
55
|
+
model: Optional[str] = None,
|
|
56
|
+
max_iterations: int = 15,
|
|
57
|
+
emit_events: bool = True,
|
|
58
|
+
**llm_kwargs,
|
|
59
|
+
) -> AgenticLoopResult:
|
|
60
|
+
"""
|
|
61
|
+
Run the standard agentic tool-calling loop.
|
|
62
|
+
|
|
63
|
+
This handles the common pattern of:
|
|
64
|
+
1. Call LLM with available tools
|
|
65
|
+
2. If LLM returns tool calls, execute them
|
|
66
|
+
3. Add tool results to messages and loop back to step 1
|
|
67
|
+
4. If LLM returns a text response (no tool calls), return it
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
llm: The LLM client to use for generation
|
|
71
|
+
messages: Initial messages (should include system prompt)
|
|
72
|
+
tools: List of tool schemas in OpenAI format, or None for no tools
|
|
73
|
+
execute_tool: Async function that executes a tool: (name, args) -> result
|
|
74
|
+
ctx: Run context for emitting events
|
|
75
|
+
model: Model to use (passed to LLM client)
|
|
76
|
+
max_iterations: Maximum loop iterations to prevent infinite loops
|
|
77
|
+
emit_events: Whether to emit TOOL_CALL and TOOL_RESULT events
|
|
78
|
+
**llm_kwargs: Additional kwargs passed to llm.generate()
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
AgenticLoopResult with final content, messages, and metadata
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
async def my_tool_executor(name: str, args: dict) -> Any:
|
|
85
|
+
if name == "get_weather":
|
|
86
|
+
return {"temp": 72, "conditions": "sunny"}
|
|
87
|
+
raise ValueError(f"Unknown tool: {name}")
|
|
88
|
+
|
|
89
|
+
result = await run_agentic_loop(
|
|
90
|
+
llm=my_llm_client,
|
|
91
|
+
messages=[{"role": "system", "content": "You are helpful."}],
|
|
92
|
+
tools=[{"type": "function", "function": {...}}],
|
|
93
|
+
execute_tool=my_tool_executor,
|
|
94
|
+
ctx=ctx,
|
|
95
|
+
model="gpt-4o",
|
|
96
|
+
)
|
|
97
|
+
"""
|
|
98
|
+
iteration = 0
|
|
99
|
+
final_content = ""
|
|
100
|
+
last_response: Optional[LLMResponse] = None
|
|
101
|
+
consecutive_errors = 0
|
|
102
|
+
max_consecutive_errors = 3 # Bail out if tool keeps failing
|
|
103
|
+
|
|
104
|
+
while iteration < max_iterations:
|
|
105
|
+
iteration += 1
|
|
106
|
+
print(f"[agentic-loop] Iteration {iteration}/{max_iterations}, messages={len(messages)}", flush=True)
|
|
107
|
+
logger.debug(f"Agentic loop iteration {iteration}/{max_iterations}")
|
|
108
|
+
|
|
109
|
+
# Call LLM
|
|
110
|
+
if tools:
|
|
111
|
+
response = await llm.generate(
|
|
112
|
+
messages,
|
|
113
|
+
model=model,
|
|
114
|
+
tools=tools,
|
|
115
|
+
**llm_kwargs,
|
|
116
|
+
)
|
|
117
|
+
else:
|
|
118
|
+
response = await llm.generate(
|
|
119
|
+
messages,
|
|
120
|
+
model=model,
|
|
121
|
+
**llm_kwargs,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
last_response = response
|
|
125
|
+
|
|
126
|
+
# Check for tool calls
|
|
127
|
+
if response.tool_calls:
|
|
128
|
+
# Add assistant message with tool calls to conversation
|
|
129
|
+
messages.append(response.message)
|
|
130
|
+
|
|
131
|
+
# Execute each tool call
|
|
132
|
+
for tool_call in response.tool_calls:
|
|
133
|
+
# Debug: log raw tool call to help diagnose empty args issue
|
|
134
|
+
print(f"[agentic-loop] Raw tool_call type={type(tool_call).__name__}", flush=True)
|
|
135
|
+
if hasattr(tool_call, '_data'):
|
|
136
|
+
print(f"[agentic-loop] tool_call._data={tool_call._data}", flush=True)
|
|
137
|
+
|
|
138
|
+
# Handle both ToolCall objects (with .id, .name, .arguments) and dicts
|
|
139
|
+
if hasattr(tool_call, 'id') and not isinstance(tool_call, dict):
|
|
140
|
+
# ToolCall object
|
|
141
|
+
tool_call_id = tool_call.id
|
|
142
|
+
tool_name = tool_call.name
|
|
143
|
+
tool_args = tool_call.arguments
|
|
144
|
+
print(f"[agentic-loop] Parsed: name={tool_name}, args={tool_args}", flush=True)
|
|
145
|
+
else:
|
|
146
|
+
# Dict format
|
|
147
|
+
tool_call_id = tool_call.get("id")
|
|
148
|
+
tool_name = tool_call.get("function", {}).get("name")
|
|
149
|
+
tool_args_str = tool_call.get("function", {}).get("arguments", "{}")
|
|
150
|
+
logger.debug(f"Dict tool_call: id={tool_call_id}, name={tool_name}, args_str={tool_args_str}")
|
|
151
|
+
# Parse arguments (handle both string and dict)
|
|
152
|
+
if isinstance(tool_args_str, dict):
|
|
153
|
+
tool_args = tool_args_str
|
|
154
|
+
else:
|
|
155
|
+
try:
|
|
156
|
+
tool_args = json.loads(tool_args_str)
|
|
157
|
+
except json.JSONDecodeError:
|
|
158
|
+
logger.warning(f"Failed to parse tool args: {tool_args_str}")
|
|
159
|
+
tool_args = {}
|
|
160
|
+
|
|
161
|
+
# Emit tool call event
|
|
162
|
+
if emit_events:
|
|
163
|
+
await ctx.emit(EventType.TOOL_CALL, {
|
|
164
|
+
"id": tool_call_id,
|
|
165
|
+
"name": tool_name,
|
|
166
|
+
"arguments": tool_args,
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
# Execute the tool
|
|
170
|
+
try:
|
|
171
|
+
result = await execute_tool(tool_name, tool_args)
|
|
172
|
+
# Reset error counter on success
|
|
173
|
+
if not isinstance(result, dict) or "error" not in result:
|
|
174
|
+
consecutive_errors = 0
|
|
175
|
+
else:
|
|
176
|
+
consecutive_errors += 1
|
|
177
|
+
except Exception as e:
|
|
178
|
+
logger.exception(f"Error executing tool {tool_name}")
|
|
179
|
+
result = {"error": str(e)}
|
|
180
|
+
consecutive_errors += 1
|
|
181
|
+
|
|
182
|
+
# Emit tool result event
|
|
183
|
+
if emit_events:
|
|
184
|
+
await ctx.emit(EventType.TOOL_RESULT, {
|
|
185
|
+
"tool_call_id": tool_call_id,
|
|
186
|
+
"name": tool_name,
|
|
187
|
+
"result": result,
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
# Add tool result to messages
|
|
191
|
+
result_str = json.dumps(result) if not isinstance(result, str) else result
|
|
192
|
+
messages.append({
|
|
193
|
+
"role": "tool",
|
|
194
|
+
"tool_call_id": tool_call_id,
|
|
195
|
+
"content": result_str,
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
# Check for too many consecutive errors
|
|
199
|
+
if consecutive_errors >= max_consecutive_errors:
|
|
200
|
+
error_msg = result.get('error', 'Unknown error') if isinstance(result, dict) else str(result)
|
|
201
|
+
logger.warning(f"Aborting agentic loop after {consecutive_errors} consecutive tool errors: {error_msg}")
|
|
202
|
+
|
|
203
|
+
# Emit error event for run history
|
|
204
|
+
if emit_events:
|
|
205
|
+
await ctx.emit(EventType.ERROR, {
|
|
206
|
+
"error": f"Tool loop aborted after {consecutive_errors} consecutive errors",
|
|
207
|
+
"last_error": error_msg,
|
|
208
|
+
"tool_name": tool_name,
|
|
209
|
+
"iterations": iteration,
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
# Add error to messages for conversation history
|
|
213
|
+
final_content = f"I encountered repeated errors while trying to complete this task. The last error was: {error_msg}"
|
|
214
|
+
messages.append({
|
|
215
|
+
"role": "assistant",
|
|
216
|
+
"content": final_content,
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
if emit_events:
|
|
220
|
+
await ctx.emit(EventType.ASSISTANT_MESSAGE, {
|
|
221
|
+
"content": final_content,
|
|
222
|
+
"role": "assistant",
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
return AgenticLoopResult(
|
|
226
|
+
final_content=final_content,
|
|
227
|
+
messages=messages,
|
|
228
|
+
iterations=iteration,
|
|
229
|
+
usage=last_response.usage if last_response else {},
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# Continue the loop to get next response
|
|
233
|
+
continue
|
|
234
|
+
|
|
235
|
+
# No tool calls - we have the final response
|
|
236
|
+
final_content = response.message.get("content", "")
|
|
237
|
+
messages.append(response.message)
|
|
238
|
+
|
|
239
|
+
# Emit assistant message event for the final response
|
|
240
|
+
if emit_events and final_content:
|
|
241
|
+
await ctx.emit(EventType.ASSISTANT_MESSAGE, {
|
|
242
|
+
"content": final_content,
|
|
243
|
+
"role": "assistant",
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
break
|
|
247
|
+
|
|
248
|
+
return AgenticLoopResult(
|
|
249
|
+
final_content=final_content,
|
|
250
|
+
messages=messages,
|
|
251
|
+
iterations=iteration,
|
|
252
|
+
usage=last_response.usage if last_response else {},
|
|
253
|
+
)
|
|
254
|
+
|
agent_runtime_core/config.py
CHANGED
|
@@ -59,7 +59,35 @@ class RuntimeConfig:
|
|
|
59
59
|
max_retries: int = 3
|
|
60
60
|
retry_backoff_base: int = 2
|
|
61
61
|
retry_backoff_max: int = 300
|
|
62
|
-
|
|
62
|
+
|
|
63
|
+
# Conversation history settings
|
|
64
|
+
# When True, agents automatically receive message history from previous runs
|
|
65
|
+
# in the same conversation. This enables multi-turn conversations by default.
|
|
66
|
+
include_conversation_history: bool = True
|
|
67
|
+
|
|
68
|
+
# Maximum number of history messages to include (None = no limit)
|
|
69
|
+
# Useful for limiting context window usage with long conversations
|
|
70
|
+
max_history_messages: Optional[int] = None
|
|
71
|
+
|
|
72
|
+
# Whether to automatically persist messages after each run
|
|
73
|
+
# When True, final_messages are saved to the conversation store
|
|
74
|
+
auto_persist_messages: bool = True
|
|
75
|
+
|
|
76
|
+
# Vector store settings
|
|
77
|
+
vector_store_backend: str = "none" # none, sqlite_vec, pgvector, vertex
|
|
78
|
+
vector_store_path: Optional[str] = None # For sqlite_vec file path
|
|
79
|
+
|
|
80
|
+
# Embedding settings
|
|
81
|
+
embedding_provider: str = "openai" # openai, vertex
|
|
82
|
+
embedding_model: str = "text-embedding-3-small"
|
|
83
|
+
|
|
84
|
+
# Vertex AI settings (for vertex vector store and embeddings)
|
|
85
|
+
vertex_project_id: Optional[str] = None
|
|
86
|
+
vertex_location: str = "us-central1"
|
|
87
|
+
vertex_index_endpoint_id: Optional[str] = None
|
|
88
|
+
vertex_deployed_index_id: Optional[str] = None
|
|
89
|
+
vertex_index_id: Optional[str] = None
|
|
90
|
+
|
|
63
91
|
def get_openai_api_key(self) -> Optional[str]:
|
|
64
92
|
"""Get OpenAI API key from config or environment."""
|
|
65
93
|
return self.openai_api_key or os.environ.get("OPENAI_API_KEY")
|
|
@@ -150,8 +178,19 @@ def _apply_env_vars(config: RuntimeConfig) -> None:
|
|
|
150
178
|
"AGENT_RUNTIME_LANGFUSE_PUBLIC_KEY": "langfuse_public_key",
|
|
151
179
|
"AGENT_RUNTIME_LANGFUSE_SECRET_KEY": "langfuse_secret_key",
|
|
152
180
|
"AGENT_RUNTIME_LANGFUSE_HOST": "langfuse_host",
|
|
181
|
+
# Vector store settings
|
|
182
|
+
"AGENT_RUNTIME_VECTOR_STORE_BACKEND": "vector_store_backend",
|
|
183
|
+
"AGENT_RUNTIME_VECTOR_STORE_PATH": "vector_store_path",
|
|
184
|
+
"AGENT_RUNTIME_EMBEDDING_PROVIDER": "embedding_provider",
|
|
185
|
+
"AGENT_RUNTIME_EMBEDDING_MODEL": "embedding_model",
|
|
186
|
+
# Vertex AI settings
|
|
187
|
+
"AGENT_RUNTIME_VERTEX_PROJECT_ID": "vertex_project_id",
|
|
188
|
+
"AGENT_RUNTIME_VERTEX_LOCATION": "vertex_location",
|
|
189
|
+
"AGENT_RUNTIME_VERTEX_INDEX_ENDPOINT_ID": "vertex_index_endpoint_id",
|
|
190
|
+
"AGENT_RUNTIME_VERTEX_DEPLOYED_INDEX_ID": "vertex_deployed_index_id",
|
|
191
|
+
"AGENT_RUNTIME_VERTEX_INDEX_ID": "vertex_index_id",
|
|
153
192
|
}
|
|
154
|
-
|
|
193
|
+
|
|
155
194
|
int_fields = {
|
|
156
195
|
"AGENT_RUNTIME_RUN_TIMEOUT_SECONDS": "run_timeout_seconds",
|
|
157
196
|
"AGENT_RUNTIME_HEARTBEAT_INTERVAL_SECONDS": "heartbeat_interval_seconds",
|
|
@@ -159,14 +198,25 @@ def _apply_env_vars(config: RuntimeConfig) -> None:
|
|
|
159
198
|
"AGENT_RUNTIME_MAX_RETRIES": "max_retries",
|
|
160
199
|
"AGENT_RUNTIME_RETRY_BACKOFF_BASE": "retry_backoff_base",
|
|
161
200
|
"AGENT_RUNTIME_RETRY_BACKOFF_MAX": "retry_backoff_max",
|
|
201
|
+
"AGENT_RUNTIME_MAX_HISTORY_MESSAGES": "max_history_messages",
|
|
162
202
|
}
|
|
163
|
-
|
|
203
|
+
|
|
204
|
+
bool_fields = {
|
|
205
|
+
"AGENT_RUNTIME_INCLUDE_CONVERSATION_HISTORY": "include_conversation_history",
|
|
206
|
+
"AGENT_RUNTIME_AUTO_PERSIST_MESSAGES": "auto_persist_messages",
|
|
207
|
+
}
|
|
208
|
+
|
|
164
209
|
for env_var, attr in env_mapping.items():
|
|
165
210
|
value = os.environ.get(env_var)
|
|
166
211
|
if value is not None:
|
|
167
212
|
setattr(config, attr, value)
|
|
168
|
-
|
|
213
|
+
|
|
169
214
|
for env_var, attr in int_fields.items():
|
|
170
215
|
value = os.environ.get(env_var)
|
|
171
216
|
if value is not None:
|
|
172
217
|
setattr(config, attr, int(value))
|
|
218
|
+
|
|
219
|
+
for env_var, attr in bool_fields.items():
|
|
220
|
+
value = os.environ.get(env_var)
|
|
221
|
+
if value is not None:
|
|
222
|
+
setattr(config, attr, value.lower() in ("true", "1", "yes", "on"))
|