quantum-framework 0.9.0__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.
- quantum/__init__.py +1 -0
- quantum/cli/__init__.py +2 -0
- quantum/cli/commands/__init__.py +15 -0
- quantum/cli/commands/build.py +417 -0
- quantum/cli/commands/dev.py +185 -0
- quantum/cli/commands/docs.py +329 -0
- quantum/cli/commands/lint.py +523 -0
- quantum/cli/commands/migrate.py +527 -0
- quantum/cli/commands/new.py +622 -0
- quantum/cli/commands/serve.py +190 -0
- quantum/cli/commands/test.py +193 -0
- quantum/cli/deploy.py +810 -0
- quantum/cli/hot_reload.py +951 -0
- quantum/cli/jobs.py +356 -0
- quantum/cli/mq.py +582 -0
- quantum/cli/pkg.py +390 -0
- quantum/cli/runner.py +547 -0
- quantum/cli/server_process.py +159 -0
- quantum/cli/utils.py +334 -0
- quantum/compiler/__init__.py +30 -0
- quantum/compiler/base_generator.py +367 -0
- quantum/compiler/cli.py +295 -0
- quantum/compiler/expression_transformer.py +444 -0
- quantum/compiler/javascript/__init__.py +10 -0
- quantum/compiler/javascript/generator.py +659 -0
- quantum/compiler/optimizer.py +270 -0
- quantum/compiler/python/__init__.py +10 -0
- quantum/compiler/python/generator.py +883 -0
- quantum/compiler/python/runtime.py +863 -0
- quantum/compiler/transpiler.py +330 -0
- quantum/core/__init__.py +3 -0
- quantum/core/ast_nodes.py +2611 -0
- quantum/core/expression_diagnostics.py +87 -0
- quantum/core/expression_stdlib.py +148 -0
- quantum/core/expressions.py +591 -0
- quantum/core/features/agents/src/__init__.py +30 -0
- quantum/core/features/agents/src/ast_node.py +540 -0
- quantum/core/features/conditionals/src/__init__.py +8 -0
- quantum/core/features/conditionals/src/ast_node.py +68 -0
- quantum/core/features/data_fetching/src/__init__.py +21 -0
- quantum/core/features/data_fetching/src/ast_node.py +312 -0
- quantum/core/features/data_fetching/src/desktop_adapter.py +351 -0
- quantum/core/features/data_fetching/src/html_adapter.py +474 -0
- quantum/core/features/data_fetching/src/parser.py +225 -0
- quantum/core/features/data_import/src/__init__.py +0 -0
- quantum/core/features/data_import/src/ast_node.py +291 -0
- quantum/core/features/data_import/src/runtime.py +538 -0
- quantum/core/features/dump/src/__init__.py +12 -0
- quantum/core/features/dump/src/ast_node.py +106 -0
- quantum/core/features/dump/src/parser.py +61 -0
- quantum/core/features/dump/src/runtime.py +246 -0
- quantum/core/features/functions/src/__init__.py +8 -0
- quantum/core/features/functions/src/ast_node.py +149 -0
- quantum/core/features/game_engine_2d/src/__init__.py +19 -0
- quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
- quantum/core/features/game_engine_2d/src/parser.py +983 -0
- quantum/core/features/invocation/src/__init__.py +0 -0
- quantum/core/features/invocation/src/ast_node.py +146 -0
- quantum/core/features/invocation/src/runtime.py +327 -0
- quantum/core/features/knowledge_base/src/__init__.py +6 -0
- quantum/core/features/knowledge_base/src/ast_node.py +113 -0
- quantum/core/features/knowledge_base/src/parser.py +82 -0
- quantum/core/features/logging/src/__init__.py +12 -0
- quantum/core/features/logging/src/ast_node.py +111 -0
- quantum/core/features/logging/src/parser.py +50 -0
- quantum/core/features/logging/src/runtime.py +190 -0
- quantum/core/features/loops/src/__init__.py +8 -0
- quantum/core/features/loops/src/ast_node.py +60 -0
- quantum/core/features/query/src/__init__.py +0 -0
- quantum/core/features/query/src/database_service.py +322 -0
- quantum/core/features/query/src/query_validators.py +20 -0
- quantum/core/features/state_management/src/__init__.py +11 -0
- quantum/core/features/state_management/src/ast_node.py +228 -0
- quantum/core/features/terminal_engine/src/__init__.py +21 -0
- quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
- quantum/core/features/terminal_engine/src/parser.py +361 -0
- quantum/core/features/testing_engine/src/__init__.py +41 -0
- quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
- quantum/core/features/testing_engine/src/parser.py +604 -0
- quantum/core/features/theming/src/__init__.py +48 -0
- quantum/core/features/theming/src/ast_node.py +137 -0
- quantum/core/features/theming/src/presets.py +405 -0
- quantum/core/features/ui_engine/src/__init__.py +20 -0
- quantum/core/features/ui_engine/src/ast_nodes.py +1854 -0
- quantum/core/features/ui_engine/src/parser.py +1106 -0
- quantum/core/features/websocket/src/__init__.py +24 -0
- quantum/core/features/websocket/src/ast_node.py +247 -0
- quantum/core/html_compat.py +299 -0
- quantum/core/parser.py +1235 -0
- quantum/core/parser_registry.py +213 -0
- quantum/core/parsers/__init__.py +76 -0
- quantum/core/parsers/ai/__init__.py +12 -0
- quantum/core/parsers/ai/agent_parser.py +106 -0
- quantum/core/parsers/ai/knowledge_parser.py +86 -0
- quantum/core/parsers/ai/llm_parser.py +78 -0
- quantum/core/parsers/ai/team_parser.py +88 -0
- quantum/core/parsers/base.py +322 -0
- quantum/core/parsers/composition/__init__.py +10 -0
- quantum/core/parsers/composition/import_parser.py +50 -0
- quantum/core/parsers/composition/slot_parser.py +48 -0
- quantum/core/parsers/control_flow/__init__.py +11 -0
- quantum/core/parsers/control_flow/if_parser.py +68 -0
- quantum/core/parsers/control_flow/loop_parser.py +105 -0
- quantum/core/parsers/control_flow/set_parser.py +89 -0
- quantum/core/parsers/data/__init__.py +12 -0
- quantum/core/parsers/data/data_parser.py +217 -0
- quantum/core/parsers/data/invoke_parser.py +116 -0
- quantum/core/parsers/data/query_parser.py +188 -0
- quantum/core/parsers/data/transaction_parser.py +77 -0
- quantum/core/parsers/events/__init__.py +9 -0
- quantum/core/parsers/events/dispatch_event_parser.py +44 -0
- quantum/core/parsers/forms/__init__.py +11 -0
- quantum/core/parsers/forms/action_parser.py +54 -0
- quantum/core/parsers/forms/flash_parser.py +34 -0
- quantum/core/parsers/forms/redirect_parser.py +33 -0
- quantum/core/parsers/functions/__init__.py +11 -0
- quantum/core/parsers/functions/function_parser.py +137 -0
- quantum/core/parsers/functions/param_parser.py +66 -0
- quantum/core/parsers/functions/return_parser.py +31 -0
- quantum/core/parsers/html/__init__.py +10 -0
- quantum/core/parsers/html/component_call_parser.py +130 -0
- quantum/core/parsers/html/html_parser.py +115 -0
- quantum/core/parsers/jobs/__init__.py +11 -0
- quantum/core/parsers/jobs/job_parser.py +71 -0
- quantum/core/parsers/jobs/schedule_parser.py +62 -0
- quantum/core/parsers/jobs/thread_parser.py +57 -0
- quantum/core/parsers/messaging/__init__.py +17 -0
- quantum/core/parsers/messaging/message_ack_parser.py +30 -0
- quantum/core/parsers/messaging/message_nack_parser.py +30 -0
- quantum/core/parsers/messaging/message_parser.py +114 -0
- quantum/core/parsers/messaging/queue_parser.py +61 -0
- quantum/core/parsers/messaging/websocket_parser.py +121 -0
- quantum/core/parsers/persistence/__init__.py +9 -0
- quantum/core/parsers/persistence/persist_parser.py +64 -0
- quantum/core/parsers/routing/__init__.py +9 -0
- quantum/core/parsers/routing/route_parser.py +41 -0
- quantum/core/parsers/scripting/__init__.py +12 -0
- quantum/core/parsers/scripting/pyclass_parser.py +62 -0
- quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
- quantum/core/parsers/scripting/pyimport_parser.py +52 -0
- quantum/core/parsers/scripting/python_parser.py +49 -0
- quantum/core/parsers/services/__init__.py +12 -0
- quantum/core/parsers/services/dump_parser.py +52 -0
- quantum/core/parsers/services/file_parser.py +48 -0
- quantum/core/parsers/services/log_parser.py +46 -0
- quantum/core/parsers/services/mail_parser.py +65 -0
- quantum/core/tiers.py +82 -0
- quantum/packages/__init__.py +28 -0
- quantum/packages/manager.py +413 -0
- quantum/packages/manifest.py +351 -0
- quantum/packages/registry.py +399 -0
- quantum/packages/resolver.py +336 -0
- quantum/plugins/__init__.py +33 -0
- quantum/plugins/hooks.py +329 -0
- quantum/plugins/loader.py +479 -0
- quantum/plugins/manifest.py +336 -0
- quantum/plugins/registry.py +371 -0
- quantum/runtime/__init__.py +28 -0
- quantum/runtime/action_handler.py +443 -0
- quantum/runtime/adapters/__init__.py +88 -0
- quantum/runtime/adapters/memory_adapter.py +690 -0
- quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
- quantum/runtime/adapters/redis_adapter.py +582 -0
- quantum/runtime/adapters/sqlite_adapter.py +414 -0
- quantum/runtime/agent_service.py +1133 -0
- quantum/runtime/api_server.py +86 -0
- quantum/runtime/ast_cache.py +506 -0
- quantum/runtime/auth_service.py +267 -0
- quantum/runtime/component.py +990 -0
- quantum/runtime/component_composer.py +319 -0
- quantum/runtime/component_resolver.py +174 -0
- quantum/runtime/database_service.py +598 -0
- quantum/runtime/email_service.py +162 -0
- quantum/runtime/error_handler.py +295 -0
- quantum/runtime/execution_context.py +286 -0
- quantum/runtime/executor_registry.py +171 -0
- quantum/runtime/executors/__init__.py +71 -0
- quantum/runtime/executors/ai/__init__.py +12 -0
- quantum/runtime/executors/ai/agent_executor.py +217 -0
- quantum/runtime/executors/ai/knowledge_executor.py +114 -0
- quantum/runtime/executors/ai/llm_executor.py +153 -0
- quantum/runtime/executors/ai/team_executor.py +171 -0
- quantum/runtime/executors/base.py +262 -0
- quantum/runtime/executors/control_flow/__init__.py +11 -0
- quantum/runtime/executors/control_flow/if_executor.py +93 -0
- quantum/runtime/executors/control_flow/loop_executor.py +307 -0
- quantum/runtime/executors/control_flow/set_executor.py +412 -0
- quantum/runtime/executors/data/__init__.py +12 -0
- quantum/runtime/executors/data/data_executor.py +145 -0
- quantum/runtime/executors/data/invoke_executor.py +176 -0
- quantum/runtime/executors/data/query_executor.py +256 -0
- quantum/runtime/executors/data/transaction_executor.py +91 -0
- quantum/runtime/executors/jobs/__init__.py +11 -0
- quantum/runtime/executors/jobs/job_executor.py +190 -0
- quantum/runtime/executors/jobs/schedule_executor.py +132 -0
- quantum/runtime/executors/jobs/thread_executor.py +127 -0
- quantum/runtime/executors/messaging/__init__.py +17 -0
- quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
- quantum/runtime/executors/messaging/message_executor.py +174 -0
- quantum/runtime/executors/messaging/queue_executor.py +103 -0
- quantum/runtime/executors/messaging/websocket_executor.py +197 -0
- quantum/runtime/executors/scripting/__init__.py +11 -0
- quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
- quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
- quantum/runtime/executors/scripting/python_executor.py +249 -0
- quantum/runtime/executors/services/__init__.py +12 -0
- quantum/runtime/executors/services/dump_executor.py +72 -0
- quantum/runtime/executors/services/file_executor.py +89 -0
- quantum/runtime/executors/services/log_executor.py +77 -0
- quantum/runtime/executors/services/mail_executor.py +81 -0
- quantum/runtime/expression_cache.py +498 -0
- quantum/runtime/file_upload_service.py +326 -0
- quantum/runtime/function_registry.py +118 -0
- quantum/runtime/game_builder.py +166 -0
- quantum/runtime/game_code_generator.py +2371 -0
- quantum/runtime/game_templates.py +2006 -0
- quantum/runtime/godot_code_generator.py +4681 -0
- quantum/runtime/godot_templates.py +1449 -0
- quantum/runtime/job_executor.py +1599 -0
- quantum/runtime/knowledge_service.py +500 -0
- quantum/runtime/llm_cache.py +100 -0
- quantum/runtime/llm_providers.py +704 -0
- quantum/runtime/llm_service.py +287 -0
- quantum/runtime/logging_setup.py +140 -0
- quantum/runtime/message_broker.py +364 -0
- quantum/runtime/message_queue_service.py +571 -0
- quantum/runtime/param_validation.py +184 -0
- quantum/runtime/pypy_compat.py +315 -0
- quantum/runtime/python_bridge.py +698 -0
- quantum/runtime/query_validators.py +304 -0
- quantum/runtime/renderer.py +733 -0
- quantum/runtime/service_container.py +444 -0
- quantum/runtime/terminal_builder.py +76 -0
- quantum/runtime/terminal_code_generator.py +607 -0
- quantum/runtime/terminal_templates.py +243 -0
- quantum/runtime/testing_builder.py +77 -0
- quantum/runtime/testing_code_generator.py +833 -0
- quantum/runtime/testing_templates.py +85 -0
- quantum/runtime/ui_builder.py +188 -0
- quantum/runtime/ui_desktop_adapter.py +1730 -0
- quantum/runtime/ui_desktop_templates.py +307 -0
- quantum/runtime/ui_html_adapter.py +2691 -0
- quantum/runtime/ui_html_templates.py +2297 -0
- quantum/runtime/ui_mobile_adapter.py +1832 -0
- quantum/runtime/ui_mobile_templates.py +1003 -0
- quantum/runtime/ui_textual_adapter.py +1866 -0
- quantum/runtime/ui_textual_templates.py +45 -0
- quantum/runtime/ui_tokens.py +465 -0
- quantum/runtime/ui_validator.py +365 -0
- quantum/runtime/validators.py +256 -0
- quantum/runtime/web_server.py +1766 -0
- quantum/runtime/websocket_adapter.py +501 -0
- quantum/runtime/websocket_service.py +585 -0
- quantum/runtime/websocket_transport.py +289 -0
- quantum/runtime/wsgi.py +101 -0
- quantum/utils/__init__.py +1 -0
- quantum_framework-0.9.0.dist-info/METADATA +244 -0
- quantum_framework-0.9.0.dist-info/RECORD +262 -0
- quantum_framework-0.9.0.dist-info/WHEEL +5 -0
- quantum_framework-0.9.0.dist-info/entry_points.txt +2 -0
- quantum_framework-0.9.0.dist-info/licenses/LICENSE +21 -0
- quantum_framework-0.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Executor - Execute q:agent statements
|
|
3
|
+
|
|
4
|
+
Handles AI agent execution with tool use capabilities.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, List, Dict, Type
|
|
8
|
+
from quantum.runtime.executors.base import BaseExecutor, ExecutorError
|
|
9
|
+
from quantum.core.ast_nodes import (
|
|
10
|
+
QuantumReturn,
|
|
11
|
+
HTMLNode, TextNode, DocTypeNode, CommentNode,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# Import from features module
|
|
15
|
+
try:
|
|
16
|
+
from quantum.core.features.agents.src.ast_node import AgentNode
|
|
17
|
+
except ImportError:
|
|
18
|
+
from quantum.core.features.agents.src import AgentNode
|
|
19
|
+
|
|
20
|
+
_RENDER_ONLY_NODE_TYPES = (HTMLNode, TextNode, DocTypeNode, CommentNode)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def build_tool_definitions(tool_nodes) -> List[Dict[str, Any]]:
|
|
24
|
+
"""
|
|
25
|
+
Build the tool definitions AgentService actually consumes.
|
|
26
|
+
|
|
27
|
+
AgentService._build_tools_description() reads name/description/params
|
|
28
|
+
(a list of param dicts), and AgentService._execute_tool() reads
|
|
29
|
+
tool_def["body"] to hand the AST statements to tool_executor. The
|
|
30
|
+
JSON-Schema shape from AgentToolNode.get_schema() has neither, which is
|
|
31
|
+
why tools used to silently degrade to the "no handler" placeholder.
|
|
32
|
+
"""
|
|
33
|
+
from quantum.runtime.agent_service import BUILTIN_TOOLS
|
|
34
|
+
|
|
35
|
+
tools = []
|
|
36
|
+
for tool in tool_nodes:
|
|
37
|
+
if tool.builtin:
|
|
38
|
+
builtin_def = BUILTIN_TOOLS.get(tool.name)
|
|
39
|
+
if builtin_def:
|
|
40
|
+
tools.append(dict(builtin_def))
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
tools.append({
|
|
44
|
+
'name': tool.name,
|
|
45
|
+
'description': tool.description,
|
|
46
|
+
'params': [p.to_dict() for p in tool.params],
|
|
47
|
+
'body': tool.body,
|
|
48
|
+
})
|
|
49
|
+
return tools
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class AgentExecutor(BaseExecutor):
|
|
53
|
+
"""
|
|
54
|
+
Executor for q:agent statements.
|
|
55
|
+
|
|
56
|
+
Supports:
|
|
57
|
+
- Multi-turn agent reasoning loop
|
|
58
|
+
- Tool calling and execution
|
|
59
|
+
- Multiple LLM providers (Ollama, OpenAI, Anthropic)
|
|
60
|
+
- Iteration limits and timeouts
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def handles(self) -> List[Type]:
|
|
65
|
+
return [AgentNode]
|
|
66
|
+
|
|
67
|
+
def execute(self, node: AgentNode, exec_context) -> Any:
|
|
68
|
+
"""
|
|
69
|
+
Execute AI agent.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
node: AgentNode with agent configuration
|
|
73
|
+
exec_context: Execution context
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Agent response dict with result and actions
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
context = exec_context.get_all_variables()
|
|
80
|
+
|
|
81
|
+
# Resolve task from execute node
|
|
82
|
+
if not node.execute:
|
|
83
|
+
raise ExecutorError("Agent requires <q:execute> to run")
|
|
84
|
+
|
|
85
|
+
task = self.apply_databinding(node.execute.task, context)
|
|
86
|
+
task_context = ""
|
|
87
|
+
if node.execute.context:
|
|
88
|
+
task_context = self.apply_databinding(node.execute.context, context)
|
|
89
|
+
|
|
90
|
+
# Build instruction
|
|
91
|
+
instruction = ""
|
|
92
|
+
if node.instruction:
|
|
93
|
+
instruction = self.apply_databinding(node.instruction.content, context)
|
|
94
|
+
|
|
95
|
+
tools = build_tool_definitions(node.tools)
|
|
96
|
+
|
|
97
|
+
api_key = ""
|
|
98
|
+
if node.api_key:
|
|
99
|
+
api_key = self.apply_databinding(node.api_key, context)
|
|
100
|
+
|
|
101
|
+
endpoint = ""
|
|
102
|
+
if node.endpoint:
|
|
103
|
+
endpoint = self.apply_databinding(node.endpoint, context)
|
|
104
|
+
|
|
105
|
+
agent_result = self.services.agent.execute(
|
|
106
|
+
instruction=instruction,
|
|
107
|
+
tools=tools,
|
|
108
|
+
task=task,
|
|
109
|
+
context=task_context,
|
|
110
|
+
model=node.model,
|
|
111
|
+
endpoint=endpoint,
|
|
112
|
+
provider=node.provider,
|
|
113
|
+
api_key=api_key,
|
|
114
|
+
max_iterations=node.max_iterations,
|
|
115
|
+
timeout_ms=node.timeout,
|
|
116
|
+
tool_executor=build_tool_executor(self, exec_context, node.tools),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
result = agent_result.to_dict()
|
|
120
|
+
|
|
121
|
+
# Store results
|
|
122
|
+
exec_context.set_variable(node.name, result.get('result', ''), scope="component")
|
|
123
|
+
exec_context.set_variable(f"{node.name}_result", result, scope="component")
|
|
124
|
+
|
|
125
|
+
return result
|
|
126
|
+
|
|
127
|
+
except Exception as e:
|
|
128
|
+
error_result = {
|
|
129
|
+
'success': False,
|
|
130
|
+
'error': {'message': str(e)},
|
|
131
|
+
'result': '',
|
|
132
|
+
'iterations': 0,
|
|
133
|
+
'actions': []
|
|
134
|
+
}
|
|
135
|
+
exec_context.set_variable(f"{node.name}_result", error_result, scope="component")
|
|
136
|
+
raise ExecutorError(f"Agent execution error: {e}")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def coerce_tool_args(params, tool_args: Dict[str, Any]) -> Dict[str, Any]:
|
|
140
|
+
"""
|
|
141
|
+
Coerce LLM-supplied tool arguments to their declared q:param types.
|
|
142
|
+
|
|
143
|
+
An LLM sends every argument as a string ({"a": "17"}), so a tool body
|
|
144
|
+
doing {a + b} would concatenate instead of adding. The declared
|
|
145
|
+
<q:param type="number"> is what tells us the intent.
|
|
146
|
+
"""
|
|
147
|
+
by_name = {p.name: p for p in (params or [])}
|
|
148
|
+
coerced = {}
|
|
149
|
+
|
|
150
|
+
for key, value in (tool_args or {}).items():
|
|
151
|
+
param = by_name.get(key)
|
|
152
|
+
if param is None or not isinstance(value, str):
|
|
153
|
+
coerced[key] = value
|
|
154
|
+
continue
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
if param.type in ('number', 'decimal'):
|
|
158
|
+
coerced[key] = float(value) if '.' in value else int(value)
|
|
159
|
+
elif param.type == 'integer':
|
|
160
|
+
coerced[key] = int(value)
|
|
161
|
+
elif param.type == 'boolean':
|
|
162
|
+
coerced[key] = value.strip().lower() in ('true', '1', 'yes', 'on')
|
|
163
|
+
elif param.type in ('array', 'object'):
|
|
164
|
+
import json
|
|
165
|
+
coerced[key] = json.loads(value)
|
|
166
|
+
else:
|
|
167
|
+
coerced[key] = value
|
|
168
|
+
except (ValueError, TypeError):
|
|
169
|
+
# Leave the raw value in place — the tool body may still handle it,
|
|
170
|
+
# and a hard failure here would be worse than a best-effort pass.
|
|
171
|
+
coerced[key] = value
|
|
172
|
+
|
|
173
|
+
return coerced
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def build_tool_executor(executor: BaseExecutor, exec_context, tool_nodes=None):
|
|
177
|
+
"""
|
|
178
|
+
Build the tool_executor callable AgentService/AgentTeam expect:
|
|
179
|
+
tool_executor(tool_name, tool_args, body) -> Any
|
|
180
|
+
|
|
181
|
+
Runs the tool's AST body in a child context with the LLM-supplied
|
|
182
|
+
arguments coerced to their declared types and bound as local variables.
|
|
183
|
+
"""
|
|
184
|
+
params_by_tool = {
|
|
185
|
+
tool.name: tool.params for tool in (tool_nodes or []) if not tool.builtin
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
def tool_executor(tool_name: str, tool_args: Dict[str, Any], body: List[Any]) -> Any:
|
|
189
|
+
child_context = exec_context.create_child_context()
|
|
190
|
+
args = coerce_tool_args(params_by_tool.get(tool_name), tool_args)
|
|
191
|
+
for key, value in args.items():
|
|
192
|
+
child_context.set_variable(key, value, scope="local")
|
|
193
|
+
return _run_tool_body(executor, body, child_context)
|
|
194
|
+
|
|
195
|
+
return tool_executor
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _run_tool_body(executor: BaseExecutor, body: List[Any], child_context) -> Any:
|
|
199
|
+
"""Execute a tool body, returning the first q:return value found."""
|
|
200
|
+
for statement in body or []:
|
|
201
|
+
if isinstance(statement, QuantumReturn):
|
|
202
|
+
return executor.resolve_value(statement.value, child_context.get_all_variables())
|
|
203
|
+
|
|
204
|
+
if isinstance(statement, _RENDER_ONLY_NODE_TYPES):
|
|
205
|
+
continue
|
|
206
|
+
|
|
207
|
+
# <q:tool> commonly wraps its implementation in a <q:function> —
|
|
208
|
+
# descend into it rather than registering it as a callable function.
|
|
209
|
+
if type(statement).__name__ == 'FunctionNode':
|
|
210
|
+
result = _run_tool_body(executor, getattr(statement, 'body', []), child_context)
|
|
211
|
+
if result is not None:
|
|
212
|
+
return result
|
|
213
|
+
continue
|
|
214
|
+
|
|
215
|
+
executor.runtime.executor_registry.execute(statement, child_context)
|
|
216
|
+
|
|
217
|
+
return None
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Knowledge Executor - Execute q:knowledge statements
|
|
3
|
+
|
|
4
|
+
Handles knowledge base creation and RAG operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import copy
|
|
8
|
+
from typing import Any, List, Dict, Type
|
|
9
|
+
from quantum.runtime.executors.base import BaseExecutor, ExecutorError
|
|
10
|
+
|
|
11
|
+
# Import from features module
|
|
12
|
+
try:
|
|
13
|
+
from quantum.core.features.knowledge_base.src.ast_node import KnowledgeNode
|
|
14
|
+
except ImportError:
|
|
15
|
+
from quantum.core.features.knowledge_base.src import KnowledgeNode
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class KnowledgeExecutor(BaseExecutor):
|
|
19
|
+
"""
|
|
20
|
+
Executor for q:knowledge statements.
|
|
21
|
+
|
|
22
|
+
Supports:
|
|
23
|
+
- Multiple source types (text, file, directory, URL, query)
|
|
24
|
+
- Embedding generation
|
|
25
|
+
- Vector storage (ChromaDB)
|
|
26
|
+
- RAG queries via q:query knowledge="name"
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def handles(self) -> List[Type]:
|
|
31
|
+
return [KnowledgeNode]
|
|
32
|
+
|
|
33
|
+
def execute(self, node: KnowledgeNode, exec_context) -> Any:
|
|
34
|
+
"""
|
|
35
|
+
Execute knowledge base creation.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
node: KnowledgeNode with knowledge base configuration
|
|
39
|
+
exec_context: Execution context
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Knowledge base info dict
|
|
43
|
+
"""
|
|
44
|
+
try:
|
|
45
|
+
context = exec_context.get_all_variables()
|
|
46
|
+
|
|
47
|
+
# KnowledgeService.index_knowledge() expects KnowledgeSourceNode
|
|
48
|
+
# objects (attribute access: source.content, source.path, ...),
|
|
49
|
+
# not dicts — build resolved copies rather than a dict config.
|
|
50
|
+
sources = []
|
|
51
|
+
for source in node.sources:
|
|
52
|
+
resolved = copy.copy(source)
|
|
53
|
+
|
|
54
|
+
if source.source_type == 'text' and source.content:
|
|
55
|
+
resolved.content = self.apply_databinding(source.content, context)
|
|
56
|
+
elif source.source_type in ('file', 'directory') and source.path:
|
|
57
|
+
resolved.path = self.apply_databinding(source.path, context)
|
|
58
|
+
elif source.source_type == 'url' and source.url:
|
|
59
|
+
resolved.url = self.apply_databinding(source.url, context)
|
|
60
|
+
elif source.source_type == 'query' and source.sql:
|
|
61
|
+
resolved.sql = self.apply_databinding(source.sql, context)
|
|
62
|
+
|
|
63
|
+
sources.append(resolved)
|
|
64
|
+
|
|
65
|
+
# Index into the SAME KnowledgeService instance that
|
|
66
|
+
# q:query datasource="knowledge:..." searches later
|
|
67
|
+
# (runtime.knowledge_service — see component.py's
|
|
68
|
+
# _execute_knowledge_query). services.knowledge is a separate
|
|
69
|
+
# instance and indexing into it would leave searches empty.
|
|
70
|
+
knowledge_service = self.runtime.knowledge_service
|
|
71
|
+
knowledge_service.index_knowledge(
|
|
72
|
+
name=node.name,
|
|
73
|
+
sources=sources,
|
|
74
|
+
embed_model=node.embed_model,
|
|
75
|
+
chunk_size=node.chunk_size,
|
|
76
|
+
chunk_overlap=node.chunk_overlap,
|
|
77
|
+
persist=node.persist,
|
|
78
|
+
persist_path=node.persist_path,
|
|
79
|
+
rebuild=node.rebuild,
|
|
80
|
+
database_service=self.services.database,
|
|
81
|
+
exec_context=exec_context,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
info = {
|
|
85
|
+
'success': True,
|
|
86
|
+
'name': node.name,
|
|
87
|
+
'model': node.model,
|
|
88
|
+
'embed_model': node.embed_model,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Store knowledge base reference (name resolves via q:query
|
|
92
|
+
# datasource="knowledge:{name}", not by reading this variable)
|
|
93
|
+
exec_context.set_variable(node.name, info, scope="component")
|
|
94
|
+
exec_context.set_variable(f"{node.name}_info", info, scope="component")
|
|
95
|
+
exec_context.set_variable(f"_knowledge_{node.name}", {
|
|
96
|
+
'embed_model': node.embed_model,
|
|
97
|
+
'model': node.model,
|
|
98
|
+
}, scope="component")
|
|
99
|
+
|
|
100
|
+
return info
|
|
101
|
+
|
|
102
|
+
except Exception as e:
|
|
103
|
+
exec_context.set_variable(f"{node.name}_info", {
|
|
104
|
+
'success': False,
|
|
105
|
+
'error': str(e),
|
|
106
|
+
'name': node.name
|
|
107
|
+
}, scope="component")
|
|
108
|
+
# _execute_knowledge_query() (component.py) checks this marker to
|
|
109
|
+
# return a graceful "still loading" result instead of raising
|
|
110
|
+
# KnowledgeError("not found") on the next q:query for this KB.
|
|
111
|
+
exec_context.set_variable(f"_knowledge_{node.name}", {
|
|
112
|
+
'_failed': True,
|
|
113
|
+
}, scope="component")
|
|
114
|
+
raise ExecutorError(f"Knowledge base creation error: {e}")
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LLM Executor - Execute q:llm statements
|
|
3
|
+
|
|
4
|
+
Handles LLM invocation across providers (Ollama, OpenAI, Anthropic, LM Studio).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Any, List, Dict, Type
|
|
9
|
+
from quantum.runtime.executors.base import BaseExecutor, ExecutorError
|
|
10
|
+
from quantum.core.ast_nodes import LLMNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LLMExecutor(BaseExecutor):
|
|
14
|
+
"""
|
|
15
|
+
Executor for q:llm statements.
|
|
16
|
+
|
|
17
|
+
Supports:
|
|
18
|
+
- Completion mode (single prompt) and chat mode (messages array)
|
|
19
|
+
- Multiple providers via provider= / endpoint= / apiKey=
|
|
20
|
+
- JSON response format
|
|
21
|
+
- Temperature and token limits
|
|
22
|
+
- Response caching with TTL
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def handles(self) -> List[Type]:
|
|
27
|
+
return [LLMNode]
|
|
28
|
+
|
|
29
|
+
def execute(self, node: LLMNode, exec_context) -> Any:
|
|
30
|
+
"""
|
|
31
|
+
Execute LLM invocation.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
node: LLMNode with LLM configuration
|
|
35
|
+
exec_context: Execution context
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
LLM response (text or parsed JSON)
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
context = exec_context.get_all_variables()
|
|
42
|
+
|
|
43
|
+
model = node.model or 'phi3'
|
|
44
|
+
provider = node.provider or None
|
|
45
|
+
endpoint = self.apply_databinding(node.endpoint, context) if node.endpoint else None
|
|
46
|
+
api_key = self.apply_databinding(node.api_key, context) if node.api_key else None
|
|
47
|
+
|
|
48
|
+
system_text = None
|
|
49
|
+
if node.system:
|
|
50
|
+
system_text = self.apply_databinding(node.system, context)
|
|
51
|
+
|
|
52
|
+
# Chat mode - build the messages array. node.system prepends a
|
|
53
|
+
# system-role message (in addition to any explicit
|
|
54
|
+
# <q:message role="system"> already in node.messages).
|
|
55
|
+
messages = []
|
|
56
|
+
if node.messages:
|
|
57
|
+
if system_text:
|
|
58
|
+
messages.append({'role': 'system', 'content': system_text})
|
|
59
|
+
for msg in node.messages:
|
|
60
|
+
content = self.apply_databinding(msg.content, context)
|
|
61
|
+
messages.append({'role': msg.role, 'content': content})
|
|
62
|
+
|
|
63
|
+
prompt_text = None
|
|
64
|
+
if not messages:
|
|
65
|
+
prompt_text = self.apply_databinding(node.prompt, context) if node.prompt else ''
|
|
66
|
+
|
|
67
|
+
temperature = float(node.temperature) if node.temperature is not None else None
|
|
68
|
+
max_tokens = int(node.max_tokens) if node.max_tokens is not None else None
|
|
69
|
+
response_format = node.response_format or 'text'
|
|
70
|
+
|
|
71
|
+
cache = self.services.llm_cache if node.cache else None
|
|
72
|
+
cache_key = None
|
|
73
|
+
if cache is not None:
|
|
74
|
+
cache_key = cache.build_key(
|
|
75
|
+
provider=provider, model=model, endpoint=endpoint,
|
|
76
|
+
messages=messages, prompt=prompt_text, system=system_text,
|
|
77
|
+
temperature=temperature, max_tokens=max_tokens,
|
|
78
|
+
response_format=response_format,
|
|
79
|
+
)
|
|
80
|
+
cached = cache.get(cache_key)
|
|
81
|
+
if cached is not None:
|
|
82
|
+
return self._store(node, cached, exec_context, cached=True)
|
|
83
|
+
|
|
84
|
+
# MultiProviderLLMService is what q:agent has always used; q:llm
|
|
85
|
+
# was pinned to the Ollama-only LLMService, which is why
|
|
86
|
+
# "multi-provider" was only half true.
|
|
87
|
+
llm = self.services.multi_llm
|
|
88
|
+
|
|
89
|
+
if messages:
|
|
90
|
+
response = llm.chat(
|
|
91
|
+
messages=messages,
|
|
92
|
+
model=model,
|
|
93
|
+
provider=provider,
|
|
94
|
+
endpoint=endpoint,
|
|
95
|
+
api_key=api_key,
|
|
96
|
+
temperature=temperature,
|
|
97
|
+
max_tokens=max_tokens,
|
|
98
|
+
response_format=response_format,
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
response = llm.generate(
|
|
102
|
+
prompt=prompt_text,
|
|
103
|
+
model=model,
|
|
104
|
+
system=system_text,
|
|
105
|
+
provider=provider,
|
|
106
|
+
endpoint=endpoint,
|
|
107
|
+
api_key=api_key,
|
|
108
|
+
temperature=temperature,
|
|
109
|
+
max_tokens=max_tokens,
|
|
110
|
+
response_format=response_format,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if cache is not None and response.get('success', True):
|
|
114
|
+
cache.set(cache_key, response, ttl=node.ttl)
|
|
115
|
+
|
|
116
|
+
return self._store(node, response, exec_context, cached=False)
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
exec_context.set_variable(f"{node.name}_result", {
|
|
120
|
+
'success': False,
|
|
121
|
+
'error': str(e),
|
|
122
|
+
'model': node.model
|
|
123
|
+
}, scope="component")
|
|
124
|
+
raise ExecutorError(f"LLM execution error: {e}")
|
|
125
|
+
|
|
126
|
+
def _store(self, node: LLMNode, response: Dict[str, Any], exec_context, cached: bool) -> Any:
|
|
127
|
+
"""Unpack a provider response, store it, and return the value."""
|
|
128
|
+
text = response.get('data', '')
|
|
129
|
+
value = text
|
|
130
|
+
|
|
131
|
+
# The provider layer returns raw text; JSON parsing is this executor's
|
|
132
|
+
# job (the single-provider service used to do it, the shared one does not).
|
|
133
|
+
if (node.response_format or 'text') == 'json':
|
|
134
|
+
parsed = response.get('parsed')
|
|
135
|
+
if parsed is None and isinstance(text, str) and text.strip():
|
|
136
|
+
try:
|
|
137
|
+
parsed = json.loads(text)
|
|
138
|
+
except json.JSONDecodeError:
|
|
139
|
+
parsed = None
|
|
140
|
+
if parsed is not None:
|
|
141
|
+
value = parsed
|
|
142
|
+
|
|
143
|
+
exec_context.set_variable(node.name, value, scope="component")
|
|
144
|
+
exec_context.set_variable(f"{node.name}_result", {
|
|
145
|
+
'success': response.get('success', True),
|
|
146
|
+
'response': value,
|
|
147
|
+
'model': response.get('model', node.model),
|
|
148
|
+
'provider': response.get('provider'),
|
|
149
|
+
'usage': response.get('usage'),
|
|
150
|
+
'cached': cached,
|
|
151
|
+
}, scope="component")
|
|
152
|
+
|
|
153
|
+
return value
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Team Executor - Execute q:team statements
|
|
3
|
+
|
|
4
|
+
Handles multi-agent team execution with handoffs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, List, Dict, Type
|
|
8
|
+
from quantum.runtime.executors.base import BaseExecutor, ExecutorError
|
|
9
|
+
from quantum.runtime.executors.ai.agent_executor import build_tool_definitions, build_tool_executor
|
|
10
|
+
|
|
11
|
+
# Import from features module
|
|
12
|
+
try:
|
|
13
|
+
from quantum.core.features.agents.src.ast_node import AgentTeamNode
|
|
14
|
+
except ImportError:
|
|
15
|
+
from quantum.core.features.agents.src import AgentTeamNode
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# The tools that make a team a team. An agent inside <q:team> gets them
|
|
20
|
+
# automatically.
|
|
21
|
+
#
|
|
22
|
+
# They used to require an explicit <q:tool name="handoff" builtin="true" /> on
|
|
23
|
+
# every agent, and nobody wrote it — examples/multi_agent_support.q advertises
|
|
24
|
+
# "Built-in handoff, readShared, writeShared, listAgents tools" in its header
|
|
25
|
+
# and instructs the router to "use the handoff tool", while declaring none of
|
|
26
|
+
# them. So the tool did not exist, the model could not call it, and the team
|
|
27
|
+
# ended at its first agent reporting success. A q:team exists in order to hand
|
|
28
|
+
# off; making that opt-in was boilerplate everyone forgets.
|
|
29
|
+
#
|
|
30
|
+
# An agent that declares a tool of the same name wins, so this cannot shadow a
|
|
31
|
+
# deliberate override.
|
|
32
|
+
_TEAM_BUILTINS = ('handoff', 'readShared', 'writeShared', 'listAgents')
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _team_tools(agent):
|
|
36
|
+
"""Explicit tools first, then any team builtin the agent did not define."""
|
|
37
|
+
from quantum.runtime.agent_service import BUILTIN_TOOLS
|
|
38
|
+
|
|
39
|
+
tools = build_tool_definitions(agent.tools)
|
|
40
|
+
declared = {t.get('name') for t in tools}
|
|
41
|
+
for name in _TEAM_BUILTINS:
|
|
42
|
+
if name not in declared and name in BUILTIN_TOOLS:
|
|
43
|
+
tools.append(dict(BUILTIN_TOOLS[name]))
|
|
44
|
+
return tools
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TeamExecutor(BaseExecutor):
|
|
48
|
+
"""
|
|
49
|
+
Executor for q:team statements.
|
|
50
|
+
|
|
51
|
+
Supports:
|
|
52
|
+
- Multi-agent orchestration
|
|
53
|
+
- Agent handoffs
|
|
54
|
+
- Shared team context
|
|
55
|
+
- Entry point routing
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def handles(self) -> List[Type]:
|
|
60
|
+
return [AgentTeamNode]
|
|
61
|
+
|
|
62
|
+
def execute(self, node: AgentTeamNode, exec_context) -> Any:
|
|
63
|
+
"""
|
|
64
|
+
Execute agent team.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
node: AgentTeamNode with team configuration
|
|
68
|
+
exec_context: Execution context
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Team execution result dict
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
context = exec_context.get_all_variables()
|
|
75
|
+
|
|
76
|
+
# Resolve task from execute node
|
|
77
|
+
if not node.execute:
|
|
78
|
+
raise ExecutorError("Team requires <q:execute> to run")
|
|
79
|
+
|
|
80
|
+
task = self.apply_databinding(node.execute.task, context)
|
|
81
|
+
|
|
82
|
+
# Determine entry agent
|
|
83
|
+
entry_agent = node.execute.entry or node.supervisor
|
|
84
|
+
if not entry_agent and node.agents:
|
|
85
|
+
entry_agent = node.agents[0].name
|
|
86
|
+
|
|
87
|
+
# Initialize shared context
|
|
88
|
+
shared_context = {}
|
|
89
|
+
for set_node in node.shared:
|
|
90
|
+
if hasattr(set_node, 'name') and hasattr(set_node, 'value'):
|
|
91
|
+
value = self.apply_databinding(set_node.value, context)
|
|
92
|
+
shared_context[set_node.name] = value
|
|
93
|
+
|
|
94
|
+
# MultiAgentService.create_team() takes a {name: config} dict —
|
|
95
|
+
# not a list, and not the team_config dict this executor used to
|
|
96
|
+
# build. execute_team() also lives on services.multi_agent, not
|
|
97
|
+
# services.agent (which is the single-agent AgentService).
|
|
98
|
+
agents = {
|
|
99
|
+
agent.name: self._build_agent_config(agent, context)
|
|
100
|
+
for agent in node.agents
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
multi_agent = self.services.multi_agent
|
|
104
|
+
multi_agent.create_team(
|
|
105
|
+
name=node.name,
|
|
106
|
+
agents=agents,
|
|
107
|
+
shared=shared_context,
|
|
108
|
+
supervisor=node.supervisor or entry_agent,
|
|
109
|
+
max_handoffs=node.max_handoffs,
|
|
110
|
+
max_total_iterations=node.max_total_iterations,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
team_result = multi_agent.execute_team(
|
|
114
|
+
name=node.name,
|
|
115
|
+
task=task,
|
|
116
|
+
entry_agent=entry_agent,
|
|
117
|
+
context="",
|
|
118
|
+
tool_executor=build_tool_executor(
|
|
119
|
+
self, exec_context,
|
|
120
|
+
[tool for agent in node.agents for tool in agent.tools],
|
|
121
|
+
),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
result = team_result.to_dict()
|
|
125
|
+
|
|
126
|
+
# Store results
|
|
127
|
+
exec_context.set_variable(node.name, result.get('finalResponse', ''), scope="component")
|
|
128
|
+
exec_context.set_variable(f"{node.name}_result", result, scope="component")
|
|
129
|
+
|
|
130
|
+
return result
|
|
131
|
+
|
|
132
|
+
except Exception as e:
|
|
133
|
+
error_result = {
|
|
134
|
+
'success': False,
|
|
135
|
+
'error': {'message': str(e)},
|
|
136
|
+
'finalResponse': '',
|
|
137
|
+
'handoffs': [],
|
|
138
|
+
'totalIterations': 0
|
|
139
|
+
}
|
|
140
|
+
exec_context.set_variable(f"{node.name}_result", error_result, scope="component")
|
|
141
|
+
raise ExecutorError(f"Team execution error: {e}")
|
|
142
|
+
|
|
143
|
+
def _build_agent_config(self, agent, context: Dict[str, Any]) -> Dict[str, Any]:
|
|
144
|
+
"""Build agent configuration dict from AgentNode.
|
|
145
|
+
|
|
146
|
+
Key names must match what AgentTeam.execute() reads:
|
|
147
|
+
instruction/model/endpoint/provider/api_key/max_iterations/timeout/tools.
|
|
148
|
+
"""
|
|
149
|
+
instruction = ""
|
|
150
|
+
if agent.instruction:
|
|
151
|
+
instruction = self.apply_databinding(agent.instruction.content, context)
|
|
152
|
+
|
|
153
|
+
api_key = ""
|
|
154
|
+
if agent.api_key:
|
|
155
|
+
api_key = self.apply_databinding(agent.api_key, context)
|
|
156
|
+
|
|
157
|
+
endpoint = ""
|
|
158
|
+
if agent.endpoint:
|
|
159
|
+
endpoint = self.apply_databinding(agent.endpoint, context)
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
'name': agent.name,
|
|
163
|
+
'model': agent.model,
|
|
164
|
+
'provider': agent.provider,
|
|
165
|
+
'endpoint': endpoint,
|
|
166
|
+
'api_key': api_key,
|
|
167
|
+
'instruction': instruction,
|
|
168
|
+
'tools': _team_tools(agent),
|
|
169
|
+
'max_iterations': agent.max_iterations,
|
|
170
|
+
'timeout': agent.timeout,
|
|
171
|
+
}
|