agentx-dev 3.0.3__tar.gz → 3.0.5__tar.gz
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.
- {agentx_dev-3.0.3/agentx_dev.egg-info → agentx_dev-3.0.5}/PKG-INFO +1 -1
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Runner/AgentRun.py +126 -44
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Runner/AsyncAgentRun.py +67 -14
- {agentx_dev-3.0.3 → agentx_dev-3.0.5/agentx_dev.egg-info}/PKG-INFO +1 -1
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/pyproject.toml +1 -1
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/AGENTX.md +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/LICENSE +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/MANIFEST.in +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/README.md +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Agents/Agent.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Agents/__init__.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/AsyncTools.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/AutoSetup.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Cache.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/ChatModel.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Config.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/DefaultTools.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Loader.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/MCP.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Memory.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Observability.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Planner.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Runner/__init__.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Runner/promptTemplate.yaml +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Session.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Streaming.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Supervisor.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/Tools.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/WebTools.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/__init__.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/resources/__init__.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev/resources/promptTemplate.yaml +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev.egg-info/SOURCES.txt +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev.egg-info/dependency_links.txt +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev.egg-info/requires.txt +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/agentx_dev.egg-info/top_level.txt +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/README.md +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/async_example.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/async_quickstart.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/auto_features_example.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/caching_example.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/chatbot_example.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/complete_example.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/concurrent_example.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/concurrent_tool_example.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/file_agent_demo.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/function_calling_demo.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/mcp_demo.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/observability_example.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/orchestration_demo.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/planner_example.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/supervisor_codebase_analysis_demo.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/supervisor_example.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/examples/sync_quickstart.py +0 -0
- {agentx_dev-3.0.3 → agentx_dev-3.0.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentx-dev
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.5
|
|
4
4
|
Summary: A lightweight LLM agent framework with multi-agent orchestration (Supervisor + dynamic specialist spawning), permission-gated default tools, MCP integration, and prompt-template presets.
|
|
5
5
|
Author-email: Bruce-Arhin Shadrach <brucearhin098@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -1047,6 +1047,7 @@ class AgentRunner:
|
|
|
1047
1047
|
permissions: Any = None,
|
|
1048
1048
|
include_denied_tools: bool = False,
|
|
1049
1049
|
system_addendum: Optional[str] = None,
|
|
1050
|
+
strict_tool_dispatch: bool = False,
|
|
1050
1051
|
):
|
|
1051
1052
|
# PEP 8 alias: prefer lowercase `agent=`. Either argument works; passing
|
|
1052
1053
|
# both raises so misuse is loud rather than silently ambiguous.
|
|
@@ -1132,6 +1133,16 @@ class AgentRunner:
|
|
|
1132
1133
|
else:
|
|
1133
1134
|
self.system_addendum = system_addendum
|
|
1134
1135
|
|
|
1136
|
+
# When True: on unknown-tool dispatch, feed the model an error
|
|
1137
|
+
# observation ("that tool doesn't exist; use one of [X,Y] or
|
|
1138
|
+
# Final_Answer") and let the loop iterate rather than
|
|
1139
|
+
# immediately falling to implicit-final. Only applies when the
|
|
1140
|
+
# unknown action looks like a tool identifier (no spaces) —
|
|
1141
|
+
# natural-language actions still go straight to implicit-final
|
|
1142
|
+
# since retrying makes no sense there. Default False keeps
|
|
1143
|
+
# the 3.0.4 behavior.
|
|
1144
|
+
self.strict_tool_dispatch = strict_tool_dispatch
|
|
1145
|
+
|
|
1135
1146
|
self.auto_cache = auto_cache and config.caching_enabled
|
|
1136
1147
|
self.auto_memory = auto_memory and config.memory_enabled
|
|
1137
1148
|
_setup = _ensure_auto_setup() if (self.auto_cache or self.auto_memory) else None
|
|
@@ -1361,53 +1372,28 @@ class AgentRunner:
|
|
|
1361
1372
|
yield {"type": "final", "content": final_answer}
|
|
1362
1373
|
break
|
|
1363
1374
|
|
|
1364
|
-
#
|
|
1365
|
-
#
|
|
1366
|
-
#
|
|
1367
|
-
#
|
|
1368
|
-
#
|
|
1369
|
-
#
|
|
1370
|
-
#
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
# 2. action="" (empty). Under FC, the model sometimes
|
|
1374
|
-
# omits `action` entirely; Pydantic falls back to the
|
|
1375
|
-
# field default. Same failure — empty tool name.
|
|
1376
|
-
# Route both to Final_Answer, built from whatever text the
|
|
1377
|
-
# model actually provided (thought + action + action_input).
|
|
1378
|
-
known_tools = set(self.func) | set(self.args)
|
|
1379
|
-
if not action or action not in known_tools:
|
|
1380
|
-
parts: List[str] = []
|
|
1381
|
-
if thought:
|
|
1382
|
-
parts.append(str(thought))
|
|
1383
|
-
# Only include `action` in the reply if it looks like
|
|
1384
|
-
# real response text (not empty, not a suspicious short
|
|
1385
|
-
# sentinel like "answer"). Skip empty.
|
|
1386
|
-
if action and action.strip():
|
|
1387
|
-
parts.append(str(action))
|
|
1388
|
-
if action_input and str(action_input).strip():
|
|
1389
|
-
parts.append(str(action_input))
|
|
1390
|
-
final_answer = "\n\n".join(parts) or (
|
|
1391
|
-
"(agent emitted an unrecognized action and no text)"
|
|
1392
|
-
)
|
|
1375
|
+
# Normalize BEFORE the known-tools check — OpenAI's FC path
|
|
1376
|
+
# sometimes returns tool names with a "functions." prefix
|
|
1377
|
+
# ("functions.get_my_scores" instead of "get_my_scores").
|
|
1378
|
+
# Without this, a valid tool call falls through to the
|
|
1379
|
+
# implicit-final guardrail, which then leaks the model's
|
|
1380
|
+
# internal Thought into user-visible text. Rewrite `action`
|
|
1381
|
+
# here so the rest of the loop uses the canonical name.
|
|
1382
|
+
normalized_action = self.registry._normalize_tool_name(action)
|
|
1383
|
+
if normalized_action != action:
|
|
1393
1384
|
if self.verbose:
|
|
1394
|
-
preview = (action or "<empty>")[:60]
|
|
1395
1385
|
print(
|
|
1396
|
-
f"\x1B[
|
|
1397
|
-
f"'{
|
|
1398
|
-
f"a Final_Answer variant; treating turn as "
|
|
1399
|
-
f"Final_Answer\x1B[0m"
|
|
1386
|
+
f"\x1B[3;33m[loop] normalized action "
|
|
1387
|
+
f"'{action}' -> '{normalized_action}'\x1B[0m"
|
|
1400
1388
|
)
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
#
|
|
1405
|
-
#
|
|
1406
|
-
#
|
|
1407
|
-
#
|
|
1408
|
-
#
|
|
1409
|
-
# traces). Cheaper than dispatch — we abort before the tool
|
|
1410
|
-
# even runs on the 3rd repeat.
|
|
1389
|
+
action = normalized_action
|
|
1390
|
+
|
|
1391
|
+
# Loop-level spiral check — 3 identical (action, args) in a
|
|
1392
|
+
# row and we bail. Placed BEFORE the known-tools branch so
|
|
1393
|
+
# it catches BOTH kinds of spiral: a valid tool getting
|
|
1394
|
+
# re-dispatched (dup-guard also catches this at the tool
|
|
1395
|
+
# layer), and an unknown tool being retried via strict-mode
|
|
1396
|
+
# (dup-guard would never see those since dispatch skips them).
|
|
1411
1397
|
try:
|
|
1412
1398
|
action_sig = f"{action}::{json.dumps(action_input, sort_keys=True, default=repr)}"
|
|
1413
1399
|
except Exception:
|
|
@@ -1442,6 +1428,102 @@ class AgentRunner:
|
|
|
1442
1428
|
yield {"type": "final", "content": final_answer}
|
|
1443
1429
|
break
|
|
1444
1430
|
|
|
1431
|
+
# Guardrail: the model emitted an `action` value that is
|
|
1432
|
+
# neither a terminal-answer variant NOR a registered tool
|
|
1433
|
+
# (even after normalization). Two scenarios trigger this:
|
|
1434
|
+
# 1. use_function_calling=True + missing schema guidance:
|
|
1435
|
+
# the model stuffs its natural-language response text
|
|
1436
|
+
# into `action` (e.g. "Provide a response to the
|
|
1437
|
+
# athlete."). Route to final so the loop terminates.
|
|
1438
|
+
# 2. action="" (missing field, Pydantic default).
|
|
1439
|
+
# NEVER include `Thought` in the user-facing final — the
|
|
1440
|
+
# Thought is internal reasoning ("The user needs their
|
|
1441
|
+
# scores; I'll fetch them") and leaking it to the user
|
|
1442
|
+
# exposes the agent's introspection. Prefer action_input
|
|
1443
|
+
# (that's what the model wanted the user to see). Only fall
|
|
1444
|
+
# back to `action` when action_input is empty AND action
|
|
1445
|
+
# looks like natural-language text (contains a space, not
|
|
1446
|
+
# just an identifier).
|
|
1447
|
+
known_tools = set(self.func) | set(self.args)
|
|
1448
|
+
if not action or action not in known_tools:
|
|
1449
|
+
# Strict mode: when the unknown `action` looks like a
|
|
1450
|
+
# tool identifier (no spaces, short), assume the model
|
|
1451
|
+
# was TRYING to call a tool and misnamed it. Feed an
|
|
1452
|
+
# error observation back so the model can retry with
|
|
1453
|
+
# a valid name, instead of collapsing to implicit-final
|
|
1454
|
+
# after the very first mistake. The existing dup-guard
|
|
1455
|
+
# + loop-level circuit breaker still bound retries.
|
|
1456
|
+
# Only skipped when: strict mode is off, we've hit
|
|
1457
|
+
# max_iterations, or the action is clearly the model's
|
|
1458
|
+
# response text (contains a space → natural language).
|
|
1459
|
+
looks_like_tool_id = (
|
|
1460
|
+
bool(action) and " " not in action and len(action) < 80
|
|
1461
|
+
)
|
|
1462
|
+
if (
|
|
1463
|
+
self.strict_tool_dispatch
|
|
1464
|
+
and looks_like_tool_id
|
|
1465
|
+
and count < self.max_iterations
|
|
1466
|
+
):
|
|
1467
|
+
available = sorted(known_tools)
|
|
1468
|
+
error_content = (
|
|
1469
|
+
f"[framework] error: '{action}' is not a "
|
|
1470
|
+
f"registered tool. Available tools: {available}. "
|
|
1471
|
+
f"Options: (1) call one of them with the correct "
|
|
1472
|
+
f"argument shape; (2) emit action=\"Final_Answer\" "
|
|
1473
|
+
f"with your final response text in action_input "
|
|
1474
|
+
f"if you're done."
|
|
1475
|
+
)
|
|
1476
|
+
tc_id = getattr(self, "_last_function_call_id", None) if self.use_function_calling else None
|
|
1477
|
+
err_msg = {
|
|
1478
|
+
"role": "tool" if tc_id else "function",
|
|
1479
|
+
"name": "tool_call_error",
|
|
1480
|
+
"content": error_content,
|
|
1481
|
+
}
|
|
1482
|
+
if tc_id:
|
|
1483
|
+
err_msg["tool_call_id"] = tc_id
|
|
1484
|
+
working_history.append(err_msg)
|
|
1485
|
+
yield {
|
|
1486
|
+
"type": "tool_result",
|
|
1487
|
+
"name": action,
|
|
1488
|
+
"result": error_content,
|
|
1489
|
+
"is_error": True,
|
|
1490
|
+
}
|
|
1491
|
+
if self.verbose:
|
|
1492
|
+
print(
|
|
1493
|
+
f"\x1B[1;33m[loop] strict-retry: action "
|
|
1494
|
+
f"'{action[:60]}' unknown; feeding error "
|
|
1495
|
+
f"back so the model can retry\x1B[0m"
|
|
1496
|
+
)
|
|
1497
|
+
self._last_function_call_id = None
|
|
1498
|
+
count += 1
|
|
1499
|
+
continue
|
|
1500
|
+
|
|
1501
|
+
# Non-strict OR strict-can't-help path: implicit-final.
|
|
1502
|
+
# Prefer action_input; fall back to action-as-text ONLY
|
|
1503
|
+
# when it's natural language (has spaces). NEVER leak
|
|
1504
|
+
# Thought — it's internal reasoning that shouldn't reach
|
|
1505
|
+
# a user, especially in third-person-persona apps.
|
|
1506
|
+
if action_input and str(action_input).strip():
|
|
1507
|
+
final_answer = str(action_input)
|
|
1508
|
+
elif action and action.strip() and " " in action:
|
|
1509
|
+
final_answer = str(action)
|
|
1510
|
+
else:
|
|
1511
|
+
final_answer = (
|
|
1512
|
+
"(agent emitted an unrecognized action and no "
|
|
1513
|
+
"answer text — try rephrasing or re-run)"
|
|
1514
|
+
)
|
|
1515
|
+
if self.verbose:
|
|
1516
|
+
preview = (action or "<empty>")[:60]
|
|
1517
|
+
print(
|
|
1518
|
+
f"\x1B[1;33m[loop] implicit-final: action "
|
|
1519
|
+
f"'{preview}' is not a registered tool and not "
|
|
1520
|
+
f"a Final_Answer variant; returning "
|
|
1521
|
+
f"action_input as final (Thought NOT leaked)"
|
|
1522
|
+
f"\x1B[0m"
|
|
1523
|
+
)
|
|
1524
|
+
yield {"type": "final", "content": final_answer}
|
|
1525
|
+
break
|
|
1526
|
+
|
|
1445
1527
|
yield {"type": "tool_call", "name": action, "args": action_input}
|
|
1446
1528
|
if self.verbose:
|
|
1447
1529
|
print(f"\x1B[3;33m[tool] Invoking '{action}' with args: {action_input}\x1B[0m")
|
|
@@ -86,6 +86,7 @@ class AsyncAgentRunner:
|
|
|
86
86
|
agent: AgentFormattor | AgentPrompt | str | None = None,
|
|
87
87
|
permissions: Any = None,
|
|
88
88
|
include_denied_tools: bool = False,
|
|
89
|
+
strict_tool_dispatch: bool = False,
|
|
89
90
|
):
|
|
90
91
|
if Agent is not None and agent is not None:
|
|
91
92
|
raise TypeError("AsyncAgentRunner received both 'Agent' and 'agent'; pass only one.")
|
|
@@ -133,6 +134,8 @@ class AsyncAgentRunner:
|
|
|
133
134
|
"are mutually exclusive. Function-calling routes through the "
|
|
134
135
|
"AgentType parser; native binding skips it. Pick one."
|
|
135
136
|
)
|
|
137
|
+
# See AgentRunner.__init__ for the strict_tool_dispatch contract.
|
|
138
|
+
self.strict_tool_dispatch = strict_tool_dispatch
|
|
136
139
|
|
|
137
140
|
self.auto_cache = auto_cache and config.caching_enabled
|
|
138
141
|
self.auto_memory = auto_memory and config.memory_enabled
|
|
@@ -506,29 +509,79 @@ class AsyncAgentRunner:
|
|
|
506
509
|
elif thought:
|
|
507
510
|
logger.info(f"thought: {thought}")
|
|
508
511
|
|
|
509
|
-
#
|
|
510
|
-
#
|
|
511
|
-
#
|
|
512
|
+
# Strip OpenAI's "functions." prefix before the known-tools
|
|
513
|
+
# check — otherwise a valid tool call falls through to the
|
|
514
|
+
# implicit-final guardrail and leaks Thought as user text.
|
|
515
|
+
normalized_action = self.registry._normalize_tool_name(action)
|
|
516
|
+
if normalized_action != action:
|
|
517
|
+
if self.verbose:
|
|
518
|
+
print(
|
|
519
|
+
f"\x1B[3;33m[loop] normalized action "
|
|
520
|
+
f"'{action}' -> '{normalized_action}'\x1B[0m"
|
|
521
|
+
)
|
|
522
|
+
action = normalized_action
|
|
523
|
+
|
|
524
|
+
# Implicit-final guardrail — see the sync AgentRunner for
|
|
525
|
+
# the full rationale. If strict_tool_dispatch=True AND the
|
|
526
|
+
# unknown action looks like a tool identifier (no spaces)
|
|
527
|
+
# AND we still have iteration budget, feed an error
|
|
528
|
+
# observation back to the model and let it retry.
|
|
512
529
|
known_tools = set(self.registry.sync_std) | set(self.registry.sync_struct) \
|
|
513
530
|
| set(self.registry.async_std) | set(self.registry.async_struct)
|
|
514
531
|
if not action or action not in known_tools:
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
parts.append(str(thought))
|
|
518
|
-
if action and action.strip():
|
|
519
|
-
parts.append(str(action))
|
|
520
|
-
if action_input and str(action_input).strip():
|
|
521
|
-
parts.append(str(action_input))
|
|
522
|
-
final_answer = "\n\n".join(parts) or (
|
|
523
|
-
"(agent emitted an unrecognized action and no text)"
|
|
532
|
+
looks_like_tool_id = (
|
|
533
|
+
bool(action) and " " not in action and len(action) < 80
|
|
524
534
|
)
|
|
535
|
+
if (
|
|
536
|
+
self.strict_tool_dispatch
|
|
537
|
+
and looks_like_tool_id
|
|
538
|
+
and count < self.max_iterations
|
|
539
|
+
):
|
|
540
|
+
available = sorted(known_tools)
|
|
541
|
+
error_content = (
|
|
542
|
+
f"[framework] error: '{action}' is not a "
|
|
543
|
+
f"registered tool. Available tools: {available}. "
|
|
544
|
+
f"Options: (1) call one of them with the correct "
|
|
545
|
+
f"argument shape; (2) emit action=\"Final_Answer\" "
|
|
546
|
+
f"with your final response text in action_input "
|
|
547
|
+
f"if you're done."
|
|
548
|
+
)
|
|
549
|
+
tc_id = getattr(self, "_last_function_call_id", None) if self.use_function_calling else None
|
|
550
|
+
err_msg = {
|
|
551
|
+
"role": "tool" if tc_id else "function",
|
|
552
|
+
"name": "tool_call_error",
|
|
553
|
+
"content": error_content,
|
|
554
|
+
}
|
|
555
|
+
if tc_id:
|
|
556
|
+
err_msg["tool_call_id"] = tc_id
|
|
557
|
+
working_history.append(err_msg)
|
|
558
|
+
if self.verbose:
|
|
559
|
+
print(
|
|
560
|
+
f"\x1B[1;33m[loop] strict-retry: action "
|
|
561
|
+
f"'{action[:60]}' unknown; feeding error "
|
|
562
|
+
f"back so the model can retry\x1B[0m"
|
|
563
|
+
)
|
|
564
|
+
self._last_function_call_id = None
|
|
565
|
+
count += 1
|
|
566
|
+
continue
|
|
567
|
+
|
|
568
|
+
# Non-strict OR strict-can't-help: implicit-final.
|
|
569
|
+
if action_input and str(action_input).strip():
|
|
570
|
+
final_answer = str(action_input)
|
|
571
|
+
elif action and action.strip() and " " in action:
|
|
572
|
+
final_answer = str(action)
|
|
573
|
+
else:
|
|
574
|
+
final_answer = (
|
|
575
|
+
"(agent emitted an unrecognized action and no "
|
|
576
|
+
"answer text — try rephrasing or re-run)"
|
|
577
|
+
)
|
|
525
578
|
if self.verbose:
|
|
526
579
|
preview = (action or "<empty>")[:60]
|
|
527
580
|
print(
|
|
528
581
|
f"\x1B[1;33m[loop] implicit-final: action "
|
|
529
582
|
f"'{preview}' is not a registered tool and not "
|
|
530
|
-
f"a Final_Answer variant;
|
|
531
|
-
f"
|
|
583
|
+
f"a Final_Answer variant; returning action_input "
|
|
584
|
+
f"as final (Thought NOT leaked)\x1B[0m"
|
|
532
585
|
)
|
|
533
586
|
break
|
|
534
587
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentx-dev
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.5
|
|
4
4
|
Summary: A lightweight LLM agent framework with multi-agent orchestration (Supervisor + dynamic specialist spawning), permission-gated default tools, MCP integration, and prompt-template presets.
|
|
5
5
|
Author-email: Bruce-Arhin Shadrach <brucearhin098@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
|
|
3
3
|
name = "agentx-dev"
|
|
4
|
-
version = "3.0.
|
|
4
|
+
version = "3.0.5"
|
|
5
5
|
description = "A lightweight LLM agent framework with multi-agent orchestration (Supervisor + dynamic specialist spawning), permission-gated default tools, MCP integration, and prompt-template presets."
|
|
6
6
|
authors = [{ name = "Bruce-Arhin Shadrach", email = "brucearhin098@gmail.com" }]
|
|
7
7
|
# Best practice: link to the license file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|