abstractagent 0.2.0__py3-none-any.whl → 0.3.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.
- abstractagent/adapters/__init__.py +2 -1
- abstractagent/adapters/codeact_runtime.py +823 -57
- abstractagent/adapters/memact_runtime.py +721 -0
- abstractagent/adapters/react_runtime.py +1114 -67
- abstractagent/agents/__init__.py +4 -0
- abstractagent/agents/base.py +58 -1
- abstractagent/agents/codeact.py +89 -18
- abstractagent/agents/memact.py +244 -0
- abstractagent/agents/react.py +91 -18
- abstractagent/logic/__init__.py +2 -0
- abstractagent/logic/builtins.py +212 -5
- abstractagent/logic/codeact.py +87 -80
- abstractagent/logic/memact.py +127 -0
- abstractagent/logic/react.py +108 -48
- abstractagent/repl.py +24 -447
- abstractagent/scripts/__init__.py +5 -0
- abstractagent/scripts/lmstudio_tool_eval.py +426 -0
- abstractagent/tools/__init__.py +3 -0
- {abstractagent-0.2.0.dist-info → abstractagent-0.3.0.dist-info}/METADATA +10 -11
- abstractagent-0.3.0.dist-info/RECORD +31 -0
- abstractagent/ui/__init__.py +0 -5
- abstractagent/ui/question.py +0 -197
- abstractagent-0.2.0.dist-info/RECORD +0 -28
- {abstractagent-0.2.0.dist-info → abstractagent-0.3.0.dist-info}/WHEEL +0 -0
- {abstractagent-0.2.0.dist-info → abstractagent-0.3.0.dist-info}/entry_points.txt +0 -0
- {abstractagent-0.2.0.dist-info → abstractagent-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {abstractagent-0.2.0.dist-info → abstractagent-0.3.0.dist-info}/top_level.txt +0 -0
abstractagent/repl.py
CHANGED
|
@@ -1,457 +1,34 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Deprecated CLI entrypoint for AbstractAgent.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
- Pause/resume capability
|
|
6
|
-
- Interactive question handling
|
|
7
|
-
- Run persistence for resume across sessions
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
import asyncio
|
|
11
|
-
import sys
|
|
12
|
-
import argparse
|
|
13
|
-
import json
|
|
14
|
-
from typing import Dict, Any, Optional
|
|
15
|
-
from datetime import datetime
|
|
16
|
-
from pathlib import Path
|
|
17
|
-
|
|
18
|
-
from abstractcore.tools import ToolDefinition
|
|
19
|
-
from abstractruntime import (
|
|
20
|
-
RunStatus,
|
|
21
|
-
InMemoryRunStore,
|
|
22
|
-
InMemoryLedgerStore,
|
|
23
|
-
JsonFileRunStore,
|
|
24
|
-
JsonlLedgerStore,
|
|
25
|
-
)
|
|
26
|
-
from abstractruntime.integrations.abstractcore import MappingToolExecutor, create_local_runtime
|
|
27
|
-
|
|
28
|
-
from .agents.react import ReactAgent
|
|
29
|
-
from .tools import ALL_TOOLS
|
|
30
|
-
from .ui.question import get_user_response_async, Colors, _c
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class AgentREPL:
|
|
34
|
-
"""Interactive REPL for the ReAct agent."""
|
|
35
|
-
|
|
36
|
-
def __init__(self, provider: str, model: str, state_file: Optional[str] = None):
|
|
37
|
-
self.provider = provider
|
|
38
|
-
self.model = model
|
|
39
|
-
self.state_file = state_file
|
|
40
|
-
self.agent: Optional[ReactAgent] = None
|
|
41
|
-
self._interrupted = False
|
|
42
|
-
self._tools = list(ALL_TOOLS)
|
|
43
|
-
|
|
44
|
-
# Setup runtime with persistence. If a state file is provided, use
|
|
45
|
-
# file-backed stores so runs can resume across process restarts.
|
|
46
|
-
if self.state_file:
|
|
47
|
-
base_dir = Path(self.state_file).expanduser().resolve()
|
|
48
|
-
store_dir = base_dir.with_name(base_dir.name + ".d")
|
|
49
|
-
self.run_store = JsonFileRunStore(store_dir)
|
|
50
|
-
self.ledger_store = JsonlLedgerStore(store_dir)
|
|
51
|
-
else:
|
|
52
|
-
self.run_store = InMemoryRunStore()
|
|
53
|
-
self.ledger_store = InMemoryLedgerStore()
|
|
3
|
+
The interactive REPL and UX components were extracted into **AbstractCode** to
|
|
4
|
+
avoid mixing UI concerns with agent patterns.
|
|
54
5
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
run_store=self.run_store,
|
|
59
|
-
ledger_store=self.ledger_store,
|
|
60
|
-
tool_executor=MappingToolExecutor.from_tools(self._tools),
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
self.agent = ReactAgent(
|
|
64
|
-
runtime=self.runtime,
|
|
65
|
-
tools=self._tools,
|
|
66
|
-
on_step=self.print_step,
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
def print_step(self, step: str, data: Dict[str, Any]) -> None:
|
|
70
|
-
"""Print a step to the console with formatting."""
|
|
71
|
-
timestamp = datetime.now().strftime("%H:%M:%S")
|
|
72
|
-
ts = _c(f"[{timestamp}]", Colors.DIM)
|
|
73
|
-
|
|
74
|
-
if step == "init":
|
|
75
|
-
task = data.get('task', '')[:60]
|
|
76
|
-
print(f"\n{ts} {_c('Starting:', Colors.CYAN, Colors.BOLD)} {task}")
|
|
77
|
-
elif step == "reason":
|
|
78
|
-
iteration = data.get('iteration', '?')
|
|
79
|
-
print(f"{ts} {_c(f'Thinking (step {iteration})...', Colors.YELLOW)}")
|
|
80
|
-
elif step == "parse":
|
|
81
|
-
has_tools = data.get('has_tool_calls', False)
|
|
82
|
-
if has_tools:
|
|
83
|
-
print(f"{ts} {_c('Decided to use tools', Colors.BLUE)}")
|
|
84
|
-
elif step == "act":
|
|
85
|
-
tool = data.get('tool', 'unknown')
|
|
86
|
-
args = data.get('args', {})
|
|
87
|
-
args_str = json.dumps(args) if args else ""
|
|
88
|
-
if len(args_str) > 50:
|
|
89
|
-
args_str = args_str[:47] + "..."
|
|
90
|
-
print(f"{ts} {_c('Tool:', Colors.GREEN)} {tool}({args_str})")
|
|
91
|
-
elif step == "observe":
|
|
92
|
-
result = data.get('result', '')[:80]
|
|
93
|
-
print(f"{ts} {_c('Result:', Colors.DIM)} {result}")
|
|
94
|
-
elif step == "ask_user":
|
|
95
|
-
print(f"{ts} {_c('Agent has a question...', Colors.MAGENTA, Colors.BOLD)}")
|
|
96
|
-
elif step == "user_response":
|
|
97
|
-
response = data.get('response', '')[:50]
|
|
98
|
-
print(f"{ts} {_c('You answered:', Colors.MAGENTA)} {response}")
|
|
99
|
-
elif step == "done":
|
|
100
|
-
answer = data.get('answer', '')
|
|
101
|
-
print(f"\n{ts} {_c('ANSWER:', Colors.GREEN, Colors.BOLD)}")
|
|
102
|
-
print(_c("─" * 60, Colors.DIM))
|
|
103
|
-
print(answer)
|
|
104
|
-
print(_c("─" * 60, Colors.DIM))
|
|
105
|
-
elif step == "max_iterations":
|
|
106
|
-
print(f"{ts} {_c('Max iterations reached', Colors.YELLOW)}")
|
|
107
|
-
|
|
108
|
-
async def handle_waiting_state(self) -> bool:
|
|
109
|
-
"""Handle agent waiting state (questions).
|
|
110
|
-
|
|
111
|
-
Returns True if handled and should continue, False to stop.
|
|
112
|
-
"""
|
|
113
|
-
if not self.agent or not self.agent.is_waiting():
|
|
114
|
-
return False
|
|
115
|
-
|
|
116
|
-
question = self.agent.get_pending_question()
|
|
117
|
-
if not question:
|
|
118
|
-
return False
|
|
119
|
-
|
|
120
|
-
# Get user response via UI
|
|
121
|
-
response = await get_user_response_async(
|
|
122
|
-
prompt=question.get("prompt", "Please respond:"),
|
|
123
|
-
choices=question.get("choices"),
|
|
124
|
-
allow_free_text=question.get("allow_free_text", True),
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
if not response:
|
|
128
|
-
print(_c("No response provided. Agent paused.", Colors.YELLOW))
|
|
129
|
-
return False
|
|
130
|
-
|
|
131
|
-
# Resume agent with response
|
|
132
|
-
self.agent.resume(response)
|
|
133
|
-
return True
|
|
134
|
-
|
|
135
|
-
async def run_agent_async(self, task: str) -> None:
|
|
136
|
-
"""Run the agent asynchronously with step visibility."""
|
|
137
|
-
self.agent.start(task)
|
|
138
|
-
if self.state_file:
|
|
139
|
-
self.agent.save_state(self.state_file)
|
|
140
|
-
self._interrupted = False
|
|
141
|
-
|
|
142
|
-
print(f"\n{_c('═' * 60, Colors.CYAN)}")
|
|
143
|
-
print(f"{_c('Task:', Colors.BOLD)} {task}")
|
|
144
|
-
print(f"{_c('═' * 60, Colors.CYAN)}")
|
|
145
|
-
|
|
146
|
-
try:
|
|
147
|
-
while not self._interrupted:
|
|
148
|
-
state = self.agent.step()
|
|
149
|
-
|
|
150
|
-
if state.status == RunStatus.COMPLETED:
|
|
151
|
-
print(f"\n{_c('═' * 60, Colors.GREEN)}")
|
|
152
|
-
print(f"{_c('Completed', Colors.GREEN, Colors.BOLD)} in {state.output.get('iterations', '?')} steps")
|
|
153
|
-
print(f"{_c('═' * 60, Colors.GREEN)}")
|
|
154
|
-
if self.state_file:
|
|
155
|
-
self.agent.clear_state(self.state_file)
|
|
156
|
-
break
|
|
157
|
-
|
|
158
|
-
elif state.status == RunStatus.WAITING:
|
|
159
|
-
# Handle question
|
|
160
|
-
handled = await self.handle_waiting_state()
|
|
161
|
-
if not handled:
|
|
162
|
-
print(f"\n{_c('Agent paused.', Colors.YELLOW)} Type 'resume' to continue.")
|
|
163
|
-
break
|
|
164
|
-
# After handling, continue the loop to process next step
|
|
165
|
-
continue
|
|
166
|
-
|
|
167
|
-
elif state.status == RunStatus.FAILED:
|
|
168
|
-
print(f"\n{_c('Failed:', Colors.YELLOW)} {state.error}")
|
|
169
|
-
if self.state_file:
|
|
170
|
-
self.agent.clear_state(self.state_file)
|
|
171
|
-
break
|
|
172
|
-
|
|
173
|
-
# Small delay for interrupt handling
|
|
174
|
-
await asyncio.sleep(0.01)
|
|
175
|
-
|
|
176
|
-
except asyncio.CancelledError:
|
|
177
|
-
print(f"\n{_c('Interrupted', Colors.YELLOW)}")
|
|
178
|
-
self._interrupted = True
|
|
179
|
-
|
|
180
|
-
def interrupt(self) -> None:
|
|
181
|
-
"""Interrupt the running agent."""
|
|
182
|
-
self._interrupted = True
|
|
183
|
-
print(f"\n{_c('Interrupting...', Colors.YELLOW)} (state preserved)")
|
|
184
|
-
|
|
185
|
-
async def resume_agent(self) -> None:
|
|
186
|
-
"""Resume a paused agent."""
|
|
187
|
-
if not self.agent:
|
|
188
|
-
print("No agent to resume. Start a new task.")
|
|
189
|
-
return
|
|
190
|
-
|
|
191
|
-
state = self.agent.get_state()
|
|
192
|
-
if not state:
|
|
193
|
-
print("No active run.")
|
|
194
|
-
return
|
|
195
|
-
|
|
196
|
-
if state.status == RunStatus.WAITING:
|
|
197
|
-
handled = await self.handle_waiting_state()
|
|
198
|
-
if handled:
|
|
199
|
-
# Continue running after handling
|
|
200
|
-
await self._continue_running()
|
|
201
|
-
elif state.status == RunStatus.RUNNING:
|
|
202
|
-
self._interrupted = False
|
|
203
|
-
await self._continue_running()
|
|
204
|
-
else:
|
|
205
|
-
print(f"Agent is {state.status.value}, cannot resume.")
|
|
206
|
-
|
|
207
|
-
async def _continue_running(self) -> None:
|
|
208
|
-
"""Continue running the agent after resume."""
|
|
209
|
-
try:
|
|
210
|
-
while not self._interrupted:
|
|
211
|
-
state = self.agent.step()
|
|
212
|
-
|
|
213
|
-
if state.status == RunStatus.COMPLETED:
|
|
214
|
-
print(f"\n{_c('═' * 60, Colors.GREEN)}")
|
|
215
|
-
print(f"{_c('Completed', Colors.GREEN, Colors.BOLD)} in {state.output.get('iterations', '?')} steps")
|
|
216
|
-
print(f"{_c('═' * 60, Colors.GREEN)}")
|
|
217
|
-
break
|
|
218
|
-
elif state.status == RunStatus.WAITING:
|
|
219
|
-
handled = await self.handle_waiting_state()
|
|
220
|
-
if not handled:
|
|
221
|
-
print(f"\n{_c('Agent paused.', Colors.YELLOW)} Type 'resume' to continue.")
|
|
222
|
-
break
|
|
223
|
-
continue
|
|
224
|
-
elif state.status == RunStatus.FAILED:
|
|
225
|
-
print(f"\n{_c('Failed:', Colors.YELLOW)} {state.error}")
|
|
226
|
-
break
|
|
227
|
-
|
|
228
|
-
await asyncio.sleep(0.01)
|
|
229
|
-
except asyncio.CancelledError:
|
|
230
|
-
self._interrupted = True
|
|
231
|
-
|
|
232
|
-
def show_status(self) -> None:
|
|
233
|
-
"""Show current agent status."""
|
|
234
|
-
if not self.agent:
|
|
235
|
-
print("No active agent")
|
|
236
|
-
return
|
|
237
|
-
|
|
238
|
-
state = self.agent.get_state()
|
|
239
|
-
if not state:
|
|
240
|
-
print("No active run")
|
|
241
|
-
return
|
|
242
|
-
|
|
243
|
-
print(f"\n{_c('Agent Status', Colors.CYAN, Colors.BOLD)}")
|
|
244
|
-
print(_c("─" * 40, Colors.DIM))
|
|
245
|
-
print(f" Run ID: {state.run_id[:16]}...")
|
|
246
|
-
print(f" Status: {_c(state.status.value, Colors.GREEN if state.status == RunStatus.COMPLETED else Colors.YELLOW)}")
|
|
247
|
-
print(f" Node: {state.current_node}")
|
|
248
|
-
print(f" Iteration: {state.vars.get('iteration', 0)}")
|
|
249
|
-
|
|
250
|
-
if state.status == RunStatus.WAITING and state.waiting:
|
|
251
|
-
print(f"\n {_c('Waiting for:', Colors.MAGENTA)} {state.waiting.reason.value}")
|
|
252
|
-
if state.waiting.prompt:
|
|
253
|
-
print(f" {_c('Question:', Colors.MAGENTA)} {state.waiting.prompt[:50]}...")
|
|
254
|
-
|
|
255
|
-
print(_c("─" * 40, Colors.DIM))
|
|
256
|
-
|
|
257
|
-
def show_history(self) -> None:
|
|
258
|
-
"""Show agent conversation history."""
|
|
259
|
-
if not self.agent:
|
|
260
|
-
print("No active agent")
|
|
261
|
-
return
|
|
262
|
-
|
|
263
|
-
state = self.agent.get_state()
|
|
264
|
-
if not state:
|
|
265
|
-
print("No active run")
|
|
266
|
-
return
|
|
267
|
-
|
|
268
|
-
history = state.vars.get('messages', [])
|
|
269
|
-
if not history:
|
|
270
|
-
print("No history yet")
|
|
271
|
-
return
|
|
272
|
-
|
|
273
|
-
print(f"\n{_c('Conversation History', Colors.CYAN, Colors.BOLD)}")
|
|
274
|
-
print(_c("─" * 60, Colors.DIM))
|
|
275
|
-
|
|
276
|
-
for i, entry in enumerate(history):
|
|
277
|
-
role = entry.get('role', 'unknown')
|
|
278
|
-
content = entry.get('content', '')
|
|
279
|
-
|
|
280
|
-
if role == "assistant":
|
|
281
|
-
role_color = Colors.GREEN
|
|
282
|
-
elif role == "tool":
|
|
283
|
-
role_color = Colors.BLUE
|
|
284
|
-
elif role == "user":
|
|
285
|
-
role_color = Colors.MAGENTA
|
|
286
|
-
else:
|
|
287
|
-
role_color = Colors.DIM
|
|
288
|
-
|
|
289
|
-
# Truncate long content
|
|
290
|
-
if len(content) > 100:
|
|
291
|
-
content = content[:97] + "..."
|
|
292
|
-
|
|
293
|
-
print(f"[{i+1}] {_c(role, role_color, Colors.BOLD)}: {content}")
|
|
294
|
-
|
|
295
|
-
print(_c("─" * 60, Colors.DIM))
|
|
296
|
-
|
|
297
|
-
def show_help(self) -> None:
|
|
298
|
-
"""Show help message."""
|
|
299
|
-
print(f"""
|
|
300
|
-
{_c('ReAct Agent REPL', Colors.CYAN, Colors.BOLD)}
|
|
301
|
-
{_c('─' * 40, Colors.DIM)}
|
|
302
|
-
|
|
303
|
-
{_c('Commands:', Colors.BOLD)}
|
|
304
|
-
<task> Start agent with a task
|
|
305
|
-
resume Resume a paused/interrupted agent
|
|
306
|
-
status Show current agent status
|
|
307
|
-
history Show conversation history
|
|
308
|
-
tools List available tools
|
|
309
|
-
help Show this help
|
|
310
|
-
quit Exit
|
|
6
|
+
Use:
|
|
7
|
+
abstractcode --agent react --provider <provider> --model <model>
|
|
8
|
+
"""
|
|
311
9
|
|
|
312
|
-
|
|
313
|
-
Ctrl+C Interrupt (preserves state)
|
|
10
|
+
from __future__ import annotations
|
|
314
11
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
> search for TODO comments in the code
|
|
319
|
-
""")
|
|
320
|
-
|
|
321
|
-
def show_tools(self) -> None:
|
|
322
|
-
"""Show available tools."""
|
|
323
|
-
print(f"\n{_c('Available Tools', Colors.CYAN, Colors.BOLD)}")
|
|
324
|
-
print(_c("─" * 50, Colors.DIM))
|
|
325
|
-
|
|
326
|
-
for tool in self._tools:
|
|
327
|
-
tool_def = getattr(tool, "_tool_definition", None)
|
|
328
|
-
if tool_def is None:
|
|
329
|
-
tool_def = ToolDefinition.from_function(tool)
|
|
12
|
+
import argparse
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
330
15
|
|
|
331
|
-
params = ", ".join(str(k) for k in (tool_def.parameters or {}).keys())
|
|
332
|
-
print(f" {_c(tool_def.name, Colors.GREEN)}({params})")
|
|
333
|
-
print(f" {_c(tool_def.description, Colors.DIM)}")
|
|
334
|
-
|
|
335
|
-
# Show built-in ask_user
|
|
336
|
-
print(f" {_c('ask_user', Colors.MAGENTA)}(question, choices?)")
|
|
337
|
-
print(f" {_c('Ask the user a question', Colors.DIM)}")
|
|
338
|
-
|
|
339
|
-
print(_c("─" * 50, Colors.DIM))
|
|
340
|
-
|
|
341
|
-
async def repl_loop(self) -> None:
|
|
342
|
-
"""Main REPL loop."""
|
|
343
|
-
# Header
|
|
344
|
-
print(f"""
|
|
345
|
-
{_c('╔' + '═' * 58 + '╗', Colors.CYAN)}
|
|
346
|
-
{_c('║', Colors.CYAN)} {_c('ReAct Agent REPL', Colors.BOLD)} {_c('║', Colors.CYAN)}
|
|
347
|
-
{_c('║', Colors.CYAN)} {_c('║', Colors.CYAN)}
|
|
348
|
-
{_c('║', Colors.CYAN)} Provider: {self.provider:<15} Model: {self.model:<17} {_c('║', Colors.CYAN)}
|
|
349
|
-
{_c('║', Colors.CYAN)} {_c('║', Colors.CYAN)}
|
|
350
|
-
{_c('║', Colors.CYAN)} Type {_c("'help'", Colors.GREEN)} for commands, or enter a task. {_c('║', Colors.CYAN)}
|
|
351
|
-
{_c('╚' + '═' * 58 + '╝', Colors.CYAN)}
|
|
352
|
-
""")
|
|
353
|
-
|
|
354
|
-
self.show_tools()
|
|
355
16
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
agent_task: Optional[asyncio.Task] = None
|
|
365
|
-
|
|
366
|
-
while True:
|
|
367
|
-
try:
|
|
368
|
-
# Get input
|
|
369
|
-
if sys.stdin.isatty():
|
|
370
|
-
prompt = f"\n{_c('>', Colors.CYAN, Colors.BOLD)} "
|
|
371
|
-
user_input = await asyncio.get_event_loop().run_in_executor(
|
|
372
|
-
None, lambda: input(prompt).strip()
|
|
373
|
-
)
|
|
374
|
-
else:
|
|
375
|
-
line = sys.stdin.readline()
|
|
376
|
-
if not line:
|
|
377
|
-
break
|
|
378
|
-
user_input = line.strip()
|
|
379
|
-
|
|
380
|
-
if not user_input:
|
|
381
|
-
continue
|
|
382
|
-
|
|
383
|
-
# Handle commands
|
|
384
|
-
cmd = user_input.lower()
|
|
385
|
-
|
|
386
|
-
if cmd in ('quit', 'exit', 'q'):
|
|
387
|
-
if agent_task and not agent_task.done():
|
|
388
|
-
agent_task.cancel()
|
|
389
|
-
try:
|
|
390
|
-
await agent_task
|
|
391
|
-
except asyncio.CancelledError:
|
|
392
|
-
pass
|
|
393
|
-
print(_c("Goodbye!", Colors.CYAN))
|
|
394
|
-
break
|
|
395
|
-
|
|
396
|
-
elif cmd == 'help':
|
|
397
|
-
self.show_help()
|
|
398
|
-
|
|
399
|
-
elif cmd == 'tools':
|
|
400
|
-
self.show_tools()
|
|
401
|
-
|
|
402
|
-
elif cmd == 'status':
|
|
403
|
-
self.show_status()
|
|
404
|
-
|
|
405
|
-
elif cmd == 'history':
|
|
406
|
-
self.show_history()
|
|
407
|
-
|
|
408
|
-
elif cmd == 'resume':
|
|
409
|
-
await self.resume_agent()
|
|
410
|
-
|
|
411
|
-
else:
|
|
412
|
-
# Treat as a task
|
|
413
|
-
if agent_task and not agent_task.done():
|
|
414
|
-
print(_c("Agent is running. Use Ctrl+C to interrupt.", Colors.YELLOW))
|
|
415
|
-
continue
|
|
416
|
-
|
|
417
|
-
agent_task = asyncio.create_task(self.run_agent_async(user_input))
|
|
418
|
-
try:
|
|
419
|
-
await agent_task
|
|
420
|
-
except asyncio.CancelledError:
|
|
421
|
-
pass
|
|
422
|
-
|
|
423
|
-
except KeyboardInterrupt:
|
|
424
|
-
print()
|
|
425
|
-
if agent_task and not agent_task.done():
|
|
426
|
-
self.interrupt()
|
|
427
|
-
agent_task.cancel()
|
|
428
|
-
try:
|
|
429
|
-
await agent_task
|
|
430
|
-
except asyncio.CancelledError:
|
|
431
|
-
pass
|
|
432
|
-
else:
|
|
433
|
-
print(_c("Type 'quit' to exit.", Colors.DIM))
|
|
434
|
-
except EOFError:
|
|
435
|
-
break
|
|
436
|
-
except Exception as e:
|
|
437
|
-
print(f"{_c('Error:', Colors.YELLOW)} {e}")
|
|
17
|
+
def main(argv: list[str] | None = None) -> int:
|
|
18
|
+
parser = argparse.ArgumentParser(
|
|
19
|
+
prog="react-agent",
|
|
20
|
+
description="Deprecated: the interactive REPL moved to AbstractCode.",
|
|
21
|
+
)
|
|
22
|
+
parser.add_argument("--provider", default=os.getenv("ABSTRACTCODE_PROVIDER", "ollama"))
|
|
23
|
+
parser.add_argument("--model", default=os.getenv("ABSTRACTCODE_MODEL", "qwen3:4b-instruct-2507-q4_K_M"))
|
|
24
|
+
args = parser.parse_args(list(argv) if argv is not None else None)
|
|
438
25
|
|
|
26
|
+
print("The AbstractAgent interactive REPL has moved to AbstractCode.\n")
|
|
27
|
+
print("Run:")
|
|
28
|
+
print(f" abstractcode --agent react --provider {args.provider} --model {args.model}")
|
|
29
|
+
return 0
|
|
439
30
|
|
|
440
|
-
def main():
|
|
441
|
-
"""Entry point for the REPL."""
|
|
442
|
-
parser = argparse.ArgumentParser(description="ReAct Agent REPL")
|
|
443
|
-
parser.add_argument("--provider", default="ollama", help="LLM provider")
|
|
444
|
-
parser.add_argument("--model", default="qwen3:1.7b-q4_K_M", help="Model name")
|
|
445
|
-
parser.add_argument("--state-file", help="File to persist agent state")
|
|
446
|
-
args = parser.parse_args()
|
|
447
|
-
|
|
448
|
-
repl = AgentREPL(
|
|
449
|
-
provider=args.provider,
|
|
450
|
-
model=args.model,
|
|
451
|
-
state_file=args.state_file,
|
|
452
|
-
)
|
|
453
|
-
asyncio.run(repl.repl_loop())
|
|
454
31
|
|
|
32
|
+
if __name__ == "__main__": # pragma: no cover
|
|
33
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
455
34
|
|
|
456
|
-
if __name__ == "__main__":
|
|
457
|
-
main()
|