mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-06-27 23:49:38 +02:00
Feature/tool group (#484)
* Tech spec for tool group * Partial tool group implementation * Tool group tests
This commit is contained in:
parent
672e358b2f
commit
e74eb5d1ff
9 changed files with 1304 additions and 6 deletions
|
|
@ -82,8 +82,8 @@ def sample_message_data():
|
|||
},
|
||||
"AgentRequest": {
|
||||
"question": "What is machine learning?",
|
||||
"plan": "",
|
||||
"state": "",
|
||||
"group": [],
|
||||
"history": []
|
||||
},
|
||||
"AgentResponse": {
|
||||
|
|
|
|||
|
|
@ -198,8 +198,8 @@ class TestAgentMessageContracts:
|
|||
# Test required fields
|
||||
request = AgentRequest(**request_data)
|
||||
assert hasattr(request, 'question')
|
||||
assert hasattr(request, 'plan')
|
||||
assert hasattr(request, 'state')
|
||||
assert hasattr(request, 'group')
|
||||
assert hasattr(request, 'history')
|
||||
|
||||
def test_agent_response_schema_contract(self, sample_message_data):
|
||||
|
|
|
|||
267
tests/integration/test_tool_group_integration.py
Normal file
267
tests/integration/test_tool_group_integration.py
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
"""
|
||||
Integration tests for the tool group system.
|
||||
|
||||
Tests the complete workflow of tool filtering and execution logic.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
from unittest.mock import Mock, AsyncMock, patch
|
||||
|
||||
# Add trustgraph paths for imports
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'trustgraph-base'))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'trustgraph-flow'))
|
||||
|
||||
from trustgraph.agent.tool_filter import filter_tools_by_group_and_state, get_next_state, validate_tool_config
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_tools():
|
||||
"""Sample tools with different groups and states for testing."""
|
||||
return {
|
||||
'knowledge_query': Mock(config={
|
||||
'group': ['read-only', 'knowledge', 'basic'],
|
||||
'state': 'analysis',
|
||||
'applicable-states': ['undefined', 'research']
|
||||
}),
|
||||
'graph_update': Mock(config={
|
||||
'group': ['write', 'knowledge', 'admin'],
|
||||
'applicable-states': ['analysis', 'modification']
|
||||
}),
|
||||
'text_completion': Mock(config={
|
||||
'group': ['read-only', 'text', 'basic'],
|
||||
'state': 'undefined'
|
||||
# No applicable-states = available in all states
|
||||
}),
|
||||
'complex_analysis': Mock(config={
|
||||
'group': ['advanced', 'compute', 'expensive'],
|
||||
'state': 'results',
|
||||
'applicable-states': ['analysis']
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
class TestToolGroupFiltering:
|
||||
"""Test tool group filtering integration scenarios."""
|
||||
|
||||
def test_basic_group_filtering(self, sample_tools):
|
||||
"""Test that filtering only returns tools matching requested groups."""
|
||||
|
||||
# Filter for read-only and knowledge tools
|
||||
filtered = filter_tools_by_group_and_state(
|
||||
sample_tools,
|
||||
['read-only', 'knowledge'],
|
||||
'undefined'
|
||||
)
|
||||
|
||||
# Should include tools with matching groups and correct state
|
||||
assert 'knowledge_query' in filtered # Has read-only + knowledge, available in undefined
|
||||
assert 'text_completion' in filtered # Has read-only, available in all states
|
||||
assert 'graph_update' not in filtered # Has knowledge but no read-only
|
||||
assert 'complex_analysis' not in filtered # Wrong groups and state
|
||||
|
||||
def test_state_based_filtering(self, sample_tools):
|
||||
"""Test filtering based on current state."""
|
||||
|
||||
# Filter for analysis state with advanced tools
|
||||
filtered = filter_tools_by_group_and_state(
|
||||
sample_tools,
|
||||
['advanced', 'compute'],
|
||||
'analysis'
|
||||
)
|
||||
|
||||
# Should only include tools available in analysis state
|
||||
assert 'complex_analysis' in filtered # Available in analysis state
|
||||
assert 'knowledge_query' not in filtered # Not available in analysis state
|
||||
assert 'graph_update' not in filtered # Wrong group (no advanced/compute)
|
||||
assert 'text_completion' not in filtered # Wrong group
|
||||
|
||||
def test_state_transition_handling(self, sample_tools):
|
||||
"""Test state transitions after tool execution."""
|
||||
|
||||
# Get knowledge_query tool and test state transition
|
||||
knowledge_tool = sample_tools['knowledge_query']
|
||||
|
||||
# Test state transition
|
||||
next_state = get_next_state(knowledge_tool, 'undefined')
|
||||
assert next_state == 'analysis' # knowledge_query should transition to analysis
|
||||
|
||||
# Test tool with no state transition
|
||||
text_tool = sample_tools['text_completion']
|
||||
next_state = get_next_state(text_tool, 'research')
|
||||
assert next_state == 'undefined' # text_completion transitions to undefined
|
||||
|
||||
def test_wildcard_group_access(self, sample_tools):
|
||||
"""Test wildcard group grants access to all tools."""
|
||||
|
||||
# Filter with wildcard group access
|
||||
filtered = filter_tools_by_group_and_state(
|
||||
sample_tools,
|
||||
['*'], # Wildcard access
|
||||
'undefined'
|
||||
)
|
||||
|
||||
# Should include all tools that are available in undefined state
|
||||
assert 'knowledge_query' in filtered # Available in undefined
|
||||
assert 'text_completion' in filtered # Available in all states
|
||||
assert 'graph_update' not in filtered # Not available in undefined
|
||||
assert 'complex_analysis' not in filtered # Not available in undefined
|
||||
|
||||
def test_no_matching_tools(self, sample_tools):
|
||||
"""Test behavior when no tools match the requested groups."""
|
||||
|
||||
# Filter with non-matching group
|
||||
filtered = filter_tools_by_group_and_state(
|
||||
sample_tools,
|
||||
['nonexistent-group'],
|
||||
'undefined'
|
||||
)
|
||||
|
||||
# Should return empty dictionary
|
||||
assert len(filtered) == 0
|
||||
|
||||
def test_default_group_behavior(self):
|
||||
"""Test default group behavior when no group is specified."""
|
||||
|
||||
# Create tools with and without explicit groups
|
||||
tools = {
|
||||
'default_tool': Mock(config={}), # No group = default group
|
||||
'admin_tool': Mock(config={'group': ['admin']})
|
||||
}
|
||||
|
||||
# Filter with no group specified (should default to ["default"])
|
||||
filtered = filter_tools_by_group_and_state(tools, None, 'undefined')
|
||||
|
||||
# Only default_tool should be available
|
||||
assert 'default_tool' in filtered
|
||||
assert 'admin_tool' not in filtered
|
||||
|
||||
|
||||
class TestToolConfigurationValidation:
|
||||
"""Test tool configuration validation with group metadata."""
|
||||
|
||||
def test_tool_config_validation_invalid(self):
|
||||
"""Test that invalid tool configurations are rejected."""
|
||||
|
||||
# Test invalid group field (should be list)
|
||||
invalid_config = {
|
||||
"name": "invalid_tool",
|
||||
"description": "Invalid tool",
|
||||
"type": "text-completion",
|
||||
"group": "not-a-list" # Should be list
|
||||
}
|
||||
|
||||
# Should raise validation error
|
||||
with pytest.raises(ValueError, match="'group' field must be a list"):
|
||||
validate_tool_config(invalid_config)
|
||||
|
||||
def test_tool_config_validation_valid(self):
|
||||
"""Test that valid tool configurations are accepted."""
|
||||
|
||||
valid_config = {
|
||||
"name": "valid_tool",
|
||||
"description": "Valid tool",
|
||||
"type": "text-completion",
|
||||
"group": ["read-only", "text"],
|
||||
"state": "analysis",
|
||||
"applicable-states": ["undefined", "research"]
|
||||
}
|
||||
|
||||
# Should not raise any exception
|
||||
validate_tool_config(valid_config)
|
||||
|
||||
def test_kebab_case_field_names(self):
|
||||
"""Test that kebab-case field names are properly handled."""
|
||||
|
||||
config = {
|
||||
"name": "test_tool",
|
||||
"group": ["basic"],
|
||||
"applicable-states": ["undefined", "analysis"] # kebab-case
|
||||
}
|
||||
|
||||
# Should validate without error
|
||||
validate_tool_config(config)
|
||||
|
||||
# Create mock tool and test filtering
|
||||
tool = Mock(config=config)
|
||||
|
||||
# Test that kebab-case field is properly read
|
||||
filtered = filter_tools_by_group_and_state(
|
||||
{'test_tool': tool},
|
||||
['basic'],
|
||||
'analysis'
|
||||
)
|
||||
|
||||
assert 'test_tool' in filtered
|
||||
|
||||
|
||||
class TestCompleteWorkflow:
|
||||
"""Test complete multi-step workflows with state transitions."""
|
||||
|
||||
def test_research_analysis_workflow(self, sample_tools):
|
||||
"""Test complete research -> analysis -> results workflow."""
|
||||
|
||||
# Step 1: Initial research phase (undefined state)
|
||||
step1_filtered = filter_tools_by_group_and_state(
|
||||
sample_tools,
|
||||
['read-only', 'knowledge'],
|
||||
'undefined'
|
||||
)
|
||||
|
||||
# Should have access to knowledge_query and text_completion
|
||||
assert 'knowledge_query' in step1_filtered
|
||||
assert 'text_completion' in step1_filtered
|
||||
assert 'complex_analysis' not in step1_filtered # Not available in undefined
|
||||
|
||||
# Simulate executing knowledge_query tool
|
||||
knowledge_tool = step1_filtered['knowledge_query']
|
||||
next_state = get_next_state(knowledge_tool, 'undefined')
|
||||
assert next_state == 'analysis' # Transition to analysis state
|
||||
|
||||
# Step 2: Analysis phase
|
||||
step2_filtered = filter_tools_by_group_and_state(
|
||||
sample_tools,
|
||||
['advanced', 'compute', 'text'], # Include text for text_completion
|
||||
'analysis'
|
||||
)
|
||||
|
||||
# Should have access to complex_analysis and text_completion
|
||||
assert 'complex_analysis' in step2_filtered
|
||||
assert 'text_completion' in step2_filtered # Available in all states
|
||||
assert 'knowledge_query' not in step2_filtered # Not available in analysis
|
||||
|
||||
# Simulate executing complex_analysis tool
|
||||
analysis_tool = step2_filtered['complex_analysis']
|
||||
final_state = get_next_state(analysis_tool, 'analysis')
|
||||
assert final_state == 'results' # Transition to results state
|
||||
|
||||
def test_multi_tenant_scenario(self, sample_tools):
|
||||
"""Test different users with different permissions."""
|
||||
|
||||
# User A: Read-only permissions in undefined state
|
||||
user_a_tools = filter_tools_by_group_and_state(
|
||||
sample_tools,
|
||||
['read-only'],
|
||||
'undefined'
|
||||
)
|
||||
|
||||
# Should only have access to read-only tools in undefined state
|
||||
assert 'knowledge_query' in user_a_tools # read-only + available in undefined
|
||||
assert 'text_completion' in user_a_tools # read-only + available in all states
|
||||
assert 'graph_update' not in user_a_tools # write permissions required
|
||||
assert 'complex_analysis' not in user_a_tools # advanced permissions required
|
||||
|
||||
# User B: Admin permissions in analysis state
|
||||
user_b_tools = filter_tools_by_group_and_state(
|
||||
sample_tools,
|
||||
['write', 'admin'],
|
||||
'analysis'
|
||||
)
|
||||
|
||||
# Should have access to admin tools available in analysis state
|
||||
assert 'graph_update' in user_b_tools # admin + available in analysis
|
||||
assert 'complex_analysis' not in user_b_tools # wrong group (needs advanced/compute)
|
||||
assert 'knowledge_query' not in user_b_tools # not available in analysis state
|
||||
assert 'text_completion' not in user_b_tools # wrong group (no admin)
|
||||
321
tests/unit/test_agent/test_tool_filter.py
Normal file
321
tests/unit/test_agent/test_tool_filter.py
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
"""
|
||||
Unit tests for the tool filtering logic in the tool group system.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
from unittest.mock import Mock
|
||||
|
||||
# Add trustgraph-flow to path for imports
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', 'trustgraph-flow'))
|
||||
|
||||
from trustgraph.agent.tool_filter import (
|
||||
filter_tools_by_group_and_state,
|
||||
get_next_state,
|
||||
validate_tool_config,
|
||||
_is_tool_available
|
||||
)
|
||||
|
||||
|
||||
class TestToolFiltering:
|
||||
"""Test tool filtering based on groups and states."""
|
||||
|
||||
def test_filter_tools_default_group(self):
|
||||
"""Tools without groups should belong to 'default' group."""
|
||||
tools = {
|
||||
'tool1': Mock(config={}),
|
||||
'tool2': Mock(config={'group': ['read-only']})
|
||||
}
|
||||
|
||||
# Request default group (implicit)
|
||||
filtered = filter_tools_by_group_and_state(tools, None, None)
|
||||
|
||||
# Only tool1 should be available (no group = default group)
|
||||
assert 'tool1' in filtered
|
||||
assert 'tool2' not in filtered
|
||||
|
||||
def test_filter_tools_explicit_groups(self):
|
||||
"""Test filtering with explicit group membership."""
|
||||
tools = {
|
||||
'read_tool': Mock(config={'group': ['read-only', 'basic']}),
|
||||
'write_tool': Mock(config={'group': ['write', 'admin']}),
|
||||
'mixed_tool': Mock(config={'group': ['read-only', 'write']})
|
||||
}
|
||||
|
||||
# Request read-only tools
|
||||
filtered = filter_tools_by_group_and_state(tools, ['read-only'], None)
|
||||
|
||||
assert 'read_tool' in filtered
|
||||
assert 'write_tool' not in filtered
|
||||
assert 'mixed_tool' in filtered # Has read-only in its groups
|
||||
|
||||
def test_filter_tools_multiple_requested_groups(self):
|
||||
"""Test filtering with multiple requested groups."""
|
||||
tools = {
|
||||
'tool1': Mock(config={'group': ['read-only']}),
|
||||
'tool2': Mock(config={'group': ['write']}),
|
||||
'tool3': Mock(config={'group': ['admin']})
|
||||
}
|
||||
|
||||
# Request read-only and write tools
|
||||
filtered = filter_tools_by_group_and_state(tools, ['read-only', 'write'], None)
|
||||
|
||||
assert 'tool1' in filtered
|
||||
assert 'tool2' in filtered
|
||||
assert 'tool3' not in filtered
|
||||
|
||||
def test_filter_tools_wildcard_group(self):
|
||||
"""Test wildcard group grants access to all tools."""
|
||||
tools = {
|
||||
'tool1': Mock(config={'group': ['read-only']}),
|
||||
'tool2': Mock(config={'group': ['admin']}),
|
||||
'tool3': Mock(config={}) # default group
|
||||
}
|
||||
|
||||
# Request wildcard access
|
||||
filtered = filter_tools_by_group_and_state(tools, ['*'], None)
|
||||
|
||||
assert len(filtered) == 3
|
||||
assert all(tool in filtered for tool in tools)
|
||||
|
||||
def test_filter_tools_by_state(self):
|
||||
"""Test filtering based on applicable-states."""
|
||||
tools = {
|
||||
'init_tool': Mock(config={'applicable-states': ['undefined']}),
|
||||
'analysis_tool': Mock(config={'applicable-states': ['analysis']}),
|
||||
'any_state_tool': Mock(config={}) # available in all states
|
||||
}
|
||||
|
||||
# Filter for 'analysis' state
|
||||
filtered = filter_tools_by_group_and_state(tools, ['default'], 'analysis')
|
||||
|
||||
assert 'init_tool' not in filtered
|
||||
assert 'analysis_tool' in filtered
|
||||
assert 'any_state_tool' in filtered
|
||||
|
||||
def test_filter_tools_state_wildcard(self):
|
||||
"""Test tools with '*' in applicable-states are always available."""
|
||||
tools = {
|
||||
'wildcard_tool': Mock(config={'applicable-states': ['*']}),
|
||||
'specific_tool': Mock(config={'applicable-states': ['research']})
|
||||
}
|
||||
|
||||
# Filter for 'analysis' state
|
||||
filtered = filter_tools_by_group_and_state(tools, ['default'], 'analysis')
|
||||
|
||||
assert 'wildcard_tool' in filtered
|
||||
assert 'specific_tool' not in filtered
|
||||
|
||||
def test_filter_tools_combined_group_and_state(self):
|
||||
"""Test combined group and state filtering."""
|
||||
tools = {
|
||||
'valid_tool': Mock(config={
|
||||
'group': ['read-only'],
|
||||
'applicable-states': ['analysis']
|
||||
}),
|
||||
'wrong_group': Mock(config={
|
||||
'group': ['admin'],
|
||||
'applicable-states': ['analysis']
|
||||
}),
|
||||
'wrong_state': Mock(config={
|
||||
'group': ['read-only'],
|
||||
'applicable-states': ['research']
|
||||
}),
|
||||
'wrong_both': Mock(config={
|
||||
'group': ['admin'],
|
||||
'applicable-states': ['research']
|
||||
})
|
||||
}
|
||||
|
||||
filtered = filter_tools_by_group_and_state(
|
||||
tools, ['read-only'], 'analysis'
|
||||
)
|
||||
|
||||
assert 'valid_tool' in filtered
|
||||
assert 'wrong_group' not in filtered
|
||||
assert 'wrong_state' not in filtered
|
||||
assert 'wrong_both' not in filtered
|
||||
|
||||
def test_filter_tools_empty_request_groups(self):
|
||||
"""Test that empty group list results in no available tools."""
|
||||
tools = {
|
||||
'tool1': Mock(config={'group': ['read-only']}),
|
||||
'tool2': Mock(config={})
|
||||
}
|
||||
|
||||
filtered = filter_tools_by_group_and_state(tools, [], None)
|
||||
|
||||
assert len(filtered) == 0
|
||||
|
||||
|
||||
class TestStateTransitions:
|
||||
"""Test state transition logic."""
|
||||
|
||||
def test_get_next_state_with_transition(self):
|
||||
"""Test state transition when tool defines next state."""
|
||||
tool = Mock(config={'state': 'analysis'})
|
||||
|
||||
next_state = get_next_state(tool, 'undefined')
|
||||
|
||||
assert next_state == 'analysis'
|
||||
|
||||
def test_get_next_state_no_transition(self):
|
||||
"""Test no state change when tool doesn't define next state."""
|
||||
tool = Mock(config={})
|
||||
|
||||
next_state = get_next_state(tool, 'research')
|
||||
|
||||
assert next_state == 'research'
|
||||
|
||||
def test_get_next_state_empty_config(self):
|
||||
"""Test with tool that has no config."""
|
||||
tool = Mock(config=None)
|
||||
tool.config = None
|
||||
|
||||
next_state = get_next_state(tool, 'initial')
|
||||
|
||||
assert next_state == 'initial'
|
||||
|
||||
|
||||
class TestConfigValidation:
|
||||
"""Test tool configuration validation."""
|
||||
|
||||
def test_validate_valid_config(self):
|
||||
"""Test validation of valid configuration."""
|
||||
config = {
|
||||
'group': ['read-only', 'basic'],
|
||||
'state': 'analysis',
|
||||
'applicable-states': ['undefined', 'research']
|
||||
}
|
||||
|
||||
# Should not raise an exception
|
||||
validate_tool_config(config)
|
||||
|
||||
def test_validate_group_not_list(self):
|
||||
"""Test validation fails when group is not a list."""
|
||||
config = {'group': 'read-only'} # Should be list
|
||||
|
||||
with pytest.raises(ValueError, match="'group' field must be a list"):
|
||||
validate_tool_config(config)
|
||||
|
||||
def test_validate_group_non_string_elements(self):
|
||||
"""Test validation fails when group contains non-strings."""
|
||||
config = {'group': ['read-only', 123]} # 123 is not string
|
||||
|
||||
with pytest.raises(ValueError, match="All group names must be strings"):
|
||||
validate_tool_config(config)
|
||||
|
||||
def test_validate_state_not_string(self):
|
||||
"""Test validation fails when state is not a string."""
|
||||
config = {'state': 123} # Should be string
|
||||
|
||||
with pytest.raises(ValueError, match="'state' field must be a string"):
|
||||
validate_tool_config(config)
|
||||
|
||||
def test_validate_applicable_states_not_list(self):
|
||||
"""Test validation fails when applicable-states is not a list."""
|
||||
config = {'applicable-states': 'undefined'} # Should be list
|
||||
|
||||
with pytest.raises(ValueError, match="'applicable-states' field must be a list"):
|
||||
validate_tool_config(config)
|
||||
|
||||
def test_validate_applicable_states_non_string_elements(self):
|
||||
"""Test validation fails when applicable-states contains non-strings."""
|
||||
config = {'applicable-states': ['undefined', 123]}
|
||||
|
||||
with pytest.raises(ValueError, match="All state names must be strings"):
|
||||
validate_tool_config(config)
|
||||
|
||||
def test_validate_minimal_config(self):
|
||||
"""Test validation of minimal valid configuration."""
|
||||
config = {'name': 'test', 'description': 'Test tool'}
|
||||
|
||||
# Should not raise an exception
|
||||
validate_tool_config(config)
|
||||
|
||||
|
||||
class TestToolAvailability:
|
||||
"""Test the internal _is_tool_available function."""
|
||||
|
||||
def test_tool_available_default_groups_and_states(self):
|
||||
"""Test tool with default groups and states."""
|
||||
tool = Mock(config={})
|
||||
|
||||
# Default group request, default state
|
||||
assert _is_tool_available(tool, ['default'], 'undefined')
|
||||
|
||||
# Non-default group request should fail
|
||||
assert not _is_tool_available(tool, ['admin'], 'undefined')
|
||||
|
||||
def test_tool_available_string_group_conversion(self):
|
||||
"""Test that single group string is converted to list."""
|
||||
tool = Mock(config={'group': 'read-only'}) # Single string
|
||||
|
||||
assert _is_tool_available(tool, ['read-only'], 'undefined')
|
||||
assert not _is_tool_available(tool, ['admin'], 'undefined')
|
||||
|
||||
def test_tool_available_string_state_conversion(self):
|
||||
"""Test that single state string is converted to list."""
|
||||
tool = Mock(config={'applicable-states': 'analysis'}) # Single string
|
||||
|
||||
assert _is_tool_available(tool, ['default'], 'analysis')
|
||||
assert not _is_tool_available(tool, ['default'], 'research')
|
||||
|
||||
def test_tool_no_config_attribute(self):
|
||||
"""Test tool without config attribute."""
|
||||
tool = Mock()
|
||||
del tool.config # Remove config attribute
|
||||
|
||||
# Should use defaults and be available for default group/state
|
||||
assert _is_tool_available(tool, ['default'], 'undefined')
|
||||
assert not _is_tool_available(tool, ['admin'], 'undefined')
|
||||
|
||||
|
||||
class TestWorkflowScenarios:
|
||||
"""Test complete workflow scenarios from the tech spec."""
|
||||
|
||||
def test_research_to_analysis_workflow(self):
|
||||
"""Test the research -> analysis workflow from tech spec."""
|
||||
tools = {
|
||||
'knowledge_query': Mock(config={
|
||||
'group': ['read-only', 'knowledge'],
|
||||
'state': 'analysis',
|
||||
'applicable-states': ['undefined', 'research']
|
||||
}),
|
||||
'complex_analysis': Mock(config={
|
||||
'group': ['advanced', 'compute'],
|
||||
'state': 'results',
|
||||
'applicable-states': ['analysis']
|
||||
}),
|
||||
'text_completion': Mock(config={
|
||||
'group': ['read-only', 'text', 'basic']
|
||||
# No applicable-states = available in all states
|
||||
})
|
||||
}
|
||||
|
||||
# Phase 1: Initial research (undefined state)
|
||||
phase1_filtered = filter_tools_by_group_and_state(
|
||||
tools, ['read-only', 'knowledge'], 'undefined'
|
||||
)
|
||||
assert 'knowledge_query' in phase1_filtered
|
||||
assert 'text_completion' in phase1_filtered
|
||||
assert 'complex_analysis' not in phase1_filtered
|
||||
|
||||
# Simulate tool execution and state transition
|
||||
executed_tool = phase1_filtered['knowledge_query']
|
||||
next_state = get_next_state(executed_tool, 'undefined')
|
||||
assert next_state == 'analysis'
|
||||
|
||||
# Phase 2: Analysis state (include basic group for text_completion)
|
||||
phase2_filtered = filter_tools_by_group_and_state(
|
||||
tools, ['advanced', 'compute', 'basic'], 'analysis'
|
||||
)
|
||||
assert 'knowledge_query' not in phase2_filtered # Not available in analysis
|
||||
assert 'complex_analysis' in phase2_filtered
|
||||
assert 'text_completion' in phase2_filtered # Always available
|
||||
|
||||
# Simulate complex analysis execution
|
||||
executed_tool = phase2_filtered['complex_analysis']
|
||||
final_state = get_next_state(executed_tool, 'analysis')
|
||||
assert final_state == 'results'
|
||||
Loading…
Add table
Add a link
Reference in a new issue