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
praisonai/deploy/api.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API server deployment functionality.
|
|
3
|
+
"""
|
|
4
|
+
import subprocess
|
|
5
|
+
import os
|
|
6
|
+
import signal
|
|
7
|
+
import time
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from .models import APIConfig, DeployResult
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def generate_api_server_code(agents_file: str, config: Optional[APIConfig] = None) -> str:
|
|
13
|
+
"""
|
|
14
|
+
Generate API server code for serving agents.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
agents_file: Path to agents.yaml file
|
|
18
|
+
config: API configuration
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Python code for API server
|
|
22
|
+
"""
|
|
23
|
+
if config is None:
|
|
24
|
+
config = APIConfig()
|
|
25
|
+
|
|
26
|
+
code = f'''"""
|
|
27
|
+
Auto-generated API server for PraisonAI agents.
|
|
28
|
+
"""
|
|
29
|
+
from flask import Flask, request, jsonify
|
|
30
|
+
from flask_cors import CORS
|
|
31
|
+
from praisonai import PraisonAI
|
|
32
|
+
import os
|
|
33
|
+
|
|
34
|
+
app = Flask(__name__)
|
|
35
|
+
|
|
36
|
+
# CORS configuration
|
|
37
|
+
{"CORS(app)" if config.cors_enabled else "# CORS disabled"}
|
|
38
|
+
|
|
39
|
+
# Authentication
|
|
40
|
+
AUTH_ENABLED = {config.auth_enabled}
|
|
41
|
+
AUTH_TOKEN = {repr(config.auth_token)}
|
|
42
|
+
|
|
43
|
+
def check_auth():
|
|
44
|
+
"""Check authentication if enabled."""
|
|
45
|
+
if not AUTH_ENABLED:
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
token = request.headers.get('Authorization', '').replace('Bearer ', '')
|
|
49
|
+
return token == AUTH_TOKEN
|
|
50
|
+
|
|
51
|
+
@app.route('/health', methods=['GET'])
|
|
52
|
+
def health():
|
|
53
|
+
"""Health check endpoint."""
|
|
54
|
+
return jsonify({{"status": "ok", "service": "praisonai-api"}})
|
|
55
|
+
|
|
56
|
+
@app.route('/chat', methods=['POST'])
|
|
57
|
+
def chat():
|
|
58
|
+
"""Chat endpoint for agent interaction."""
|
|
59
|
+
if not check_auth():
|
|
60
|
+
return jsonify({{"error": "Unauthorized"}}), 401
|
|
61
|
+
|
|
62
|
+
data = request.get_json()
|
|
63
|
+
if not data or 'message' not in data:
|
|
64
|
+
return jsonify({{"error": "Message required"}}), 400
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
praisonai = PraisonAI(agent_file="{agents_file}")
|
|
68
|
+
result = praisonai.run()
|
|
69
|
+
|
|
70
|
+
return jsonify({{
|
|
71
|
+
"response": result,
|
|
72
|
+
"status": "success"
|
|
73
|
+
}})
|
|
74
|
+
except Exception as e:
|
|
75
|
+
return jsonify({{
|
|
76
|
+
"error": str(e),
|
|
77
|
+
"status": "error"
|
|
78
|
+
}}), 500
|
|
79
|
+
|
|
80
|
+
@app.route('/agents', methods=['GET'])
|
|
81
|
+
def list_agents():
|
|
82
|
+
"""List available agents."""
|
|
83
|
+
if not check_auth():
|
|
84
|
+
return jsonify({{"error": "Unauthorized"}}), 401
|
|
85
|
+
|
|
86
|
+
return jsonify({{
|
|
87
|
+
"agents": ["default"],
|
|
88
|
+
"agent_file": "{agents_file}"
|
|
89
|
+
}})
|
|
90
|
+
|
|
91
|
+
if __name__ == '__main__':
|
|
92
|
+
app.run(
|
|
93
|
+
host='{config.host}',
|
|
94
|
+
port={config.port},
|
|
95
|
+
debug={config.reload}
|
|
96
|
+
)
|
|
97
|
+
'''
|
|
98
|
+
|
|
99
|
+
return code
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def start_api_server(
|
|
103
|
+
agents_file: str,
|
|
104
|
+
config: Optional[APIConfig] = None,
|
|
105
|
+
background: bool = False
|
|
106
|
+
) -> DeployResult:
|
|
107
|
+
"""
|
|
108
|
+
Start API server for agents.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
agents_file: Path to agents.yaml file
|
|
112
|
+
config: API configuration
|
|
113
|
+
background: Run in background mode
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
DeployResult with server information
|
|
117
|
+
"""
|
|
118
|
+
if config is None:
|
|
119
|
+
config = APIConfig()
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
# Generate server code
|
|
123
|
+
server_code = generate_api_server_code(agents_file, config)
|
|
124
|
+
|
|
125
|
+
# Write to temporary file
|
|
126
|
+
server_file = "api_server.py"
|
|
127
|
+
with open(server_file, 'w') as f:
|
|
128
|
+
f.write(server_code)
|
|
129
|
+
|
|
130
|
+
# Install flask and flask-cors if needed
|
|
131
|
+
try:
|
|
132
|
+
subprocess.run(
|
|
133
|
+
['pip', 'install', '-q', 'flask', 'flask-cors'],
|
|
134
|
+
check=False,
|
|
135
|
+
capture_output=True
|
|
136
|
+
)
|
|
137
|
+
except Exception:
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
# Start server
|
|
141
|
+
if background:
|
|
142
|
+
process = subprocess.Popen(
|
|
143
|
+
['python', server_file],
|
|
144
|
+
stdout=subprocess.PIPE,
|
|
145
|
+
stderr=subprocess.PIPE,
|
|
146
|
+
start_new_session=True
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Wait a bit to check if it started successfully
|
|
150
|
+
time.sleep(2)
|
|
151
|
+
|
|
152
|
+
if process.poll() is None:
|
|
153
|
+
url = f"http://{config.host}:{config.port}"
|
|
154
|
+
return DeployResult(
|
|
155
|
+
success=True,
|
|
156
|
+
message=f"API server started in background (PID: {process.pid})",
|
|
157
|
+
url=url,
|
|
158
|
+
metadata={"pid": process.pid, "server_file": server_file}
|
|
159
|
+
)
|
|
160
|
+
else:
|
|
161
|
+
stderr = process.stderr.read().decode() if process.stderr else "Unknown error"
|
|
162
|
+
return DeployResult(
|
|
163
|
+
success=False,
|
|
164
|
+
message="Failed to start API server",
|
|
165
|
+
error=stderr
|
|
166
|
+
)
|
|
167
|
+
else:
|
|
168
|
+
# Run in foreground
|
|
169
|
+
url = f"http://{config.host}:{config.port}"
|
|
170
|
+
print(f"\n🚀 Starting API server at {url}")
|
|
171
|
+
print(f"📁 Serving agents from: {agents_file}")
|
|
172
|
+
print(f"🔗 Health check: {url}/health")
|
|
173
|
+
print(f"💬 Chat endpoint: {url}/chat")
|
|
174
|
+
print("\nPress Ctrl+C to stop the server\n")
|
|
175
|
+
|
|
176
|
+
process = subprocess.Popen(['python', server_file])
|
|
177
|
+
|
|
178
|
+
return DeployResult(
|
|
179
|
+
success=True,
|
|
180
|
+
message=f"API server running at {url}",
|
|
181
|
+
url=url,
|
|
182
|
+
metadata={"pid": process.pid, "server_file": server_file}
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
except Exception as e:
|
|
186
|
+
return DeployResult(
|
|
187
|
+
success=False,
|
|
188
|
+
message="Failed to start API server",
|
|
189
|
+
error=str(e)
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def check_api_health(url: str, timeout: int = 5) -> bool:
|
|
194
|
+
"""
|
|
195
|
+
Check if API server is healthy.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
url: Base URL of API server
|
|
199
|
+
timeout: Request timeout in seconds
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
True if healthy, False otherwise
|
|
203
|
+
"""
|
|
204
|
+
try:
|
|
205
|
+
import urllib.request
|
|
206
|
+
health_url = f"{url}/health"
|
|
207
|
+
|
|
208
|
+
req = urllib.request.Request(health_url)
|
|
209
|
+
with urllib.request.urlopen(req, timeout=timeout) as response:
|
|
210
|
+
return response.status == 200
|
|
211
|
+
except Exception:
|
|
212
|
+
return False
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def stop_api_server(pid: int) -> bool:
|
|
216
|
+
"""
|
|
217
|
+
Stop API server by PID.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
pid: Process ID of server
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
True if stopped successfully, False otherwise
|
|
224
|
+
"""
|
|
225
|
+
try:
|
|
226
|
+
os.kill(pid, signal.SIGTERM)
|
|
227
|
+
return True
|
|
228
|
+
except ProcessLookupError:
|
|
229
|
+
return False
|
|
230
|
+
except Exception:
|
|
231
|
+
return False
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Docker deployment functionality.
|
|
3
|
+
"""
|
|
4
|
+
import subprocess
|
|
5
|
+
import json
|
|
6
|
+
from typing import Optional, Dict
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from .models import DockerConfig, DeployResult, DeployStatus, DestroyResult, ServiceState
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def generate_dockerfile(agents_file: str, config: Optional[DockerConfig] = None) -> str:
|
|
12
|
+
"""
|
|
13
|
+
Generate Dockerfile for agents.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
agents_file: Path to agents.yaml file
|
|
17
|
+
config: Docker configuration
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Dockerfile content as string
|
|
21
|
+
"""
|
|
22
|
+
if config is None:
|
|
23
|
+
config = DockerConfig()
|
|
24
|
+
|
|
25
|
+
# Build expose statements
|
|
26
|
+
expose_lines = "\n".join([f"EXPOSE {port}" for port in config.expose])
|
|
27
|
+
|
|
28
|
+
dockerfile = f"""FROM {config.base_image}
|
|
29
|
+
|
|
30
|
+
WORKDIR /app
|
|
31
|
+
|
|
32
|
+
# Copy application files
|
|
33
|
+
COPY {agents_file} /app/{agents_file}
|
|
34
|
+
COPY . /app/
|
|
35
|
+
|
|
36
|
+
# Install dependencies
|
|
37
|
+
RUN pip install --no-cache-dir praisonai flask flask-cors gunicorn
|
|
38
|
+
|
|
39
|
+
# Expose ports
|
|
40
|
+
{expose_lines}
|
|
41
|
+
|
|
42
|
+
# Health check
|
|
43
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \\
|
|
44
|
+
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:{config.expose[0]}/health')" || exit 1
|
|
45
|
+
|
|
46
|
+
# Run the application
|
|
47
|
+
CMD ["gunicorn", "-b", "0.0.0.0:{config.expose[0]}", "-w", "1", "api_server:app"]
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
return dockerfile
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def check_docker_installed() -> bool:
|
|
54
|
+
"""
|
|
55
|
+
Check if Docker is installed and available.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
True if Docker is available, False otherwise
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
result = subprocess.run(
|
|
62
|
+
['docker', '--version'],
|
|
63
|
+
capture_output=True,
|
|
64
|
+
timeout=5
|
|
65
|
+
)
|
|
66
|
+
return result.returncode == 0
|
|
67
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def build_docker_image(config: DockerConfig, build_context: str = ".") -> DeployResult:
|
|
72
|
+
"""
|
|
73
|
+
Build Docker image.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
config: Docker configuration
|
|
77
|
+
build_context: Build context directory
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
DeployResult with build information
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
if not check_docker_installed():
|
|
84
|
+
return DeployResult(
|
|
85
|
+
success=False,
|
|
86
|
+
message="Docker not installed",
|
|
87
|
+
error="Docker is required for Docker deployment"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Build image tag
|
|
91
|
+
image_tag = f"{config.image_name}:{config.tag}"
|
|
92
|
+
if config.registry:
|
|
93
|
+
image_tag = f"{config.registry}/{image_tag}"
|
|
94
|
+
|
|
95
|
+
# Build command
|
|
96
|
+
cmd = ['docker', 'build', '-t', image_tag]
|
|
97
|
+
|
|
98
|
+
# Add build args if provided
|
|
99
|
+
if config.build_args:
|
|
100
|
+
for key, value in config.build_args.items():
|
|
101
|
+
cmd.extend(['--build-arg', f"{key}={value}"])
|
|
102
|
+
|
|
103
|
+
cmd.append(build_context)
|
|
104
|
+
|
|
105
|
+
print(f"🐳 Building Docker image: {image_tag}")
|
|
106
|
+
|
|
107
|
+
# Run build
|
|
108
|
+
result = subprocess.run(
|
|
109
|
+
cmd,
|
|
110
|
+
capture_output=True,
|
|
111
|
+
text=True
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if result.returncode == 0:
|
|
115
|
+
return DeployResult(
|
|
116
|
+
success=True,
|
|
117
|
+
message=f"Docker image built successfully: {image_tag}",
|
|
118
|
+
metadata={"image": image_tag, "tag": config.tag}
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
return DeployResult(
|
|
122
|
+
success=False,
|
|
123
|
+
message="Docker build failed",
|
|
124
|
+
error=result.stderr
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
except Exception as e:
|
|
128
|
+
return DeployResult(
|
|
129
|
+
success=False,
|
|
130
|
+
message="Docker build failed",
|
|
131
|
+
error=str(e)
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def run_docker_container(
|
|
136
|
+
config: DockerConfig,
|
|
137
|
+
env_vars: Optional[Dict[str, str]] = None,
|
|
138
|
+
detached: bool = True
|
|
139
|
+
) -> DeployResult:
|
|
140
|
+
"""
|
|
141
|
+
Run Docker container.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
config: Docker configuration
|
|
145
|
+
env_vars: Environment variables to pass to container
|
|
146
|
+
detached: Run in detached mode
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
DeployResult with container information
|
|
150
|
+
"""
|
|
151
|
+
try:
|
|
152
|
+
# Build image tag
|
|
153
|
+
image_tag = f"{config.image_name}:{config.tag}"
|
|
154
|
+
if config.registry:
|
|
155
|
+
image_tag = f"{config.registry}/{image_tag}"
|
|
156
|
+
|
|
157
|
+
# Build run command
|
|
158
|
+
cmd = ['docker', 'run']
|
|
159
|
+
|
|
160
|
+
if detached:
|
|
161
|
+
cmd.append('-d')
|
|
162
|
+
|
|
163
|
+
# Add port mappings
|
|
164
|
+
for port in config.expose:
|
|
165
|
+
cmd.extend(['-p', f"{port}:{port}"])
|
|
166
|
+
|
|
167
|
+
# Add environment variables
|
|
168
|
+
if env_vars:
|
|
169
|
+
for key, value in env_vars.items():
|
|
170
|
+
cmd.extend(['-e', f"{key}={value}"])
|
|
171
|
+
|
|
172
|
+
# Add container name
|
|
173
|
+
container_name = f"{config.image_name}-{config.tag}".replace(':', '-')
|
|
174
|
+
cmd.extend(['--name', container_name])
|
|
175
|
+
|
|
176
|
+
cmd.append(image_tag)
|
|
177
|
+
|
|
178
|
+
print(f"🚀 Starting Docker container: {container_name}")
|
|
179
|
+
|
|
180
|
+
# Run container
|
|
181
|
+
result = subprocess.run(
|
|
182
|
+
cmd,
|
|
183
|
+
capture_output=True,
|
|
184
|
+
text=True
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
if result.returncode == 0:
|
|
188
|
+
container_id = result.stdout.strip()
|
|
189
|
+
url = f"http://localhost:{config.expose[0]}"
|
|
190
|
+
|
|
191
|
+
return DeployResult(
|
|
192
|
+
success=True,
|
|
193
|
+
message=f"Container started successfully: {container_name}",
|
|
194
|
+
url=url,
|
|
195
|
+
metadata={
|
|
196
|
+
"container_id": container_id,
|
|
197
|
+
"container_name": container_name,
|
|
198
|
+
"image": image_tag
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
else:
|
|
202
|
+
return DeployResult(
|
|
203
|
+
success=False,
|
|
204
|
+
message="Failed to start container",
|
|
205
|
+
error=result.stderr
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
except Exception as e:
|
|
209
|
+
return DeployResult(
|
|
210
|
+
success=False,
|
|
211
|
+
message="Failed to start container",
|
|
212
|
+
error=str(e)
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def push_docker_image(config: DockerConfig) -> DeployResult:
|
|
217
|
+
"""
|
|
218
|
+
Push Docker image to registry.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
config: Docker configuration
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
DeployResult with push information
|
|
225
|
+
"""
|
|
226
|
+
try:
|
|
227
|
+
if not config.registry:
|
|
228
|
+
return DeployResult(
|
|
229
|
+
success=False,
|
|
230
|
+
message="No registry specified",
|
|
231
|
+
error="Registry URL required for push operation"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Build image tag
|
|
235
|
+
image_tag = f"{config.registry}/{config.image_name}:{config.tag}"
|
|
236
|
+
|
|
237
|
+
print(f"📤 Pushing Docker image to registry: {image_tag}")
|
|
238
|
+
|
|
239
|
+
# Push image
|
|
240
|
+
result = subprocess.run(
|
|
241
|
+
['docker', 'push', image_tag],
|
|
242
|
+
capture_output=True,
|
|
243
|
+
text=True
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
if result.returncode == 0:
|
|
247
|
+
return DeployResult(
|
|
248
|
+
success=True,
|
|
249
|
+
message=f"Image pushed successfully: {image_tag}",
|
|
250
|
+
metadata={"image": image_tag}
|
|
251
|
+
)
|
|
252
|
+
else:
|
|
253
|
+
return DeployResult(
|
|
254
|
+
success=False,
|
|
255
|
+
message="Docker push failed",
|
|
256
|
+
error=result.stderr
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
except Exception as e:
|
|
260
|
+
return DeployResult(
|
|
261
|
+
success=False,
|
|
262
|
+
message="Docker push failed",
|
|
263
|
+
error=str(e)
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def stop_docker_container(container_id: str) -> bool:
|
|
268
|
+
"""
|
|
269
|
+
Stop Docker container.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
container_id: Container ID or name
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
True if stopped successfully, False otherwise
|
|
276
|
+
"""
|
|
277
|
+
try:
|
|
278
|
+
result = subprocess.run(
|
|
279
|
+
['docker', 'stop', container_id],
|
|
280
|
+
capture_output=True,
|
|
281
|
+
timeout=30
|
|
282
|
+
)
|
|
283
|
+
return result.returncode == 0
|
|
284
|
+
except Exception:
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def save_dockerfile(agents_file: str, config: Optional[DockerConfig] = None, output_path: str = "Dockerfile"):
|
|
289
|
+
"""
|
|
290
|
+
Save generated Dockerfile to file.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
agents_file: Path to agents.yaml file
|
|
294
|
+
config: Docker configuration
|
|
295
|
+
output_path: Path to save Dockerfile
|
|
296
|
+
"""
|
|
297
|
+
dockerfile_content = generate_dockerfile(agents_file, config)
|
|
298
|
+
|
|
299
|
+
path = Path(output_path)
|
|
300
|
+
with open(path, 'w') as f:
|
|
301
|
+
f.write(dockerfile_content)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def get_docker_container_status(config: DockerConfig) -> DeployStatus:
|
|
305
|
+
"""
|
|
306
|
+
Get status of a Docker container.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
config: Docker configuration
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
DeployStatus with container information
|
|
313
|
+
"""
|
|
314
|
+
try:
|
|
315
|
+
container_name = f"{config.image_name}-{config.tag}".replace(':', '-')
|
|
316
|
+
|
|
317
|
+
result = subprocess.run(
|
|
318
|
+
['docker', 'inspect', container_name, '--format', '{{json .}}'],
|
|
319
|
+
capture_output=True,
|
|
320
|
+
text=True,
|
|
321
|
+
timeout=10
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
if result.returncode != 0:
|
|
325
|
+
return DeployStatus(
|
|
326
|
+
state=ServiceState.NOT_FOUND,
|
|
327
|
+
message=f"Container not found: {container_name}",
|
|
328
|
+
service_name=container_name,
|
|
329
|
+
provider="docker"
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
data = json.loads(result.stdout)
|
|
333
|
+
state_data = data.get('State', {})
|
|
334
|
+
running = state_data.get('Running', False)
|
|
335
|
+
status_str = state_data.get('Status', 'unknown')
|
|
336
|
+
|
|
337
|
+
# Map Docker status to ServiceState
|
|
338
|
+
if running:
|
|
339
|
+
state = ServiceState.RUNNING
|
|
340
|
+
elif status_str == 'exited':
|
|
341
|
+
state = ServiceState.STOPPED
|
|
342
|
+
elif status_str == 'created':
|
|
343
|
+
state = ServiceState.PENDING
|
|
344
|
+
elif status_str == 'dead':
|
|
345
|
+
state = ServiceState.FAILED
|
|
346
|
+
else:
|
|
347
|
+
state = ServiceState.UNKNOWN
|
|
348
|
+
|
|
349
|
+
# Get port mapping
|
|
350
|
+
ports = data.get('NetworkSettings', {}).get('Ports', {})
|
|
351
|
+
url = None
|
|
352
|
+
if ports and config.expose:
|
|
353
|
+
port_key = f"{config.expose[0]}/tcp"
|
|
354
|
+
if port_key in ports and ports[port_key]:
|
|
355
|
+
host_port = ports[port_key][0].get('HostPort')
|
|
356
|
+
if host_port:
|
|
357
|
+
url = f"http://localhost:{host_port}"
|
|
358
|
+
|
|
359
|
+
return DeployStatus(
|
|
360
|
+
state=state,
|
|
361
|
+
url=url,
|
|
362
|
+
message=f"Status: {status_str}",
|
|
363
|
+
service_name=container_name,
|
|
364
|
+
provider="docker",
|
|
365
|
+
healthy=running,
|
|
366
|
+
instances_running=1 if running else 0,
|
|
367
|
+
instances_desired=1,
|
|
368
|
+
created_at=data.get('Created'),
|
|
369
|
+
metadata={
|
|
370
|
+
"container_id": data.get('Id', '')[:12],
|
|
371
|
+
"image": data.get('Config', {}).get('Image'),
|
|
372
|
+
"status": status_str,
|
|
373
|
+
"started_at": state_data.get('StartedAt'),
|
|
374
|
+
"finished_at": state_data.get('FinishedAt')
|
|
375
|
+
}
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
except Exception as e:
|
|
379
|
+
return DeployStatus(
|
|
380
|
+
state=ServiceState.UNKNOWN,
|
|
381
|
+
message=f"Failed to get status: {e}",
|
|
382
|
+
service_name=config.image_name,
|
|
383
|
+
provider="docker"
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def remove_docker_container(config: DockerConfig, force: bool = False) -> DestroyResult:
|
|
388
|
+
"""
|
|
389
|
+
Remove Docker container and optionally the image.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
config: Docker configuration
|
|
393
|
+
force: Force removal and also remove image
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
DestroyResult with removal information
|
|
397
|
+
"""
|
|
398
|
+
try:
|
|
399
|
+
container_name = f"{config.image_name}-{config.tag}".replace(':', '-')
|
|
400
|
+
deleted_resources = []
|
|
401
|
+
|
|
402
|
+
# Stop container first
|
|
403
|
+
print(f"🛑 Stopping container: {container_name}")
|
|
404
|
+
subprocess.run(
|
|
405
|
+
['docker', 'stop', container_name],
|
|
406
|
+
capture_output=True,
|
|
407
|
+
timeout=30
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# Remove container
|
|
411
|
+
print(f"🗑️ Removing container: {container_name}")
|
|
412
|
+
result = subprocess.run(
|
|
413
|
+
['docker', 'rm', container_name] + (['-f'] if force else []),
|
|
414
|
+
capture_output=True,
|
|
415
|
+
text=True,
|
|
416
|
+
timeout=30
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
if result.returncode == 0:
|
|
420
|
+
deleted_resources.append(f"container:{container_name}")
|
|
421
|
+
else:
|
|
422
|
+
return DestroyResult(
|
|
423
|
+
success=False,
|
|
424
|
+
message="Failed to remove container",
|
|
425
|
+
error=result.stderr,
|
|
426
|
+
resources_deleted=deleted_resources
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
# Optionally remove image
|
|
430
|
+
if force:
|
|
431
|
+
image_tag = f"{config.image_name}:{config.tag}"
|
|
432
|
+
print(f"🗑️ Removing image: {image_tag}")
|
|
433
|
+
img_result = subprocess.run(
|
|
434
|
+
['docker', 'rmi', image_tag, '-f'],
|
|
435
|
+
capture_output=True,
|
|
436
|
+
text=True,
|
|
437
|
+
timeout=30
|
|
438
|
+
)
|
|
439
|
+
if img_result.returncode == 0:
|
|
440
|
+
deleted_resources.append(f"image:{image_tag}")
|
|
441
|
+
|
|
442
|
+
return DestroyResult(
|
|
443
|
+
success=True,
|
|
444
|
+
message="Successfully removed Docker container",
|
|
445
|
+
resources_deleted=deleted_resources,
|
|
446
|
+
metadata={"container_name": container_name}
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
except Exception as e:
|
|
450
|
+
return DestroyResult(
|
|
451
|
+
success=False,
|
|
452
|
+
message="Failed to remove Docker resources",
|
|
453
|
+
error=str(e)
|
|
454
|
+
)
|