PraisonAI 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- praisonai/__init__.py +54 -0
- praisonai/__main__.py +15 -0
- praisonai/acp/__init__.py +54 -0
- praisonai/acp/config.py +159 -0
- praisonai/acp/server.py +587 -0
- praisonai/acp/session.py +219 -0
- praisonai/adapters/__init__.py +50 -0
- praisonai/adapters/readers.py +395 -0
- praisonai/adapters/rerankers.py +315 -0
- praisonai/adapters/retrievers.py +394 -0
- praisonai/adapters/vector_stores.py +409 -0
- praisonai/agent_scheduler.py +337 -0
- praisonai/agents_generator.py +903 -0
- praisonai/api/call.py +292 -0
- praisonai/auto.py +1197 -0
- praisonai/capabilities/__init__.py +275 -0
- praisonai/capabilities/a2a.py +140 -0
- praisonai/capabilities/assistants.py +283 -0
- praisonai/capabilities/audio.py +320 -0
- praisonai/capabilities/batches.py +469 -0
- praisonai/capabilities/completions.py +336 -0
- praisonai/capabilities/container_files.py +155 -0
- praisonai/capabilities/containers.py +93 -0
- praisonai/capabilities/embeddings.py +158 -0
- praisonai/capabilities/files.py +467 -0
- praisonai/capabilities/fine_tuning.py +293 -0
- praisonai/capabilities/guardrails.py +182 -0
- praisonai/capabilities/images.py +330 -0
- praisonai/capabilities/mcp.py +190 -0
- praisonai/capabilities/messages.py +270 -0
- praisonai/capabilities/moderations.py +154 -0
- praisonai/capabilities/ocr.py +217 -0
- praisonai/capabilities/passthrough.py +204 -0
- praisonai/capabilities/rag.py +207 -0
- praisonai/capabilities/realtime.py +160 -0
- praisonai/capabilities/rerank.py +165 -0
- praisonai/capabilities/responses.py +266 -0
- praisonai/capabilities/search.py +109 -0
- praisonai/capabilities/skills.py +133 -0
- praisonai/capabilities/vector_store_files.py +334 -0
- praisonai/capabilities/vector_stores.py +304 -0
- praisonai/capabilities/videos.py +141 -0
- praisonai/chainlit_ui.py +304 -0
- praisonai/chat/__init__.py +106 -0
- praisonai/chat/app.py +125 -0
- praisonai/cli/__init__.py +26 -0
- praisonai/cli/app.py +213 -0
- praisonai/cli/commands/__init__.py +75 -0
- praisonai/cli/commands/acp.py +70 -0
- praisonai/cli/commands/completion.py +333 -0
- praisonai/cli/commands/config.py +166 -0
- praisonai/cli/commands/debug.py +142 -0
- praisonai/cli/commands/diag.py +55 -0
- praisonai/cli/commands/doctor.py +166 -0
- praisonai/cli/commands/environment.py +179 -0
- praisonai/cli/commands/lsp.py +112 -0
- praisonai/cli/commands/mcp.py +210 -0
- praisonai/cli/commands/profile.py +457 -0
- praisonai/cli/commands/run.py +228 -0
- praisonai/cli/commands/schedule.py +150 -0
- praisonai/cli/commands/serve.py +97 -0
- praisonai/cli/commands/session.py +212 -0
- praisonai/cli/commands/traces.py +145 -0
- praisonai/cli/commands/version.py +101 -0
- praisonai/cli/configuration/__init__.py +18 -0
- praisonai/cli/configuration/loader.py +353 -0
- praisonai/cli/configuration/paths.py +114 -0
- praisonai/cli/configuration/schema.py +164 -0
- praisonai/cli/features/__init__.py +268 -0
- praisonai/cli/features/acp.py +236 -0
- praisonai/cli/features/action_orchestrator.py +546 -0
- praisonai/cli/features/agent_scheduler.py +773 -0
- praisonai/cli/features/agent_tools.py +474 -0
- praisonai/cli/features/agents.py +375 -0
- praisonai/cli/features/at_mentions.py +471 -0
- praisonai/cli/features/auto_memory.py +182 -0
- praisonai/cli/features/autonomy_mode.py +490 -0
- praisonai/cli/features/background.py +356 -0
- praisonai/cli/features/base.py +168 -0
- praisonai/cli/features/capabilities.py +1326 -0
- praisonai/cli/features/checkpoints.py +338 -0
- praisonai/cli/features/code_intelligence.py +652 -0
- praisonai/cli/features/compaction.py +294 -0
- praisonai/cli/features/compare.py +534 -0
- praisonai/cli/features/cost_tracker.py +514 -0
- praisonai/cli/features/debug.py +810 -0
- praisonai/cli/features/deploy.py +517 -0
- praisonai/cli/features/diag.py +289 -0
- praisonai/cli/features/doctor/__init__.py +63 -0
- praisonai/cli/features/doctor/checks/__init__.py +24 -0
- praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
- praisonai/cli/features/doctor/checks/config_checks.py +366 -0
- praisonai/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai/cli/features/doctor/checks/env_checks.py +543 -0
- praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
- praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
- praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
- praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai/cli/features/doctor/engine.py +266 -0
- praisonai/cli/features/doctor/formatters.py +310 -0
- praisonai/cli/features/doctor/handler.py +397 -0
- praisonai/cli/features/doctor/models.py +264 -0
- praisonai/cli/features/doctor/registry.py +239 -0
- praisonai/cli/features/endpoints.py +1019 -0
- praisonai/cli/features/eval.py +560 -0
- praisonai/cli/features/external_agents.py +231 -0
- praisonai/cli/features/fast_context.py +410 -0
- praisonai/cli/features/flow_display.py +566 -0
- praisonai/cli/features/git_integration.py +651 -0
- praisonai/cli/features/guardrail.py +171 -0
- praisonai/cli/features/handoff.py +185 -0
- praisonai/cli/features/hooks.py +583 -0
- praisonai/cli/features/image.py +384 -0
- praisonai/cli/features/interactive_runtime.py +585 -0
- praisonai/cli/features/interactive_tools.py +380 -0
- praisonai/cli/features/interactive_tui.py +603 -0
- praisonai/cli/features/jobs.py +632 -0
- praisonai/cli/features/knowledge.py +531 -0
- praisonai/cli/features/lite.py +244 -0
- praisonai/cli/features/lsp_cli.py +225 -0
- praisonai/cli/features/mcp.py +169 -0
- praisonai/cli/features/message_queue.py +587 -0
- praisonai/cli/features/metrics.py +211 -0
- praisonai/cli/features/n8n.py +673 -0
- praisonai/cli/features/observability.py +293 -0
- praisonai/cli/features/ollama.py +361 -0
- praisonai/cli/features/output_style.py +273 -0
- praisonai/cli/features/package.py +631 -0
- praisonai/cli/features/performance.py +308 -0
- praisonai/cli/features/persistence.py +636 -0
- praisonai/cli/features/profile.py +226 -0
- praisonai/cli/features/profiler/__init__.py +81 -0
- praisonai/cli/features/profiler/core.py +558 -0
- praisonai/cli/features/profiler/optimizations.py +652 -0
- praisonai/cli/features/profiler/suite.py +386 -0
- praisonai/cli/features/profiling.py +350 -0
- praisonai/cli/features/queue/__init__.py +73 -0
- praisonai/cli/features/queue/manager.py +395 -0
- praisonai/cli/features/queue/models.py +286 -0
- praisonai/cli/features/queue/persistence.py +564 -0
- praisonai/cli/features/queue/scheduler.py +484 -0
- praisonai/cli/features/queue/worker.py +372 -0
- praisonai/cli/features/recipe.py +1723 -0
- praisonai/cli/features/recipes.py +449 -0
- praisonai/cli/features/registry.py +229 -0
- praisonai/cli/features/repo_map.py +860 -0
- praisonai/cli/features/router.py +466 -0
- praisonai/cli/features/sandbox_executor.py +515 -0
- praisonai/cli/features/serve.py +829 -0
- praisonai/cli/features/session.py +222 -0
- praisonai/cli/features/skills.py +856 -0
- praisonai/cli/features/slash_commands.py +650 -0
- praisonai/cli/features/telemetry.py +179 -0
- praisonai/cli/features/templates.py +1384 -0
- praisonai/cli/features/thinking.py +305 -0
- praisonai/cli/features/todo.py +334 -0
- praisonai/cli/features/tools.py +680 -0
- praisonai/cli/features/tui/__init__.py +83 -0
- praisonai/cli/features/tui/app.py +580 -0
- praisonai/cli/features/tui/cli.py +566 -0
- praisonai/cli/features/tui/debug.py +511 -0
- praisonai/cli/features/tui/events.py +99 -0
- praisonai/cli/features/tui/mock_provider.py +328 -0
- praisonai/cli/features/tui/orchestrator.py +652 -0
- praisonai/cli/features/tui/screens/__init__.py +50 -0
- praisonai/cli/features/tui/screens/main.py +245 -0
- praisonai/cli/features/tui/screens/queue.py +174 -0
- praisonai/cli/features/tui/screens/session.py +124 -0
- praisonai/cli/features/tui/screens/settings.py +148 -0
- praisonai/cli/features/tui/widgets/__init__.py +56 -0
- praisonai/cli/features/tui/widgets/chat.py +261 -0
- praisonai/cli/features/tui/widgets/composer.py +224 -0
- praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
- praisonai/cli/features/tui/widgets/status.py +167 -0
- praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
- praisonai/cli/features/workflow.py +720 -0
- praisonai/cli/legacy.py +236 -0
- praisonai/cli/main.py +5559 -0
- praisonai/cli/schedule_cli.py +54 -0
- praisonai/cli/state/__init__.py +31 -0
- praisonai/cli/state/identifiers.py +161 -0
- praisonai/cli/state/sessions.py +313 -0
- praisonai/code/__init__.py +93 -0
- praisonai/code/agent_tools.py +344 -0
- praisonai/code/diff/__init__.py +21 -0
- praisonai/code/diff/diff_strategy.py +432 -0
- praisonai/code/tools/__init__.py +27 -0
- praisonai/code/tools/apply_diff.py +221 -0
- praisonai/code/tools/execute_command.py +275 -0
- praisonai/code/tools/list_files.py +274 -0
- praisonai/code/tools/read_file.py +206 -0
- praisonai/code/tools/search_replace.py +248 -0
- praisonai/code/tools/write_file.py +217 -0
- praisonai/code/utils/__init__.py +46 -0
- praisonai/code/utils/file_utils.py +307 -0
- praisonai/code/utils/ignore_utils.py +308 -0
- praisonai/code/utils/text_utils.py +276 -0
- praisonai/db/__init__.py +64 -0
- praisonai/db/adapter.py +531 -0
- praisonai/deploy/__init__.py +62 -0
- praisonai/deploy/api.py +231 -0
- praisonai/deploy/docker.py +454 -0
- praisonai/deploy/doctor.py +367 -0
- praisonai/deploy/main.py +327 -0
- praisonai/deploy/models.py +179 -0
- praisonai/deploy/providers/__init__.py +33 -0
- praisonai/deploy/providers/aws.py +331 -0
- praisonai/deploy/providers/azure.py +358 -0
- praisonai/deploy/providers/base.py +101 -0
- praisonai/deploy/providers/gcp.py +314 -0
- praisonai/deploy/schema.py +208 -0
- praisonai/deploy.py +185 -0
- praisonai/endpoints/__init__.py +53 -0
- praisonai/endpoints/a2u_server.py +410 -0
- praisonai/endpoints/discovery.py +165 -0
- praisonai/endpoints/providers/__init__.py +28 -0
- praisonai/endpoints/providers/a2a.py +253 -0
- praisonai/endpoints/providers/a2u.py +208 -0
- praisonai/endpoints/providers/agents_api.py +171 -0
- praisonai/endpoints/providers/base.py +231 -0
- praisonai/endpoints/providers/mcp.py +263 -0
- praisonai/endpoints/providers/recipe.py +206 -0
- praisonai/endpoints/providers/tools_mcp.py +150 -0
- praisonai/endpoints/registry.py +131 -0
- praisonai/endpoints/server.py +161 -0
- praisonai/inbuilt_tools/__init__.py +24 -0
- praisonai/inbuilt_tools/autogen_tools.py +117 -0
- praisonai/inc/__init__.py +2 -0
- praisonai/inc/config.py +96 -0
- praisonai/inc/models.py +155 -0
- praisonai/integrations/__init__.py +56 -0
- praisonai/integrations/base.py +303 -0
- praisonai/integrations/claude_code.py +270 -0
- praisonai/integrations/codex_cli.py +255 -0
- praisonai/integrations/cursor_cli.py +195 -0
- praisonai/integrations/gemini_cli.py +222 -0
- praisonai/jobs/__init__.py +67 -0
- praisonai/jobs/executor.py +425 -0
- praisonai/jobs/models.py +230 -0
- praisonai/jobs/router.py +314 -0
- praisonai/jobs/server.py +186 -0
- praisonai/jobs/store.py +203 -0
- praisonai/llm/__init__.py +66 -0
- praisonai/llm/registry.py +382 -0
- praisonai/mcp_server/__init__.py +152 -0
- praisonai/mcp_server/adapters/__init__.py +74 -0
- praisonai/mcp_server/adapters/agents.py +128 -0
- praisonai/mcp_server/adapters/capabilities.py +168 -0
- praisonai/mcp_server/adapters/cli_tools.py +568 -0
- praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
- praisonai/mcp_server/adapters/knowledge.py +93 -0
- praisonai/mcp_server/adapters/memory.py +104 -0
- praisonai/mcp_server/adapters/prompts.py +306 -0
- praisonai/mcp_server/adapters/resources.py +124 -0
- praisonai/mcp_server/adapters/tools_bridge.py +280 -0
- praisonai/mcp_server/auth/__init__.py +48 -0
- praisonai/mcp_server/auth/api_key.py +291 -0
- praisonai/mcp_server/auth/oauth.py +460 -0
- praisonai/mcp_server/auth/oidc.py +289 -0
- praisonai/mcp_server/auth/scopes.py +260 -0
- praisonai/mcp_server/cli.py +852 -0
- praisonai/mcp_server/elicitation.py +445 -0
- praisonai/mcp_server/icons.py +302 -0
- praisonai/mcp_server/recipe_adapter.py +573 -0
- praisonai/mcp_server/recipe_cli.py +824 -0
- praisonai/mcp_server/registry.py +703 -0
- praisonai/mcp_server/sampling.py +422 -0
- praisonai/mcp_server/server.py +490 -0
- praisonai/mcp_server/tasks.py +443 -0
- praisonai/mcp_server/transports/__init__.py +18 -0
- praisonai/mcp_server/transports/http_stream.py +376 -0
- praisonai/mcp_server/transports/stdio.py +132 -0
- praisonai/persistence/__init__.py +84 -0
- praisonai/persistence/config.py +238 -0
- praisonai/persistence/conversation/__init__.py +25 -0
- praisonai/persistence/conversation/async_mysql.py +427 -0
- praisonai/persistence/conversation/async_postgres.py +410 -0
- praisonai/persistence/conversation/async_sqlite.py +371 -0
- praisonai/persistence/conversation/base.py +151 -0
- praisonai/persistence/conversation/json_store.py +250 -0
- praisonai/persistence/conversation/mysql.py +387 -0
- praisonai/persistence/conversation/postgres.py +401 -0
- praisonai/persistence/conversation/singlestore.py +240 -0
- praisonai/persistence/conversation/sqlite.py +341 -0
- praisonai/persistence/conversation/supabase.py +203 -0
- praisonai/persistence/conversation/surrealdb.py +287 -0
- praisonai/persistence/factory.py +301 -0
- praisonai/persistence/hooks/__init__.py +18 -0
- praisonai/persistence/hooks/agent_hooks.py +297 -0
- praisonai/persistence/knowledge/__init__.py +26 -0
- praisonai/persistence/knowledge/base.py +144 -0
- praisonai/persistence/knowledge/cassandra.py +232 -0
- praisonai/persistence/knowledge/chroma.py +295 -0
- praisonai/persistence/knowledge/clickhouse.py +242 -0
- praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
- praisonai/persistence/knowledge/couchbase.py +286 -0
- praisonai/persistence/knowledge/lancedb.py +216 -0
- praisonai/persistence/knowledge/langchain_adapter.py +291 -0
- praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
- praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
- praisonai/persistence/knowledge/milvus.py +277 -0
- praisonai/persistence/knowledge/mongodb_vector.py +306 -0
- praisonai/persistence/knowledge/pgvector.py +335 -0
- praisonai/persistence/knowledge/pinecone.py +253 -0
- praisonai/persistence/knowledge/qdrant.py +301 -0
- praisonai/persistence/knowledge/redis_vector.py +291 -0
- praisonai/persistence/knowledge/singlestore_vector.py +299 -0
- praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
- praisonai/persistence/knowledge/upstash_vector.py +266 -0
- praisonai/persistence/knowledge/weaviate.py +223 -0
- praisonai/persistence/migrations/__init__.py +10 -0
- praisonai/persistence/migrations/manager.py +251 -0
- praisonai/persistence/orchestrator.py +406 -0
- praisonai/persistence/state/__init__.py +21 -0
- praisonai/persistence/state/async_mongodb.py +200 -0
- praisonai/persistence/state/base.py +107 -0
- praisonai/persistence/state/dynamodb.py +226 -0
- praisonai/persistence/state/firestore.py +175 -0
- praisonai/persistence/state/gcs.py +155 -0
- praisonai/persistence/state/memory.py +245 -0
- praisonai/persistence/state/mongodb.py +158 -0
- praisonai/persistence/state/redis.py +190 -0
- praisonai/persistence/state/upstash.py +144 -0
- praisonai/persistence/tests/__init__.py +3 -0
- praisonai/persistence/tests/test_all_backends.py +633 -0
- praisonai/profiler.py +1214 -0
- praisonai/recipe/__init__.py +134 -0
- praisonai/recipe/bridge.py +278 -0
- praisonai/recipe/core.py +893 -0
- praisonai/recipe/exceptions.py +54 -0
- praisonai/recipe/history.py +402 -0
- praisonai/recipe/models.py +266 -0
- praisonai/recipe/operations.py +440 -0
- praisonai/recipe/policy.py +422 -0
- praisonai/recipe/registry.py +849 -0
- praisonai/recipe/runtime.py +214 -0
- praisonai/recipe/security.py +711 -0
- praisonai/recipe/serve.py +859 -0
- praisonai/recipe/server.py +613 -0
- praisonai/scheduler/__init__.py +45 -0
- praisonai/scheduler/agent_scheduler.py +552 -0
- praisonai/scheduler/base.py +124 -0
- praisonai/scheduler/daemon_manager.py +225 -0
- praisonai/scheduler/state_manager.py +155 -0
- praisonai/scheduler/yaml_loader.py +193 -0
- praisonai/scheduler.py +194 -0
- praisonai/setup/__init__.py +1 -0
- praisonai/setup/build.py +21 -0
- praisonai/setup/post_install.py +23 -0
- praisonai/setup/setup_conda_env.py +25 -0
- praisonai/setup.py +16 -0
- praisonai/templates/__init__.py +116 -0
- praisonai/templates/cache.py +364 -0
- praisonai/templates/dependency_checker.py +358 -0
- praisonai/templates/discovery.py +391 -0
- praisonai/templates/loader.py +564 -0
- praisonai/templates/registry.py +511 -0
- praisonai/templates/resolver.py +206 -0
- praisonai/templates/security.py +327 -0
- praisonai/templates/tool_override.py +498 -0
- praisonai/templates/tools_doctor.py +256 -0
- praisonai/test.py +105 -0
- praisonai/train.py +562 -0
- praisonai/train_vision.py +306 -0
- praisonai/ui/agents.py +824 -0
- praisonai/ui/callbacks.py +57 -0
- praisonai/ui/chainlit_compat.py +246 -0
- praisonai/ui/chat.py +532 -0
- praisonai/ui/code.py +717 -0
- praisonai/ui/colab.py +474 -0
- praisonai/ui/colab_chainlit.py +81 -0
- praisonai/ui/components/aicoder.py +284 -0
- praisonai/ui/context.py +283 -0
- praisonai/ui/database_config.py +56 -0
- praisonai/ui/db.py +294 -0
- praisonai/ui/realtime.py +488 -0
- praisonai/ui/realtimeclient/__init__.py +756 -0
- praisonai/ui/realtimeclient/tools.py +242 -0
- praisonai/ui/sql_alchemy.py +710 -0
- praisonai/upload_vision.py +140 -0
- praisonai/version.py +1 -0
- praisonai-3.0.0.dist-info/METADATA +3493 -0
- praisonai-3.0.0.dist-info/RECORD +393 -0
- praisonai-3.0.0.dist-info/WHEEL +5 -0
- praisonai-3.0.0.dist-info/entry_points.txt +4 -0
- praisonai-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool Override Loader for PraisonAI.
|
|
3
|
+
|
|
4
|
+
Allows loading custom tools from files, modules, and directories.
|
|
5
|
+
Supports runtime tool registration with context manager pattern.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import ast
|
|
9
|
+
import importlib.util
|
|
10
|
+
import sys
|
|
11
|
+
from contextlib import contextmanager
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Callable, Dict, Generator, List, Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SecurityError(Exception):
|
|
17
|
+
"""Raised when a security violation is detected."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ToolOverrideLoader:
|
|
22
|
+
"""
|
|
23
|
+
Loads custom tools from various sources.
|
|
24
|
+
|
|
25
|
+
Supports:
|
|
26
|
+
- Python file paths
|
|
27
|
+
- Module import paths
|
|
28
|
+
- Directories containing tool files
|
|
29
|
+
|
|
30
|
+
Security:
|
|
31
|
+
- Only local paths allowed by default
|
|
32
|
+
- Remote URLs rejected
|
|
33
|
+
- No auto-execution of arbitrary code
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
# Default custom tool directories
|
|
37
|
+
DEFAULT_TOOL_DIRS = [
|
|
38
|
+
"~/.praison/tools",
|
|
39
|
+
"~/.config/praison/tools",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
def __init__(self):
|
|
43
|
+
"""Initialize tool override loader."""
|
|
44
|
+
self._loaded_tools: Dict[str, Callable] = {}
|
|
45
|
+
|
|
46
|
+
def get_default_tool_dirs(self) -> List[Path]:
|
|
47
|
+
"""
|
|
48
|
+
Get default custom tool directories.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
List of Path objects for default tool directories
|
|
52
|
+
"""
|
|
53
|
+
return [Path(d).expanduser() for d in self.DEFAULT_TOOL_DIRS]
|
|
54
|
+
|
|
55
|
+
def load_from_file(self, file_path: str) -> Dict[str, Callable]:
|
|
56
|
+
"""
|
|
57
|
+
Load tools from a Python file.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
file_path: Path to Python file containing tool functions
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Dict mapping tool names to callable functions
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
SecurityError: If path is a remote URL
|
|
67
|
+
FileNotFoundError: If file doesn't exist
|
|
68
|
+
"""
|
|
69
|
+
# Security check - reject remote URLs
|
|
70
|
+
if file_path.startswith(("http://", "https://", "ftp://")):
|
|
71
|
+
raise SecurityError(
|
|
72
|
+
f"Remote URLs not allowed for security: {file_path}. "
|
|
73
|
+
"Only local file paths are permitted."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
path = Path(file_path).expanduser().resolve()
|
|
77
|
+
|
|
78
|
+
if not path.exists():
|
|
79
|
+
raise FileNotFoundError(f"Tool file not found: {path}")
|
|
80
|
+
|
|
81
|
+
if not path.suffix == ".py":
|
|
82
|
+
raise ValueError(f"Tool file must be a Python file (.py): {path}")
|
|
83
|
+
|
|
84
|
+
# Load the module
|
|
85
|
+
spec = importlib.util.spec_from_file_location(
|
|
86
|
+
f"custom_tools_{path.stem}",
|
|
87
|
+
path
|
|
88
|
+
)
|
|
89
|
+
if spec is None or spec.loader is None:
|
|
90
|
+
raise ImportError(f"Could not load module from: {path}")
|
|
91
|
+
|
|
92
|
+
module = importlib.util.module_from_spec(spec)
|
|
93
|
+
sys.modules[spec.name] = module
|
|
94
|
+
spec.loader.exec_module(module)
|
|
95
|
+
|
|
96
|
+
# Extract callable functions (tools)
|
|
97
|
+
tools = {}
|
|
98
|
+
for name in dir(module):
|
|
99
|
+
if name.startswith("_"):
|
|
100
|
+
continue
|
|
101
|
+
obj = getattr(module, name)
|
|
102
|
+
if callable(obj) and not isinstance(obj, type):
|
|
103
|
+
tools[name] = obj
|
|
104
|
+
|
|
105
|
+
self._loaded_tools.update(tools)
|
|
106
|
+
return tools
|
|
107
|
+
|
|
108
|
+
def load_from_module(self, module_path: str) -> Dict[str, Callable]:
|
|
109
|
+
"""
|
|
110
|
+
Load tools from a module import path.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
module_path: Python module path (e.g., 'mypackage.tools')
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Dict mapping tool names to callable functions
|
|
117
|
+
"""
|
|
118
|
+
try:
|
|
119
|
+
module = importlib.import_module(module_path)
|
|
120
|
+
except ImportError as e:
|
|
121
|
+
raise ImportError(f"Could not import module: {module_path}") from e
|
|
122
|
+
|
|
123
|
+
# Extract callable functions (tools)
|
|
124
|
+
tools = {}
|
|
125
|
+
for name in dir(module):
|
|
126
|
+
if name.startswith("_"):
|
|
127
|
+
continue
|
|
128
|
+
obj = getattr(module, name)
|
|
129
|
+
if callable(obj) and not isinstance(obj, type):
|
|
130
|
+
tools[name] = obj
|
|
131
|
+
|
|
132
|
+
self._loaded_tools.update(tools)
|
|
133
|
+
return tools
|
|
134
|
+
|
|
135
|
+
def load_from_directory(self, dir_path: str) -> Dict[str, Callable]:
|
|
136
|
+
"""
|
|
137
|
+
Load tools from all Python files in a directory.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
dir_path: Path to directory containing tool files
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Dict mapping tool names to callable functions
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
SecurityError: If path is a remote URL
|
|
147
|
+
"""
|
|
148
|
+
# Security check
|
|
149
|
+
if dir_path.startswith(("http://", "https://", "ftp://")):
|
|
150
|
+
raise SecurityError(
|
|
151
|
+
f"Remote URLs not allowed for security: {dir_path}. "
|
|
152
|
+
"Only local directory paths are permitted."
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
path = Path(dir_path).expanduser().resolve()
|
|
156
|
+
|
|
157
|
+
if not path.exists():
|
|
158
|
+
raise FileNotFoundError(f"Tool directory not found: {path}")
|
|
159
|
+
|
|
160
|
+
if not path.is_dir():
|
|
161
|
+
raise ValueError(f"Path is not a directory: {path}")
|
|
162
|
+
|
|
163
|
+
tools = {}
|
|
164
|
+
for py_file in path.glob("*.py"):
|
|
165
|
+
if py_file.name.startswith("_"):
|
|
166
|
+
continue
|
|
167
|
+
try:
|
|
168
|
+
file_tools = self.load_from_file(str(py_file))
|
|
169
|
+
tools.update(file_tools)
|
|
170
|
+
except Exception:
|
|
171
|
+
# Skip files that fail to load
|
|
172
|
+
pass
|
|
173
|
+
|
|
174
|
+
return tools
|
|
175
|
+
|
|
176
|
+
def discover_tools_in_directory(self, dir_path: str) -> List[str]:
|
|
177
|
+
"""
|
|
178
|
+
Discover tool names in a directory without executing code.
|
|
179
|
+
|
|
180
|
+
Uses AST parsing to find function definitions.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
dir_path: Path to directory to scan
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
List of discovered tool function names
|
|
187
|
+
"""
|
|
188
|
+
path = Path(dir_path).expanduser().resolve()
|
|
189
|
+
|
|
190
|
+
if not path.exists() or not path.is_dir():
|
|
191
|
+
return []
|
|
192
|
+
|
|
193
|
+
tool_names = []
|
|
194
|
+
for py_file in path.glob("*.py"):
|
|
195
|
+
if py_file.name.startswith("_"):
|
|
196
|
+
continue
|
|
197
|
+
try:
|
|
198
|
+
content = py_file.read_text()
|
|
199
|
+
tree = ast.parse(content)
|
|
200
|
+
|
|
201
|
+
for node in ast.walk(tree):
|
|
202
|
+
if isinstance(node, ast.FunctionDef):
|
|
203
|
+
if not node.name.startswith("_"):
|
|
204
|
+
tool_names.append(node.name)
|
|
205
|
+
except Exception:
|
|
206
|
+
# Skip files that fail to parse
|
|
207
|
+
pass
|
|
208
|
+
|
|
209
|
+
return tool_names
|
|
210
|
+
|
|
211
|
+
@contextmanager
|
|
212
|
+
def override_context(
|
|
213
|
+
self,
|
|
214
|
+
files: Optional[List[str]] = None,
|
|
215
|
+
modules: Optional[List[str]] = None,
|
|
216
|
+
directories: Optional[List[str]] = None
|
|
217
|
+
) -> Generator[Dict[str, Callable], None, None]:
|
|
218
|
+
"""
|
|
219
|
+
Context manager for temporary tool overrides.
|
|
220
|
+
|
|
221
|
+
Tools loaded within this context are available only during
|
|
222
|
+
the context and are cleaned up afterward.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
files: List of Python file paths to load
|
|
226
|
+
modules: List of module paths to import
|
|
227
|
+
directories: List of directories to scan
|
|
228
|
+
|
|
229
|
+
Yields:
|
|
230
|
+
Dict of loaded tools
|
|
231
|
+
"""
|
|
232
|
+
# Store original state
|
|
233
|
+
original_tools = self._loaded_tools.copy()
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
# Load tools from all sources
|
|
237
|
+
loaded = {}
|
|
238
|
+
|
|
239
|
+
if files:
|
|
240
|
+
for f in files:
|
|
241
|
+
loaded.update(self.load_from_file(f))
|
|
242
|
+
|
|
243
|
+
if modules:
|
|
244
|
+
for m in modules:
|
|
245
|
+
loaded.update(self.load_from_module(m))
|
|
246
|
+
|
|
247
|
+
if directories:
|
|
248
|
+
for d in directories:
|
|
249
|
+
loaded.update(self.load_from_directory(d))
|
|
250
|
+
|
|
251
|
+
yield loaded
|
|
252
|
+
|
|
253
|
+
finally:
|
|
254
|
+
# Restore original state
|
|
255
|
+
self._loaded_tools = original_tools
|
|
256
|
+
|
|
257
|
+
def get_loaded_tools(self) -> Dict[str, Callable]:
|
|
258
|
+
"""
|
|
259
|
+
Get all currently loaded tools.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Dict mapping tool names to callable functions
|
|
263
|
+
"""
|
|
264
|
+
return self._loaded_tools.copy()
|
|
265
|
+
|
|
266
|
+
def clear_loaded_tools(self) -> None:
|
|
267
|
+
"""Clear all loaded tools."""
|
|
268
|
+
self._loaded_tools.clear()
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def create_tool_registry_with_overrides(
|
|
272
|
+
override_files: Optional[List[str]] = None,
|
|
273
|
+
override_dirs: Optional[List[str]] = None,
|
|
274
|
+
include_defaults: bool = True,
|
|
275
|
+
tools_sources: Optional[List[str]] = None,
|
|
276
|
+
template_dir: Optional[str] = None,
|
|
277
|
+
) -> Dict[str, Callable]:
|
|
278
|
+
"""
|
|
279
|
+
Create a tool registry with custom overrides.
|
|
280
|
+
|
|
281
|
+
Resolution order (highest priority first):
|
|
282
|
+
1. Override files (explicit CLI --tools)
|
|
283
|
+
2. Override directories (explicit CLI --tools-dir)
|
|
284
|
+
3. Template tools_sources (from TEMPLATE.yaml)
|
|
285
|
+
4. Template-local tools.py
|
|
286
|
+
4.5. Current working directory tools.py (./tools.py)
|
|
287
|
+
5. Default custom dirs (~/.praison/tools, etc.)
|
|
288
|
+
6. Package discovery (praisonai-tools if installed)
|
|
289
|
+
7. Built-in tools
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
override_files: Explicit tool files to load
|
|
293
|
+
override_dirs: Directories to scan for tools
|
|
294
|
+
include_defaults: Whether to include default tool directories
|
|
295
|
+
tools_sources: Template-declared tool sources (modules or paths)
|
|
296
|
+
template_dir: Template directory for local tools.py
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
Dict mapping tool names to callable functions
|
|
300
|
+
"""
|
|
301
|
+
registry = {}
|
|
302
|
+
loader = ToolOverrideLoader()
|
|
303
|
+
|
|
304
|
+
# 7. Start with built-in tools (lowest priority)
|
|
305
|
+
try:
|
|
306
|
+
from praisonaiagents.tools import TOOL_MAPPINGS
|
|
307
|
+
registry.update(TOOL_MAPPINGS)
|
|
308
|
+
except ImportError:
|
|
309
|
+
pass
|
|
310
|
+
|
|
311
|
+
# 6. Package discovery - try praisonai-tools if installed
|
|
312
|
+
try:
|
|
313
|
+
import praisonai_tools.tools as external_tools
|
|
314
|
+
# Get all exported tools from praisonai_tools
|
|
315
|
+
for name in dir(external_tools):
|
|
316
|
+
if not name.startswith('_'):
|
|
317
|
+
obj = getattr(external_tools, name, None)
|
|
318
|
+
if callable(obj) or (hasattr(obj, 'run') and callable(getattr(obj, 'run', None))):
|
|
319
|
+
registry[name] = obj
|
|
320
|
+
except ImportError:
|
|
321
|
+
pass
|
|
322
|
+
|
|
323
|
+
# 5. Add default custom dirs
|
|
324
|
+
if include_defaults:
|
|
325
|
+
for dir_path in loader.get_default_tool_dirs():
|
|
326
|
+
if dir_path.exists():
|
|
327
|
+
try:
|
|
328
|
+
tools = loader.load_from_directory(str(dir_path))
|
|
329
|
+
registry.update(tools)
|
|
330
|
+
except Exception:
|
|
331
|
+
pass
|
|
332
|
+
|
|
333
|
+
# 4.5. Current working directory tools.py (if exists)
|
|
334
|
+
cwd_tools_py = Path.cwd() / "tools.py"
|
|
335
|
+
if cwd_tools_py.exists():
|
|
336
|
+
try:
|
|
337
|
+
tools = loader.load_from_file(str(cwd_tools_py))
|
|
338
|
+
registry.update(tools)
|
|
339
|
+
except Exception:
|
|
340
|
+
pass
|
|
341
|
+
|
|
342
|
+
# 4. Template-local tools.py
|
|
343
|
+
if template_dir:
|
|
344
|
+
tools_py = Path(template_dir) / "tools.py"
|
|
345
|
+
if tools_py.exists():
|
|
346
|
+
try:
|
|
347
|
+
tools = loader.load_from_file(str(tools_py))
|
|
348
|
+
registry.update(tools)
|
|
349
|
+
except Exception:
|
|
350
|
+
pass
|
|
351
|
+
|
|
352
|
+
# 3. Template tools_sources (from TEMPLATE.yaml)
|
|
353
|
+
if tools_sources:
|
|
354
|
+
for source in tools_sources:
|
|
355
|
+
try:
|
|
356
|
+
# Security: only allow local paths and python modules
|
|
357
|
+
if source.startswith(("http://", "https://", "ftp://")):
|
|
358
|
+
continue # Skip remote URLs
|
|
359
|
+
|
|
360
|
+
source_path = Path(source).expanduser()
|
|
361
|
+
|
|
362
|
+
if source_path.exists():
|
|
363
|
+
# It's a local path
|
|
364
|
+
if source_path.is_file() and source_path.suffix == ".py":
|
|
365
|
+
tools = loader.load_from_file(str(source_path))
|
|
366
|
+
registry.update(tools)
|
|
367
|
+
elif source_path.is_dir():
|
|
368
|
+
tools = loader.load_from_directory(str(source_path))
|
|
369
|
+
registry.update(tools)
|
|
370
|
+
else:
|
|
371
|
+
# Try as a Python module path
|
|
372
|
+
tools = loader.load_from_module(source)
|
|
373
|
+
registry.update(tools)
|
|
374
|
+
except Exception:
|
|
375
|
+
pass
|
|
376
|
+
|
|
377
|
+
# 2. Add override directories (CLI --tools-dir)
|
|
378
|
+
if override_dirs:
|
|
379
|
+
for dir_path in override_dirs:
|
|
380
|
+
try:
|
|
381
|
+
tools = loader.load_from_directory(dir_path)
|
|
382
|
+
registry.update(tools)
|
|
383
|
+
except Exception:
|
|
384
|
+
pass
|
|
385
|
+
|
|
386
|
+
# 1. Add override files (highest priority, CLI --tools)
|
|
387
|
+
if override_files:
|
|
388
|
+
for file_path in override_files:
|
|
389
|
+
try:
|
|
390
|
+
tools = loader.load_from_file(file_path)
|
|
391
|
+
registry.update(tools)
|
|
392
|
+
except Exception:
|
|
393
|
+
pass
|
|
394
|
+
|
|
395
|
+
return registry
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def resolve_tools(
|
|
399
|
+
tool_names: List[Any],
|
|
400
|
+
registry: Optional[Dict[str, Callable]] = None,
|
|
401
|
+
template_dir: Optional[str] = None,
|
|
402
|
+
) -> List[Callable]:
|
|
403
|
+
"""
|
|
404
|
+
Resolve tool names to callable tools from registry.
|
|
405
|
+
|
|
406
|
+
Handles:
|
|
407
|
+
- String tool names (looked up in registry)
|
|
408
|
+
- Already-callable tools (passed through)
|
|
409
|
+
- Built-in tool names (shell_tool, file_tool, etc.)
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
tool_names: List of tool names (strings) or callables
|
|
413
|
+
registry: Tool registry to look up names in
|
|
414
|
+
template_dir: Optional template directory for local tools.py autoload
|
|
415
|
+
|
|
416
|
+
Returns:
|
|
417
|
+
List of resolved callable tools
|
|
418
|
+
"""
|
|
419
|
+
if not tool_names:
|
|
420
|
+
return []
|
|
421
|
+
|
|
422
|
+
resolved = []
|
|
423
|
+
|
|
424
|
+
# Build registry if not provided
|
|
425
|
+
if registry is None:
|
|
426
|
+
registry = create_tool_registry_with_overrides(include_defaults=True)
|
|
427
|
+
|
|
428
|
+
# Load template-local tools.py if exists
|
|
429
|
+
if template_dir:
|
|
430
|
+
loader = ToolOverrideLoader()
|
|
431
|
+
tools_py = Path(template_dir) / "tools.py"
|
|
432
|
+
if tools_py.exists():
|
|
433
|
+
try:
|
|
434
|
+
local_tools = loader.load_from_file(str(tools_py))
|
|
435
|
+
registry.update(local_tools)
|
|
436
|
+
except Exception:
|
|
437
|
+
pass
|
|
438
|
+
|
|
439
|
+
for tool in tool_names:
|
|
440
|
+
if callable(tool):
|
|
441
|
+
# Already a callable, use directly
|
|
442
|
+
resolved.append(tool)
|
|
443
|
+
elif isinstance(tool, str):
|
|
444
|
+
# Look up by name in registry
|
|
445
|
+
tool_name = tool.strip()
|
|
446
|
+
|
|
447
|
+
# Try exact match first
|
|
448
|
+
if tool_name in registry:
|
|
449
|
+
tool_obj = registry[tool_name]
|
|
450
|
+
# Handle lazy-loaded tools (tuples of module, class)
|
|
451
|
+
if isinstance(tool_obj, tuple):
|
|
452
|
+
try:
|
|
453
|
+
module_name, class_name = tool_obj
|
|
454
|
+
import importlib
|
|
455
|
+
module = importlib.import_module(module_name)
|
|
456
|
+
tool_class = getattr(module, class_name)
|
|
457
|
+
resolved.append(tool_class())
|
|
458
|
+
except Exception:
|
|
459
|
+
pass
|
|
460
|
+
elif callable(tool_obj):
|
|
461
|
+
resolved.append(tool_obj)
|
|
462
|
+
else:
|
|
463
|
+
# Try to instantiate if it's a class
|
|
464
|
+
try:
|
|
465
|
+
resolved.append(tool_obj())
|
|
466
|
+
except Exception:
|
|
467
|
+
resolved.append(tool_obj)
|
|
468
|
+
else:
|
|
469
|
+
# Try common variations
|
|
470
|
+
variations = [
|
|
471
|
+
tool_name,
|
|
472
|
+
tool_name.lower(),
|
|
473
|
+
tool_name.replace("-", "_"),
|
|
474
|
+
tool_name.replace("_", "-"),
|
|
475
|
+
f"{tool_name}_tool",
|
|
476
|
+
f"{tool_name}Tool",
|
|
477
|
+
]
|
|
478
|
+
found = False
|
|
479
|
+
for var in variations:
|
|
480
|
+
if var in registry:
|
|
481
|
+
tool_obj = registry[var]
|
|
482
|
+
if callable(tool_obj):
|
|
483
|
+
resolved.append(tool_obj)
|
|
484
|
+
found = True
|
|
485
|
+
break
|
|
486
|
+
|
|
487
|
+
if not found:
|
|
488
|
+
# Try to import from praisonaiagents.tools
|
|
489
|
+
try:
|
|
490
|
+
from praisonaiagents import tools as agent_tools
|
|
491
|
+
if hasattr(agent_tools, tool_name):
|
|
492
|
+
resolved.append(getattr(agent_tools, tool_name))
|
|
493
|
+
elif hasattr(agent_tools, f"{tool_name}_tool"):
|
|
494
|
+
resolved.append(getattr(agent_tools, f"{tool_name}_tool"))
|
|
495
|
+
except (ImportError, AttributeError):
|
|
496
|
+
pass
|
|
497
|
+
|
|
498
|
+
return resolved
|