|
4 | 4 | import json |
5 | 5 | import sys |
6 | 6 | import time |
7 | | -from typing import Any, Optional |
| 7 | +from typing import Any |
8 | 8 |
|
9 | | -from teaagent.chat_agent import ChatAgentConfig, run_chat_agent |
10 | | -from teaagent.graphqlite_store import ( |
11 | | - GraphQLiteConfig, |
12 | | - GraphQLiteGraphStore, |
13 | | -) |
14 | | -from teaagent.intent import build_task_spec, clarify_task |
15 | | -from teaagent.llm import ( |
16 | | - LLMMessage, |
17 | | - LLMRequest, |
18 | | - available_providers, |
19 | | -) |
| 9 | +from teaagent.graphqlite_store import GraphQLiteConfig, GraphQLiteGraphStore |
| 10 | +from teaagent.intent import clarify_task |
| 11 | +from teaagent.llm import LLMMessage, LLMRequest, available_providers |
20 | 12 | from teaagent.mcp_http import is_loopback_host |
21 | 13 | from teaagent.mcp_server import serve_mcp_stdio |
22 | 14 | from teaagent.memory import MemoryCatalog |
23 | 15 | from teaagent.model_routing import route_model |
24 | 16 | from teaagent.policy import parse_permission_mode |
25 | | -from teaagent.preflight import preflight |
26 | 17 | from teaagent.run_store import RunStore |
27 | | -from teaagent.runner import ApprovalRequest, RunResult |
28 | 18 | from teaagent.tui import run_tui |
29 | 19 | from teaagent.ultrawork import UltraworkStore |
30 | 20 | from teaagent.workspace_tools import build_workspace_tool_registry |
31 | 21 |
|
32 | | -# --------------------------------------------------------------------------- |
33 | | -# Command handlers |
34 | | -# --------------------------------------------------------------------------- |
| 22 | +from ._agent import ( |
| 23 | + agent_preflight_command as agent_preflight_command, |
| 24 | +) |
| 25 | +from ._agent import ( |
| 26 | + agent_resume_command as agent_resume_command, |
| 27 | +) |
| 28 | +from ._agent import ( |
| 29 | + agent_run_show as agent_run_show, |
| 30 | +) |
| 31 | +from ._agent import ( |
| 32 | + agent_run_task as agent_run_task, |
| 33 | +) |
| 34 | +from ._agent import ( |
| 35 | + agent_runs_list as agent_runs_list, |
| 36 | +) |
| 37 | +from ._agent import ( |
| 38 | + agent_status_command as agent_status_command, |
| 39 | +) |
35 | 40 |
|
36 | 41 |
|
37 | 42 | def doctor_graphqlite(args: argparse.Namespace) -> int: |
@@ -87,203 +92,6 @@ def memory_show_command(args: argparse.Namespace) -> int: |
87 | 92 | return 0 |
88 | 93 |
|
89 | 94 |
|
90 | | -def agent_run_task(args: argparse.Namespace) -> int: |
91 | | - return _execute_agent_task(args, args.task) |
92 | | - |
93 | | - |
94 | | -def agent_resume_command(args: argparse.Namespace) -> int: |
95 | | - store = RunStore(args.root) |
96 | | - try: |
97 | | - original_task = store.task_for_run(args.run_id) |
98 | | - except (FileNotFoundError, ValueError) as exc: |
99 | | - print_json({'status': 'error', 'message': str(exc)}) |
100 | | - return 1 |
101 | | - |
102 | | - initial_observations: list[dict[str, Any]] = [] |
103 | | - auto_approved: Optional[str] = None |
104 | | - if not args.fresh_restart: |
105 | | - initial_observations = store.observations_for_run(args.run_id) |
106 | | - pending = store.pending_approval_for_run(args.run_id) |
107 | | - if pending and pending['call_id'] not in args.approve_call_id: |
108 | | - args.approve_call_id = list(args.approve_call_id) + [pending['call_id']] |
109 | | - auto_approved = pending['call_id'] |
110 | | - |
111 | | - return _execute_agent_task( |
112 | | - args, |
113 | | - original_task, |
114 | | - resumed_from=args.run_id, |
115 | | - initial_observations=initial_observations, |
116 | | - auto_approved_call_id=auto_approved, |
117 | | - ) |
118 | | - |
119 | | - |
120 | | -def _execute_agent_task( |
121 | | - args: argparse.Namespace, |
122 | | - task: str, |
123 | | - *, |
124 | | - resumed_from: Optional[str] = None, |
125 | | - initial_observations: Optional[list[dict[str, Any]]] = None, |
126 | | - auto_approved_call_id: Optional[str] = None, |
127 | | -) -> int: |
128 | | - task_spec = None |
129 | | - if args.clarify: |
130 | | - clarification = clarify_task(task) |
131 | | - if clarification.needs_clarification: |
132 | | - print_json( |
133 | | - { |
134 | | - 'status': 'needs_clarification', |
135 | | - 'clarification': clarification.to_dict(), |
136 | | - } |
137 | | - ) |
138 | | - return 2 |
139 | | - task_spec = build_task_spec(task, clarification) |
140 | | - |
141 | | - routing = ( |
142 | | - route_model(task, provider=args.provider, model=args.model) |
143 | | - if args.route_model |
144 | | - else None |
145 | | - ) |
146 | | - selected_model = routing.model if routing else args.model |
147 | | - adapter = args._adapter_factory(args.provider, model=selected_model) # type: ignore[attr-defined] |
148 | | - store = RunStore(args.root) |
149 | | - audit = store.audit_logger() |
150 | | - |
151 | | - # --- OpenTelemetry wiring --- |
152 | | - _telemetry_sink = None |
153 | | - if getattr(args, 'telemetry_otlp_endpoint', None) or getattr( |
154 | | - args, 'telemetry_console', False |
155 | | - ): |
156 | | - try: |
157 | | - from teaagent.telemetry import ( |
158 | | - TelemetryConfig, |
159 | | - TracingHTTPTransport, |
160 | | - configure_telemetry, |
161 | | - ) |
162 | | - |
163 | | - cfg = TelemetryConfig( |
164 | | - service_name=getattr(args, 'telemetry_service_name', 'teaagent'), |
165 | | - otlp_endpoint=getattr(args, 'telemetry_otlp_endpoint', None), |
166 | | - console=getattr(args, 'telemetry_console', False), |
167 | | - ) |
168 | | - _telemetry_sink, tracer = configure_telemetry(cfg) |
169 | | - audit.add_sink(_telemetry_sink.handle_event) |
170 | | - adapter = args._adapter_factory( # type: ignore[attr-defined] |
171 | | - args.provider, |
172 | | - model=selected_model, |
173 | | - transport=TracingHTTPTransport(adapter.transport, tracer), # type: ignore[attr-defined] |
174 | | - ) |
175 | | - except Exception as exc: |
176 | | - print(f'Telemetry setup failed: {exc}', file=sys.stderr) |
177 | | - # --- end telemetry wiring --- |
178 | | - |
179 | | - approval_handler = cli_approval_handler if args.hitl_approval else None |
180 | | - result = run_chat_agent( |
181 | | - task=task, |
182 | | - adapter=adapter, |
183 | | - config=ChatAgentConfig.from_root( |
184 | | - args.root, |
185 | | - max_iterations=args.max_iterations, |
186 | | - max_tool_calls=args.max_tool_calls, |
187 | | - allow_destructive=args.allow_destructive, |
188 | | - model=selected_model, |
189 | | - permission_mode=parse_permission_mode(args.permission_mode), |
190 | | - approved_call_ids=frozenset(args.approve_call_id), |
191 | | - enable_subagent=args.subagent, |
192 | | - max_subagent_depth=args.max_subagent_depth, |
193 | | - heartbeat_seconds=args.heartbeat, |
194 | | - approval_handler=approval_handler, |
195 | | - ), |
196 | | - audit=audit, |
197 | | - task_spec=task_spec, |
198 | | - initial_observations=initial_observations, |
199 | | - ) |
200 | | - store.logger_for_result(result, audit) |
201 | | - if _telemetry_sink is not None: |
202 | | - from contextlib import suppress |
203 | | - |
204 | | - with suppress(Exception): |
205 | | - _telemetry_sink.force_flush() |
206 | | - payload = run_result_payload(result, routing=routing.to_dict() if routing else None) |
207 | | - if resumed_from: |
208 | | - payload['resumed_from'] = resumed_from |
209 | | - payload['task'] = task |
210 | | - if initial_observations: |
211 | | - payload['replayed_observations'] = len(initial_observations) |
212 | | - if auto_approved_call_id is not None: |
213 | | - payload['auto_approved_call_id'] = auto_approved_call_id |
214 | | - print_json(payload) |
215 | | - return 0 if result.status == 'completed' else 1 |
216 | | - |
217 | | - |
218 | | -def run_result_payload( |
219 | | - result: RunResult, *, routing: Optional[dict[str, Any]] |
220 | | -) -> dict[str, Any]: |
221 | | - payload: dict[str, Any] = { |
222 | | - 'run_id': result.run_id, |
223 | | - 'status': result.status, |
224 | | - 'iterations': result.iterations, |
225 | | - 'tool_calls': result.tool_calls, |
226 | | - 'routing': routing, |
227 | | - 'final_answer': result.final_answer.content if result.final_answer else None, |
228 | | - } |
229 | | - if 'approval' in result.metadata: |
230 | | - payload['approval'] = result.metadata['approval'] |
231 | | - return payload |
232 | | - |
233 | | - |
234 | | -def cli_approval_handler(request: ApprovalRequest) -> bool: |
235 | | - print( |
236 | | - json.dumps( |
237 | | - {'status': 'approval_required', 'approval': request.to_dict()}, |
238 | | - sort_keys=True, |
239 | | - ), |
240 | | - file=sys.stderr, |
241 | | - ) |
242 | | - print( |
243 | | - f'Approve destructive tool call {request.call_id} ({request.tool_name})? [y/N] ', |
244 | | - end='', |
245 | | - file=sys.stderr, |
246 | | - ) |
247 | | - answer = input() |
248 | | - return answer.strip().lower() in {'y', 'yes'} |
249 | | - |
250 | | - |
251 | | -def agent_preflight_command(args: argparse.Namespace) -> int: |
252 | | - report = preflight( |
253 | | - args.task, |
254 | | - root=args.root, |
255 | | - provider=args.provider, |
256 | | - model=args.model, |
257 | | - permission_mode=parse_permission_mode(args.permission_mode), |
258 | | - route=args.route_model, |
259 | | - memory_limit=args.memory_limit, |
260 | | - ) |
261 | | - print_json(report.to_dict()) |
262 | | - return 0 if report.to_dict()['ready'] else 2 |
263 | | - |
264 | | - |
265 | | -def agent_status_command(args: argparse.Namespace) -> int: |
266 | | - store = RunStore(args.root) |
267 | | - try: |
268 | | - print_json(store.heartbeat_for_run(args.run_id)) |
269 | | - except FileNotFoundError as exc: |
270 | | - print_json({'status': 'error', 'message': str(exc)}) |
271 | | - return 1 |
272 | | - return 0 |
273 | | - |
274 | | - |
275 | | -def agent_runs_list(args: argparse.Namespace) -> int: |
276 | | - store = RunStore(args.root) |
277 | | - print_json([summary.to_dict() for summary in store.list_runs(limit=args.limit)]) |
278 | | - return 0 |
279 | | - |
280 | | - |
281 | | -def agent_run_show(args: argparse.Namespace) -> int: |
282 | | - store = RunStore(args.root) |
283 | | - print_json(store.show_run(args.run_id)) |
284 | | - return 0 |
285 | | - |
286 | | - |
287 | 95 | def doctor_model(args: argparse.Namespace) -> int: |
288 | 96 | ok, message = args._check_llm(args.provider) # type: ignore[attr-defined] |
289 | 97 | print( |
|
0 commit comments