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,564 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Template Loader
|
|
3
|
+
|
|
4
|
+
Loads and materializes templates into Agent/Workflow configurations.
|
|
5
|
+
Handles TEMPLATE.yaml parsing, config merging, and skills integration.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
import secrets
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
|
|
15
|
+
from .resolver import ResolvedTemplate # noqa: F401 - used by registry
|
|
16
|
+
from .cache import TemplateCache
|
|
17
|
+
from .registry import TemplateRegistry
|
|
18
|
+
from .security import TemplateSecurity
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class TemplateConfig:
|
|
23
|
+
"""Parsed template configuration."""
|
|
24
|
+
name: str
|
|
25
|
+
description: str = ""
|
|
26
|
+
version: str = "1.0.0"
|
|
27
|
+
author: Optional[str] = None
|
|
28
|
+
license: Optional[str] = None
|
|
29
|
+
tags: List[str] = field(default_factory=list)
|
|
30
|
+
|
|
31
|
+
# Dependencies
|
|
32
|
+
requires: Dict[str, Any] = field(default_factory=dict)
|
|
33
|
+
|
|
34
|
+
# Entry points
|
|
35
|
+
workflow_file: str = "workflow.yaml"
|
|
36
|
+
agents_file: str = "agents.yaml"
|
|
37
|
+
|
|
38
|
+
# Configuration schema
|
|
39
|
+
config_schema: Dict[str, Any] = field(default_factory=dict)
|
|
40
|
+
|
|
41
|
+
# Default configuration values
|
|
42
|
+
defaults: Dict[str, Any] = field(default_factory=dict)
|
|
43
|
+
|
|
44
|
+
# Skills to load
|
|
45
|
+
skills: List[str] = field(default_factory=list)
|
|
46
|
+
|
|
47
|
+
# CLI integration
|
|
48
|
+
cli: Dict[str, Any] = field(default_factory=dict)
|
|
49
|
+
|
|
50
|
+
# Runtime configuration (background/job/schedule)
|
|
51
|
+
runtime: Optional[Any] = None # RuntimeConfig, lazy loaded
|
|
52
|
+
|
|
53
|
+
# Raw config dict
|
|
54
|
+
raw: Dict[str, Any] = field(default_factory=dict)
|
|
55
|
+
|
|
56
|
+
# Source path
|
|
57
|
+
path: Optional[Path] = None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TemplateLoader:
|
|
61
|
+
"""
|
|
62
|
+
Loads templates and materializes them into usable configurations.
|
|
63
|
+
|
|
64
|
+
Supports:
|
|
65
|
+
- TEMPLATE.yaml parsing
|
|
66
|
+
- Config override merging
|
|
67
|
+
- Skills integration
|
|
68
|
+
- Workflow/Agent file loading
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
TEMPLATE_FILE = "TEMPLATE.yaml"
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
cache: Optional[TemplateCache] = None,
|
|
76
|
+
registry: Optional[TemplateRegistry] = None,
|
|
77
|
+
security: Optional[TemplateSecurity] = None,
|
|
78
|
+
offline: bool = False
|
|
79
|
+
):
|
|
80
|
+
"""
|
|
81
|
+
Initialize the loader.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
cache: Template cache instance
|
|
85
|
+
registry: Template registry instance
|
|
86
|
+
security: Security handler instance
|
|
87
|
+
offline: If True, only use cached templates
|
|
88
|
+
"""
|
|
89
|
+
self.cache = cache or TemplateCache()
|
|
90
|
+
self.registry = registry or TemplateRegistry(cache=self.cache, offline=offline)
|
|
91
|
+
self.security = security or TemplateSecurity()
|
|
92
|
+
self.offline = offline
|
|
93
|
+
|
|
94
|
+
def load(
|
|
95
|
+
self,
|
|
96
|
+
uri: str,
|
|
97
|
+
config: Optional[Dict[str, Any]] = None,
|
|
98
|
+
offline: bool = False
|
|
99
|
+
) -> TemplateConfig:
|
|
100
|
+
"""
|
|
101
|
+
Load a template by URI.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
uri: Template URI
|
|
105
|
+
config: Optional config overrides
|
|
106
|
+
offline: If True, only use cache
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
TemplateConfig with parsed configuration
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
ValueError: If template not found or invalid
|
|
113
|
+
SecurityError: If template fails security checks
|
|
114
|
+
"""
|
|
115
|
+
# Security check
|
|
116
|
+
if not self.security.is_source_allowed(uri):
|
|
117
|
+
raise ValueError(f"Template source not allowed: {uri}")
|
|
118
|
+
|
|
119
|
+
# Get template (from cache or remote)
|
|
120
|
+
use_offline = offline or self.offline
|
|
121
|
+
cached = self.registry.get_template(uri, offline=use_offline)
|
|
122
|
+
|
|
123
|
+
# Validate security
|
|
124
|
+
errors = self.security.validate_template_directory(cached.path)
|
|
125
|
+
if errors:
|
|
126
|
+
raise ValueError("Template security validation failed:\n" + "\n".join(errors))
|
|
127
|
+
|
|
128
|
+
# Parse TEMPLATE.yaml
|
|
129
|
+
template_config = self._parse_template_file(cached.path)
|
|
130
|
+
template_config.path = cached.path
|
|
131
|
+
|
|
132
|
+
# Merge config overrides
|
|
133
|
+
if config:
|
|
134
|
+
template_config = self._merge_config(template_config, config)
|
|
135
|
+
|
|
136
|
+
return template_config
|
|
137
|
+
|
|
138
|
+
def _parse_template_file(self, template_dir: Path) -> TemplateConfig:
|
|
139
|
+
"""Parse TEMPLATE.yaml file."""
|
|
140
|
+
import yaml
|
|
141
|
+
|
|
142
|
+
template_file = template_dir / self.TEMPLATE_FILE
|
|
143
|
+
|
|
144
|
+
if not template_file.exists():
|
|
145
|
+
# Try to infer from directory name
|
|
146
|
+
return TemplateConfig(
|
|
147
|
+
name=template_dir.name,
|
|
148
|
+
path=template_dir
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
with open(template_file) as f:
|
|
152
|
+
raw = yaml.safe_load(f) or {}
|
|
153
|
+
|
|
154
|
+
# Sanitize config
|
|
155
|
+
raw = self.security.sanitize_template_config(raw)
|
|
156
|
+
|
|
157
|
+
# Extract requires
|
|
158
|
+
requires = raw.get("requires", {})
|
|
159
|
+
if isinstance(requires, list):
|
|
160
|
+
requires = {"tools": requires}
|
|
161
|
+
|
|
162
|
+
# Handle workflow - can be inline dict or file reference string
|
|
163
|
+
workflow = raw.get("workflow", "workflow.yaml")
|
|
164
|
+
|
|
165
|
+
# Handle agents - can be inline list or file reference string
|
|
166
|
+
agents = raw.get("agents", "agents.yaml")
|
|
167
|
+
|
|
168
|
+
# Parse runtime configuration (lazy import to avoid circular deps)
|
|
169
|
+
runtime = None
|
|
170
|
+
if "runtime" in raw:
|
|
171
|
+
from praisonai.recipe.runtime import parse_runtime_config
|
|
172
|
+
runtime = parse_runtime_config(raw.get("runtime"), expand_env=True)
|
|
173
|
+
|
|
174
|
+
return TemplateConfig(
|
|
175
|
+
name=raw.get("name", template_dir.name),
|
|
176
|
+
description=raw.get("description", ""),
|
|
177
|
+
version=raw.get("version", "1.0.0"),
|
|
178
|
+
author=raw.get("author"),
|
|
179
|
+
license=raw.get("license"),
|
|
180
|
+
tags=raw.get("tags", []),
|
|
181
|
+
requires=requires,
|
|
182
|
+
workflow_file=workflow,
|
|
183
|
+
agents_file=agents,
|
|
184
|
+
config_schema=raw.get("config", {}),
|
|
185
|
+
defaults=raw.get("defaults", {}),
|
|
186
|
+
skills=raw.get("skills", []),
|
|
187
|
+
cli=raw.get("cli", {}),
|
|
188
|
+
runtime=runtime,
|
|
189
|
+
raw=raw,
|
|
190
|
+
path=template_dir
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def _merge_config(
|
|
194
|
+
self,
|
|
195
|
+
template: TemplateConfig,
|
|
196
|
+
overrides: Dict[str, Any]
|
|
197
|
+
) -> TemplateConfig:
|
|
198
|
+
"""Merge config overrides into template config."""
|
|
199
|
+
# Start with defaults
|
|
200
|
+
merged = dict(template.defaults)
|
|
201
|
+
|
|
202
|
+
# Apply overrides
|
|
203
|
+
merged.update(overrides)
|
|
204
|
+
|
|
205
|
+
# Update template
|
|
206
|
+
template.defaults = merged
|
|
207
|
+
|
|
208
|
+
return template
|
|
209
|
+
|
|
210
|
+
def load_workflow_config(
|
|
211
|
+
self,
|
|
212
|
+
template: TemplateConfig
|
|
213
|
+
) -> Dict[str, Any]:
|
|
214
|
+
"""
|
|
215
|
+
Load workflow configuration from template.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
template: Parsed template config
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Workflow configuration dict
|
|
222
|
+
|
|
223
|
+
Supports:
|
|
224
|
+
- Inline workflow definition in TEMPLATE.yaml (workflow: {agents: [...], tasks: [...]})
|
|
225
|
+
- Separate workflow file reference (workflow: "workflow.yaml")
|
|
226
|
+
"""
|
|
227
|
+
import yaml
|
|
228
|
+
|
|
229
|
+
# Check if workflow is inline (dict) or a file reference (string)
|
|
230
|
+
if isinstance(template.workflow_file, dict):
|
|
231
|
+
# Inline workflow definition
|
|
232
|
+
config = template.workflow_file
|
|
233
|
+
elif isinstance(template.workflow_file, str):
|
|
234
|
+
# File reference
|
|
235
|
+
workflow_file = template.path / template.workflow_file
|
|
236
|
+
|
|
237
|
+
if not workflow_file.exists():
|
|
238
|
+
# Check if workflow is in raw config
|
|
239
|
+
if "workflow" in template.raw and isinstance(template.raw["workflow"], dict):
|
|
240
|
+
config = template.raw["workflow"]
|
|
241
|
+
else:
|
|
242
|
+
raise ValueError(f"Workflow file not found: {workflow_file}")
|
|
243
|
+
else:
|
|
244
|
+
with open(workflow_file) as f:
|
|
245
|
+
config = yaml.safe_load(f) or {}
|
|
246
|
+
else:
|
|
247
|
+
raise ValueError(f"Invalid workflow configuration type: {type(template.workflow_file)}")
|
|
248
|
+
|
|
249
|
+
# Substitute variables from template config
|
|
250
|
+
config = self._substitute_variables(config, template.defaults)
|
|
251
|
+
|
|
252
|
+
return config
|
|
253
|
+
|
|
254
|
+
def load_agents_config(
|
|
255
|
+
self,
|
|
256
|
+
template: TemplateConfig
|
|
257
|
+
) -> Dict[str, Any]:
|
|
258
|
+
"""
|
|
259
|
+
Load agents configuration from template.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
template: Parsed template config
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
Agents configuration dict
|
|
266
|
+
"""
|
|
267
|
+
import yaml
|
|
268
|
+
|
|
269
|
+
agents_file = template.path / template.agents_file
|
|
270
|
+
|
|
271
|
+
if not agents_file.exists():
|
|
272
|
+
# Try workflow file for inline agents
|
|
273
|
+
return {}
|
|
274
|
+
|
|
275
|
+
with open(agents_file) as f:
|
|
276
|
+
config = yaml.safe_load(f) or {}
|
|
277
|
+
|
|
278
|
+
# Substitute variables
|
|
279
|
+
config = self._substitute_variables(config, template.defaults)
|
|
280
|
+
|
|
281
|
+
return config
|
|
282
|
+
|
|
283
|
+
def _substitute_variables(
|
|
284
|
+
self,
|
|
285
|
+
config: Any,
|
|
286
|
+
variables: Dict[str, Any]
|
|
287
|
+
) -> Any:
|
|
288
|
+
"""
|
|
289
|
+
Recursively substitute variables in config with sentinel protection.
|
|
290
|
+
|
|
291
|
+
This method implements a secure multi-phase substitution strategy:
|
|
292
|
+
1. Generate a unique sentinel token for this render
|
|
293
|
+
2. Protect user-provided variable values that contain {{...}} syntax
|
|
294
|
+
3. Perform template variable substitution on the config
|
|
295
|
+
4. Restore protected values
|
|
296
|
+
|
|
297
|
+
This prevents user input containing {{variable}} syntax from being
|
|
298
|
+
interpreted as template variables (injection protection).
|
|
299
|
+
"""
|
|
300
|
+
if isinstance(config, str):
|
|
301
|
+
# Generate per-render unique sentinel to prevent collision attacks
|
|
302
|
+
sentinel_id = secrets.token_hex(8)
|
|
303
|
+
sentinel_prefix = f"__PRAISONAI_SENTINEL_{sentinel_id}_"
|
|
304
|
+
|
|
305
|
+
# Phase 1: Protect variable values that contain template syntax
|
|
306
|
+
protected_values, safe_variables = self._protect_variable_values(
|
|
307
|
+
variables, sentinel_prefix
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# Phase 2: Perform substitution with safe values
|
|
311
|
+
pattern = r'\{\{(\w+)\}\}'
|
|
312
|
+
|
|
313
|
+
def replace(match):
|
|
314
|
+
var_name = match.group(1)
|
|
315
|
+
return str(safe_variables.get(var_name, match.group(0)))
|
|
316
|
+
|
|
317
|
+
result = re.sub(pattern, replace, config)
|
|
318
|
+
|
|
319
|
+
# Phase 3: Restore protected values
|
|
320
|
+
result = self._restore_protected_values(result, protected_values)
|
|
321
|
+
|
|
322
|
+
return result
|
|
323
|
+
|
|
324
|
+
elif isinstance(config, dict):
|
|
325
|
+
return {k: self._substitute_variables(v, variables) for k, v in config.items()}
|
|
326
|
+
|
|
327
|
+
elif isinstance(config, list):
|
|
328
|
+
return [self._substitute_variables(item, variables) for item in config]
|
|
329
|
+
|
|
330
|
+
return config
|
|
331
|
+
|
|
332
|
+
def _protect_variable_values(
|
|
333
|
+
self,
|
|
334
|
+
variables: Dict[str, Any],
|
|
335
|
+
sentinel_prefix: str
|
|
336
|
+
) -> Tuple[Dict[str, str], Dict[str, Any]]:
|
|
337
|
+
"""
|
|
338
|
+
Protect variable values that contain template syntax.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
variables: Original variable dict
|
|
342
|
+
sentinel_prefix: Unique prefix for this render
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
Tuple of (protected_values mapping, safe_variables dict)
|
|
346
|
+
"""
|
|
347
|
+
protected_values = {} # sentinel -> original value
|
|
348
|
+
safe_variables = {}
|
|
349
|
+
|
|
350
|
+
template_pattern = re.compile(r'\{\{.*?\}\}')
|
|
351
|
+
|
|
352
|
+
for key, value in variables.items():
|
|
353
|
+
if isinstance(value, str) and template_pattern.search(value):
|
|
354
|
+
# This value contains template syntax - protect it
|
|
355
|
+
sentinel = f"{sentinel_prefix}{key}__"
|
|
356
|
+
protected_values[sentinel] = value
|
|
357
|
+
safe_variables[key] = sentinel
|
|
358
|
+
else:
|
|
359
|
+
safe_variables[key] = value
|
|
360
|
+
|
|
361
|
+
return protected_values, safe_variables
|
|
362
|
+
|
|
363
|
+
def _restore_protected_values(
|
|
364
|
+
self,
|
|
365
|
+
content: str,
|
|
366
|
+
protected_values: Dict[str, str]
|
|
367
|
+
) -> str:
|
|
368
|
+
"""
|
|
369
|
+
Restore protected values after substitution.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
content: Content with sentinel tokens
|
|
373
|
+
protected_values: Mapping of sentinel -> original value
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
Content with sentinels replaced by original values
|
|
377
|
+
"""
|
|
378
|
+
for sentinel, original in protected_values.items():
|
|
379
|
+
content = content.replace(sentinel, original)
|
|
380
|
+
return content
|
|
381
|
+
|
|
382
|
+
def get_required_tools(self, template: TemplateConfig) -> List[str]:
|
|
383
|
+
"""Get list of required tools for a template."""
|
|
384
|
+
tools = template.requires.get("tools", [])
|
|
385
|
+
if isinstance(tools, str):
|
|
386
|
+
tools = [tools]
|
|
387
|
+
return tools
|
|
388
|
+
|
|
389
|
+
def get_required_packages(self, template: TemplateConfig) -> List[str]:
|
|
390
|
+
"""Get list of required Python packages for a template."""
|
|
391
|
+
packages = template.requires.get("packages", [])
|
|
392
|
+
if isinstance(packages, str):
|
|
393
|
+
packages = [packages]
|
|
394
|
+
return packages
|
|
395
|
+
|
|
396
|
+
def get_required_env(self, template: TemplateConfig) -> List[str]:
|
|
397
|
+
"""Get list of required environment variables for a template."""
|
|
398
|
+
env = template.requires.get("env", [])
|
|
399
|
+
if isinstance(env, str):
|
|
400
|
+
env = [env]
|
|
401
|
+
return env
|
|
402
|
+
|
|
403
|
+
def check_requirements(
|
|
404
|
+
self,
|
|
405
|
+
template: TemplateConfig
|
|
406
|
+
) -> Dict[str, List[str]]:
|
|
407
|
+
"""
|
|
408
|
+
Check if template requirements are satisfied.
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
Dict with 'missing_packages', 'missing_env', 'missing_tools' lists
|
|
412
|
+
"""
|
|
413
|
+
result = {
|
|
414
|
+
"missing_packages": [],
|
|
415
|
+
"missing_env": [],
|
|
416
|
+
"missing_tools": []
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
# Check packages
|
|
420
|
+
for package in self.get_required_packages(template):
|
|
421
|
+
try:
|
|
422
|
+
__import__(package.replace("-", "_"))
|
|
423
|
+
except ImportError:
|
|
424
|
+
result["missing_packages"].append(package)
|
|
425
|
+
|
|
426
|
+
# Check environment variables
|
|
427
|
+
for env_var in self.get_required_env(template):
|
|
428
|
+
if not os.environ.get(env_var):
|
|
429
|
+
result["missing_env"].append(env_var)
|
|
430
|
+
|
|
431
|
+
return result
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def load_template(
|
|
435
|
+
uri: str,
|
|
436
|
+
config: Optional[Dict[str, Any]] = None,
|
|
437
|
+
offline: bool = False
|
|
438
|
+
) -> TemplateConfig:
|
|
439
|
+
"""
|
|
440
|
+
Convenience function to load a template.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
uri: Template URI
|
|
444
|
+
config: Optional config overrides
|
|
445
|
+
offline: If True, only use cache
|
|
446
|
+
|
|
447
|
+
Returns:
|
|
448
|
+
TemplateConfig with parsed configuration
|
|
449
|
+
"""
|
|
450
|
+
loader = TemplateLoader(offline=offline)
|
|
451
|
+
return loader.load(uri, config=config, offline=offline)
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def create_agent_from_template(
|
|
455
|
+
uri: str,
|
|
456
|
+
config: Optional[Dict[str, Any]] = None,
|
|
457
|
+
offline: bool = False,
|
|
458
|
+
**agent_kwargs
|
|
459
|
+
):
|
|
460
|
+
"""
|
|
461
|
+
Create an Agent from a template.
|
|
462
|
+
|
|
463
|
+
Args:
|
|
464
|
+
uri: Template URI
|
|
465
|
+
config: Optional config overrides
|
|
466
|
+
offline: If True, only use cache
|
|
467
|
+
**agent_kwargs: Additional Agent constructor arguments
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
Configured Agent instance
|
|
471
|
+
"""
|
|
472
|
+
loader = TemplateLoader(offline=offline)
|
|
473
|
+
template = loader.load(uri, config=config, offline=offline)
|
|
474
|
+
|
|
475
|
+
# Load agents config
|
|
476
|
+
agents_config = loader.load_agents_config(template)
|
|
477
|
+
|
|
478
|
+
# Get first agent or use template defaults
|
|
479
|
+
if agents_config and "agents" in agents_config:
|
|
480
|
+
agent_list = list(agents_config["agents"].values())
|
|
481
|
+
if agent_list:
|
|
482
|
+
agent_config = agent_list[0]
|
|
483
|
+
else:
|
|
484
|
+
agent_config = {}
|
|
485
|
+
else:
|
|
486
|
+
agent_config = {}
|
|
487
|
+
|
|
488
|
+
# Merge with kwargs
|
|
489
|
+
final_config = {**agent_config, **agent_kwargs}
|
|
490
|
+
|
|
491
|
+
# Import Agent lazily
|
|
492
|
+
from praisonaiagents import Agent
|
|
493
|
+
|
|
494
|
+
# Handle skills
|
|
495
|
+
if template.skills:
|
|
496
|
+
final_config["skills"] = template.skills
|
|
497
|
+
|
|
498
|
+
return Agent(**final_config)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def create_workflow_from_template(
|
|
502
|
+
uri: str,
|
|
503
|
+
config: Optional[Dict[str, Any]] = None,
|
|
504
|
+
offline: bool = False,
|
|
505
|
+
**workflow_kwargs
|
|
506
|
+
):
|
|
507
|
+
"""
|
|
508
|
+
Create a Workflow from a template.
|
|
509
|
+
|
|
510
|
+
Args:
|
|
511
|
+
uri: Template URI
|
|
512
|
+
config: Optional config overrides
|
|
513
|
+
offline: If True, only use cache
|
|
514
|
+
**workflow_kwargs: Additional Workflow constructor arguments
|
|
515
|
+
|
|
516
|
+
Returns:
|
|
517
|
+
Configured Workflow instance
|
|
518
|
+
"""
|
|
519
|
+
loader = TemplateLoader(offline=offline)
|
|
520
|
+
template = loader.load(uri, config=config, offline=offline)
|
|
521
|
+
|
|
522
|
+
# Load workflow config
|
|
523
|
+
workflow_config = loader.load_workflow_config(template)
|
|
524
|
+
|
|
525
|
+
# Merge with kwargs
|
|
526
|
+
final_config = {**workflow_config, **workflow_kwargs}
|
|
527
|
+
|
|
528
|
+
# Import Workflow lazily
|
|
529
|
+
from praisonaiagents import Workflow
|
|
530
|
+
|
|
531
|
+
return Workflow(**final_config)
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def create_agents_from_template(
|
|
535
|
+
uri: str,
|
|
536
|
+
config: Optional[Dict[str, Any]] = None,
|
|
537
|
+
offline: bool = False,
|
|
538
|
+
**agents_kwargs
|
|
539
|
+
):
|
|
540
|
+
"""
|
|
541
|
+
Create a PraisonAIAgents team from a template.
|
|
542
|
+
|
|
543
|
+
Args:
|
|
544
|
+
uri: Template URI
|
|
545
|
+
config: Optional config overrides
|
|
546
|
+
offline: If True, only use cache
|
|
547
|
+
**agents_kwargs: Additional PraisonAIAgents constructor arguments
|
|
548
|
+
|
|
549
|
+
Returns:
|
|
550
|
+
Configured PraisonAIAgents instance
|
|
551
|
+
"""
|
|
552
|
+
loader = TemplateLoader(offline=offline)
|
|
553
|
+
template = loader.load(uri, config=config, offline=offline)
|
|
554
|
+
|
|
555
|
+
# Load workflow config (contains agents and tasks)
|
|
556
|
+
workflow_config = loader.load_workflow_config(template)
|
|
557
|
+
|
|
558
|
+
# Import PraisonAIAgents lazily
|
|
559
|
+
from praisonaiagents import PraisonAIAgents
|
|
560
|
+
|
|
561
|
+
# Merge with kwargs
|
|
562
|
+
final_config = {**workflow_config, **agents_kwargs}
|
|
563
|
+
|
|
564
|
+
return PraisonAIAgents(**final_config)
|