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,165 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unified Discovery Schema
|
|
3
|
+
|
|
4
|
+
Provides versioned discovery document format for all PraisonAI serve features.
|
|
5
|
+
This schema is consistent across all provider types.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field, asdict
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
from enum import Enum
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Schema version - increment on breaking changes
|
|
14
|
+
SCHEMA_VERSION = "1.0.0"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ProviderType(str, Enum):
|
|
18
|
+
"""Supported provider types."""
|
|
19
|
+
RECIPE = "recipe"
|
|
20
|
+
AGENTS_API = "agents-api"
|
|
21
|
+
MCP = "mcp"
|
|
22
|
+
TOOLS_MCP = "tools-mcp"
|
|
23
|
+
A2A = "a2a"
|
|
24
|
+
A2U = "a2u"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class StreamingMode(str, Enum):
|
|
28
|
+
"""Supported streaming modes."""
|
|
29
|
+
NONE = "none"
|
|
30
|
+
SSE = "sse"
|
|
31
|
+
WEBSOCKET = "websocket"
|
|
32
|
+
MCP_STREAM = "mcp-stream"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AuthMode(str, Enum):
|
|
36
|
+
"""Supported authentication modes."""
|
|
37
|
+
NONE = "none"
|
|
38
|
+
API_KEY = "api-key"
|
|
39
|
+
BEARER = "bearer"
|
|
40
|
+
JWT = "jwt"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class EndpointInfo:
|
|
45
|
+
"""Information about a single endpoint."""
|
|
46
|
+
name: str
|
|
47
|
+
description: str = ""
|
|
48
|
+
provider_type: str = "recipe"
|
|
49
|
+
tags: List[str] = field(default_factory=list)
|
|
50
|
+
version: str = "1.0.0"
|
|
51
|
+
|
|
52
|
+
# Schema information
|
|
53
|
+
input_schema: Optional[Dict[str, Any]] = None
|
|
54
|
+
output_schema: Optional[Dict[str, Any]] = None
|
|
55
|
+
|
|
56
|
+
# Capabilities
|
|
57
|
+
streaming: List[str] = field(default_factory=lambda: ["none"])
|
|
58
|
+
auth_modes: List[str] = field(default_factory=lambda: ["none"])
|
|
59
|
+
|
|
60
|
+
# Metadata
|
|
61
|
+
examples: List[Dict[str, Any]] = field(default_factory=list)
|
|
62
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
63
|
+
|
|
64
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
65
|
+
"""Convert to dictionary."""
|
|
66
|
+
return asdict(self)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class ProviderInfo:
|
|
71
|
+
"""Information about a provider."""
|
|
72
|
+
type: str
|
|
73
|
+
name: str
|
|
74
|
+
version: str = "1.0.0"
|
|
75
|
+
description: str = ""
|
|
76
|
+
capabilities: List[str] = field(default_factory=list)
|
|
77
|
+
|
|
78
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
79
|
+
"""Convert to dictionary."""
|
|
80
|
+
return asdict(self)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class DiscoveryDocument:
|
|
85
|
+
"""
|
|
86
|
+
Unified discovery document for PraisonAI servers.
|
|
87
|
+
|
|
88
|
+
This document is served at /__praisonai__/discovery and provides
|
|
89
|
+
a consistent interface for discovering endpoints across all provider types.
|
|
90
|
+
"""
|
|
91
|
+
schema_version: str = SCHEMA_VERSION
|
|
92
|
+
server_name: str = "praisonai"
|
|
93
|
+
server_version: str = "1.0.0"
|
|
94
|
+
|
|
95
|
+
# Provider information
|
|
96
|
+
providers: List[ProviderInfo] = field(default_factory=list)
|
|
97
|
+
|
|
98
|
+
# Endpoint information
|
|
99
|
+
endpoints: List[EndpointInfo] = field(default_factory=list)
|
|
100
|
+
|
|
101
|
+
# Server capabilities
|
|
102
|
+
auth_modes: List[str] = field(default_factory=lambda: ["none"])
|
|
103
|
+
streaming_modes: List[str] = field(default_factory=lambda: ["none", "sse"])
|
|
104
|
+
|
|
105
|
+
# Metadata
|
|
106
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
107
|
+
|
|
108
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
109
|
+
"""Convert to dictionary for JSON serialization."""
|
|
110
|
+
return {
|
|
111
|
+
"schema_version": self.schema_version,
|
|
112
|
+
"server_name": self.server_name,
|
|
113
|
+
"server_version": self.server_version,
|
|
114
|
+
"providers": [p.to_dict() for p in self.providers],
|
|
115
|
+
"endpoints": [e.to_dict() for e in self.endpoints],
|
|
116
|
+
"auth_modes": self.auth_modes,
|
|
117
|
+
"streaming_modes": self.streaming_modes,
|
|
118
|
+
"metadata": self.metadata,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
def add_provider(self, provider: ProviderInfo) -> None:
|
|
122
|
+
"""Add a provider to the discovery document."""
|
|
123
|
+
self.providers.append(provider)
|
|
124
|
+
|
|
125
|
+
def add_endpoint(self, endpoint: EndpointInfo) -> None:
|
|
126
|
+
"""Add an endpoint to the discovery document."""
|
|
127
|
+
self.endpoints.append(endpoint)
|
|
128
|
+
|
|
129
|
+
def get_endpoints_by_type(self, provider_type: str) -> List[EndpointInfo]:
|
|
130
|
+
"""Get endpoints filtered by provider type."""
|
|
131
|
+
return [e for e in self.endpoints if e.provider_type == provider_type]
|
|
132
|
+
|
|
133
|
+
def get_endpoint_by_name(self, name: str) -> Optional[EndpointInfo]:
|
|
134
|
+
"""Get an endpoint by name."""
|
|
135
|
+
for e in self.endpoints:
|
|
136
|
+
if e.name == name:
|
|
137
|
+
return e
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def create_discovery_document(
|
|
142
|
+
server_name: str = "praisonai",
|
|
143
|
+
server_version: Optional[str] = None,
|
|
144
|
+
) -> DiscoveryDocument:
|
|
145
|
+
"""
|
|
146
|
+
Create a new discovery document.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
server_name: Name of the server
|
|
150
|
+
server_version: Version of the server (auto-detected if not provided)
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
DiscoveryDocument instance
|
|
154
|
+
"""
|
|
155
|
+
if server_version is None:
|
|
156
|
+
try:
|
|
157
|
+
from praisonai.version import __version__
|
|
158
|
+
server_version = __version__
|
|
159
|
+
except ImportError:
|
|
160
|
+
server_version = "unknown"
|
|
161
|
+
|
|
162
|
+
return DiscoveryDocument(
|
|
163
|
+
server_name=server_name,
|
|
164
|
+
server_version=server_version,
|
|
165
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provider Adapters
|
|
3
|
+
|
|
4
|
+
Each provider adapter implements the BaseProvider interface
|
|
5
|
+
and handles protocol-specific details for discovery and invocation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Lazy loading for all exports
|
|
9
|
+
_lazy_imports = {
|
|
10
|
+
"BaseProvider": ".base",
|
|
11
|
+
"RecipeProvider": ".recipe",
|
|
12
|
+
"AgentsAPIProvider": ".agents_api",
|
|
13
|
+
"MCPProvider": ".mcp",
|
|
14
|
+
"ToolsMCPProvider": ".tools_mcp",
|
|
15
|
+
"A2AProvider": ".a2a",
|
|
16
|
+
"A2UProvider": ".a2u",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def __getattr__(name: str):
|
|
20
|
+
if name in _lazy_imports:
|
|
21
|
+
module_path = _lazy_imports[name]
|
|
22
|
+
import importlib
|
|
23
|
+
module = importlib.import_module(module_path, package=__name__)
|
|
24
|
+
return getattr(module, name)
|
|
25
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
26
|
+
|
|
27
|
+
def __dir__():
|
|
28
|
+
return list(_lazy_imports.keys())
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2A Provider
|
|
3
|
+
|
|
4
|
+
Provider adapter for Agent-to-Agent protocol endpoints.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Dict, Iterator, List, Optional
|
|
8
|
+
|
|
9
|
+
from .base import BaseProvider, InvokeResult, HealthResult
|
|
10
|
+
from ..discovery import EndpointInfo, ProviderInfo
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class A2AProvider(BaseProvider):
|
|
14
|
+
"""
|
|
15
|
+
Provider adapter for A2A (Agent-to-Agent) protocol endpoints.
|
|
16
|
+
|
|
17
|
+
A2A enables agent-to-agent communication following the A2A protocol spec.
|
|
18
|
+
Supports discovery via /.well-known/agent.json and message passing.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
provider_type = "a2a"
|
|
22
|
+
|
|
23
|
+
def get_provider_info(self) -> ProviderInfo:
|
|
24
|
+
"""Get provider information."""
|
|
25
|
+
return ProviderInfo(
|
|
26
|
+
type=self.provider_type,
|
|
27
|
+
name="A2A Protocol",
|
|
28
|
+
description="Agent-to-Agent communication protocol",
|
|
29
|
+
capabilities=["agent-card", "message-send", "task-management"],
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def list_endpoints(self, tags: Optional[List[str]] = None) -> List[EndpointInfo]:
|
|
33
|
+
"""List available A2A agents."""
|
|
34
|
+
# Try unified discovery first
|
|
35
|
+
result = self._make_request("GET", "/__praisonai__/discovery")
|
|
36
|
+
|
|
37
|
+
if not result.get("error") and result.get("data"):
|
|
38
|
+
data = result.get("data", {})
|
|
39
|
+
endpoints = []
|
|
40
|
+
for ep in data.get("endpoints", []):
|
|
41
|
+
if ep.get("provider_type") == self.provider_type:
|
|
42
|
+
endpoints.append(EndpointInfo(
|
|
43
|
+
name=ep.get("name", ""),
|
|
44
|
+
description=ep.get("description", ""),
|
|
45
|
+
provider_type=self.provider_type,
|
|
46
|
+
tags=ep.get("tags", []),
|
|
47
|
+
streaming=ep.get("streaming", ["sse"]),
|
|
48
|
+
auth_modes=ep.get("auth_modes", ["none"]),
|
|
49
|
+
))
|
|
50
|
+
return endpoints
|
|
51
|
+
|
|
52
|
+
# Fallback: try agent card
|
|
53
|
+
result = self._make_request("GET", "/.well-known/agent.json")
|
|
54
|
+
|
|
55
|
+
if result.get("error"):
|
|
56
|
+
return []
|
|
57
|
+
|
|
58
|
+
agent_card = result.get("data", {})
|
|
59
|
+
|
|
60
|
+
return [EndpointInfo(
|
|
61
|
+
name=agent_card.get("name", "a2a-agent"),
|
|
62
|
+
description=agent_card.get("description", "A2A Agent"),
|
|
63
|
+
provider_type=self.provider_type,
|
|
64
|
+
version=agent_card.get("version", "1.0.0"),
|
|
65
|
+
streaming=["sse"] if agent_card.get("capabilities", {}).get("streaming") else ["none"],
|
|
66
|
+
auth_modes=["none"],
|
|
67
|
+
metadata={"agent_card": agent_card},
|
|
68
|
+
)]
|
|
69
|
+
|
|
70
|
+
def describe_endpoint(self, name: str) -> Optional[EndpointInfo]:
|
|
71
|
+
"""Get detailed information about an A2A agent."""
|
|
72
|
+
# Get agent card
|
|
73
|
+
result = self._make_request("GET", "/.well-known/agent.json")
|
|
74
|
+
|
|
75
|
+
if result.get("error"):
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
agent_card = result.get("data", {})
|
|
79
|
+
|
|
80
|
+
return EndpointInfo(
|
|
81
|
+
name=agent_card.get("name", name),
|
|
82
|
+
description=agent_card.get("description", ""),
|
|
83
|
+
provider_type=self.provider_type,
|
|
84
|
+
version=agent_card.get("version", "1.0.0"),
|
|
85
|
+
streaming=["sse"] if agent_card.get("capabilities", {}).get("streaming") else ["none"],
|
|
86
|
+
auth_modes=["none"],
|
|
87
|
+
metadata={
|
|
88
|
+
"agent_card": agent_card,
|
|
89
|
+
"skills": agent_card.get("skills", []),
|
|
90
|
+
"capabilities": agent_card.get("capabilities", {}),
|
|
91
|
+
},
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def invoke(
|
|
95
|
+
self,
|
|
96
|
+
name: str,
|
|
97
|
+
input_data: Optional[Dict[str, Any]] = None,
|
|
98
|
+
config: Optional[Dict[str, Any]] = None,
|
|
99
|
+
stream: bool = False,
|
|
100
|
+
) -> InvokeResult:
|
|
101
|
+
"""Send a message to an A2A agent."""
|
|
102
|
+
import uuid
|
|
103
|
+
|
|
104
|
+
# Build A2A message
|
|
105
|
+
message = input_data.get("message", "") if input_data else ""
|
|
106
|
+
if not message and config:
|
|
107
|
+
message = config.get("message", config.get("query", ""))
|
|
108
|
+
|
|
109
|
+
body = {
|
|
110
|
+
"jsonrpc": "2.0",
|
|
111
|
+
"method": "message/send",
|
|
112
|
+
"id": str(uuid.uuid4()),
|
|
113
|
+
"params": {
|
|
114
|
+
"message": {
|
|
115
|
+
"role": "user",
|
|
116
|
+
"parts": [{"type": "text", "text": message}],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
result = self._make_request("POST", "/a2a", json_data=body)
|
|
122
|
+
|
|
123
|
+
if result.get("error"):
|
|
124
|
+
return InvokeResult(
|
|
125
|
+
ok=False,
|
|
126
|
+
status="error",
|
|
127
|
+
error=result["error"].get("message", str(result["error"])),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
data = result.get("data", {})
|
|
131
|
+
|
|
132
|
+
# Handle JSON-RPC response
|
|
133
|
+
if "result" in data:
|
|
134
|
+
return InvokeResult(
|
|
135
|
+
ok=True,
|
|
136
|
+
status="success",
|
|
137
|
+
data=data["result"],
|
|
138
|
+
)
|
|
139
|
+
elif "error" in data:
|
|
140
|
+
return InvokeResult(
|
|
141
|
+
ok=False,
|
|
142
|
+
status="error",
|
|
143
|
+
error=data["error"].get("message", str(data["error"])),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
return InvokeResult(
|
|
147
|
+
ok=True,
|
|
148
|
+
status="success",
|
|
149
|
+
data=data,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def invoke_stream(
|
|
153
|
+
self,
|
|
154
|
+
name: str,
|
|
155
|
+
input_data: Optional[Dict[str, Any]] = None,
|
|
156
|
+
config: Optional[Dict[str, Any]] = None,
|
|
157
|
+
) -> Iterator[Dict[str, Any]]:
|
|
158
|
+
"""Send a streaming message to an A2A agent."""
|
|
159
|
+
import json
|
|
160
|
+
import urllib.request
|
|
161
|
+
import uuid
|
|
162
|
+
|
|
163
|
+
message = input_data.get("message", "") if input_data else ""
|
|
164
|
+
if not message and config:
|
|
165
|
+
message = config.get("message", config.get("query", ""))
|
|
166
|
+
|
|
167
|
+
body = {
|
|
168
|
+
"jsonrpc": "2.0",
|
|
169
|
+
"method": "message/stream",
|
|
170
|
+
"id": str(uuid.uuid4()),
|
|
171
|
+
"params": {
|
|
172
|
+
"message": {
|
|
173
|
+
"role": "user",
|
|
174
|
+
"parts": [{"type": "text", "text": message}],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
full_url = f"{self.base_url}/a2a"
|
|
180
|
+
|
|
181
|
+
headers = {
|
|
182
|
+
"Content-Type": "application/json",
|
|
183
|
+
"Accept": "text/event-stream",
|
|
184
|
+
}
|
|
185
|
+
if self.api_key:
|
|
186
|
+
headers["X-API-Key"] = self.api_key
|
|
187
|
+
|
|
188
|
+
data = json.dumps(body).encode("utf-8")
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
req = urllib.request.Request(full_url, data=data, headers=headers, method="POST")
|
|
192
|
+
|
|
193
|
+
with urllib.request.urlopen(req, timeout=300) as response:
|
|
194
|
+
buffer = ""
|
|
195
|
+
for line in response:
|
|
196
|
+
line = line.decode("utf-8")
|
|
197
|
+
buffer += line
|
|
198
|
+
|
|
199
|
+
if buffer.endswith("\n\n"):
|
|
200
|
+
event_type = "message"
|
|
201
|
+
event_data = ""
|
|
202
|
+
|
|
203
|
+
for part in buffer.strip().split("\n"):
|
|
204
|
+
if part.startswith("event:"):
|
|
205
|
+
event_type = part[6:].strip()
|
|
206
|
+
elif part.startswith("data:"):
|
|
207
|
+
event_data = part[5:].strip()
|
|
208
|
+
|
|
209
|
+
if event_data:
|
|
210
|
+
try:
|
|
211
|
+
yield {"event": event_type, "data": json.loads(event_data)}
|
|
212
|
+
except json.JSONDecodeError:
|
|
213
|
+
yield {"event": event_type, "data": event_data}
|
|
214
|
+
|
|
215
|
+
buffer = ""
|
|
216
|
+
|
|
217
|
+
except Exception as e:
|
|
218
|
+
yield {"event": "error", "data": {"error": str(e)}}
|
|
219
|
+
|
|
220
|
+
def health(self) -> HealthResult:
|
|
221
|
+
"""Check A2A server health."""
|
|
222
|
+
# Try status endpoint first
|
|
223
|
+
result = self._make_request("GET", "/status")
|
|
224
|
+
|
|
225
|
+
if not result.get("error"):
|
|
226
|
+
data = result.get("data", {})
|
|
227
|
+
return HealthResult(
|
|
228
|
+
healthy=data.get("status") == "ok",
|
|
229
|
+
status=data.get("status", "unknown"),
|
|
230
|
+
server_name=data.get("name", "A2A Agent"),
|
|
231
|
+
server_version=data.get("version"),
|
|
232
|
+
provider_type=self.provider_type,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Fallback: try agent card
|
|
236
|
+
result = self._make_request("GET", "/.well-known/agent.json")
|
|
237
|
+
|
|
238
|
+
if result.get("error"):
|
|
239
|
+
return HealthResult(
|
|
240
|
+
healthy=False,
|
|
241
|
+
status="unhealthy",
|
|
242
|
+
provider_type=self.provider_type,
|
|
243
|
+
metadata={"error": result["error"].get("message", str(result["error"]))},
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
agent_card = result.get("data", {})
|
|
247
|
+
return HealthResult(
|
|
248
|
+
healthy=True,
|
|
249
|
+
status="healthy",
|
|
250
|
+
server_name=agent_card.get("name", "A2A Agent"),
|
|
251
|
+
server_version=agent_card.get("version"),
|
|
252
|
+
provider_type=self.provider_type,
|
|
253
|
+
)
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2U Provider
|
|
3
|
+
|
|
4
|
+
Provider adapter for Agent-to-User event stream endpoints.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Dict, Iterator, List, Optional
|
|
8
|
+
|
|
9
|
+
from .base import BaseProvider, InvokeResult, HealthResult
|
|
10
|
+
from ..discovery import EndpointInfo, ProviderInfo
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class A2UProvider(BaseProvider):
|
|
14
|
+
"""
|
|
15
|
+
Provider adapter for A2U (Agent-to-User) event stream endpoints.
|
|
16
|
+
|
|
17
|
+
A2U provides a protocol surface for streaming agent events to users,
|
|
18
|
+
typically via SSE or WebSocket. This enables real-time UI updates.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
provider_type = "a2u"
|
|
22
|
+
|
|
23
|
+
def get_provider_info(self) -> ProviderInfo:
|
|
24
|
+
"""Get provider information."""
|
|
25
|
+
return ProviderInfo(
|
|
26
|
+
type=self.provider_type,
|
|
27
|
+
name="A2U Event Stream",
|
|
28
|
+
description="Agent-to-User event streaming protocol",
|
|
29
|
+
capabilities=["event-stream", "subscribe", "unsubscribe"],
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def list_endpoints(self, tags: Optional[List[str]] = None) -> List[EndpointInfo]:
|
|
33
|
+
"""List available A2U event streams."""
|
|
34
|
+
# Try unified discovery first
|
|
35
|
+
result = self._make_request("GET", "/__praisonai__/discovery")
|
|
36
|
+
|
|
37
|
+
if not result.get("error") and result.get("data"):
|
|
38
|
+
data = result.get("data", {})
|
|
39
|
+
endpoints = []
|
|
40
|
+
for ep in data.get("endpoints", []):
|
|
41
|
+
if ep.get("provider_type") == self.provider_type:
|
|
42
|
+
endpoints.append(EndpointInfo(
|
|
43
|
+
name=ep.get("name", ""),
|
|
44
|
+
description=ep.get("description", ""),
|
|
45
|
+
provider_type=self.provider_type,
|
|
46
|
+
tags=ep.get("tags", []),
|
|
47
|
+
streaming=ep.get("streaming", ["sse"]),
|
|
48
|
+
auth_modes=ep.get("auth_modes", ["none"]),
|
|
49
|
+
))
|
|
50
|
+
return endpoints
|
|
51
|
+
|
|
52
|
+
# Fallback: check for events endpoint
|
|
53
|
+
result = self._make_request("GET", "/a2u/info")
|
|
54
|
+
|
|
55
|
+
if result.get("error"):
|
|
56
|
+
# Return default event stream endpoint
|
|
57
|
+
return [EndpointInfo(
|
|
58
|
+
name="events",
|
|
59
|
+
description="Agent event stream",
|
|
60
|
+
provider_type=self.provider_type,
|
|
61
|
+
streaming=["sse"],
|
|
62
|
+
auth_modes=["none"],
|
|
63
|
+
)]
|
|
64
|
+
|
|
65
|
+
data = result.get("data", {})
|
|
66
|
+
streams = data.get("streams", [{"name": "events", "description": "Agent event stream"}])
|
|
67
|
+
|
|
68
|
+
endpoints = []
|
|
69
|
+
for stream in streams:
|
|
70
|
+
endpoints.append(EndpointInfo(
|
|
71
|
+
name=stream.get("name", "events"),
|
|
72
|
+
description=stream.get("description", "Event stream"),
|
|
73
|
+
provider_type=self.provider_type,
|
|
74
|
+
streaming=["sse", "websocket"],
|
|
75
|
+
auth_modes=["none"],
|
|
76
|
+
))
|
|
77
|
+
|
|
78
|
+
return endpoints
|
|
79
|
+
|
|
80
|
+
def describe_endpoint(self, name: str) -> Optional[EndpointInfo]:
|
|
81
|
+
"""Get detailed information about an A2U event stream."""
|
|
82
|
+
endpoints = self.list_endpoints()
|
|
83
|
+
for ep in endpoints:
|
|
84
|
+
if ep.name == name:
|
|
85
|
+
return ep
|
|
86
|
+
|
|
87
|
+
# Return default info
|
|
88
|
+
return EndpointInfo(
|
|
89
|
+
name=name,
|
|
90
|
+
description=f"Event stream: {name}",
|
|
91
|
+
provider_type=self.provider_type,
|
|
92
|
+
streaming=["sse"],
|
|
93
|
+
auth_modes=["none"],
|
|
94
|
+
metadata={
|
|
95
|
+
"event_types": [
|
|
96
|
+
"agent.started",
|
|
97
|
+
"agent.thinking",
|
|
98
|
+
"agent.tool_call",
|
|
99
|
+
"agent.response",
|
|
100
|
+
"agent.completed",
|
|
101
|
+
"agent.error",
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def invoke(
|
|
107
|
+
self,
|
|
108
|
+
name: str,
|
|
109
|
+
input_data: Optional[Dict[str, Any]] = None,
|
|
110
|
+
config: Optional[Dict[str, Any]] = None,
|
|
111
|
+
stream: bool = False,
|
|
112
|
+
) -> InvokeResult:
|
|
113
|
+
"""Subscribe to an A2U event stream (returns subscription info)."""
|
|
114
|
+
body = {
|
|
115
|
+
"stream": name,
|
|
116
|
+
"filters": input_data.get("filters", []) if input_data else [],
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
result = self._make_request("POST", "/a2u/subscribe", json_data=body)
|
|
120
|
+
|
|
121
|
+
if result.get("error"):
|
|
122
|
+
return InvokeResult(
|
|
123
|
+
ok=False,
|
|
124
|
+
status="error",
|
|
125
|
+
error=result["error"].get("message", str(result["error"])),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
data = result.get("data", {})
|
|
129
|
+
return InvokeResult(
|
|
130
|
+
ok=True,
|
|
131
|
+
status="subscribed",
|
|
132
|
+
data={
|
|
133
|
+
"subscription_id": data.get("subscription_id"),
|
|
134
|
+
"stream_url": data.get("stream_url", f"{self.base_url}/a2u/events/{name}"),
|
|
135
|
+
},
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def invoke_stream(
|
|
139
|
+
self,
|
|
140
|
+
name: str,
|
|
141
|
+
input_data: Optional[Dict[str, Any]] = None,
|
|
142
|
+
config: Optional[Dict[str, Any]] = None,
|
|
143
|
+
) -> Iterator[Dict[str, Any]]:
|
|
144
|
+
"""Stream events from an A2U endpoint."""
|
|
145
|
+
import json
|
|
146
|
+
import urllib.request
|
|
147
|
+
|
|
148
|
+
stream_path = f"/a2u/events/{name}"
|
|
149
|
+
full_url = f"{self.base_url}{stream_path}"
|
|
150
|
+
|
|
151
|
+
headers = {"Accept": "text/event-stream"}
|
|
152
|
+
if self.api_key:
|
|
153
|
+
headers["X-API-Key"] = self.api_key
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
req = urllib.request.Request(full_url, headers=headers, method="GET")
|
|
157
|
+
|
|
158
|
+
with urllib.request.urlopen(req, timeout=300) as response:
|
|
159
|
+
buffer = ""
|
|
160
|
+
for line in response:
|
|
161
|
+
line = line.decode("utf-8")
|
|
162
|
+
buffer += line
|
|
163
|
+
|
|
164
|
+
if buffer.endswith("\n\n"):
|
|
165
|
+
event_type = "event"
|
|
166
|
+
event_data = ""
|
|
167
|
+
|
|
168
|
+
for part in buffer.strip().split("\n"):
|
|
169
|
+
if part.startswith("event:"):
|
|
170
|
+
event_type = part[6:].strip()
|
|
171
|
+
elif part.startswith("data:"):
|
|
172
|
+
event_data = part[5:].strip()
|
|
173
|
+
|
|
174
|
+
if event_data:
|
|
175
|
+
try:
|
|
176
|
+
yield {"event": event_type, "data": json.loads(event_data)}
|
|
177
|
+
except json.JSONDecodeError:
|
|
178
|
+
yield {"event": event_type, "data": event_data}
|
|
179
|
+
|
|
180
|
+
buffer = ""
|
|
181
|
+
|
|
182
|
+
except Exception as e:
|
|
183
|
+
yield {"event": "error", "data": {"error": str(e)}}
|
|
184
|
+
|
|
185
|
+
def health(self) -> HealthResult:
|
|
186
|
+
"""Check A2U server health."""
|
|
187
|
+
result = self._make_request("GET", "/health")
|
|
188
|
+
|
|
189
|
+
if result.get("error"):
|
|
190
|
+
# Try a2u-specific health
|
|
191
|
+
result = self._make_request("GET", "/a2u/health")
|
|
192
|
+
|
|
193
|
+
if result.get("error"):
|
|
194
|
+
return HealthResult(
|
|
195
|
+
healthy=False,
|
|
196
|
+
status="unhealthy",
|
|
197
|
+
provider_type=self.provider_type,
|
|
198
|
+
metadata={"error": result["error"].get("message", str(result["error"]))},
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
data = result.get("data", {})
|
|
202
|
+
return HealthResult(
|
|
203
|
+
healthy=True,
|
|
204
|
+
status="healthy",
|
|
205
|
+
server_name=data.get("server_name", "A2U Server"),
|
|
206
|
+
server_version=data.get("version"),
|
|
207
|
+
provider_type=self.provider_type,
|
|
208
|
+
)
|