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,358 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Azure provider for cloud deployment (Container Apps).
|
|
3
|
+
"""
|
|
4
|
+
import subprocess
|
|
5
|
+
import json
|
|
6
|
+
from typing import Dict, Any
|
|
7
|
+
from .base import BaseProvider
|
|
8
|
+
from ..models import DeployResult, DeployStatus, DestroyResult, ServiceState
|
|
9
|
+
from ..doctor import DoctorReport, DoctorCheckResult, check_azure_cli
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AzureProvider(BaseProvider):
|
|
13
|
+
"""Azure deployment provider using Container Apps."""
|
|
14
|
+
|
|
15
|
+
def doctor(self) -> DoctorReport:
|
|
16
|
+
"""Run Azure-specific health checks."""
|
|
17
|
+
checks = [check_azure_cli()]
|
|
18
|
+
|
|
19
|
+
# Check if resource group exists
|
|
20
|
+
if self.config.resource_group:
|
|
21
|
+
try:
|
|
22
|
+
result = subprocess.run(
|
|
23
|
+
['az', 'group', 'show',
|
|
24
|
+
'--name', self.config.resource_group,
|
|
25
|
+
'--output', 'json'],
|
|
26
|
+
capture_output=True,
|
|
27
|
+
text=True,
|
|
28
|
+
timeout=10
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if result.returncode == 0:
|
|
32
|
+
checks.append(DoctorCheckResult(
|
|
33
|
+
name="Resource Group",
|
|
34
|
+
passed=True,
|
|
35
|
+
message=f"Resource group exists: {self.config.resource_group}"
|
|
36
|
+
))
|
|
37
|
+
else:
|
|
38
|
+
checks.append(DoctorCheckResult(
|
|
39
|
+
name="Resource Group",
|
|
40
|
+
passed=False,
|
|
41
|
+
message=f"Resource group not found: {self.config.resource_group}",
|
|
42
|
+
fix_suggestion=f"Run: az group create --name {self.config.resource_group} --location {self.config.region}"
|
|
43
|
+
))
|
|
44
|
+
except Exception as e:
|
|
45
|
+
checks.append(DoctorCheckResult(
|
|
46
|
+
name="Resource Group",
|
|
47
|
+
passed=False,
|
|
48
|
+
message=f"Failed to check resource group: {e}",
|
|
49
|
+
fix_suggestion="Check Azure CLI configuration"
|
|
50
|
+
))
|
|
51
|
+
|
|
52
|
+
return DoctorReport(checks=checks)
|
|
53
|
+
|
|
54
|
+
def plan(self) -> Dict[str, Any]:
|
|
55
|
+
"""Generate Azure deployment plan."""
|
|
56
|
+
plan = {
|
|
57
|
+
"provider": "azure",
|
|
58
|
+
"service_name": self.config.service_name,
|
|
59
|
+
"region": self.config.region,
|
|
60
|
+
"resource_group": self.config.resource_group,
|
|
61
|
+
"subscription_id": self.config.subscription_id,
|
|
62
|
+
"cpu": self.config.cpu,
|
|
63
|
+
"memory": self.config.memory,
|
|
64
|
+
"min_replicas": self.config.min_instances,
|
|
65
|
+
"max_replicas": self.config.max_instances,
|
|
66
|
+
"image": self.config.image or f"{self.config.service_name}:latest",
|
|
67
|
+
"steps": [
|
|
68
|
+
"1. Create resource group (if not exists)",
|
|
69
|
+
"2. Create Container Apps environment",
|
|
70
|
+
"3. Create or update Container App",
|
|
71
|
+
"4. Get app URL"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return plan
|
|
76
|
+
|
|
77
|
+
def deploy(self) -> DeployResult:
|
|
78
|
+
"""Deploy to Azure Container Apps."""
|
|
79
|
+
try:
|
|
80
|
+
if not self.config.resource_group:
|
|
81
|
+
return DeployResult(
|
|
82
|
+
success=False,
|
|
83
|
+
message="Resource group is required for Azure deployment",
|
|
84
|
+
error="Please specify resource_group in cloud config"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Step 1: Create resource group if not exists
|
|
88
|
+
print(f"📦 Creating resource group: {self.config.resource_group}")
|
|
89
|
+
try:
|
|
90
|
+
subprocess.run(
|
|
91
|
+
['az', 'group', 'create',
|
|
92
|
+
'--name', self.config.resource_group,
|
|
93
|
+
'--location', self.config.region],
|
|
94
|
+
capture_output=True,
|
|
95
|
+
timeout=30
|
|
96
|
+
)
|
|
97
|
+
except Exception:
|
|
98
|
+
pass # May already exist
|
|
99
|
+
|
|
100
|
+
# Step 2: Create Container Apps environment
|
|
101
|
+
env_name = f"{self.config.service_name}-env"
|
|
102
|
+
print(f"🌍 Creating Container Apps environment: {env_name}")
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
subprocess.run(
|
|
106
|
+
['az', 'containerapp', 'env', 'create',
|
|
107
|
+
'--name', env_name,
|
|
108
|
+
'--resource-group', self.config.resource_group,
|
|
109
|
+
'--location', self.config.region],
|
|
110
|
+
capture_output=True,
|
|
111
|
+
timeout=120
|
|
112
|
+
)
|
|
113
|
+
except Exception:
|
|
114
|
+
pass # May already exist
|
|
115
|
+
|
|
116
|
+
# Step 3: Create or update Container App
|
|
117
|
+
print(f"🚀 Deploying Container App: {self.config.service_name}")
|
|
118
|
+
|
|
119
|
+
cmd = [
|
|
120
|
+
'az', 'containerapp', 'create',
|
|
121
|
+
'--name', self.config.service_name,
|
|
122
|
+
'--resource-group', self.config.resource_group,
|
|
123
|
+
'--environment', env_name,
|
|
124
|
+
'--image', self.config.image or f"{self.config.service_name}:latest",
|
|
125
|
+
'--target-port', '8005',
|
|
126
|
+
'--ingress', 'external',
|
|
127
|
+
'--cpu', str(self.config.cpu),
|
|
128
|
+
'--memory', f"{self.config.memory}Gi",
|
|
129
|
+
'--min-replicas', str(self.config.min_instances),
|
|
130
|
+
'--max-replicas', str(self.config.max_instances)
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
# Add environment variables
|
|
134
|
+
if self.config.env_vars:
|
|
135
|
+
env_args = []
|
|
136
|
+
for k, v in self.config.env_vars.items():
|
|
137
|
+
env_args.extend(['--env-vars', f"{k}={v}"])
|
|
138
|
+
cmd.extend(env_args)
|
|
139
|
+
|
|
140
|
+
result = subprocess.run(
|
|
141
|
+
cmd,
|
|
142
|
+
capture_output=True,
|
|
143
|
+
text=True,
|
|
144
|
+
timeout=180
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if result.returncode != 0:
|
|
148
|
+
# Try update instead
|
|
149
|
+
cmd[1] = 'update'
|
|
150
|
+
result = subprocess.run(
|
|
151
|
+
cmd,
|
|
152
|
+
capture_output=True,
|
|
153
|
+
text=True,
|
|
154
|
+
timeout=180
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if result.returncode == 0:
|
|
158
|
+
# Get app URL
|
|
159
|
+
try:
|
|
160
|
+
show_result = subprocess.run(
|
|
161
|
+
['az', 'containerapp', 'show',
|
|
162
|
+
'--name', self.config.service_name,
|
|
163
|
+
'--resource-group', self.config.resource_group,
|
|
164
|
+
'--output', 'json'],
|
|
165
|
+
capture_output=True,
|
|
166
|
+
text=True,
|
|
167
|
+
timeout=30
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if show_result.returncode == 0:
|
|
171
|
+
app_data = json.loads(show_result.stdout)
|
|
172
|
+
url = app_data.get('properties', {}).get('configuration', {}).get('ingress', {}).get('fqdn')
|
|
173
|
+
if url:
|
|
174
|
+
url = f"https://{url}"
|
|
175
|
+
else:
|
|
176
|
+
url = None
|
|
177
|
+
except Exception:
|
|
178
|
+
url = None
|
|
179
|
+
|
|
180
|
+
return DeployResult(
|
|
181
|
+
success=True,
|
|
182
|
+
message=f"Container App deployed successfully",
|
|
183
|
+
url=url,
|
|
184
|
+
metadata={
|
|
185
|
+
"resource_group": self.config.resource_group,
|
|
186
|
+
"app_name": self.config.service_name,
|
|
187
|
+
"region": self.config.region
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
else:
|
|
191
|
+
return DeployResult(
|
|
192
|
+
success=False,
|
|
193
|
+
message="Failed to deploy Container App",
|
|
194
|
+
error=result.stderr
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
except Exception as e:
|
|
198
|
+
return DeployResult(
|
|
199
|
+
success=False,
|
|
200
|
+
message="Azure deployment failed",
|
|
201
|
+
error=str(e)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def status(self) -> DeployStatus:
|
|
205
|
+
"""Get current Azure Container App status."""
|
|
206
|
+
try:
|
|
207
|
+
if not self.config.resource_group:
|
|
208
|
+
return DeployStatus(
|
|
209
|
+
state=ServiceState.UNKNOWN,
|
|
210
|
+
message="Resource group not configured",
|
|
211
|
+
service_name=self.config.service_name,
|
|
212
|
+
provider="azure",
|
|
213
|
+
region=self.config.region
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
result = subprocess.run(
|
|
217
|
+
['az', 'containerapp', 'show',
|
|
218
|
+
'--name', self.config.service_name,
|
|
219
|
+
'--resource-group', self.config.resource_group,
|
|
220
|
+
'--output', 'json'],
|
|
221
|
+
capture_output=True,
|
|
222
|
+
text=True,
|
|
223
|
+
timeout=30
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
if result.returncode != 0:
|
|
227
|
+
return DeployStatus(
|
|
228
|
+
state=ServiceState.NOT_FOUND,
|
|
229
|
+
message=f"Container App not found: {result.stderr}",
|
|
230
|
+
service_name=self.config.service_name,
|
|
231
|
+
provider="azure",
|
|
232
|
+
region=self.config.region
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
data = json.loads(result.stdout)
|
|
236
|
+
properties = data.get('properties', {})
|
|
237
|
+
provisioning_state = properties.get('provisioningState', 'Unknown')
|
|
238
|
+
running_status = properties.get('runningStatus', {}).get('state', 'Unknown')
|
|
239
|
+
|
|
240
|
+
# Get URL
|
|
241
|
+
ingress = properties.get('configuration', {}).get('ingress', {})
|
|
242
|
+
fqdn = ingress.get('fqdn')
|
|
243
|
+
url = f"https://{fqdn}" if fqdn else None
|
|
244
|
+
|
|
245
|
+
# Get replica count
|
|
246
|
+
template = properties.get('template', {})
|
|
247
|
+
scale = template.get('scale', {})
|
|
248
|
+
min_replicas = scale.get('minReplicas', 0)
|
|
249
|
+
max_replicas = scale.get('maxReplicas', 0)
|
|
250
|
+
|
|
251
|
+
# Map status to ServiceState
|
|
252
|
+
if provisioning_state == 'Succeeded' and running_status == 'Running':
|
|
253
|
+
state = ServiceState.RUNNING
|
|
254
|
+
elif provisioning_state == 'Succeeded' and running_status == 'Stopped':
|
|
255
|
+
state = ServiceState.STOPPED
|
|
256
|
+
elif provisioning_state in ['Creating', 'Updating']:
|
|
257
|
+
state = ServiceState.PENDING
|
|
258
|
+
elif provisioning_state == 'Failed':
|
|
259
|
+
state = ServiceState.FAILED
|
|
260
|
+
else:
|
|
261
|
+
state = ServiceState.UNKNOWN
|
|
262
|
+
|
|
263
|
+
return DeployStatus(
|
|
264
|
+
state=state,
|
|
265
|
+
url=url,
|
|
266
|
+
message=f"Provisioning: {provisioning_state}, Running: {running_status}",
|
|
267
|
+
service_name=self.config.service_name,
|
|
268
|
+
provider="azure",
|
|
269
|
+
region=self.config.region,
|
|
270
|
+
healthy=state == ServiceState.RUNNING,
|
|
271
|
+
instances_running=min_replicas if state == ServiceState.RUNNING else 0,
|
|
272
|
+
instances_desired=min_replicas,
|
|
273
|
+
created_at=data.get('systemData', {}).get('createdAt'),
|
|
274
|
+
updated_at=data.get('systemData', {}).get('lastModifiedAt'),
|
|
275
|
+
metadata={
|
|
276
|
+
"resource_group": self.config.resource_group,
|
|
277
|
+
"provisioning_state": provisioning_state,
|
|
278
|
+
"running_status": running_status,
|
|
279
|
+
"min_replicas": min_replicas,
|
|
280
|
+
"max_replicas": max_replicas
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
except Exception as e:
|
|
285
|
+
return DeployStatus(
|
|
286
|
+
state=ServiceState.UNKNOWN,
|
|
287
|
+
message=f"Failed to get status: {e}",
|
|
288
|
+
service_name=self.config.service_name,
|
|
289
|
+
provider="azure",
|
|
290
|
+
region=self.config.region
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def destroy(self, force: bool = False) -> DestroyResult:
|
|
294
|
+
"""Destroy Azure Container App and related resources."""
|
|
295
|
+
try:
|
|
296
|
+
if not self.config.resource_group:
|
|
297
|
+
return DestroyResult(
|
|
298
|
+
success=False,
|
|
299
|
+
message="Resource group not configured",
|
|
300
|
+
error="Please specify resource_group in cloud config"
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
deleted_resources = []
|
|
304
|
+
|
|
305
|
+
# Step 1: Delete the Container App
|
|
306
|
+
print(f"🗑️ Deleting Container App: {self.config.service_name}")
|
|
307
|
+
result = subprocess.run(
|
|
308
|
+
['az', 'containerapp', 'delete',
|
|
309
|
+
'--name', self.config.service_name,
|
|
310
|
+
'--resource-group', self.config.resource_group,
|
|
311
|
+
'--yes'],
|
|
312
|
+
capture_output=True,
|
|
313
|
+
text=True,
|
|
314
|
+
timeout=120
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
if result.returncode == 0:
|
|
318
|
+
deleted_resources.append(f"containerapp:{self.config.service_name}")
|
|
319
|
+
else:
|
|
320
|
+
return DestroyResult(
|
|
321
|
+
success=False,
|
|
322
|
+
message="Failed to delete Container App",
|
|
323
|
+
error=result.stderr,
|
|
324
|
+
resources_deleted=deleted_resources
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# Step 2: Optionally delete environment if force
|
|
328
|
+
if force:
|
|
329
|
+
env_name = f"{self.config.service_name}-env"
|
|
330
|
+
print(f"🗑️ Deleting Container Apps environment: {env_name}")
|
|
331
|
+
env_result = subprocess.run(
|
|
332
|
+
['az', 'containerapp', 'env', 'delete',
|
|
333
|
+
'--name', env_name,
|
|
334
|
+
'--resource-group', self.config.resource_group,
|
|
335
|
+
'--yes'],
|
|
336
|
+
capture_output=True,
|
|
337
|
+
text=True,
|
|
338
|
+
timeout=120
|
|
339
|
+
)
|
|
340
|
+
if env_result.returncode == 0:
|
|
341
|
+
deleted_resources.append(f"containerapp-env:{env_name}")
|
|
342
|
+
|
|
343
|
+
return DestroyResult(
|
|
344
|
+
success=True,
|
|
345
|
+
message="Successfully destroyed Azure Container App",
|
|
346
|
+
resources_deleted=deleted_resources,
|
|
347
|
+
metadata={
|
|
348
|
+
"resource_group": self.config.resource_group,
|
|
349
|
+
"region": self.config.region
|
|
350
|
+
}
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
except Exception as e:
|
|
354
|
+
return DestroyResult(
|
|
355
|
+
success=False,
|
|
356
|
+
message="Failed to destroy Azure resources",
|
|
357
|
+
error=str(e)
|
|
358
|
+
)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base provider interface for cloud deployments.
|
|
3
|
+
"""
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from typing import Dict, Any
|
|
6
|
+
from ..models import CloudConfig, DeployResult, DeployStatus, DestroyResult
|
|
7
|
+
from ..doctor import DoctorReport
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BaseProvider(ABC):
|
|
11
|
+
"""Abstract base class for cloud providers."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, config: CloudConfig):
|
|
14
|
+
"""
|
|
15
|
+
Initialize provider with configuration.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
config: Cloud configuration
|
|
19
|
+
"""
|
|
20
|
+
self.config = config
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def deploy(self) -> DeployResult:
|
|
24
|
+
"""
|
|
25
|
+
Deploy to cloud provider.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
DeployResult with deployment information
|
|
29
|
+
"""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def doctor(self) -> DoctorReport:
|
|
34
|
+
"""
|
|
35
|
+
Run provider-specific health checks.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
DoctorReport with check results
|
|
39
|
+
"""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def plan(self) -> Dict[str, Any]:
|
|
44
|
+
"""
|
|
45
|
+
Generate deployment plan without executing.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Dictionary with planned deployment configuration
|
|
49
|
+
"""
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def status(self) -> DeployStatus:
|
|
54
|
+
"""
|
|
55
|
+
Get current deployment status.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
DeployStatus with current state and info
|
|
59
|
+
"""
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def destroy(self, force: bool = False) -> DestroyResult:
|
|
64
|
+
"""
|
|
65
|
+
Destroy/delete the deployed service.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
force: Force deletion without confirmation
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
DestroyResult with deletion information
|
|
72
|
+
"""
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_provider(config: CloudConfig) -> BaseProvider:
|
|
77
|
+
"""
|
|
78
|
+
Get provider instance based on configuration.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
config: Cloud configuration
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Provider instance
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
ValueError: If provider is not supported
|
|
88
|
+
"""
|
|
89
|
+
from ..models import CloudProvider
|
|
90
|
+
|
|
91
|
+
if config.provider == CloudProvider.AWS:
|
|
92
|
+
from .aws import AWSProvider
|
|
93
|
+
return AWSProvider(config)
|
|
94
|
+
elif config.provider == CloudProvider.AZURE:
|
|
95
|
+
from .azure import AzureProvider
|
|
96
|
+
return AzureProvider(config)
|
|
97
|
+
elif config.provider == CloudProvider.GCP:
|
|
98
|
+
from .gcp import GCPProvider
|
|
99
|
+
return GCPProvider(config)
|
|
100
|
+
else:
|
|
101
|
+
raise ValueError(f"Unsupported provider: {config.provider}")
|