b3851693c8
CI / push-validation (push) Successful in 38s
CI / helm (push) Successful in 49s
CI / lint (push) Successful in 1m20s
CI / build (push) Successful in 1m13s
CI / quality (push) Successful in 1m38s
CI / typecheck (push) Successful in 2m6s
CI / security (push) Successful in 2m5s
CI / benchmark-regression (push) Failing after 1m3s
CI / e2e_tests (push) Successful in 57s
CI / integration_tests (push) Successful in 4m37s
CI / unit_tests (push) Successful in 7m0s
CI / docker (push) Successful in 1m31s
CI / coverage (push) Successful in 13m59s
CI / status-check (push) Successful in 2s
CI / benchmark-publish (push) Successful in 1h37m21s
Two code paths in the reactive actor run pipeline silently discarded the options: block from v3 actor YAML, preventing custom OpenAI-compatible backends (llama.cpp, Ollama, etc.) from being used. Review fixes applied: - Fix 1: Relabeled issue #11223 from Type/Task to Type/Bug; added @tdd_issue/@tdd_issue_11223 tags to all 5 Behave scenarios. - Fix 2: openai_api_key in options now routes through the registry's __api_key_sentinel mechanism so user-provided keys correctly override environment defaults. (stream_router.py) - Fix 3: type: graph actors now propagate actor-level options to individual node configs via setdefault. (config_parser.py) - Fix 4: Options keys are validated against an explicit allowlist; reserved keys (provider_type, model_id) are excluded; unrecognized keys log a WARNING instead of being silently forwarded. (stream_router.py) - Fix 5: Updated _build_from_v3 docstring to list options as a propagated field. (config_parser.py) - Fix 6: Removed inconsistent and options_raw emptiness guard; empty options dicts are now preserved consistently. (config_parser.py) - Fix 7: Reserved keys provider_type and model_id are excluded from the options merge loop to prevent TypeError. (stream_router.py) - Fix 8: Added Behave scenario verifying top-level temperature takes precedence over options duplicate. (consolidated_routing.feature + steps) - Fix 9: Strengthened "no extra kwargs" assertion to assert kwargs == {} directly instead of using an allow-list filter. (stream_router steps) - Fix 10: Strengthened options assertion to exact dict equality. (actor_v3_schema_extended_steps.py) - N1: Comment style aligned to M5: prefix convention. - N2: Type annotations changed from Any to Context (behave.runner). - N3: Added Behave scenario for empty options: {} dict behavior. Tests: 5 new Behave scenarios (3 in actor_v3_schema.feature, 2 in consolidated_routing.feature) with @tdd_issue/@tdd_issue_11223 tags. ISSUES CLOSED: #11223
1403 lines
57 KiB
Gherkin
1403 lines
57 KiB
Gherkin
Feature: Consolidated Routing
|
|
Combined scenarios from: message_router_new_coverage, reactive_registry_adapter_coverage, reactive_routing_port, route_bridge_coverage, route_bridge_uncovered_lines, route_coverage_comprehensive, routing_adapter_coverage, routing_langgraph_port, stream_router_additional_coverage, stream_router_agent_tool_coverage, stream_router_coverage, stream_router_misc_uncovered, stream_router_new_branches, stream_router_remaining_coverage, stream_router_uncovered_paths, stream_router_unsafe_and_llm_coverage
|
|
|
|
# ============================================================
|
|
# Originally from: message_router_new_coverage.feature
|
|
# Feature: Message router new coverage
|
|
# ============================================================
|
|
|
|
Scenario: build_message_router_node creates NodeConfig with correct type
|
|
When I build a message router node named "router1" with rules
|
|
Then the node name should be "router1"
|
|
And the node type should be MESSAGE_ROUTER
|
|
And the node metadata should contain rules
|
|
|
|
|
|
Scenario: build_message_router_edges creates edges from rules
|
|
When I build message router edges for "router1" with two target rules
|
|
Then the edge count should be 3
|
|
And the last edge target should be "end"
|
|
|
|
|
|
Scenario: build_message_router_edges handles empty rules
|
|
When I build message router edges for "router1" with no rules
|
|
Then the edge count should be 1
|
|
And the last edge target should be "end"
|
|
|
|
|
|
Scenario: build_message_router_edges skips rules without target
|
|
When I build message router edges for "router1" with a rule missing target
|
|
Then the edge count should be 1
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: reactive_registry_adapter_coverage.feature
|
|
# Feature: Reactive registry adapter coverage
|
|
# ============================================================
|
|
|
|
Scenario: Registry list failure propagates to caller
|
|
Given a reactive stream router and route bridge
|
|
And an actor registry that raises on list
|
|
When I attempt to register registry actors with the adapter
|
|
Then a RuntimeError is raised
|
|
|
|
|
|
Scenario: Registers only actors with processing methods
|
|
Given a reactive stream router and route bridge
|
|
And an actor registry with mixed capabilities
|
|
When I register registry actors with the adapter
|
|
Then only actors with processing are registered on the stream router
|
|
And the route bridge agents cache includes all listed actors
|
|
|
|
|
|
Scenario: Route bridge refresh failure propagates to caller
|
|
Given a reactive stream router and route bridge
|
|
And an actor registry that returns actors without names
|
|
When I attempt to register registry actors with the adapter
|
|
Then an AttributeError is raised
|
|
|
|
|
|
Scenario: Route bridge refresh failure propagates when registry returns none
|
|
Given a reactive stream router and route bridge
|
|
And an actor registry that returns none
|
|
When I attempt to register registry actors with the adapter
|
|
Then a TypeError is raised
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: reactive_routing_port.feature
|
|
# Feature: Reactive routing and route bridge port
|
|
# ============================================================
|
|
|
|
Scenario: Create a cold stream and send a message
|
|
Given a reactive stream router
|
|
When I create a cold stream named "alpha"
|
|
And I send message "hello" to stream "alpha"
|
|
Then the stream "alpha" should exist
|
|
|
|
|
|
Scenario: Initialize route bridge with dummy registry
|
|
Given a reactive stream router
|
|
And a dummy actor registry
|
|
When I create a route bridge
|
|
Then the route bridge should include agent "dummy"
|
|
|
|
|
|
Scenario: Convert stream route to graph config via bridge
|
|
Given a reactive stream router
|
|
And a dummy actor registry
|
|
And a stream route config named "s1" with a map agent operator
|
|
When I create a route bridge
|
|
And I convert the stream route to a graph config
|
|
Then the graph config should contain nodes
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: route_bridge_coverage.feature
|
|
# Feature: Route Bridge Module Coverage
|
|
# ============================================================
|
|
|
|
Scenario: Test RouteBridge initialization
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a route bridge stream router and agents
|
|
When I create the route bridge instance
|
|
Then the route bridge should be initialized correctly
|
|
And the route bridge logger should be set up
|
|
And route bridge active conversions should be empty
|
|
|
|
|
|
Scenario: Test RouteBridge initialization with scheduler
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a route bridge stream router and agents
|
|
And I have an AsyncIO scheduler
|
|
When I create a RouteBridge instance with scheduler
|
|
Then the route bridge should be initialized with scheduler
|
|
And the route bridge scheduler should be stored correctly
|
|
|
|
|
|
Scenario: Test check_upgrade_conditions - no bridge config
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a route config without bridge configuration
|
|
And I have a stream message
|
|
When I check upgrade conditions
|
|
Then the route bridge upgrade result should be False
|
|
|
|
|
|
Scenario: Test check_upgrade_conditions - wrong route type
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a graph route config with bridge configuration
|
|
And I have a stream message
|
|
When I check upgrade conditions
|
|
Then the route bridge upgrade result should be False
|
|
|
|
|
|
Scenario: Test check_upgrade_conditions - needs_state condition
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a stream route config with needs_state upgrade condition
|
|
And I have a stream message with requires_state metadata
|
|
When I check upgrade conditions
|
|
Then the route bridge upgrade result should be True
|
|
|
|
|
|
Scenario: Test check_upgrade_conditions - message_count condition
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a stream route config with message_count upgrade condition
|
|
And I have processed enough messages to trigger upgrade
|
|
When I check upgrade conditions
|
|
Then the route bridge upgrade result should be True
|
|
And the route bridge message count should be at least 5
|
|
|
|
|
|
Scenario: Test check_upgrade_conditions - complexity_threshold condition
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a stream route config with complexity_threshold upgrade condition
|
|
And I have a complex route configuration
|
|
When I check upgrade conditions
|
|
Then the route bridge upgrade result should be True
|
|
|
|
|
|
Scenario: Test check_upgrade_conditions - non-callable predicate
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a stream route config with non-callable custom_predicate
|
|
And I have a stream message
|
|
When I check upgrade conditions
|
|
Then the route bridge upgrade result should be False
|
|
|
|
|
|
Scenario: Test check_downgrade_conditions - no bridge config
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a route config without bridge configuration
|
|
And I have a graph state
|
|
When I check downgrade conditions
|
|
Then the downgrade result should be False
|
|
|
|
|
|
Scenario: Test check_downgrade_conditions - wrong route type
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a stream route config with bridge configuration
|
|
And I have a graph state
|
|
When I check downgrade conditions
|
|
Then the downgrade result should be False
|
|
|
|
|
|
Scenario: Test check_downgrade_conditions - idle_time condition
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a graph route config with idle_time downgrade condition
|
|
And I have a graph state that has been idle too long
|
|
When I check downgrade conditions
|
|
Then the downgrade result should be True
|
|
|
|
|
|
Scenario: Test check_downgrade_conditions - state_size condition
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a graph route config with state_size downgrade condition
|
|
And I have a graph state with minimal messages
|
|
When I check downgrade conditions
|
|
Then the downgrade result should be True
|
|
|
|
|
|
Scenario: Test check_downgrade_conditions - no_conditionals_used condition
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a graph route config with no_conditionals_used downgrade condition
|
|
And I have a graph state with no conditional edges used
|
|
When I check downgrade conditions
|
|
Then the downgrade result should be True
|
|
|
|
|
|
Scenario: Test _get_message_count functionality
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I seed a message count for the route
|
|
When I get message count for a route
|
|
Then the route bridge message count should be returned
|
|
And the route bridge message count result should be a valid integer
|
|
|
|
|
|
Scenario: Test error handling in downgrade_graph_to_stream
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a graph route config for downgrade
|
|
And I have a corrupted LangGraph instance
|
|
When I attempt to downgrade graph to stream
|
|
Then route bridge error handling should occur
|
|
And the route bridge system should remain stable
|
|
|
|
|
|
|
|
Scenario: Test complex upgrade downgrade cycle
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a route config that supports both upgrade and downgrade
|
|
And I have a stream message with requires_state metadata
|
|
When I perform an upgrade and then downgrade cycle
|
|
Then the cycle should complete successfully
|
|
And the final configuration should be consistent
|
|
And active conversions should be tracked correctly
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: route_bridge_uncovered_lines.feature
|
|
# Feature: Additional route bridge uncovered lines coverage
|
|
# ============================================================
|
|
|
|
Scenario: Bridge falls back to empty agents on registry failure
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a failing agent registry for route bridge
|
|
When I create the route bridge instance
|
|
Then the route bridge should be initialized correctly
|
|
And the route bridge agents should be empty
|
|
|
|
|
|
Scenario: Upgrade conditions not met when below message_count threshold
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a stream route config with message_count upgrade condition
|
|
And I have a pending message count below threshold
|
|
And I have a stream message
|
|
When I check upgrade conditions
|
|
Then the route bridge upgrade result should be False
|
|
|
|
|
|
Scenario: Complexity threshold too high prevents upgrade
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a simple stream route config with high complexity threshold
|
|
And I have a stream message
|
|
When I check upgrade conditions
|
|
Then the route bridge upgrade result should be False
|
|
|
|
|
|
Scenario: Callable custom predicate triggers upgrade
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a stream route config with callable custom_predicate
|
|
And I have a stream message
|
|
When I check upgrade conditions synchronously
|
|
Then the route bridge upgrade result should be True
|
|
|
|
|
|
Scenario: Upgrade applies state extractor and preserves subscriptions loop
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a stream route config with state extractor and subscriptions
|
|
And I have a stream message
|
|
When I upgrade the stream route to graph
|
|
Then the graph initial state should include extracted data
|
|
|
|
|
|
Scenario: Downgrade uses flattener and checkpointing
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a graph route config with flattener and checkpointing
|
|
And I have a LangGraph instance with checkpointing
|
|
When I downgrade the graph with flattener
|
|
Then the state flattener should run
|
|
And the checkpoint should be saved
|
|
|
|
|
|
Scenario: _create_graph_from_stream uses function node when no agent
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
When I create a graph config from a stream without agents
|
|
Then the resulting graph should use function nodes
|
|
|
|
|
|
Scenario: _get_message_count falls back to update_count attribute
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I seed a fallback update_count conversion
|
|
When I get message count for a route
|
|
Then the route bridge fallback message count should be returned
|
|
|
|
|
|
Scenario: Downgrade conditions not met when idle time short
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a graph route config with idle_time not exceeded
|
|
And I have a graph state
|
|
When I check downgrade conditions
|
|
Then the downgrade result should be False
|
|
|
|
|
|
Scenario: Downgrade conditions not met when state size large
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a clean test environment for route bridge
|
|
Given I have a RouteBridge instance
|
|
And I have a graph route config with large state size
|
|
And I have a graph state with multiple messages
|
|
When I check downgrade conditions
|
|
Then the downgrade result should be False
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: route_coverage_comprehensive.feature
|
|
# Feature: Route Module Comprehensive Coverage
|
|
# ============================================================
|
|
|
|
Scenario: RouteConfig validation for graph routes without nodes
|
|
Given the route system is available
|
|
Given I create a RouteConfig with graph type but no nodes
|
|
When I validate the configuration
|
|
Then I should get a ConfigurationError about missing nodes
|
|
|
|
|
|
Scenario: RouteConfig to_stream_config conversion error
|
|
Given the route system is available
|
|
Given I create a RouteConfig with graph type
|
|
When I try to convert it to StreamConfig
|
|
Then I should get a ValueError about invalid conversion
|
|
|
|
|
|
Scenario: RouteConfig to_graph_config conversion error
|
|
Given the route system is available
|
|
Given I create a RouteConfig with stream type
|
|
When I try to convert it to GraphConfig
|
|
Then I should get a ValueError about invalid conversion
|
|
|
|
|
|
Scenario: RouteConfig from_stream_config class method
|
|
Given the route system is available
|
|
Given I have a valid StreamConfig
|
|
When I create a RouteConfig from the StreamConfig
|
|
Then the RouteConfig should have stream type
|
|
And it should preserve all stream configuration details
|
|
|
|
|
|
Scenario: RouteConfig from_graph_config class method
|
|
Given the route system is available
|
|
Given I have a valid GraphConfig with nodes and edges
|
|
When I create a RouteConfig from the GraphConfig
|
|
Then the RouteConfig should have graph type
|
|
And it should preserve all graph configuration details
|
|
And node configurations should be converted to dictionaries
|
|
And edge configurations should be converted to dictionaries
|
|
|
|
|
|
Scenario: RouteComplexityAnalyzer bridge type analysis
|
|
Given the route system is available
|
|
Given I have a RouteConfig with bridge type
|
|
When I analyze the route complexity
|
|
Then it should return bridge complexity analysis
|
|
And the complexity should be "bridge"
|
|
And the score should be 0
|
|
|
|
|
|
Scenario: Stream complexity analysis with various features
|
|
Given the route system is available
|
|
Given I have stream routes with different features
|
|
When I analyze each route complexity
|
|
Then routes with more operators should have higher complexity scores
|
|
And routes with routing connections should get additional score
|
|
And hot streams should get additional score
|
|
And the complexity classification should match the score ranges
|
|
|
|
|
|
Scenario: Graph complexity analysis with conditional edges
|
|
Given the route system is available
|
|
Given I have graph routes with conditional edges
|
|
When I analyze the graph complexity
|
|
Then conditional edges should increase the complexity score
|
|
And the features should include conditional edge information
|
|
And checkpointing should increase the score
|
|
And time travel should increase the score
|
|
|
|
|
|
Scenario: Stream complexity recommendation logic
|
|
Given the route system is available
|
|
Given I have streams with different complexity scores
|
|
When I get recommendations for each stream
|
|
Then simple streams should get simple transformation recommendation
|
|
And moderate streams should get multi-step processing recommendation
|
|
And complex streams should get graph consideration recommendation
|
|
|
|
|
|
Scenario: Graph complexity recommendation logic
|
|
Given the route system is available
|
|
Given I have graphs with different complexity scores
|
|
When I get recommendations for each graph
|
|
Then moderate graphs should get conditional logic recommendation
|
|
And complex graphs should get stateful workflow recommendation
|
|
And advanced graphs should get feature evaluation recommendation
|
|
|
|
|
|
Scenario: Route type suggestion based on requirements
|
|
Given the route system is available
|
|
Given I have different requirement scenarios
|
|
When I ask for route type suggestions
|
|
Then persistence requirements should suggest graph
|
|
And state with conditionals should suggest graph
|
|
And conditionals without continuous should suggest graph
|
|
And stateless continuous should suggest stream
|
|
And simple cases should default to stream
|
|
|
|
|
|
Scenario: RouteConfig post_init stream type defaulting
|
|
Given the route system is available
|
|
Given I create a stream RouteConfig without specifying stream_type
|
|
When the post_init validation runs
|
|
Then the stream_type should default to COLD
|
|
|
|
|
|
Scenario: RouteConfig to_graph_config with complex node types
|
|
Given the route system is available
|
|
Given I have a RouteConfig with various node types and configurations
|
|
When I convert it to GraphConfig
|
|
Then all node types should be properly converted
|
|
And all node properties should be preserved
|
|
And retry policies and timeouts should be included
|
|
And conditions and subgraphs should be handled
|
|
And node metadata should be preserved in conversion
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: routing_adapter_coverage.feature
|
|
# Feature: Routing adapter coverage
|
|
# ============================================================
|
|
|
|
Scenario: Map operator creates agent node chain
|
|
Given route operators include a map agent and function
|
|
When I build route nodes from that stream
|
|
Then it creates an agent node and chained edges to end
|
|
And the function node is created with its type
|
|
|
|
|
|
Scenario: Map without agent falls back to function node
|
|
Given route operators include a map without agent
|
|
When I build route nodes from that stream
|
|
Then it treats the map as function and terminates chain with end node
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: routing_langgraph_port.feature
|
|
# Feature: Actor-first port of v2 routing and LangGraph suites
|
|
# ============================================================
|
|
|
|
Scenario: Route bridge converts stream to graph configuration
|
|
Given the routing system is initialized for actor-first operation
|
|
Given I have a stream route configuration
|
|
"""
|
|
name: "test-stream"
|
|
type: "stream"
|
|
stream_type: "sequential"
|
|
batch_size: 10
|
|
"""
|
|
When I bridge the route to graph type
|
|
Then the bridged route should be a graph configuration
|
|
And the graph should have sequential execution
|
|
And the graph should preserve batch settings
|
|
|
|
|
|
Scenario: Unified route handles actor-only context variables
|
|
Given the routing system is initialized for actor-first operation
|
|
Given I have a unified route with context
|
|
"""
|
|
name: "actor-route"
|
|
type: "graph"
|
|
actor: "openai/gpt-4"
|
|
context_vars:
|
|
temperature: 0.7
|
|
max_tokens: 2000
|
|
"""
|
|
When I process the unified route
|
|
Then the route should use the specified actor
|
|
And the context should include temperature and max_tokens
|
|
And no provider/model fields should exist
|
|
|
|
|
|
Scenario: Stream router validates actor configuration
|
|
Given the routing system is initialized for actor-first operation
|
|
Given I have a stream router configuration
|
|
"""
|
|
routes:
|
|
- name: "primary"
|
|
actor: "anthropic/claude-3"
|
|
stream_type: "parallel"
|
|
- name: "fallback"
|
|
actor: "openai/gpt-3.5-turbo"
|
|
stream_type: "sequential"
|
|
"""
|
|
When I initialize the stream router
|
|
Then the router should have 2 routes
|
|
And each route should use its configured actor
|
|
And the router should support actor-based fallback
|
|
|
|
|
|
Scenario: LangGraph bridge handles state management
|
|
Given the routing system is initialized for actor-first operation
|
|
Given I have a LangGraph configuration with state
|
|
"""
|
|
name: "stateful-graph"
|
|
type: "graph"
|
|
actor: "local/custom-agent"
|
|
enable_checkpointing: true
|
|
state_schema:
|
|
current_task: "string"
|
|
completed_steps: "array"
|
|
"""
|
|
When I create a LangGraph bridge
|
|
Then the bridge should enable checkpointing
|
|
And the state schema should be preserved
|
|
And the bridge should use the custom actor
|
|
|
|
|
|
Scenario: Route complexity analysis with actor paths
|
|
Given the routing system is initialized for actor-first operation
|
|
Given I have a complex actor route configuration
|
|
"""
|
|
name: "complex-route"
|
|
type: "graph"
|
|
nodes:
|
|
- name: "analyze"
|
|
actor: "openai/gpt-4"
|
|
type: "llm"
|
|
- name: "generate"
|
|
actor: "anthropic/claude-3"
|
|
type: "llm"
|
|
- name: "validate"
|
|
actor: "local/validator"
|
|
type: "tool"
|
|
edges:
|
|
- from: "analyze"
|
|
to: "generate"
|
|
- from: "generate"
|
|
to: "validate"
|
|
"""
|
|
When I analyze the actor route complexity
|
|
Then the complexity score should reflect multi-actor coordination
|
|
And the analysis should identify 3 nodes and 2 edges
|
|
And each node should use its assigned actor
|
|
|
|
|
|
Scenario: Stream router handles parallel actor execution
|
|
Given the routing system is initialized for actor-first operation
|
|
Given I have parallel stream routes
|
|
"""
|
|
routes:
|
|
- name: "fast-route"
|
|
actor: "openai/gpt-3.5-turbo"
|
|
stream_type: "parallel"
|
|
priority: 1
|
|
- name: "accurate-route"
|
|
actor: "anthropic/claude-3-opus"
|
|
stream_type: "parallel"
|
|
priority: 2
|
|
execution_mode: "race"
|
|
"""
|
|
When I execute the parallel streams
|
|
Then both actors should be invoked concurrently
|
|
And the first completion should be used
|
|
And unused results should be properly cleaned up
|
|
|
|
|
|
Scenario: Conditional routing based on actor capabilities
|
|
Given the routing system is initialized for actor-first operation
|
|
Given I have conditional routing configuration
|
|
"""
|
|
name: "conditional-graph"
|
|
type: "graph"
|
|
nodes:
|
|
- name: "router"
|
|
type: "conditional"
|
|
conditions:
|
|
- if: "requires_vision"
|
|
then: "vision_node"
|
|
- if: "requires_code"
|
|
then: "code_node"
|
|
- else: "default_node"
|
|
- name: "vision_node"
|
|
actor: "openai/gpt-4-vision"
|
|
type: "llm"
|
|
- name: "code_node"
|
|
actor: "anthropic/claude-3-opus"
|
|
type: "llm"
|
|
- name: "default_node"
|
|
actor: "openai/gpt-3.5-turbo"
|
|
type: "llm"
|
|
"""
|
|
When I route with vision requirements
|
|
Then the vision actor should be selected
|
|
And the routing decision should be logged
|
|
|
|
|
|
Scenario: Route validation rejects invalid actors
|
|
Given the routing system is initialized for actor-first operation
|
|
Given I have a route with invalid actor
|
|
"""
|
|
name: "invalid-route"
|
|
type: "stream"
|
|
actor: "nonexistent/model"
|
|
"""
|
|
When I validate the route configuration
|
|
Then validation should fail with actor not found error
|
|
And the error should suggest available actors
|
|
|
|
|
|
Scenario: Bridge preserves actor-specific options
|
|
Given the routing system is initialized for actor-first operation
|
|
Given I have a route with actor options
|
|
"""
|
|
name: "options-route"
|
|
type: "stream"
|
|
actor: "openai/gpt-4"
|
|
actor_options:
|
|
temperature: 0.9
|
|
top_p: 0.95
|
|
frequency_penalty: 0.5
|
|
"""
|
|
When I bridge to a different route type
|
|
Then the actor options should be preserved
|
|
And the options should override defaults
|
|
And the bridged route should maintain actor identity
|
|
|
|
|
|
Scenario: Reactive streams with actor-based transformations
|
|
Given the routing system is initialized for actor-first operation
|
|
Given I have a reactive stream configuration
|
|
"""
|
|
name: "transform-stream"
|
|
type: "stream"
|
|
stream_type: "transform"
|
|
transformations:
|
|
- stage: "parse"
|
|
actor: "local/parser"
|
|
- stage: "enrich"
|
|
actor: "openai/gpt-3.5-turbo"
|
|
- stage: "format"
|
|
actor: "local/formatter"
|
|
"""
|
|
When I process data through the stream
|
|
Then each transformation should use its actor
|
|
And the data should flow through all stages
|
|
And errors should be handled per-actor
|
|
|
|
# ============================================================
|
|
# Originally from: stream_router_additional_coverage.feature
|
|
# Feature: Stream Router additional uncovered branches
|
|
# ============================================================
|
|
|
|
Scenario: Copying stream message with metadata override uses provided metadata
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
Given I have a base stream message with context metadata
|
|
When I copy the message with explicit metadata override
|
|
Then the copy should use the provided metadata
|
|
|
|
|
|
Scenario: Creating a hot stream initializes with initial value
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I create a hot stream named "hot_stream" with initial value 5
|
|
Then the hot stream should store the initial value
|
|
|
|
|
|
Scenario: Creating a replay stream uses buffer size
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I create a replay stream named "replay_stream" with buffer size 3
|
|
Then the replay stream should be created with buffer size 3
|
|
|
|
|
|
Scenario: LangGraph operator without bridge raises routing error
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I build a langgraph operator "graph_execute" without bridge
|
|
Then I should get a stream routing error about missing langgraph bridge
|
|
|
|
|
|
Scenario: Map operator with unknown agent raises routing error
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I build a map operator targeting unknown agent "ghost"
|
|
Then I should get a stream routing error about missing agent
|
|
|
|
|
|
Scenario: Debounce, throttle, and delay operators are created
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I build debounce, throttle, and delay operators
|
|
Then the temporal operators should be created successfully
|
|
|
|
|
|
Scenario: Buffer and window operators with count are created
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I build buffer and window operators with count 2
|
|
Then the buffer and window operators should be created successfully
|
|
|
|
|
|
Scenario: Take and skip operators are created
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I build take and skip operators
|
|
Then the take and skip operators should be created successfully
|
|
|
|
|
|
Scenario: Merge operator merges existing streams
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
Given I have two source streams named "merge_x" and "merge_y"
|
|
When I create a merge operator for those streams
|
|
Then the merge operator should be usable
|
|
|
|
|
|
Scenario: Accumulate operator with sum processes numbers
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I build an accumulate operator with sum
|
|
Then the accumulate operator should sum incoming numbers
|
|
|
|
|
|
Scenario: Transform extract_field returns field value
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I apply an extract_field transform to a dictionary message
|
|
Then the extracted field should be returned
|
|
|
|
|
|
Scenario: Evaluate condition equals and empty cases
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I evaluate empty and equals conditions against messages
|
|
Then the empty condition should be true and equals should match
|
|
|
|
|
|
Scenario: Agent mapper uses sync processor and handles errors
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I map messages through agent mapper success and failure paths
|
|
Then the mapper should emit processed content and blank on error
|
|
|
|
|
|
Scenario: Append accumulator with non-list leaves accumulator unchanged
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I apply append accumulator to non-list accumulator
|
|
Then the accumulator should remain unchanged
|
|
|
|
|
|
Scenario: Setup subscriptions ignores missing sources
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I setup subscriptions including a missing source
|
|
Then no subscription should be added for the missing source
|
|
|
|
|
|
Scenario: Split stream routes message when condition matches
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
Given I have a stream "source_split" and target "target_split"
|
|
When I split the source stream with a matching condition
|
|
Then the target stream should receive the routed message
|
|
|
|
|
|
Scenario: Subscribing to output and error streams delivers messages
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
When I subscribe to output and error streams and emit messages
|
|
Then the observers should receive the respective messages
|
|
|
|
|
|
Scenario: Dispose handles stream dispose errors gracefully
|
|
Given the CleverAgents reactive system is available
|
|
And I have a base stream message with context metadata
|
|
Given I have a stream router with disposable streams and subscriptions that may fail
|
|
When I dispose the stream router with failing disposables
|
|
Then the stream router internals should be cleared even on dispose errors
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: stream_router_agent_tool_coverage.feature
|
|
# Feature: Stream Router agent and tool coverage
|
|
# ============================================================
|
|
|
|
Scenario: SimpleToolAgent returns content when tool code is missing
|
|
Given a SimpleToolAgent with a tool missing code
|
|
When I process "payload" through the SimpleToolAgent
|
|
Then the SimpleToolAgent result should be "payload"
|
|
|
|
|
|
Scenario: SimpleToolAgent rejects tool with code block
|
|
Given a SimpleToolAgent with a code block tool
|
|
When I process "payload" through the SimpleToolAgent expecting rejection
|
|
Then the SimpleToolAgent should raise a routing error about code blocks
|
|
|
|
|
|
Scenario: SimpleToolAgent rejects code block via process_message_sync
|
|
Given a SimpleToolAgent with a code block tool
|
|
When I call process_message_sync with "payload" expecting rejection
|
|
Then the SimpleToolAgent should raise a routing error about code blocks
|
|
|
|
# TODO: Uncomment when step definitions are implemented
|
|
# Scenario: SimpleLLMAgent returns empty prompt for blank template
|
|
# Given a SimpleLLMAgent for prompt rendering
|
|
# When I render the template "<empty>" with context:
|
|
# | key | value |
|
|
# | name | Ada |
|
|
# Then the rendered prompt should be "<empty>"
|
|
#
|
|
# TODO: Uncomment when step definitions are implemented
|
|
# Scenario: SimpleLLMAgent returns raw template when environment is missing
|
|
# Given a SimpleLLMAgent for prompt rendering
|
|
# And the SimpleLLMAgent template environment is "none"
|
|
# When I render the template "Hello" with context:
|
|
# | key | value |
|
|
# | name | Ada |
|
|
# Then the rendered prompt should be "Hello"
|
|
#
|
|
# TODO: Uncomment when step definitions are implemented
|
|
# Scenario: SimpleLLMAgent renders template when environment succeeds
|
|
# Given a SimpleLLMAgent for prompt rendering
|
|
# And the SimpleLLMAgent template environment is "working"
|
|
# When I render the template "Hello {{ name }}" with context:
|
|
# | key | value |
|
|
# | name | Ada |
|
|
# Then the rendered prompt should be "Hello Ada"
|
|
#
|
|
# TODO: Uncomment when step definitions are implemented
|
|
# Scenario: SimpleLLMAgent falls back to template on render failure
|
|
# Given a SimpleLLMAgent for prompt rendering
|
|
# And the SimpleLLMAgent template environment is "failing"
|
|
# When I render the template "Hello {{ name }}" with context:
|
|
# | key | value |
|
|
# | name | Ada |
|
|
# Then the rendered prompt should be "Hello {{ name }}"
|
|
|
|
|
|
Scenario: SimpleLLMAgent resolves LLM with configured kwargs and caching
|
|
Given a SimpleLLMAgent with provider config
|
|
And a stub provider registry is installed
|
|
When I resolve the LLM twice
|
|
Then the registry should be called once with the configured kwargs
|
|
And the resolved LLM should be cached
|
|
|
|
|
|
Scenario: SimpleLLMAgent raises when LangChain messages are unavailable
|
|
Given a SimpleLLMAgent with provider config
|
|
And a stub provider registry is installed
|
|
And LangChain message classes are unavailable
|
|
When I process "ping" through the LLM agent
|
|
Then I should receive a stream routing error about missing messages
|
|
|
|
|
|
Scenario: SimpleLLMAgent process_message_sync returns LLM content
|
|
Given a SimpleLLMAgent with system prompt and provider config
|
|
And a stub provider registry is installed
|
|
And LangChain message stubs are installed
|
|
When I process 123 through the LLM agent via process_message_sync
|
|
Then the LLM should receive system and human messages
|
|
And the LLM agent result should be "llm-output"
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: stream_router_coverage.feature
|
|
# Feature: Stream Router Coverage
|
|
# ============================================================
|
|
|
|
# ============================================================
|
|
# Originally from: stream_router_misc_uncovered.feature
|
|
# Feature: Stream Router remaining uncovered lines
|
|
# ============================================================
|
|
|
|
Scenario: Converting dict route config uses defaults
|
|
Given the CleverAgents reactive system is available
|
|
When I convert a dict route config with only a name
|
|
Then the stream config should use default values
|
|
|
|
|
|
Scenario: Converting object route config uses defaults for missing attrs
|
|
Given the CleverAgents reactive system is available
|
|
When I convert a minimal object route config
|
|
Then the stream config should use default values
|
|
|
|
|
|
Scenario: Transform operator with type parameter applies replace
|
|
Given the CleverAgents reactive system is available
|
|
When I apply a transform operator with type replace
|
|
Then the transform operator should update the field
|
|
|
|
|
|
Scenario: Switch operator emits original message with no matches or defaults
|
|
Given the CleverAgents reactive system is available
|
|
When I run a switch operator with no matches and no defaults
|
|
Then the switch operator should emit the original message
|
|
|
|
|
|
Scenario: Unsupported condition returns false
|
|
Given the CleverAgents reactive system is available
|
|
When I evaluate an unsupported condition
|
|
Then the unsupported condition result should be false
|
|
|
|
|
|
Scenario: Agent mapper falls back when no processing methods exist
|
|
Given the CleverAgents reactive system is available
|
|
When I map a message with an agent lacking processors
|
|
Then the mapper should return the original content with metadata
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: stream_router_new_branches.feature
|
|
# Feature: Stream Router additional uncovered branches
|
|
# ============================================================
|
|
|
|
Scenario: LangGraph operator uses bridge factory when available
|
|
Given the CleverAgents reactive system is available
|
|
Given a LangGraph bridge stub is registered on the stream router
|
|
When I build a graph_execute operator through the bridge
|
|
Then the bridge factory should be invoked and map data
|
|
|
|
|
|
Scenario: Map operator processes message through registered agent
|
|
Given the CleverAgents reactive system is available
|
|
Given a dummy agent is registered on the stream router
|
|
When I map a message using that agent
|
|
Then the agent-mapped content should be returned with metadata
|
|
|
|
|
|
Scenario: Filter operator passes matching condition
|
|
Given the CleverAgents reactive system is available
|
|
When I build a filter operator with equals condition
|
|
Then the filter should emit only matching values
|
|
|
|
|
|
Scenario: Transform operator rejects unregistered fn string
|
|
Given the CleverAgents reactive system is available
|
|
When I build a transform operator with an unregistered function string
|
|
Then I should get a stream routing error about unregistered transform
|
|
|
|
|
|
Scenario: Switch operator handles case operators pipeline
|
|
Given the CleverAgents reactive system is available
|
|
When I run a switch operator with a case operator pipeline
|
|
Then the switch case should apply its operators
|
|
|
|
|
|
Scenario: Switch operator routes to a target stream case
|
|
Given the CleverAgents reactive system is available
|
|
Given I have an existing target stream named "case_target"
|
|
When I run a switch operator that routes to that case target
|
|
Then the case target stream should receive the message
|
|
|
|
|
|
Scenario: Unknown operator raises routing error
|
|
Given the CleverAgents reactive system is available
|
|
When I build an operator with an unknown type
|
|
Then I should get a stream routing error about unknown operator
|
|
|
|
|
|
Scenario: Extract field transform returns requested value and fallback
|
|
Given the CleverAgents reactive system is available
|
|
When I apply an extract_field transform to different message types
|
|
Then the transform should return the field value or original message
|
|
|
|
|
|
Scenario: Evaluate condition supports equals and field branches
|
|
Given the CleverAgents reactive system is available
|
|
When I evaluate equals and field conditions
|
|
Then both condition checks should return true
|
|
|
|
|
|
Scenario: Append accumulator returns unchanged when accumulator is not a list
|
|
Given the CleverAgents reactive system is available
|
|
When I apply the append accumulator to a non-list accumulator
|
|
Then the accumulator should be returned unchanged
|
|
|
|
|
|
Scenario: Setup subscriptions skips missing sources and subscribes existing
|
|
Given the CleverAgents reactive system is available
|
|
Given I prepare stream config with existing and missing subscriptions
|
|
When I setup subscriptions for that config
|
|
Then only existing sources should be subscribed
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: stream_router_remaining_coverage.feature
|
|
# Feature: Reactive Stream Router Edge Cases
|
|
# ============================================================
|
|
|
|
Scenario: Registering an operation with non-alphanumeric characters is rejected
|
|
Given a fresh SimpleToolAgent registry
|
|
When I register an operation named "bad-name!" with a lambda
|
|
Then a ValueError should be raised about operation name format
|
|
|
|
|
|
Scenario: A tool agent with a malformed tool entry passes content through unchanged
|
|
Given a SimpleToolAgent whose tools list contains a non-dict entry
|
|
When I process content "hello" through that SimpleToolAgent
|
|
Then the SimpleToolAgent should return "hello" unchanged
|
|
|
|
|
|
Scenario: A tool agent with an unrecognised operation name passes content through unchanged
|
|
Given a SimpleToolAgent with an unknown operation "nonexistent_op"
|
|
When I process content "data" through that SimpleToolAgent
|
|
Then the SimpleToolAgent should return "data" unchanged
|
|
|
|
|
|
Scenario: A tool agent safely returns empty output when an operation throws
|
|
Given a SimpleToolAgent with an operation that raises an exception
|
|
When I process content "boom" through that SimpleToolAgent
|
|
Then the SimpleToolAgent should return an empty string
|
|
|
|
# Transform registration safety
|
|
|
|
|
|
Scenario: Registering a transform with non-alphanumeric characters is rejected
|
|
Given a fresh ReactiveStreamRouter registry
|
|
When I register a transform named "bad@name" with a lambda
|
|
Then a ValueError should be raised about transform name format
|
|
|
|
# Operator construction validation
|
|
|
|
|
|
Scenario: A transform operator created without a function or type is rejected
|
|
Given a reactive stream router for coverage
|
|
And a stream named "transform_test" exists on the router
|
|
When I create a transform operator with neither fn nor type
|
|
Then a StreamRoutingError should be raised about missing transform params
|
|
|
|
# LLM agent provider resolution
|
|
|
|
|
|
Scenario: An LLM agent resolves its provider without forwarding absent optional parameters
|
|
Given a SimpleLLMAgent with no temperature or max_tokens or max_retries
|
|
And a stub provider registry is active
|
|
When I resolve the LLM on that agent
|
|
Then the LLM should be resolved without optional kwargs
|
|
|
|
|
|
Scenario: An LLM agent with no system prompt sends only the user message to the model
|
|
Given a SimpleLLMAgent with an empty system prompt
|
|
And a stub provider registry is active
|
|
When I process content "test input" through that SimpleLLMAgent
|
|
Then the LLM should receive only a HumanMessage
|
|
|
|
# LangGraph bridge delegation
|
|
|
|
|
|
Scenario: A graph operator delegates to the bridge factory when the method exists
|
|
Given a reactive stream router for coverage
|
|
And a LangGraph bridge stub with a graph_execute factory is registered
|
|
When I create a graph_execute operator via the bridge
|
|
Then the bridge factory method should produce a valid operator
|
|
|
|
|
|
Scenario: A graph operator raises a clear error when the bridge lacks the factory method
|
|
Given a reactive stream router for coverage
|
|
And a LangGraph bridge stub without the requested factory method is registered
|
|
When I attempt to create a graph_execute operator via the bridge
|
|
Then a StreamRoutingError should be raised about LangGraph bridge
|
|
|
|
# Switch operator resilience
|
|
|
|
|
|
Scenario: A switch case pipeline tolerates an operator that resolves to nothing
|
|
Given a reactive stream router for coverage
|
|
And a stream named "switch_null_op" exists on the router
|
|
When I run a switch operator whose case pipeline includes a None operator
|
|
Then the switch should still emit the message through the pipeline
|
|
|
|
|
|
Scenario: A switch case with a nonexistent target stream falls through to the default
|
|
Given a reactive stream router for coverage
|
|
And a stream named "switch_missing_target" exists on the router
|
|
When I run a switch with a matching case whose target stream does not exist
|
|
Then the switch should fall through to the default
|
|
|
|
|
|
Scenario: A switch default pipeline tolerates an operator that resolves to nothing
|
|
Given a reactive stream router for coverage
|
|
And a stream named "switch_default_null" exists on the router
|
|
When I run a switch operator whose default pipeline includes a None operator
|
|
Then the switch should still emit the message through the default pipeline
|
|
|
|
# Message transformation edge cases
|
|
|
|
|
|
Scenario: A replace transform on a non-dictionary message passes it through unchanged
|
|
Given a reactive stream router for coverage
|
|
When I apply a replace transform to a non-dict message
|
|
Then the message should be returned unchanged
|
|
|
|
|
|
Scenario: An extract-field transform retrieves the named value from a dictionary message
|
|
Given a reactive stream router for coverage
|
|
When I apply an extract_field transform to a dict message with the field
|
|
Then the extracted field value should be returned
|
|
|
|
|
|
Scenario: An extract-field transform on a non-dictionary message passes it through
|
|
Given a reactive stream router for coverage
|
|
When I apply an extract_field transform to a non-dict message
|
|
Then the non-dict message should be returned from extract_field
|
|
|
|
|
|
Scenario: An unrecognised transform type passes the message through unchanged
|
|
Given a reactive stream router for coverage
|
|
When I apply a transform with unknown type to a message
|
|
Then the original message should be returned
|
|
|
|
# Condition evaluation edge cases
|
|
|
|
|
|
Scenario: A field condition matches when the dictionary message contains the field
|
|
Given a reactive stream router for coverage
|
|
When I evaluate a field condition against a dict message containing the field
|
|
Then the condition should return true
|
|
|
|
|
|
Scenario: A field condition does not match when the dictionary message lacks the field
|
|
Given a reactive stream router for coverage
|
|
When I evaluate a field condition against a dict message missing the field
|
|
Then the condition should return false
|
|
|
|
|
|
Scenario: A field condition does not match against a non-dictionary message
|
|
Given a reactive stream router for coverage
|
|
When I evaluate a field condition against a non-dict message
|
|
Then the condition should return false
|
|
|
|
# Stream splitting and merging edge cases
|
|
|
|
|
|
Scenario: Splitting a stream ignores condition targets that do not exist
|
|
Given a reactive stream router for coverage
|
|
And a stream named "split_source" exists on the router
|
|
When I split the stream with a condition targeting a nonexistent stream
|
|
Then no error should occur and the message should not route
|
|
|
|
|
|
Scenario: Merging streams into a nonexistent target auto-creates it
|
|
Given a reactive stream router for coverage
|
|
And a stream named "merge_src_a" exists on the router
|
|
And a stream named "merge_src_b" exists on the router
|
|
When I merge streams into a target that does not exist yet
|
|
Then the target stream should be created and receive merged messages
|
|
|
|
|
|
Scenario: Merging streams silently skips source streams that do not exist
|
|
Given a reactive stream router for coverage
|
|
And a stream named "merge_src_c" exists on the router
|
|
When I merge streams including a nonexistent source into an existing target
|
|
Then only the existing source should be subscribed
|
|
|
|
# Router disposal
|
|
|
|
|
|
Scenario: Disposing the router tolerates streams that lack a dispose method
|
|
Given a reactive stream router for coverage
|
|
And a stream that lacks a dispose method is injected into the router
|
|
When I dispose the router
|
|
Then the router should be fully cleared without errors
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: stream_router_uncovered_paths.feature
|
|
# Feature: Stream Router uncovered branches
|
|
# ============================================================
|
|
|
|
Scenario: Copying stream message without metadata override deep copies entries
|
|
Given the CleverAgents reactive system is available
|
|
Given I have a base stream message with context metadata
|
|
When I copy the message without metadata override
|
|
Then the metadata should be deep-copied except context
|
|
|
|
|
|
Scenario: Map operator without params should raise routing error
|
|
Given the CleverAgents reactive system is available
|
|
When I build a map operator without required params
|
|
Then I should get a stream routing error about missing map params
|
|
|
|
|
|
Scenario: Buffer operator without count should raise routing error
|
|
Given the CleverAgents reactive system is available
|
|
When I build a buffer operator without count
|
|
Then I should get a stream routing error about buffer count
|
|
|
|
|
|
Scenario: Window operator without count should raise routing error
|
|
Given the CleverAgents reactive system is available
|
|
When I build a window operator without count
|
|
Then I should get a stream routing error about window count
|
|
|
|
|
|
Scenario: Condition check for missing field returns false
|
|
Given the CleverAgents reactive system is available
|
|
When I evaluate a field condition against a message without that field
|
|
Then the condition result should be false
|
|
|
|
|
|
Scenario: Accumulator append initializes list and appends items
|
|
Given the CleverAgents reactive system is available
|
|
When I apply the append accumulator with list initialization
|
|
Then the accumulator should return a list with the item
|
|
|
|
|
|
Scenario: Sending message to unknown stream raises routing error
|
|
Given the CleverAgents reactive system is available
|
|
When I send a message to an unknown stream
|
|
Then I should get a stream routing error about missing stream
|
|
|
|
|
|
Scenario: Splitting from unknown source raises routing error
|
|
Given the CleverAgents reactive system is available
|
|
When I split a stream from an unknown source
|
|
Then I should get a stream routing error about missing source
|
|
|
|
|
|
Scenario: Creating stream using string name initializes config
|
|
Given the CleverAgents reactive system is available
|
|
When I create a stream from the string name "string_stream"
|
|
Then the stream should exist in the router
|
|
|
|
|
|
Scenario: Duplicate stream creation raises routing error
|
|
Given the CleverAgents reactive system is available
|
|
Given I have already created a stream named "dup_stream"
|
|
When I try to create the stream "dup_stream" again
|
|
Then I should get a stream routing error about duplicate stream
|
|
|
|
|
|
Scenario: Map operator with function param uses fallback
|
|
Given the CleverAgents reactive system is available
|
|
When I map a message using function param "noop_function"
|
|
Then the mapped result should equal the original message
|
|
|
|
|
|
Scenario: Transform operator with unregistered fn raises error
|
|
Given the CleverAgents reactive system is available
|
|
When I build a transform operator with an invalid function string
|
|
Then I should get a stream routing error about unregistered transform fn
|
|
|
|
|
|
Scenario: Filter operator without condition raises routing error
|
|
Given the CleverAgents reactive system is available
|
|
When I build a filter operator without condition
|
|
Then I should get a stream routing error about missing filter condition
|
|
|
|
|
|
Scenario: Switch operator applies default operators pipeline
|
|
Given the CleverAgents reactive system is available
|
|
When I run a switch operator with default operators against a message
|
|
Then the switch operator should output the transformed value
|
|
|
|
|
|
Scenario: Switch operator routes to default stream subject
|
|
Given the CleverAgents reactive system is available
|
|
When I run a switch operator that targets a default stream subject
|
|
Then the default stream should emit its initial value
|
|
|
|
|
|
Scenario: Apply replace transform updates dictionary field
|
|
Given the CleverAgents reactive system is available
|
|
When I apply a replace transform to a dictionary message
|
|
Then the dictionary field should be updated
|
|
|
|
|
|
Scenario: Sum accumulator initializes and sums numeric messages
|
|
Given the CleverAgents reactive system is available
|
|
When I apply the sum accumulator to numeric messages
|
|
Then the accumulator should produce the summed value
|
|
|
|
|
|
Scenario: Merge streams creates target when missing
|
|
Given the CleverAgents reactive system is available
|
|
Given I have two source streams named "merge_a" and "merge_b"
|
|
When I merge the streams into a new target "merge_output"
|
|
Then the merged target stream should exist
|
|
|
|
|
|
Scenario: Dispose cleans up subscriptions and stream disposables
|
|
Given the CleverAgents reactive system is available
|
|
Given I have a stream router with disposable streams and subscriptions
|
|
When I dispose the stream router
|
|
Then the stream router internals should be cleared
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: stream_router_unsafe_and_llm_coverage.feature
|
|
# Feature: SimpleToolAgent code block rejection and SimpleLLMAgent render_prompt coverage
|
|
# ============================================================
|
|
|
|
Scenario: SimpleToolAgent rejects code block even with unsafe flag
|
|
Given a stream router unsafe tool agent with a valid code block
|
|
When the stream router unsafe agent processes input expecting rejection
|
|
Then the stream router unsafe agent should raise StreamRoutingError about code
|
|
|
|
|
|
Scenario: SimpleToolAgent rejects failing code block even with unsafe flag
|
|
Given a stream router unsafe tool agent with a failing code block
|
|
When the stream router unsafe agent processes input expecting rejection
|
|
Then the stream router unsafe agent should raise StreamRoutingError about code
|
|
|
|
|
|
Scenario: SimpleToolAgent safe mode rejects code blocks
|
|
Given a stream router safe tool agent with a code block
|
|
When the stream router safe agent processes input expecting rejection
|
|
Then the stream router safe agent should raise StreamRoutingError about code
|
|
|
|
|
|
Scenario: SimpleLLMAgent render_prompt with no template env returns template as-is
|
|
Given a stream router llm agent with no template env
|
|
When the stream router llm agent renders "Hello {{ name }}" with context name "World"
|
|
Then the stream router llm render result should be "Hello {{ name }}"
|
|
|
|
|
|
Scenario: SimpleLLMAgent render_prompt with template env failure returns template
|
|
Given a stream router llm agent with a failing template env
|
|
When the stream router llm agent renders "Hello {{ name }}" with context name "World"
|
|
Then the stream router llm render result should be "Hello {{ name }}"
|
|
|
|
|
|
Scenario: SimpleLLMAgent render_prompt with empty template returns empty string
|
|
Given a stream router llm agent for empty template test
|
|
When the stream router llm agent renders empty template
|
|
Then the stream router llm render result should be empty string
|
|
|
|
|
|
Scenario: SimpleToolAgent processes named operation successfully
|
|
Given a stream router tool agent with uppercase operation
|
|
When the stream router tool agent processes "hello" through operation
|
|
Then the stream router tool agent operation result should be "HELLO"
|
|
|
|
|
|
Scenario: SimpleToolAgent processes unknown operation falls back to identity
|
|
Given a stream router tool agent with unknown operation "nonexistent_xyz"
|
|
When the stream router tool agent processes "keep_me" through operation
|
|
Then the stream router tool agent operation result should be "keep_me"
|
|
|
|
# M5: SimpleLLMAgent._resolve_llm forwards options to registry.create_llm (#11223).
|
|
@tdd_issue @tdd_issue_11223
|
|
Scenario: SimpleLLMAgent forwards options block to LLM constructor
|
|
Given a stream router llm agent with options block containing custom base url
|
|
When the stream router llm agent resolves the llm
|
|
Then the llm constructor should have received the custom base url
|
|
|
|
# M5: actor without options block is unaffected (#11223).
|
|
@tdd_issue @tdd_issue_11223
|
|
Scenario: SimpleLLMAgent without options block calls LLM constructor without extra kwargs
|
|
Given a stream router llm agent without options block
|
|
When the stream router llm agent resolves the llm
|
|
Then the llm constructor should not have received extra kwargs
|
|
|
|
# M5: top-level keys take precedence over duplicates in options (#11223).
|
|
@tdd_issue @tdd_issue_11223
|
|
Scenario: SimpleLLMAgent top-level key takes precedence over options duplicate
|
|
Given a stream router llm agent with top-level temperature and options block
|
|
When the stream router llm agent resolves the llm
|
|
Then the llm constructor should have received the top-level temperature
|
|
|