mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-07 17:33:33 -10:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e0912499c | ||
|
|
d866f31c09 | ||
|
|
b35ac2d71e | ||
|
|
61f4205b37 | ||
|
|
15cd8e2e42 |
No files matched your search
@@ -1377,7 +1377,7 @@ export { // Python parity additions
|
||||
MinimalTelemetry, AgentTelemetry, PerformanceMonitor, TelemetryCollector, TelemetryIntegration, cleanupTelemetry, cleanupTelemetryResources, createAgentTelemetry, createConsoleSink, createHTTPSink, createPerformanceMonitor, createTelemetryIntegration, disablePerformanceMode, disableTelemetry, enablePerformanceMode, enableTelemetry, getMinimalTelemetry, getTelemetry } from "./telemetry";
|
||||
export { // Subagent Tool (agent-as-tool pattern)
|
||||
SubagentTool, BaseTool, FunctionTool, ToolRegistry, ToolResult, ToolValidationError, createDelegator, createSubagentTool, createSubagentTools, createTool, getRegistry, getTool, registerTool, tool, validateTool } from "./tools";
|
||||
export { airweaveSearch, bedrockBrowserClick, bedrockBrowserFill, bedrockBrowserNavigate, bedrockCodeInterpreter, codeExecution, codeMode, createCustomTool, exaSearch, firecrawlCrawl, firecrawlScrape, parallelSearch, perplexitySearch, registerCustomTool, registerLocalTool, registerNpmTool, superagentGuard, superagentRedact, superagentVerify, tavilyCrawl, tavilyExtract, tavilySearch, valyuBioSearch, valyuCompanyResearch, valyuEconomicsSearch, valyuFinanceSearch, valyuPaperSearch, valyuPatentSearch, valyuSecSearch, valyuWebSearch } from "./tools/builtins";
|
||||
export { airweaveSearch, bedrockBrowserClick, bedrockBrowserFill, bedrockBrowserNavigate, bedrockCodeInterpreter, codeExecution, codeMode, createCustomTool, crwCrawl, crwScrape, exaSearch, firecrawlCrawl, firecrawlScrape, parallelSearch, perplexitySearch, registerCustomTool, registerLocalTool, registerNpmTool, superagentGuard, superagentRedact, superagentVerify, tavilyCrawl, tavilyExtract, tavilySearch, valyuBioSearch, valyuCompanyResearch, valyuEconomicsSearch, valyuFinanceSearch, valyuPaperSearch, valyuPatentSearch, valyuSecSearch, valyuWebSearch } from "./tools/builtins";
|
||||
export { BudgetExceededError, MissingDependencyError, MissingEnvVarError, ToolsRegistry, composeMiddleware, createLoggingMiddleware, createRateLimitMiddleware, createRedactionMiddleware, createRetryMiddleware, createTimeoutMiddleware, createToolsRegistry, createTracingMiddleware, createValidationMiddleware, getToolsRegistry, get_registry, get_tool, register_tool, resetToolsRegistry, validate_tool } from "./tools/registry";
|
||||
export type { InstallHints, PraisonTool, RedactionHooks, RegisteredTool, ToolCapabilities, ToolExecutionContext, ToolExecutionResult, ToolFactory, ToolHooks, ToolInstallStatus, ToolLimits, ToolLogger, ToolMetadata, ToolMiddleware, ToolParameterProperty, ToolParameterSchema } from "./tools/registry";
|
||||
export { registerBuiltinTools, tools } from "./tools/tools";
|
||||
|
||||
@@ -119,7 +119,6 @@ class TestMemoryConfigHistoryFields:
|
||||
assert agent._history_limit == 15
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="History injection implementation differs from test expectations - needs API alignment")
|
||||
class TestHistoryInjection:
|
||||
"""Test that history is actually injected into messages."""
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ instead of direct TOOL_MAPPINGS access.
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
|
||||
from praisonai.cli.main import PraisonCLI
|
||||
from praisonai.cli.main import PraisonAI
|
||||
|
||||
|
||||
class TestCLIToolResolverSmoke:
|
||||
@@ -16,10 +16,10 @@ class TestCLIToolResolverSmoke:
|
||||
|
||||
def test_load_tools_imports_tool_resolver(self):
|
||||
"""Test that _load_tools imports ToolResolver from correct module."""
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
# Mock the ToolResolver import
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_instance = Mock()
|
||||
MockResolver.return_value = mock_instance
|
||||
mock_instance.resolve.return_value = None # No tools found
|
||||
@@ -33,9 +33,9 @@ class TestCLIToolResolverSmoke:
|
||||
|
||||
def test_load_tools_resolver_instantiation_pattern(self):
|
||||
"""Test that ToolResolver is instantiated correctly."""
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_instance = Mock()
|
||||
MockResolver.return_value = mock_instance
|
||||
mock_instance.resolve.return_value = Mock() # Mock tool
|
||||
@@ -47,9 +47,9 @@ class TestCLIToolResolverSmoke:
|
||||
|
||||
def test_load_tools_calls_resolve_with_instantiate_true(self):
|
||||
"""Test that resolve is called with instantiate=True."""
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_instance = Mock()
|
||||
MockResolver.return_value = mock_instance
|
||||
mock_tool = Mock()
|
||||
@@ -63,9 +63,9 @@ class TestCLIToolResolverSmoke:
|
||||
|
||||
def test_load_tools_multiple_comma_separated(self):
|
||||
"""Test loading multiple comma-separated tools."""
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_instance = Mock()
|
||||
MockResolver.return_value = mock_instance
|
||||
|
||||
@@ -85,9 +85,9 @@ class TestCLIToolResolverSmoke:
|
||||
|
||||
def test_load_tools_whitespace_handling(self):
|
||||
"""Test that whitespace around tool names is handled correctly."""
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_instance = Mock()
|
||||
MockResolver.return_value = mock_instance
|
||||
mock_tool = Mock()
|
||||
@@ -104,9 +104,9 @@ class TestCLIToolResolverSmoke:
|
||||
|
||||
def test_load_tools_empty_string_filtering(self):
|
||||
"""Test that empty strings are filtered out."""
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_instance = Mock()
|
||||
MockResolver.return_value = mock_instance
|
||||
mock_tool = Mock()
|
||||
@@ -122,9 +122,9 @@ class TestCLIToolResolverSmoke:
|
||||
|
||||
def test_load_tools_none_return_handling(self):
|
||||
"""Test handling when resolver returns None (tool not found)."""
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_instance = Mock()
|
||||
MockResolver.return_value = mock_instance
|
||||
mock_instance.resolve.return_value = None # Tool not found
|
||||
@@ -137,9 +137,9 @@ class TestCLIToolResolverSmoke:
|
||||
|
||||
def test_load_tools_exception_handling(self):
|
||||
"""Test that exceptions during tool resolution are handled gracefully."""
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_instance = Mock()
|
||||
MockResolver.return_value = mock_instance
|
||||
mock_instance.resolve.side_effect = Exception("Tool resolution error")
|
||||
@@ -153,13 +153,13 @@ class TestCLIToolResolverSmoke:
|
||||
|
||||
def test_load_tools_file_path_bypass(self):
|
||||
"""Test that file paths bypass the ToolResolver and use file loading."""
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
# Mock os.path.isfile to return True for file path test
|
||||
with patch('os.path.isfile', return_value=True):
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
# Also need to mock the file loading parts
|
||||
with patch('praisonai.cli.main.load_user_module', return_value=None):
|
||||
with patch('praisonai._safe_loader.load_user_module', return_value=None):
|
||||
result = cli._load_tools("/path/to/tools.py")
|
||||
|
||||
# ToolResolver should NOT be called for file paths
|
||||
@@ -167,9 +167,9 @@ class TestCLIToolResolverSmoke:
|
||||
|
||||
def test_load_tools_empty_input(self):
|
||||
"""Test handling of empty or None input."""
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
# Test empty string
|
||||
result = cli._load_tools("")
|
||||
assert result == []
|
||||
@@ -182,7 +182,7 @@ class TestCLIToolResolverSmoke:
|
||||
|
||||
def test_load_tools_preserves_tool_instances(self):
|
||||
"""Test that tool instances returned by resolver are preserved."""
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
# Create mock tools with specific attributes
|
||||
mock_tool1 = Mock()
|
||||
@@ -190,7 +190,7 @@ class TestCLIToolResolverSmoke:
|
||||
mock_tool2 = Mock()
|
||||
mock_tool2.name = "tool2"
|
||||
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_instance = Mock()
|
||||
MockResolver.return_value = mock_instance
|
||||
mock_instance.resolve.side_effect = [mock_tool1, mock_tool2]
|
||||
@@ -213,23 +213,23 @@ class TestCLIToolResolverConsistency:
|
||||
# This test ensures the import path is correct and consistent
|
||||
try:
|
||||
from praisonai.tool_resolver import ToolResolver
|
||||
from praisonai.cli.main import PraisonCLI
|
||||
from praisonai.cli.main import PraisonAI
|
||||
|
||||
# If both imports work, the path is consistent
|
||||
assert ToolResolver is not None
|
||||
assert PraisonCLI is not None
|
||||
assert PraisonAI is not None
|
||||
|
||||
except ImportError as e:
|
||||
pytest.fail(f"Import path inconsistency detected: {e}")
|
||||
|
||||
def test_resolver_instantiation_consistency(self):
|
||||
"""Test that CLI instantiates ToolResolver the same way as other components."""
|
||||
from praisonai.cli.main import PraisonCLI
|
||||
from praisonai.cli.main import PraisonAI
|
||||
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
# Mock to capture how ToolResolver is instantiated
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_instance = Mock()
|
||||
MockResolver.return_value = mock_instance
|
||||
mock_instance.resolve.return_value = Mock()
|
||||
@@ -241,11 +241,11 @@ class TestCLIToolResolverConsistency:
|
||||
|
||||
def test_resolve_method_signature_consistency(self):
|
||||
"""Test that resolve method is called with expected signature."""
|
||||
from praisonai.cli.main import PraisonCLI
|
||||
from praisonai.cli.main import PraisonAI
|
||||
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
with patch('praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_instance = Mock()
|
||||
MockResolver.return_value = mock_instance
|
||||
mock_instance.resolve.return_value = Mock()
|
||||
|
||||
@@ -93,8 +93,10 @@ class TestAsyncIODaemonStateUpdates:
|
||||
# Mock the _update_state_if_daemon method
|
||||
with patch.object(scheduler, '_update_state_if_daemon') as mock_update:
|
||||
with patch('asyncio.to_thread') as mock_to_thread:
|
||||
mock_to_thread.return_value = asyncio.create_future()
|
||||
mock_to_thread.return_value.set_result(None)
|
||||
future = asyncio.Future()
|
||||
future.set_result(None)
|
||||
mock_to_thread.return_value = future
|
||||
|
||||
|
||||
# Execute with retry - should call _update_state_if_daemon via asyncio.to_thread
|
||||
await scheduler._execute_with_retry(max_retries=1)
|
||||
@@ -110,8 +112,10 @@ class TestAsyncIODaemonStateUpdates:
|
||||
scheduler = AsyncAgentScheduler(mock_agent, "test task")
|
||||
|
||||
with patch('asyncio.to_thread') as mock_to_thread:
|
||||
mock_to_thread.return_value = asyncio.create_future()
|
||||
mock_to_thread.return_value.set_result(None)
|
||||
future = asyncio.Future()
|
||||
future.set_result(None)
|
||||
mock_to_thread.return_value = future
|
||||
|
||||
|
||||
await scheduler._execute_with_retry(max_retries=1)
|
||||
|
||||
@@ -127,9 +131,11 @@ class TestAsyncIODaemonStateUpdates:
|
||||
scheduler = AsyncAgentScheduler(mock_agent, "test task")
|
||||
|
||||
with patch('asyncio.to_thread') as mock_to_thread:
|
||||
mock_to_thread.return_value = asyncio.create_future()
|
||||
mock_to_thread.return_value.set_result(None)
|
||||
future = asyncio.Future()
|
||||
future.set_result(None)
|
||||
mock_to_thread.return_value = future
|
||||
|
||||
|
||||
await scheduler._execute_with_retry(max_retries=1)
|
||||
|
||||
# Should be called once on failure path
|
||||
@@ -271,12 +277,12 @@ class TestCLIToolResolverIntegration:
|
||||
|
||||
def test_cli_load_tools_uses_resolver(self):
|
||||
"""Test that _load_tools uses ToolResolver.resolve instead of direct TOOL_MAPPINGS."""
|
||||
from praisonai.praisonai.cli.main import PraisonCLI
|
||||
from praisonai.cli.main import PraisonAI
|
||||
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
# Mock ToolResolver
|
||||
with patch('praisonai.praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_resolver_instance = Mock()
|
||||
MockResolver.return_value = mock_resolver_instance
|
||||
mock_resolver_instance.resolve.return_value = Mock() # Mock tool
|
||||
@@ -294,11 +300,11 @@ class TestCLIToolResolverIntegration:
|
||||
|
||||
def test_cli_load_tools_handles_empty_strings(self):
|
||||
"""Test that _load_tools filters out empty strings from tool names."""
|
||||
from praisonai.praisonai.cli.main import PraisonCLI
|
||||
from praisonai.cli.main import PraisonAI
|
||||
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
with patch('praisonai.praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_resolver_instance = Mock()
|
||||
MockResolver.return_value = mock_resolver_instance
|
||||
mock_resolver_instance.resolve.return_value = Mock()
|
||||
@@ -313,11 +319,11 @@ class TestCLIToolResolverIntegration:
|
||||
|
||||
def test_cli_load_tools_error_handling(self):
|
||||
"""Test that _load_tools handles resolution errors gracefully."""
|
||||
from praisonai.praisonai.cli.main import PraisonCLI
|
||||
from praisonai.cli.main import PraisonAI
|
||||
|
||||
cli = PraisonCLI()
|
||||
cli = PraisonAI()
|
||||
|
||||
with patch('praisonai.praisonai.cli.main.ToolResolver') as MockResolver:
|
||||
with patch("praisonai.tool_resolver.ToolResolver") as MockResolver:
|
||||
mock_resolver_instance = Mock()
|
||||
MockResolver.return_value = mock_resolver_instance
|
||||
# Simulate tool resolution failure
|
||||
@@ -335,7 +341,7 @@ class TestFrameworkAdapterDuplication:
|
||||
|
||||
def test_no_duplicate_arun_in_protocol(self):
|
||||
"""Test that FrameworkAdapter protocol has single arun method."""
|
||||
from praisonai.praisonai.framework_adapters.base import FrameworkAdapter
|
||||
from praisonai.framework_adapters.base import FrameworkAdapter
|
||||
|
||||
# Get all method names from the protocol
|
||||
import inspect
|
||||
@@ -348,7 +354,7 @@ class TestFrameworkAdapterDuplication:
|
||||
|
||||
def test_framework_adapter_has_cleanup_method(self):
|
||||
"""Test that FrameworkAdapter protocol includes cleanup method."""
|
||||
from praisonai.praisonai.framework_adapters.base import FrameworkAdapter
|
||||
from praisonai.framework_adapters.base import FrameworkAdapter
|
||||
|
||||
# Check that cleanup method is defined in the protocol
|
||||
import inspect
|
||||
|
||||
@@ -1415,18 +1415,22 @@ def custom_search(query: str) -> str:
|
||||
|
||||
# After context, original state should be restored
|
||||
# (implementation detail - registry should be isolated)
|
||||
|
||||
|
||||
def test_default_custom_dirs(self):
|
||||
"""Test that default custom tool directories are defined."""
|
||||
from praisonai.templates.tool_override import ToolOverrideLoader
|
||||
|
||||
|
||||
loader = ToolOverrideLoader()
|
||||
default_dirs = loader.get_default_tool_dirs()
|
||||
|
||||
|
||||
assert len(default_dirs) >= 2
|
||||
assert any(".praison/tools" in str(d) for d in default_dirs)
|
||||
assert any(".config/praison/tools" in str(d) or ".praison/tools" in str(d) for d in default_dirs)
|
||||
|
||||
assert any(".praison/tools" in Path(d).as_posix() for d in default_dirs)
|
||||
assert any(
|
||||
".config/praison/tools" in Path(d).as_posix()
|
||||
or ".praison/tools" in Path(d).as_posix()
|
||||
for d in default_dirs
|
||||
)
|
||||
|
||||
def test_no_scanning_on_import(self):
|
||||
"""Test that no filesystem scanning occurs on import."""
|
||||
import sys
|
||||
|
||||
Reference in new issue
Block a user