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,511 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Template Registry
|
|
3
|
+
|
|
4
|
+
Handles fetching templates from remote sources (GitHub, HTTP).
|
|
5
|
+
All network operations are lazy and only performed when explicitly requested.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import hashlib
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import tempfile
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Dict, List, Optional
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
|
|
16
|
+
from .resolver import ResolvedTemplate, TemplateResolver, TemplateSource
|
|
17
|
+
from .cache import TemplateCache, CachedTemplate
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class TemplateInfo:
|
|
22
|
+
"""Information about a template."""
|
|
23
|
+
name: str
|
|
24
|
+
description: str
|
|
25
|
+
version: str = "1.0.0"
|
|
26
|
+
author: Optional[str] = None
|
|
27
|
+
tags: List[str] = field(default_factory=list)
|
|
28
|
+
requires: Dict[str, Any] = field(default_factory=dict)
|
|
29
|
+
source: Optional[str] = None
|
|
30
|
+
path: Optional[str] = None
|
|
31
|
+
sha256: Optional[str] = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TemplateRegistry:
|
|
35
|
+
"""
|
|
36
|
+
Registry for discovering and fetching templates.
|
|
37
|
+
|
|
38
|
+
Supports:
|
|
39
|
+
- GitHub repositories (via contents API or raw.githubusercontent.com)
|
|
40
|
+
- HTTP URLs
|
|
41
|
+
- Local package installations
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
GITHUB_API_BASE = "https://api.github.com"
|
|
45
|
+
GITHUB_RAW_BASE = "https://raw.githubusercontent.com"
|
|
46
|
+
|
|
47
|
+
# Default recipes repository
|
|
48
|
+
DEFAULT_OWNER = "MervinPraison"
|
|
49
|
+
DEFAULT_REPO = "agent-recipes"
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
cache: Optional[TemplateCache] = None,
|
|
54
|
+
github_token: Optional[str] = None,
|
|
55
|
+
offline: bool = False
|
|
56
|
+
):
|
|
57
|
+
"""
|
|
58
|
+
Initialize the registry.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
cache: Template cache instance
|
|
62
|
+
github_token: Optional GitHub token for API requests
|
|
63
|
+
offline: If True, only use cached templates
|
|
64
|
+
"""
|
|
65
|
+
self.cache = cache or TemplateCache()
|
|
66
|
+
self.github_token = github_token or os.environ.get("GITHUB_TOKEN")
|
|
67
|
+
self.offline = offline
|
|
68
|
+
self._http_client = None
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def http_client(self):
|
|
72
|
+
"""Lazy-load HTTP client."""
|
|
73
|
+
if self._http_client is None:
|
|
74
|
+
try:
|
|
75
|
+
import httpx
|
|
76
|
+
self._http_client = httpx.Client(timeout=30.0)
|
|
77
|
+
except ImportError:
|
|
78
|
+
import urllib.request
|
|
79
|
+
self._http_client = "urllib"
|
|
80
|
+
return self._http_client
|
|
81
|
+
|
|
82
|
+
def _make_request(
|
|
83
|
+
self,
|
|
84
|
+
url: str,
|
|
85
|
+
headers: Optional[Dict[str, str]] = None
|
|
86
|
+
) -> bytes:
|
|
87
|
+
"""Make an HTTP request."""
|
|
88
|
+
all_headers = headers or {}
|
|
89
|
+
if self.github_token and "api.github.com" in url:
|
|
90
|
+
all_headers["Authorization"] = f"token {self.github_token}"
|
|
91
|
+
all_headers["User-Agent"] = "PraisonAI-Templates/1.0"
|
|
92
|
+
|
|
93
|
+
if self.http_client == "urllib":
|
|
94
|
+
import urllib.request
|
|
95
|
+
req = urllib.request.Request(url, headers=all_headers)
|
|
96
|
+
with urllib.request.urlopen(req, timeout=30) as response:
|
|
97
|
+
return response.read()
|
|
98
|
+
else:
|
|
99
|
+
response = self.http_client.get(url, headers=all_headers)
|
|
100
|
+
response.raise_for_status()
|
|
101
|
+
return response.content
|
|
102
|
+
|
|
103
|
+
def fetch_github_template(
|
|
104
|
+
self,
|
|
105
|
+
owner: str,
|
|
106
|
+
repo: str,
|
|
107
|
+
template_path: str,
|
|
108
|
+
ref: str = "main"
|
|
109
|
+
) -> Path:
|
|
110
|
+
"""
|
|
111
|
+
Fetch a template from GitHub.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
owner: Repository owner
|
|
115
|
+
repo: Repository name
|
|
116
|
+
template_path: Path to template within repo
|
|
117
|
+
ref: Git ref (branch, tag, or commit)
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Path to downloaded template directory
|
|
121
|
+
"""
|
|
122
|
+
# Create temp directory for download
|
|
123
|
+
temp_dir = Path(tempfile.mkdtemp(prefix="praison_template_"))
|
|
124
|
+
|
|
125
|
+
# Fetch template files using GitHub API
|
|
126
|
+
api_url = f"{self.GITHUB_API_BASE}/repos/{owner}/{repo}/contents/templates/{template_path}?ref={ref}"
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
response = self._make_request(api_url)
|
|
130
|
+
contents = json.loads(response)
|
|
131
|
+
|
|
132
|
+
if isinstance(contents, list):
|
|
133
|
+
# Directory listing
|
|
134
|
+
for item in contents:
|
|
135
|
+
if item["type"] == "file":
|
|
136
|
+
file_content = self._fetch_github_file(item["download_url"])
|
|
137
|
+
file_path = temp_dir / item["name"]
|
|
138
|
+
file_path.write_bytes(file_content)
|
|
139
|
+
elif item["type"] == "dir":
|
|
140
|
+
# Recursively fetch subdirectory
|
|
141
|
+
subdir = temp_dir / item["name"]
|
|
142
|
+
subdir.mkdir(parents=True, exist_ok=True)
|
|
143
|
+
self._fetch_github_dir(
|
|
144
|
+
owner, repo, f"templates/{template_path}/{item['name']}",
|
|
145
|
+
ref, subdir
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
# Single file
|
|
149
|
+
file_content = self._fetch_github_file(contents["download_url"])
|
|
150
|
+
file_path = temp_dir / contents["name"]
|
|
151
|
+
file_path.write_bytes(file_content)
|
|
152
|
+
|
|
153
|
+
except Exception:
|
|
154
|
+
# Fallback to raw.githubusercontent.com
|
|
155
|
+
raw_url = f"{self.GITHUB_RAW_BASE}/{owner}/{repo}/{ref}/templates/{template_path}"
|
|
156
|
+
self._fetch_raw_template(raw_url, temp_dir, template_path)
|
|
157
|
+
|
|
158
|
+
return temp_dir
|
|
159
|
+
|
|
160
|
+
def _fetch_github_file(self, url: str) -> bytes:
|
|
161
|
+
"""Fetch a single file from GitHub."""
|
|
162
|
+
return self._make_request(url)
|
|
163
|
+
|
|
164
|
+
def _fetch_github_dir(
|
|
165
|
+
self,
|
|
166
|
+
owner: str,
|
|
167
|
+
repo: str,
|
|
168
|
+
path: str,
|
|
169
|
+
ref: str,
|
|
170
|
+
target_dir: Path
|
|
171
|
+
) -> None:
|
|
172
|
+
"""Recursively fetch a directory from GitHub."""
|
|
173
|
+
api_url = f"{self.GITHUB_API_BASE}/repos/{owner}/{repo}/contents/{path}?ref={ref}"
|
|
174
|
+
response = self._make_request(api_url)
|
|
175
|
+
contents = json.loads(response)
|
|
176
|
+
|
|
177
|
+
for item in contents:
|
|
178
|
+
if item["type"] == "file":
|
|
179
|
+
file_content = self._fetch_github_file(item["download_url"])
|
|
180
|
+
file_path = target_dir / item["name"]
|
|
181
|
+
file_path.write_bytes(file_content)
|
|
182
|
+
elif item["type"] == "dir":
|
|
183
|
+
subdir = target_dir / item["name"]
|
|
184
|
+
subdir.mkdir(parents=True, exist_ok=True)
|
|
185
|
+
self._fetch_github_dir(owner, repo, f"{path}/{item['name']}", ref, subdir)
|
|
186
|
+
|
|
187
|
+
def _fetch_raw_template(
|
|
188
|
+
self,
|
|
189
|
+
base_url: str,
|
|
190
|
+
target_dir: Path,
|
|
191
|
+
template_name: str
|
|
192
|
+
) -> None:
|
|
193
|
+
"""Fetch template using raw URLs (fallback)."""
|
|
194
|
+
# Try to fetch common template files
|
|
195
|
+
files_to_try = [
|
|
196
|
+
"TEMPLATE.yaml",
|
|
197
|
+
"workflow.yaml",
|
|
198
|
+
"agents.yaml",
|
|
199
|
+
"README.md"
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
for filename in files_to_try:
|
|
203
|
+
try:
|
|
204
|
+
url = f"{base_url}/{filename}"
|
|
205
|
+
content = self._make_request(url)
|
|
206
|
+
(target_dir / filename).write_bytes(content)
|
|
207
|
+
except Exception:
|
|
208
|
+
continue
|
|
209
|
+
|
|
210
|
+
def fetch_http_template(self, url: str) -> Path:
|
|
211
|
+
"""
|
|
212
|
+
Fetch a template from an HTTP URL.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
url: URL to template (YAML file or directory index)
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
Path to downloaded template
|
|
219
|
+
"""
|
|
220
|
+
temp_dir = Path(tempfile.mkdtemp(prefix="praison_template_"))
|
|
221
|
+
|
|
222
|
+
content = self._make_request(url)
|
|
223
|
+
|
|
224
|
+
# Determine filename from URL
|
|
225
|
+
filename = url.split("/")[-1]
|
|
226
|
+
if not filename.endswith((".yaml", ".yml")):
|
|
227
|
+
filename = "TEMPLATE.yaml"
|
|
228
|
+
|
|
229
|
+
(temp_dir / filename).write_bytes(content)
|
|
230
|
+
|
|
231
|
+
return temp_dir
|
|
232
|
+
|
|
233
|
+
def get_template(
|
|
234
|
+
self,
|
|
235
|
+
uri: str,
|
|
236
|
+
offline: bool = False
|
|
237
|
+
) -> CachedTemplate:
|
|
238
|
+
"""
|
|
239
|
+
Get a template by URI, fetching if necessary.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
uri: Template URI
|
|
243
|
+
offline: If True, only use cache
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
CachedTemplate with path and metadata
|
|
247
|
+
|
|
248
|
+
Raises:
|
|
249
|
+
ValueError: If template not found
|
|
250
|
+
"""
|
|
251
|
+
resolved = TemplateResolver.resolve(uri)
|
|
252
|
+
use_offline = offline or self.offline
|
|
253
|
+
|
|
254
|
+
# Check cache first
|
|
255
|
+
cached = self.cache.get(resolved, offline=use_offline)
|
|
256
|
+
if cached:
|
|
257
|
+
return cached
|
|
258
|
+
|
|
259
|
+
if use_offline:
|
|
260
|
+
raise ValueError(
|
|
261
|
+
f"Template not found in cache: {uri}\n"
|
|
262
|
+
"Run without --offline to fetch from remote."
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
# Fetch based on source
|
|
266
|
+
if resolved.source == TemplateSource.LOCAL:
|
|
267
|
+
path = Path(resolved.path)
|
|
268
|
+
if not path.exists():
|
|
269
|
+
raise ValueError(f"Local template not found: {resolved.path}")
|
|
270
|
+
return CachedTemplate(
|
|
271
|
+
path=path,
|
|
272
|
+
metadata=self.cache.put(resolved, path).metadata
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
elif resolved.source == TemplateSource.PACKAGE:
|
|
276
|
+
# Try to find in installed package
|
|
277
|
+
return self._get_package_template(resolved)
|
|
278
|
+
|
|
279
|
+
elif resolved.source == TemplateSource.GITHUB:
|
|
280
|
+
temp_dir = self.fetch_github_template(
|
|
281
|
+
resolved.owner,
|
|
282
|
+
resolved.repo,
|
|
283
|
+
resolved.path,
|
|
284
|
+
resolved.ref or "main"
|
|
285
|
+
)
|
|
286
|
+
# Calculate checksum
|
|
287
|
+
sha256 = self._calculate_dir_checksum(temp_dir)
|
|
288
|
+
return self.cache.put(resolved, temp_dir, sha256=sha256)
|
|
289
|
+
|
|
290
|
+
elif resolved.source == TemplateSource.HTTP:
|
|
291
|
+
temp_dir = self.fetch_http_template(resolved.url)
|
|
292
|
+
sha256 = self._calculate_dir_checksum(temp_dir)
|
|
293
|
+
return self.cache.put(resolved, temp_dir, sha256=sha256)
|
|
294
|
+
|
|
295
|
+
raise ValueError(f"Unknown template source: {resolved.source}")
|
|
296
|
+
|
|
297
|
+
def _get_package_template(self, resolved: ResolvedTemplate) -> CachedTemplate:
|
|
298
|
+
"""Get template from installed Python package."""
|
|
299
|
+
parts = resolved.path.split("/")
|
|
300
|
+
if len(parts) < 2:
|
|
301
|
+
raise ValueError(f"Invalid package template path: {resolved.path}")
|
|
302
|
+
|
|
303
|
+
package_name = parts[0]
|
|
304
|
+
template_name = "/".join(parts[1:])
|
|
305
|
+
|
|
306
|
+
try:
|
|
307
|
+
# Try importlib.resources first (Python 3.9+)
|
|
308
|
+
import importlib.resources as pkg_resources
|
|
309
|
+
package = __import__(package_name)
|
|
310
|
+
|
|
311
|
+
# Navigate to templates directory
|
|
312
|
+
templates_path = Path(package.__file__).parent / "templates" / template_name
|
|
313
|
+
if templates_path.exists():
|
|
314
|
+
return CachedTemplate(
|
|
315
|
+
path=templates_path,
|
|
316
|
+
metadata=self.cache.put(resolved, templates_path).metadata
|
|
317
|
+
)
|
|
318
|
+
except (ImportError, AttributeError):
|
|
319
|
+
pass
|
|
320
|
+
|
|
321
|
+
# Try pkg_resources fallback
|
|
322
|
+
try:
|
|
323
|
+
import pkg_resources as pkg_res
|
|
324
|
+
template_path = pkg_res.resource_filename(
|
|
325
|
+
package_name,
|
|
326
|
+
f"templates/{template_name}"
|
|
327
|
+
)
|
|
328
|
+
if Path(template_path).exists():
|
|
329
|
+
return CachedTemplate(
|
|
330
|
+
path=Path(template_path),
|
|
331
|
+
metadata=self.cache.put(resolved, Path(template_path)).metadata
|
|
332
|
+
)
|
|
333
|
+
except Exception:
|
|
334
|
+
pass
|
|
335
|
+
|
|
336
|
+
raise ValueError(
|
|
337
|
+
f"Package template not found: {resolved.path}\n"
|
|
338
|
+
f"Make sure package '{package_name}' is installed."
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
def _calculate_dir_checksum(self, directory: Path) -> str:
|
|
342
|
+
"""Calculate SHA256 checksum of directory contents."""
|
|
343
|
+
hasher = hashlib.sha256()
|
|
344
|
+
|
|
345
|
+
for file_path in sorted(directory.rglob("*")):
|
|
346
|
+
if file_path.is_file() and not file_path.name.startswith("."):
|
|
347
|
+
hasher.update(file_path.name.encode())
|
|
348
|
+
hasher.update(file_path.read_bytes())
|
|
349
|
+
|
|
350
|
+
return hasher.hexdigest()
|
|
351
|
+
|
|
352
|
+
def list_remote_templates(
|
|
353
|
+
self,
|
|
354
|
+
owner: str = None,
|
|
355
|
+
repo: str = None,
|
|
356
|
+
ref: str = "main"
|
|
357
|
+
) -> List[TemplateInfo]:
|
|
358
|
+
"""
|
|
359
|
+
List templates available in a GitHub repository.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
owner: Repository owner (default: MervinPraison)
|
|
363
|
+
repo: Repository name (default: agent-recipes)
|
|
364
|
+
ref: Git ref
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
List of TemplateInfo objects
|
|
368
|
+
"""
|
|
369
|
+
owner = owner or self.DEFAULT_OWNER
|
|
370
|
+
repo = repo or self.DEFAULT_REPO
|
|
371
|
+
|
|
372
|
+
if self.offline:
|
|
373
|
+
return []
|
|
374
|
+
|
|
375
|
+
templates = []
|
|
376
|
+
|
|
377
|
+
try:
|
|
378
|
+
# Fetch templates directory listing
|
|
379
|
+
api_url = f"{self.GITHUB_API_BASE}/repos/{owner}/{repo}/contents/templates?ref={ref}"
|
|
380
|
+
response = self._make_request(api_url)
|
|
381
|
+
contents = json.loads(response)
|
|
382
|
+
|
|
383
|
+
for item in contents:
|
|
384
|
+
if item["type"] == "dir":
|
|
385
|
+
# Try to fetch TEMPLATE.yaml for metadata
|
|
386
|
+
try:
|
|
387
|
+
template_url = f"{self.GITHUB_RAW_BASE}/{owner}/{repo}/{ref}/templates/{item['name']}/TEMPLATE.yaml"
|
|
388
|
+
template_content = self._make_request(template_url)
|
|
389
|
+
|
|
390
|
+
import yaml
|
|
391
|
+
config = yaml.safe_load(template_content)
|
|
392
|
+
|
|
393
|
+
templates.append(TemplateInfo(
|
|
394
|
+
name=config.get("name", item["name"]),
|
|
395
|
+
description=config.get("description", ""),
|
|
396
|
+
version=config.get("version", "1.0.0"),
|
|
397
|
+
author=config.get("author"),
|
|
398
|
+
tags=config.get("tags", []),
|
|
399
|
+
requires=config.get("requires", {}),
|
|
400
|
+
source=f"github:{owner}/{repo}/{item['name']}",
|
|
401
|
+
path=item["name"]
|
|
402
|
+
))
|
|
403
|
+
except Exception:
|
|
404
|
+
# Fallback to basic info
|
|
405
|
+
templates.append(TemplateInfo(
|
|
406
|
+
name=item["name"],
|
|
407
|
+
description="",
|
|
408
|
+
source=f"github:{owner}/{repo}/{item['name']}",
|
|
409
|
+
path=item["name"]
|
|
410
|
+
))
|
|
411
|
+
except Exception:
|
|
412
|
+
# Return empty list on error
|
|
413
|
+
pass
|
|
414
|
+
|
|
415
|
+
return templates
|
|
416
|
+
|
|
417
|
+
def search_templates(
|
|
418
|
+
self,
|
|
419
|
+
query: str,
|
|
420
|
+
owner: str = None,
|
|
421
|
+
repo: str = None
|
|
422
|
+
) -> List[TemplateInfo]:
|
|
423
|
+
"""
|
|
424
|
+
Search templates by name or tags.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
query: Search query
|
|
428
|
+
owner: Repository owner
|
|
429
|
+
repo: Repository name
|
|
430
|
+
|
|
431
|
+
Returns:
|
|
432
|
+
List of matching TemplateInfo objects
|
|
433
|
+
"""
|
|
434
|
+
all_templates = self.list_remote_templates(owner, repo)
|
|
435
|
+
query_lower = query.lower()
|
|
436
|
+
|
|
437
|
+
return [
|
|
438
|
+
t for t in all_templates
|
|
439
|
+
if query_lower in t.name.lower() or
|
|
440
|
+
query_lower in t.description.lower() or
|
|
441
|
+
any(query_lower in tag.lower() for tag in t.tags)
|
|
442
|
+
]
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def list_templates(
|
|
446
|
+
source: str = "all",
|
|
447
|
+
offline: bool = False
|
|
448
|
+
) -> List[TemplateInfo]:
|
|
449
|
+
"""
|
|
450
|
+
List available templates.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
source: 'all', 'local', 'remote', or 'cached'
|
|
454
|
+
offline: If True, only list cached templates
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
List of TemplateInfo objects
|
|
458
|
+
"""
|
|
459
|
+
registry = TemplateRegistry(offline=offline)
|
|
460
|
+
templates = []
|
|
461
|
+
|
|
462
|
+
if source in ("all", "cached"):
|
|
463
|
+
# List cached templates
|
|
464
|
+
cache = TemplateCache()
|
|
465
|
+
for path, metadata in cache.list_cached():
|
|
466
|
+
templates.append(TemplateInfo(
|
|
467
|
+
name=path.name,
|
|
468
|
+
description="(cached)",
|
|
469
|
+
version=metadata.version or "unknown",
|
|
470
|
+
source=str(path)
|
|
471
|
+
))
|
|
472
|
+
|
|
473
|
+
if source in ("all", "remote") and not offline:
|
|
474
|
+
# List remote templates
|
|
475
|
+
remote = registry.list_remote_templates()
|
|
476
|
+
templates.extend(remote)
|
|
477
|
+
|
|
478
|
+
return templates
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def search_templates(query: str, offline: bool = False) -> List[TemplateInfo]:
|
|
482
|
+
"""
|
|
483
|
+
Search templates by query.
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
query: Search query
|
|
487
|
+
offline: If True, only search cached templates
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
List of matching TemplateInfo objects
|
|
491
|
+
"""
|
|
492
|
+
registry = TemplateRegistry(offline=offline)
|
|
493
|
+
return registry.search_templates(query)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def install_template(
|
|
497
|
+
uri: str,
|
|
498
|
+
offline: bool = False
|
|
499
|
+
) -> CachedTemplate:
|
|
500
|
+
"""
|
|
501
|
+
Install (fetch and cache) a template.
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
uri: Template URI
|
|
505
|
+
offline: If True, fail if not cached
|
|
506
|
+
|
|
507
|
+
Returns:
|
|
508
|
+
CachedTemplate with path and metadata
|
|
509
|
+
"""
|
|
510
|
+
registry = TemplateRegistry(offline=offline)
|
|
511
|
+
return registry.get_template(uri, offline=offline)
|