0c5724c2f6
CI / push-validation (pull_request) Successful in 42s
CI / helm (pull_request) Successful in 51s
CI / lint (pull_request) Successful in 1m38s
CI / build (pull_request) Successful in 1m35s
CI / quality (pull_request) Successful in 2m21s
CI / security (pull_request) Successful in 2m26s
CI / typecheck (pull_request) Successful in 2m51s
CI / integration_tests (pull_request) Successful in 4m9s
CI / unit_tests (pull_request) Successful in 6m5s
CI / docker (pull_request) Successful in 1m28s
CI / coverage (pull_request) Successful in 10m52s
CI / status-check (pull_request) Successful in 3s
CI / push-validation (push) Successful in 31s
CI / helm (push) Successful in 38s
CI / build (push) Successful in 1m10s
CI / quality (push) Successful in 1m28s
CI / lint (push) Successful in 1m31s
CI / typecheck (push) Successful in 1m53s
CI / security (push) Successful in 2m4s
CI / benchmark-regression (push) Failing after 39s
CI / integration_tests (push) Successful in 3m39s
CI / e2e_tests (push) Successful in 56s
CI / unit_tests (push) Successful in 5m21s
CI / docker (push) Successful in 1m28s
CI / coverage (push) Successful in 11m3s
CI / status-check (push) Successful in 3s
CI / benchmark-publish (push) Successful in 1h23m13s
Implements the full tool-calling path for `agents actor run --skill` so that LLM
actors can actually invoke tools through the ToolCallingRuntime loop when a skill
is attached.
Core changes:
- reactive/tool_caller.py (new): ToolCallingLLMCaller implements the LLMCaller protocol;
binds tool schemas via bind_tools(), threads SystemMessage+HumanMessage on first call
and AIMessage+ToolMessages on subsequent calls, extracts tool calls from LangChain
responses following the LangChainSessionCaller pattern.
- reactive/tool_caller.py: bidirectional tool name encoding via uppercase sentinels
(_encode_tool_name / _decode_tool_name) to make CleverAgents namespaced tool names
("builtin/file-read", "server:local/tool") compatible with Anthropic's tool name
pattern ^[a-zA-Z0-9_-]{1,128}$. Uses _C_ for ":" and _S_ for "/" — uppercase
sentinels are safe because valid CleverAgents tool names forbid uppercase letters.
Encoding applied in _resolve_llm() before bind_tools(); decoding applied in invoke()
when extracting tool calls from the LLM response.
- reactive/tool_agent.py (new): ToolCallingAgent builds a per-run local ToolRegistry from
resolved skill tool entries by looking up names in the shared builtin_registry; drives
ToolCallingRuntime.run_tool_loop(); exposes last_result for tool_calls surfacing.
- reactive/application.py: (ST-1) _make_agent_instance() now always merges skill tools
instead of silently dropping them when actor has no base tools list; routes
tools+llm→ToolCallingAgent, tools+non-llm→SimpleToolAgent, no-tools+llm→SimpleLLMAgent;
(ST-4) _builtin_registry created at startup with register_file_tools/git/subplan;
(ST-6) _tally_tool_calls() + last_run_tool_calls property.
- reactive/graph_executor.py: (ST-5) ToolCallingAgent added to isinstance check in
_invoke_agent() so context dict is forwarded for Jinja2 rendering.
- cli/commands/actor_run.py: prints "Tool Calls: {n}" when > 0.
Test fixes:
- features/steps/actor_cli_run_steps.py: _make_app() sets last_run_tool_calls=0 to avoid
MagicMock>int TypeError in Python 3.13.
- features/steps/actor_run_signature_resolve_steps.py: same fix.
- robot/helper_actor_run_signature.py: same fix.
- features/reactive_application_coverage_boost.feature: updated scenario to verify new
correct behavior (LLM+skills → ToolCallingAgent, not silently kept as SimpleLLMAgent).
BDD coverage: 34 scenarios in features/actor_run_tool_calling.feature covering
tool call success, multi-turn loop, no-skill regression, silent-drop fix, LLMCaller
internals, _build_tool_registry edge cases, last_run_tool_calls tallying,
tool name encoding/decoding, and LLM response decoding.
ISSUES CLOSED: #11211
125 lines
5.8 KiB
Gherkin
125 lines
5.8 KiB
Gherkin
Feature: Reactive application coverage boost
|
|
As a developer
|
|
I want to exercise the remaining uncovered lines in reactive/application.py
|
|
So that code coverage is maximized
|
|
|
|
@coverage
|
|
Scenario: Logging adds a handler when root logger has no handlers
|
|
Given a reactive app with all root logger handlers removed
|
|
When I configure logging with verbose level 2
|
|
Then the root logger should have at least one handler
|
|
And the handler format should include the module name pattern
|
|
|
|
@coverage
|
|
Scenario: Ensure agent registered creates instance from config when agent not already present
|
|
Given a reactive app with a route referencing an agent not yet registered but present in config
|
|
When I register agents from config triggering deferred registration
|
|
Then the deferred config agent should be registered in the stream router
|
|
And the deferred config agent alias should also be registered
|
|
|
|
@coverage
|
|
Scenario: Initialize graph context refreshes stage order when stages are missing
|
|
Given a reactive app with a global context that has a partial stage order list
|
|
When I initialize the graph context
|
|
Then the stage order should be refreshed to the full default list
|
|
|
|
@coverage
|
|
Scenario: Initialize graph context resets writing stage when current value is invalid
|
|
Given a reactive app with a global context that has an invalid writing stage
|
|
When I initialize the graph context
|
|
Then the writing stage should be reset to intro
|
|
|
|
@coverage
|
|
Scenario: Follow chained edges returns immediately when next node is end
|
|
Given a reactive app configured for chained edge traversal to end node
|
|
When I follow chained edges starting from the end node
|
|
Then the chained edge result should be the current message with should_return true
|
|
|
|
@coverage
|
|
Scenario: Follow chained edges falls through when next node becomes falsy
|
|
Given a reactive app configured for chained edge traversal to a falsy node
|
|
When I follow chained edges starting from a node that chains to a falsy node
|
|
Then the chained edge result should be the current message with should_return false
|
|
|
|
@coverage
|
|
Scenario: Reactive app resolves skill names and stores tools
|
|
Given a reactive app with skill names resolved via a mock skill service
|
|
When I check the resolved skill tools
|
|
Then the app should have resolved skill tool entries
|
|
And the skill names property should match the input
|
|
|
|
@coverage
|
|
Scenario: Reactive app raises error for unknown skill name
|
|
Given a reactive app configured with an unknown skill name
|
|
When I attempt to create the app with the unknown skill
|
|
Then a CleverAgentsException should be raised with skill not found message
|
|
|
|
@coverage
|
|
Scenario: Reactive app merges skill tools into agent tool list
|
|
Given a reactive app with skill tools resolved and a config with agents
|
|
When agents are registered from config with skill tools
|
|
Then the agents should have skill tools merged into their tool list
|
|
|
|
@coverage
|
|
Scenario: Reactive app works without skill names
|
|
Given a reactive app created without any skill names
|
|
When I check the skill related properties
|
|
Then the skill names should be empty
|
|
And the resolved skill tools should be empty
|
|
|
|
@coverage
|
|
Scenario: Reactive app resolves skill with tool overrides
|
|
Given a reactive app with skill names resolved including overrides
|
|
When I check the resolved skill tools
|
|
Then the resolved tool entry should include overrides
|
|
|
|
@coverage
|
|
Scenario: Reactive app deduplicates skill names
|
|
Given a reactive app with duplicate skill names resolved via a mock skill service
|
|
When I check the resolved skill tools
|
|
Then the skill names should be deduplicated
|
|
And resolve_tools should be called once per unique skill
|
|
|
|
@coverage
|
|
Scenario: Reactive app raises error for empty string skill name
|
|
Given a reactive app configured with an empty string skill name
|
|
When I attempt to create the app with the empty skill
|
|
Then a CleverAgentsException should be raised for resolution failure
|
|
|
|
@coverage
|
|
Scenario: Reactive app skill injection upgrades LLM agents to ToolCallingAgent
|
|
Given a reactive app with skill tools and a config with an LLM agent
|
|
When agents are registered from config with skill tools present
|
|
Then the LLM agent should be a ToolCallingAgent not a SimpleLLMAgent
|
|
|
|
@coverage
|
|
Scenario: Reactive app raises error for skill resolution ValueError
|
|
Given a reactive app configured with a skill that triggers a ValueError
|
|
When I attempt to create the app with the ValueError skill
|
|
Then a CleverAgentsException should be raised with resolution failed message
|
|
|
|
@coverage
|
|
Scenario: Reactive app handles skill that resolves to zero tools
|
|
Given a reactive app with a skill that resolves to zero tools
|
|
When I check the resolved skill tools after zero tool resolution
|
|
Then the resolved skill tools should be empty
|
|
And the skill names should contain the zero-tool skill
|
|
|
|
@coverage
|
|
Scenario: Reactive app rejects skill name exceeding max length
|
|
Given a reactive app configured with an overly long skill name
|
|
When I attempt to create the app with the long skill name
|
|
Then a CleverAgentsException should be raised for invalid name format
|
|
|
|
@coverage
|
|
Scenario: Reactive app strips ANSI escape codes from skill names
|
|
Given a reactive app configured with a skill name containing ANSI codes
|
|
When I attempt to create the app with the ANSI skill name
|
|
Then a CleverAgentsException should be raised for invalid name format
|
|
|
|
@coverage
|
|
Scenario: Reactive app rejects skill name with disallowed characters
|
|
Given a reactive app configured with a skill name containing special characters
|
|
When I attempt to create the app with the special char skill name
|
|
Then a CleverAgentsException should be raised for invalid name format
|