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/api/call.py
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import base64
|
|
4
|
+
import asyncio
|
|
5
|
+
import websockets
|
|
6
|
+
from fastapi import FastAPI, WebSocket, Request
|
|
7
|
+
from fastapi.responses import HTMLResponse
|
|
8
|
+
from fastapi.websockets import WebSocketDisconnect
|
|
9
|
+
from twilio.twiml.voice_response import VoiceResponse, Connect
|
|
10
|
+
from dotenv import load_dotenv
|
|
11
|
+
import uvicorn
|
|
12
|
+
from pyngrok import ngrok, conf
|
|
13
|
+
from rich import print
|
|
14
|
+
import argparse
|
|
15
|
+
import logging
|
|
16
|
+
import importlib.util
|
|
17
|
+
|
|
18
|
+
load_dotenv()
|
|
19
|
+
|
|
20
|
+
# Configuration
|
|
21
|
+
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY') # requires OpenAI Realtime API Access
|
|
22
|
+
PORT = int(os.getenv('PORT', 8090))
|
|
23
|
+
NGROK_AUTH_TOKEN = os.getenv('NGROK_AUTH_TOKEN')
|
|
24
|
+
PUBLIC = os.getenv('PUBLIC', 'false').lower() == 'true'
|
|
25
|
+
SYSTEM_MESSAGE = (
|
|
26
|
+
"You are a helpful and bubbly AI assistant who loves to chat about "
|
|
27
|
+
"anything the user is interested in and is prepared to offer them facts. "
|
|
28
|
+
"Keep your responses short and to the point. "
|
|
29
|
+
"You have a penchant for dad jokes, owl jokes, and rickrolling – subtly. "
|
|
30
|
+
"Always stay positive, but work in a joke when appropriate."
|
|
31
|
+
"Start your conversation by saying 'Hi! I'm Praison AI. How can I help you today?'"
|
|
32
|
+
)
|
|
33
|
+
VOICE = 'alloy'
|
|
34
|
+
LOG_EVENT_TYPES = [
|
|
35
|
+
'response.content.done', 'rate_limits.updated', 'response.done',
|
|
36
|
+
'input_audio_buffer.committed', 'input_audio_buffer.speech_stopped',
|
|
37
|
+
'input_audio_buffer.speech_started', 'session.created'
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
app = FastAPI()
|
|
41
|
+
|
|
42
|
+
# Set up logging
|
|
43
|
+
logger = logging.getLogger(__name__)
|
|
44
|
+
log_level = os.getenv("LOGLEVEL", "INFO").upper()
|
|
45
|
+
logger.handlers = []
|
|
46
|
+
|
|
47
|
+
# Try to import tools from the root directory
|
|
48
|
+
tools = []
|
|
49
|
+
tools_path = os.path.join(os.getcwd(), 'tools.py')
|
|
50
|
+
logger.debug(f"Tools path: {tools_path}")
|
|
51
|
+
|
|
52
|
+
def import_tools_from_file(file_path):
|
|
53
|
+
spec = importlib.util.spec_from_file_location("custom_tools", file_path)
|
|
54
|
+
custom_tools_module = importlib.util.module_from_spec(spec)
|
|
55
|
+
spec.loader.exec_module(custom_tools_module)
|
|
56
|
+
logger.debug(f"Imported tools from {file_path}")
|
|
57
|
+
return custom_tools_module
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
if os.path.exists(tools_path):
|
|
61
|
+
# tools.py exists in the root directory, import from file
|
|
62
|
+
custom_tools_module = import_tools_from_file(tools_path)
|
|
63
|
+
logger.debug("Successfully imported custom tools from root tools.py")
|
|
64
|
+
else:
|
|
65
|
+
logger.debug("No custom tools.py file found in the root directory")
|
|
66
|
+
custom_tools_module = None
|
|
67
|
+
|
|
68
|
+
if custom_tools_module:
|
|
69
|
+
# Update the tools list with custom tools
|
|
70
|
+
if hasattr(custom_tools_module, 'tools') and isinstance(custom_tools_module.tools, list):
|
|
71
|
+
tools.extend(custom_tools_module.tools)
|
|
72
|
+
else:
|
|
73
|
+
for name, obj in custom_tools_module.__dict__.items():
|
|
74
|
+
if callable(obj) and not name.startswith("__"):
|
|
75
|
+
tool_definition = getattr(obj, 'definition', None)
|
|
76
|
+
if tool_definition:
|
|
77
|
+
tools.append(tool_definition)
|
|
78
|
+
|
|
79
|
+
except Exception as e:
|
|
80
|
+
logger.warning(f"Error importing custom tools: {str(e)}. Continuing without custom tools.")
|
|
81
|
+
|
|
82
|
+
@app.get("/status", response_class=HTMLResponse)
|
|
83
|
+
async def index_page():
|
|
84
|
+
return """
|
|
85
|
+
<html>
|
|
86
|
+
<head>
|
|
87
|
+
<title>Praison AI Call Server</title>
|
|
88
|
+
</head>
|
|
89
|
+
<body>
|
|
90
|
+
<h1>Praison AI Call Server is running!</h1>
|
|
91
|
+
</body>
|
|
92
|
+
</html>
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
@app.api_route("/", methods=["GET", "POST"])
|
|
96
|
+
async def handle_incoming_call(request: Request):
|
|
97
|
+
"""Handle incoming call and return TwiML response to connect to Media Stream."""
|
|
98
|
+
response = VoiceResponse()
|
|
99
|
+
response.say("")
|
|
100
|
+
response.pause(length=1)
|
|
101
|
+
# response.say("")
|
|
102
|
+
host = request.url.hostname
|
|
103
|
+
connect = Connect()
|
|
104
|
+
connect.stream(url=f'wss://{host}/media-stream')
|
|
105
|
+
response.append(connect)
|
|
106
|
+
return HTMLResponse(content=str(response), media_type="application/xml")
|
|
107
|
+
|
|
108
|
+
@app.websocket("/media-stream")
|
|
109
|
+
async def handle_media_stream(websocket: WebSocket):
|
|
110
|
+
"""Handle WebSocket connections between Twilio and OpenAI."""
|
|
111
|
+
print("Client connected")
|
|
112
|
+
await websocket.accept()
|
|
113
|
+
|
|
114
|
+
async with websockets.connect(
|
|
115
|
+
'wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01',
|
|
116
|
+
extra_headers={
|
|
117
|
+
"Authorization": f"Bearer {OPENAI_API_KEY}",
|
|
118
|
+
"OpenAI-Beta": "realtime=v1"
|
|
119
|
+
}
|
|
120
|
+
) as openai_ws:
|
|
121
|
+
await send_session_update(openai_ws)
|
|
122
|
+
stream_sid = None
|
|
123
|
+
|
|
124
|
+
async def receive_from_twilio():
|
|
125
|
+
"""Receive audio data from Twilio and send it to the OpenAI Realtime API."""
|
|
126
|
+
nonlocal stream_sid
|
|
127
|
+
try:
|
|
128
|
+
async for message in websocket.iter_text():
|
|
129
|
+
data = json.loads(message)
|
|
130
|
+
if data['event'] == 'media' and openai_ws.open:
|
|
131
|
+
audio_append = {
|
|
132
|
+
"type": "input_audio_buffer.append",
|
|
133
|
+
"audio": data['media']['payload']
|
|
134
|
+
}
|
|
135
|
+
await openai_ws.send(json.dumps(audio_append))
|
|
136
|
+
elif data['event'] == 'start':
|
|
137
|
+
stream_sid = data['start']['streamSid']
|
|
138
|
+
print(f"Incoming stream has started {stream_sid}")
|
|
139
|
+
except WebSocketDisconnect:
|
|
140
|
+
print("Client disconnected.")
|
|
141
|
+
if openai_ws.open:
|
|
142
|
+
await openai_ws.close()
|
|
143
|
+
|
|
144
|
+
async def send_to_twilio():
|
|
145
|
+
"""Receive events from the OpenAI Realtime API, send audio back to Twilio."""
|
|
146
|
+
nonlocal stream_sid
|
|
147
|
+
try:
|
|
148
|
+
async for openai_message in openai_ws:
|
|
149
|
+
response = json.loads(openai_message)
|
|
150
|
+
if response['type'] in LOG_EVENT_TYPES:
|
|
151
|
+
print(f"Received event: {response['type']}", response)
|
|
152
|
+
if response['type'] == 'session.updated':
|
|
153
|
+
print("Session updated successfully:", response)
|
|
154
|
+
|
|
155
|
+
if response['type'] == 'response.done':
|
|
156
|
+
await handle_response_done(response, openai_ws)
|
|
157
|
+
|
|
158
|
+
if response['type'] == 'response.audio.delta' and response.get('delta'):
|
|
159
|
+
# Audio from OpenAI
|
|
160
|
+
try:
|
|
161
|
+
audio_payload = base64.b64encode(base64.b64decode(response['delta'])).decode('utf-8')
|
|
162
|
+
audio_delta = {
|
|
163
|
+
"event": "media",
|
|
164
|
+
"streamSid": stream_sid,
|
|
165
|
+
"media": {
|
|
166
|
+
"payload": audio_payload
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
await websocket.send_json(audio_delta)
|
|
170
|
+
except Exception as e:
|
|
171
|
+
print(f"Error processing audio data: {e}")
|
|
172
|
+
except Exception as e:
|
|
173
|
+
print(f"Error in Sending to Phone: {e}")
|
|
174
|
+
|
|
175
|
+
await asyncio.gather(receive_from_twilio(), send_to_twilio())
|
|
176
|
+
|
|
177
|
+
async def handle_response_done(response, openai_ws):
|
|
178
|
+
"""Handle the response.done event and process any function calls."""
|
|
179
|
+
print("Handling response.done:", response)
|
|
180
|
+
output_items = response.get('response', {}).get('output', [])
|
|
181
|
+
for item in output_items:
|
|
182
|
+
if item.get('type') == 'function_call':
|
|
183
|
+
await process_function_call(item, openai_ws)
|
|
184
|
+
|
|
185
|
+
async def process_function_call(item, openai_ws):
|
|
186
|
+
"""Process a function call item and send the result back to OpenAI."""
|
|
187
|
+
function_name = item.get('name')
|
|
188
|
+
arguments = json.loads(item.get('arguments', '{}'))
|
|
189
|
+
call_id = item.get('call_id')
|
|
190
|
+
|
|
191
|
+
print(f"Processing function call: {function_name}")
|
|
192
|
+
print(f"Arguments: {arguments}")
|
|
193
|
+
|
|
194
|
+
result = await call_tool(function_name, arguments)
|
|
195
|
+
|
|
196
|
+
# Send the function call result back to OpenAI
|
|
197
|
+
await openai_ws.send(json.dumps({
|
|
198
|
+
"type": "conversation.item.create",
|
|
199
|
+
"item": {
|
|
200
|
+
"type": "function_call_output",
|
|
201
|
+
"call_id": call_id,
|
|
202
|
+
"output": json.dumps(result)
|
|
203
|
+
}
|
|
204
|
+
}))
|
|
205
|
+
|
|
206
|
+
# Create a new response after sending the function call result
|
|
207
|
+
await openai_ws.send(json.dumps({
|
|
208
|
+
"type": "response.create"
|
|
209
|
+
}))
|
|
210
|
+
|
|
211
|
+
async def call_tool(function_name, arguments):
|
|
212
|
+
"""Call the appropriate tool function and return the result."""
|
|
213
|
+
tool = next((t for t in tools if t[0]['name'] == function_name), None)
|
|
214
|
+
if not tool:
|
|
215
|
+
return {"error": f"Function {function_name} not found"}
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
# Assuming the tool function is the second element in the tuple
|
|
219
|
+
result = await tool[1](**arguments)
|
|
220
|
+
return result
|
|
221
|
+
except Exception as e:
|
|
222
|
+
return {"error": str(e)}
|
|
223
|
+
|
|
224
|
+
async def send_session_update(openai_ws):
|
|
225
|
+
"""Send session update to OpenAI WebSocket."""
|
|
226
|
+
global tools
|
|
227
|
+
print(f"Formatted tools: {tools}")
|
|
228
|
+
|
|
229
|
+
use_tools = [
|
|
230
|
+
{**tool[0], "type": "function"}
|
|
231
|
+
for tool in tools
|
|
232
|
+
if isinstance(tool, tuple) and len(tool) > 0 and isinstance(tool[0], dict)
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
session_update = {
|
|
236
|
+
"type": "session.update",
|
|
237
|
+
"session": {
|
|
238
|
+
"turn_detection": {
|
|
239
|
+
"type": "server_vad",
|
|
240
|
+
"threshold": 0.5,
|
|
241
|
+
"prefix_padding_ms": 300,
|
|
242
|
+
"silence_duration_ms": 200
|
|
243
|
+
},
|
|
244
|
+
"input_audio_format": "g711_ulaw",
|
|
245
|
+
"output_audio_format": "g711_ulaw",
|
|
246
|
+
"voice": VOICE,
|
|
247
|
+
"tools": use_tools,
|
|
248
|
+
"tool_choice": "auto",
|
|
249
|
+
"instructions": SYSTEM_MESSAGE,
|
|
250
|
+
"modalities": ["text", "audio"],
|
|
251
|
+
"temperature": 0.8
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
print('Sending session update:', json.dumps(session_update))
|
|
255
|
+
await openai_ws.send(json.dumps(session_update))
|
|
256
|
+
|
|
257
|
+
def setup_public_url(port):
|
|
258
|
+
if NGROK_AUTH_TOKEN:
|
|
259
|
+
conf.get_default().auth_token = NGROK_AUTH_TOKEN
|
|
260
|
+
public_url = ngrok.connect(addr=str(port)).public_url
|
|
261
|
+
print(f"Praison AI Voice URL: {public_url}")
|
|
262
|
+
return public_url
|
|
263
|
+
|
|
264
|
+
def run_server(port: int, use_public: bool = False):
|
|
265
|
+
"""Run the FastAPI server using uvicorn."""
|
|
266
|
+
if not OPENAI_API_KEY:
|
|
267
|
+
raise ValueError('Missing the OpenAI API key. Please set it in the .env file or configure it through the GUI.')
|
|
268
|
+
|
|
269
|
+
if use_public:
|
|
270
|
+
setup_public_url(port)
|
|
271
|
+
else:
|
|
272
|
+
print(f"Starting Praison AI Call Server on http://localhost:{port}")
|
|
273
|
+
uvicorn.run(app, host="0.0.0.0", port=port, log_level="warning")
|
|
274
|
+
|
|
275
|
+
def main(args=None):
|
|
276
|
+
"""Run the Praison AI Call Server."""
|
|
277
|
+
parser = argparse.ArgumentParser(description="Run the Praison AI Call Server.")
|
|
278
|
+
parser.add_argument('--public', action='store_true', help="Use ngrok to expose the server publicly")
|
|
279
|
+
parser.add_argument('--port', type=int, default=PORT, help="Port to run the server on")
|
|
280
|
+
|
|
281
|
+
if args is None:
|
|
282
|
+
args = parser.parse_args()
|
|
283
|
+
else:
|
|
284
|
+
args = parser.parse_args(args)
|
|
285
|
+
|
|
286
|
+
port = args.port
|
|
287
|
+
use_public = args.public or PUBLIC
|
|
288
|
+
|
|
289
|
+
run_server(port=port, use_public=use_public)
|
|
290
|
+
|
|
291
|
+
if __name__ == "__main__":
|
|
292
|
+
main()
|