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/ui/agents.py
ADDED
|
@@ -0,0 +1,824 @@
|
|
|
1
|
+
from chainlit.input_widget import Select, TextInput
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import yaml
|
|
5
|
+
import logging
|
|
6
|
+
import inspect
|
|
7
|
+
import chainlit as cl
|
|
8
|
+
from praisonaiagents import Agent, Task, PraisonAIAgents, register_display_callback
|
|
9
|
+
|
|
10
|
+
framework = "praisonai"
|
|
11
|
+
config_list = [
|
|
12
|
+
{
|
|
13
|
+
'model': os.environ.get("OPENAI_MODEL_NAME", "gpt-5-nano"),
|
|
14
|
+
'base_url': os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1"),
|
|
15
|
+
'api_key': os.environ.get("OPENAI_API_KEY", "")
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
actions = [
|
|
20
|
+
cl.Action(name="run", payload={"value": "run"}, label="run"),
|
|
21
|
+
cl.Action(name="modify", payload={"value": "modify"}, label="Modify"),
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
@cl.action_callback("run")
|
|
25
|
+
async def on_run(action):
|
|
26
|
+
await main(cl.Message(content=""))
|
|
27
|
+
|
|
28
|
+
@cl.action_callback("modify")
|
|
29
|
+
async def on_modify(action):
|
|
30
|
+
await cl.Message(content="Modify the agents and tools from below settings").send()
|
|
31
|
+
|
|
32
|
+
import os
|
|
33
|
+
import sys
|
|
34
|
+
import yaml
|
|
35
|
+
import logging
|
|
36
|
+
import inspect
|
|
37
|
+
import asyncio
|
|
38
|
+
import importlib.util
|
|
39
|
+
import sqlite3
|
|
40
|
+
from queue import Queue
|
|
41
|
+
from datetime import datetime
|
|
42
|
+
from dotenv import load_dotenv
|
|
43
|
+
|
|
44
|
+
# Chainlit imports
|
|
45
|
+
import chainlit as cl
|
|
46
|
+
from chainlit.types import ThreadDict
|
|
47
|
+
import chainlit.data as cl_data
|
|
48
|
+
|
|
49
|
+
# -----------------------------------------------------------------------------
|
|
50
|
+
# Global Setup
|
|
51
|
+
# -----------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
load_dotenv()
|
|
54
|
+
log_level = os.getenv("LOGLEVEL", "INFO").upper()
|
|
55
|
+
logger = logging.getLogger(__name__)
|
|
56
|
+
logger.setLevel(log_level)
|
|
57
|
+
|
|
58
|
+
message_queue = Queue() # Queue to handle messages sent to Chainlit UI
|
|
59
|
+
agent_file = "agents.yaml"
|
|
60
|
+
|
|
61
|
+
# -----------------------------------------------------------------------------
|
|
62
|
+
# Database and Settings Logic
|
|
63
|
+
# -----------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
MAX_RETRIES = 3
|
|
66
|
+
RETRY_DELAY = 1 # seconds
|
|
67
|
+
|
|
68
|
+
from db import DatabaseManager
|
|
69
|
+
|
|
70
|
+
async def init_database_with_retry():
|
|
71
|
+
db = DatabaseManager()
|
|
72
|
+
for attempt in range(MAX_RETRIES):
|
|
73
|
+
try:
|
|
74
|
+
db.initialize()
|
|
75
|
+
return db
|
|
76
|
+
except sqlite3.OperationalError as e:
|
|
77
|
+
if "database is locked" in str(e) and attempt < MAX_RETRIES - 1:
|
|
78
|
+
await asyncio.sleep(RETRY_DELAY)
|
|
79
|
+
continue
|
|
80
|
+
raise
|
|
81
|
+
|
|
82
|
+
db_manager = asyncio.run(init_database_with_retry())
|
|
83
|
+
cl_data._data_layer = db_manager
|
|
84
|
+
|
|
85
|
+
async def save_setting_with_retry(key: str, value: str):
|
|
86
|
+
for attempt in range(MAX_RETRIES):
|
|
87
|
+
try:
|
|
88
|
+
await db_manager.save_setting(key, value)
|
|
89
|
+
return
|
|
90
|
+
except sqlite3.OperationalError as e:
|
|
91
|
+
if "database is locked" in str(e) and attempt < MAX_RETRIES - 1:
|
|
92
|
+
await asyncio.sleep(RETRY_DELAY)
|
|
93
|
+
continue
|
|
94
|
+
raise
|
|
95
|
+
|
|
96
|
+
async def load_setting_with_retry(key: str) -> str:
|
|
97
|
+
for attempt in range(MAX_RETRIES):
|
|
98
|
+
try:
|
|
99
|
+
return await db_manager.load_setting(key)
|
|
100
|
+
except sqlite3.OperationalError as e:
|
|
101
|
+
if "database is locked" in str(e) and attempt < MAX_RETRIES - 1:
|
|
102
|
+
await asyncio.sleep(RETRY_DELAY)
|
|
103
|
+
continue
|
|
104
|
+
raise
|
|
105
|
+
return ""
|
|
106
|
+
|
|
107
|
+
def save_setting(key: str, value: str):
|
|
108
|
+
asyncio.run(save_setting_with_retry(key, value))
|
|
109
|
+
|
|
110
|
+
def load_setting(key: str) -> str:
|
|
111
|
+
return asyncio.run(load_setting_with_retry(key))
|
|
112
|
+
|
|
113
|
+
async def update_thread_metadata(thread_id: str, metadata: dict):
|
|
114
|
+
for attempt in range(MAX_RETRIES):
|
|
115
|
+
try:
|
|
116
|
+
await cl_data.update_thread(thread_id, metadata=metadata)
|
|
117
|
+
return
|
|
118
|
+
except sqlite3.OperationalError as e:
|
|
119
|
+
if "database is locked" in str(e) and attempt < MAX_RETRIES - 1:
|
|
120
|
+
await asyncio.sleep(RETRY_DELAY)
|
|
121
|
+
continue
|
|
122
|
+
raise
|
|
123
|
+
|
|
124
|
+
# -----------------------------------------------------------------------------
|
|
125
|
+
# Callback Manager
|
|
126
|
+
# -----------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
class CallbackManager:
|
|
129
|
+
def __init__(self):
|
|
130
|
+
self._callbacks = {}
|
|
131
|
+
|
|
132
|
+
def register(self, name: str, callback, is_async: bool = False) -> None:
|
|
133
|
+
self._callbacks[name] = {'func': callback, 'is_async': is_async}
|
|
134
|
+
|
|
135
|
+
async def call(self, name: str, **kwargs) -> None:
|
|
136
|
+
if name not in self._callbacks:
|
|
137
|
+
logger.warning(f"No callback registered for {name}")
|
|
138
|
+
return
|
|
139
|
+
callback_info = self._callbacks[name]
|
|
140
|
+
func = callback_info['func']
|
|
141
|
+
is_async = callback_info['is_async']
|
|
142
|
+
try:
|
|
143
|
+
if is_async:
|
|
144
|
+
await func(**kwargs)
|
|
145
|
+
else:
|
|
146
|
+
if asyncio.iscoroutinefunction(func):
|
|
147
|
+
await func(**kwargs)
|
|
148
|
+
else:
|
|
149
|
+
await asyncio.get_event_loop().run_in_executor(None, lambda: func(**kwargs))
|
|
150
|
+
except Exception as e:
|
|
151
|
+
logger.error(f"Error in callback {name}: {str(e)}")
|
|
152
|
+
|
|
153
|
+
callback_manager = CallbackManager()
|
|
154
|
+
|
|
155
|
+
def register_callback(name: str, callback, is_async: bool = False) -> None:
|
|
156
|
+
callback_manager.register(name, callback, is_async)
|
|
157
|
+
|
|
158
|
+
async def trigger_callback(name: str, **kwargs) -> None:
|
|
159
|
+
await callback_manager.call(name, **kwargs)
|
|
160
|
+
|
|
161
|
+
def callback(name: str, is_async: bool = False):
|
|
162
|
+
def decorator(func):
|
|
163
|
+
register_callback(name, func, is_async)
|
|
164
|
+
return func
|
|
165
|
+
return decorator
|
|
166
|
+
|
|
167
|
+
# -----------------------------------------------------------------------------
|
|
168
|
+
# ADDITIONAL CALLBACKS
|
|
169
|
+
# -----------------------------------------------------------------------------
|
|
170
|
+
def interaction_callback(message=None, response=None, **kwargs):
|
|
171
|
+
logger.debug(f"[CALLBACK: interaction] Message: {message} | Response: {response}")
|
|
172
|
+
message_queue.put({
|
|
173
|
+
"content": f"[CALLBACK: interaction] Message: {message} | Response: {response}",
|
|
174
|
+
"author": "Callback"
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
def error_callback(message=None, **kwargs):
|
|
178
|
+
logger.error(f"[CALLBACK: error] Message: {message}")
|
|
179
|
+
message_queue.put({
|
|
180
|
+
"content": f"[CALLBACK: error] Message: {message}",
|
|
181
|
+
"author": "Callback"
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
def tool_call_callback(message=None, **kwargs):
|
|
185
|
+
logger.debug(f"[CALLBACK: tool_call] Tool used: {message}")
|
|
186
|
+
message_queue.put({
|
|
187
|
+
"content": f"[CALLBACK: tool_call] Tool used: {message}",
|
|
188
|
+
"author": "Callback"
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
def instruction_callback(message=None, **kwargs):
|
|
192
|
+
logger.debug(f"[CALLBACK: instruction] Instruction: {message}")
|
|
193
|
+
message_queue.put({
|
|
194
|
+
"content": f"[CALLBACK: instruction] Instruction: {message}",
|
|
195
|
+
"author": "Callback"
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
def self_reflection_callback(message=None, **kwargs):
|
|
199
|
+
logger.debug(f"[CALLBACK: self_reflection] Reflection: {message}")
|
|
200
|
+
message_queue.put({
|
|
201
|
+
"content": f"[CALLBACK: self_reflection] Reflection: {message}",
|
|
202
|
+
"author": "Callback"
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
register_display_callback('error', error_callback)
|
|
206
|
+
register_display_callback('tool_call', tool_call_callback)
|
|
207
|
+
register_display_callback('instruction', instruction_callback)
|
|
208
|
+
register_display_callback('self_reflection', self_reflection_callback)
|
|
209
|
+
|
|
210
|
+
# -----------------------------------------------------------------------------
|
|
211
|
+
# Tools Loader
|
|
212
|
+
# -----------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
def load_tools_from_tools_py():
|
|
215
|
+
"""
|
|
216
|
+
Imports and returns all contents from tools.py file.
|
|
217
|
+
Also adds the tools to the global namespace.
|
|
218
|
+
"""
|
|
219
|
+
tools_dict = {}
|
|
220
|
+
try:
|
|
221
|
+
spec = importlib.util.spec_from_file_location("tools", "tools.py")
|
|
222
|
+
if spec is None:
|
|
223
|
+
logger.info("tools.py not found in current directory")
|
|
224
|
+
return tools_dict
|
|
225
|
+
|
|
226
|
+
module = importlib.util.module_from_spec(spec)
|
|
227
|
+
spec.loader.exec_module(module)
|
|
228
|
+
|
|
229
|
+
for name, obj in inspect.getmembers(module):
|
|
230
|
+
if not name.startswith('_') and callable(obj) and not inspect.isclass(obj):
|
|
231
|
+
# Store the function in globals
|
|
232
|
+
globals()[name] = obj
|
|
233
|
+
|
|
234
|
+
# Build the function definition
|
|
235
|
+
tool_def = {
|
|
236
|
+
"type": "function",
|
|
237
|
+
"function": {
|
|
238
|
+
"name": name,
|
|
239
|
+
"description": obj.__doc__ or f"Function to {name.replace('_', ' ')}",
|
|
240
|
+
"parameters": {
|
|
241
|
+
"type": "object",
|
|
242
|
+
"properties": {},
|
|
243
|
+
"required": []
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
# Keep the actual callable as well
|
|
247
|
+
"callable": obj,
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
tools_dict[name] = tool_def
|
|
251
|
+
logger.info(f"Loaded and globalized tool function: {name}")
|
|
252
|
+
|
|
253
|
+
logger.info(f"Loaded {len(tools_dict)} tool functions from tools.py")
|
|
254
|
+
except Exception as e:
|
|
255
|
+
logger.warning(f"Error loading tools from tools.py: {e}")
|
|
256
|
+
return tools_dict
|
|
257
|
+
|
|
258
|
+
# -----------------------------------------------------------------------------
|
|
259
|
+
# Async Queue Processor
|
|
260
|
+
# -----------------------------------------------------------------------------
|
|
261
|
+
|
|
262
|
+
async def process_message_queue():
|
|
263
|
+
while True:
|
|
264
|
+
try:
|
|
265
|
+
if not message_queue.empty():
|
|
266
|
+
msg_data = message_queue.get()
|
|
267
|
+
await cl.Message(**msg_data).send()
|
|
268
|
+
await asyncio.sleep(0.1)
|
|
269
|
+
except Exception as e:
|
|
270
|
+
logger.error(f"Error processing message queue: {e}")
|
|
271
|
+
|
|
272
|
+
# -----------------------------------------------------------------------------
|
|
273
|
+
# Step & Task Callbacks
|
|
274
|
+
# -----------------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
async def step_callback(step_details):
|
|
277
|
+
logger.info(f"[CALLBACK DEBUG] step_callback: {step_details}")
|
|
278
|
+
agent_name = step_details.get("agent_name", "Agent")
|
|
279
|
+
try:
|
|
280
|
+
if step_details.get("response"):
|
|
281
|
+
message_queue.put({
|
|
282
|
+
"content": f"Agent Response: {step_details['response']}",
|
|
283
|
+
"author": agent_name
|
|
284
|
+
})
|
|
285
|
+
if step_details.get("tool_name"):
|
|
286
|
+
message_queue.put({
|
|
287
|
+
"content": f"🛠️ Using tool: {step_details['tool_name']}",
|
|
288
|
+
"author": "System"
|
|
289
|
+
})
|
|
290
|
+
except Exception as e:
|
|
291
|
+
logger.error(f"Error in step_callback: {e}", exc_info=True)
|
|
292
|
+
|
|
293
|
+
async def task_callback(task_output):
|
|
294
|
+
logger.info(f"[CALLBACK DEBUG] task_callback: type={type(task_output)}")
|
|
295
|
+
try:
|
|
296
|
+
if hasattr(task_output, 'raw'):
|
|
297
|
+
content = task_output.raw
|
|
298
|
+
elif hasattr(task_output, 'content'):
|
|
299
|
+
content = task_output.content
|
|
300
|
+
else:
|
|
301
|
+
content = str(task_output)
|
|
302
|
+
message_queue.put({
|
|
303
|
+
"content": f"Task Output: {content}",
|
|
304
|
+
"author": "Task"
|
|
305
|
+
})
|
|
306
|
+
except Exception as e:
|
|
307
|
+
logger.error(f"Error in task_callback: {e}", exc_info=True)
|
|
308
|
+
|
|
309
|
+
async def step_callback_wrapper(step_details):
|
|
310
|
+
logger.info(f"[CALLBACK DEBUG] step_callback_wrapper: {step_details}")
|
|
311
|
+
agent_name = step_details.get("agent_name", "Agent")
|
|
312
|
+
try:
|
|
313
|
+
if not cl.context.context_var.get():
|
|
314
|
+
logger.warning("[CALLBACK DEBUG] No Chainlit context in wrapper.")
|
|
315
|
+
return
|
|
316
|
+
if step_details.get("response"):
|
|
317
|
+
await cl.Message(
|
|
318
|
+
content=f"{agent_name}: {step_details['response']}",
|
|
319
|
+
author=agent_name,
|
|
320
|
+
).send()
|
|
321
|
+
if step_details.get("tool_name"):
|
|
322
|
+
await cl.Message(
|
|
323
|
+
content=f"🛠️ {agent_name} is using tool: {step_details['tool_name']}",
|
|
324
|
+
author="System",
|
|
325
|
+
).send()
|
|
326
|
+
if step_details.get("thought"):
|
|
327
|
+
await cl.Message(
|
|
328
|
+
content=f"💭 {agent_name}'s thought: {step_details['thought']}",
|
|
329
|
+
author=agent_name,
|
|
330
|
+
).send()
|
|
331
|
+
except Exception as e:
|
|
332
|
+
logger.error(f"Error in step_callback_wrapper: {e}", exc_info=True)
|
|
333
|
+
try:
|
|
334
|
+
await cl.Message(content=f"Error in step callback: {e}", author="System").send()
|
|
335
|
+
except Exception as send_error:
|
|
336
|
+
logger.error(f"Error sending error message: {send_error}")
|
|
337
|
+
|
|
338
|
+
async def task_callback_wrapper(task_output):
|
|
339
|
+
logger.info("[CALLBACK DEBUG] task_callback_wrapper triggered")
|
|
340
|
+
try:
|
|
341
|
+
if not cl.context.context_var.get():
|
|
342
|
+
logger.warning("[CALLBACK DEBUG] No Chainlit context in task wrapper.")
|
|
343
|
+
return
|
|
344
|
+
if hasattr(task_output, 'raw'):
|
|
345
|
+
content = task_output.raw
|
|
346
|
+
elif hasattr(task_output, 'content'):
|
|
347
|
+
content = task_output.content
|
|
348
|
+
else:
|
|
349
|
+
content = str(task_output)
|
|
350
|
+
|
|
351
|
+
await cl.Message(
|
|
352
|
+
content=f"✅ Agent completed task:\n{content}",
|
|
353
|
+
author="Agent",
|
|
354
|
+
).send()
|
|
355
|
+
|
|
356
|
+
if hasattr(task_output, 'details'):
|
|
357
|
+
await cl.Message(
|
|
358
|
+
content=f"📝 Additional details:\n{task_output.details}",
|
|
359
|
+
author="Agent",
|
|
360
|
+
).send()
|
|
361
|
+
except Exception as e:
|
|
362
|
+
logger.error(f"Error in task_callback_wrapper: {e}", exc_info=True)
|
|
363
|
+
try:
|
|
364
|
+
await cl.Message(content=f"Error in task callback: {e}", author="System").send()
|
|
365
|
+
except Exception as send_error:
|
|
366
|
+
logger.error(f"Error sending error message: {send_error}")
|
|
367
|
+
|
|
368
|
+
def sync_task_callback_wrapper(task_output):
|
|
369
|
+
logger.info("[CALLBACK DEBUG] sync_task_callback_wrapper")
|
|
370
|
+
try:
|
|
371
|
+
try:
|
|
372
|
+
loop = asyncio.get_event_loop()
|
|
373
|
+
except RuntimeError:
|
|
374
|
+
loop = asyncio.new_event_loop()
|
|
375
|
+
asyncio.set_event_loop(loop)
|
|
376
|
+
|
|
377
|
+
if loop.is_running():
|
|
378
|
+
asyncio.run_coroutine_threadsafe(task_callback_wrapper(task_output), loop)
|
|
379
|
+
else:
|
|
380
|
+
loop.run_until_complete(task_callback_wrapper(task_output))
|
|
381
|
+
except Exception as e:
|
|
382
|
+
logger.error(f"Error in sync_task_callback_wrapper: {e}", exc_info=True)
|
|
383
|
+
|
|
384
|
+
def sync_step_callback_wrapper(step_details):
|
|
385
|
+
logger.info("[CALLBACK DEBUG] sync_step_callback_wrapper")
|
|
386
|
+
try:
|
|
387
|
+
try:
|
|
388
|
+
loop = asyncio.get_event_loop()
|
|
389
|
+
except RuntimeError:
|
|
390
|
+
loop = asyncio.new_event_loop()
|
|
391
|
+
asyncio.set_event_loop(loop)
|
|
392
|
+
|
|
393
|
+
if loop.is_running():
|
|
394
|
+
asyncio.run_coroutine_threadsafe(step_callback_wrapper(step_details), loop)
|
|
395
|
+
else:
|
|
396
|
+
loop.run_until_complete(step_callback_wrapper(step_details))
|
|
397
|
+
except Exception as e:
|
|
398
|
+
logger.error(f"Error in sync_step_callback_wrapper: {e}", exc_info=True)
|
|
399
|
+
|
|
400
|
+
# -----------------------------------------------------------------------------
|
|
401
|
+
# Main PraisonAI Runner
|
|
402
|
+
# -----------------------------------------------------------------------------
|
|
403
|
+
async def ui_run_praisonai(config, topic, tools_dict):
|
|
404
|
+
logger.info("Starting ui_run_praisonai")
|
|
405
|
+
agents_map = {}
|
|
406
|
+
tasks = []
|
|
407
|
+
tasks_dict = {}
|
|
408
|
+
|
|
409
|
+
try:
|
|
410
|
+
queue_processor = asyncio.create_task(process_message_queue())
|
|
411
|
+
|
|
412
|
+
# Create agents
|
|
413
|
+
for role, details in config['roles'].items():
|
|
414
|
+
role_name = details.get('name', role).format(topic=topic)
|
|
415
|
+
role_filled = details.get('role', role).format(topic=topic)
|
|
416
|
+
goal_filled = details['goal'].format(topic=topic)
|
|
417
|
+
backstory_filled = details['backstory'].format(topic=topic)
|
|
418
|
+
|
|
419
|
+
def step_callback_sync(step_details):
|
|
420
|
+
step_details["agent_name"] = role_name
|
|
421
|
+
try:
|
|
422
|
+
loop_ = asyncio.new_event_loop()
|
|
423
|
+
asyncio.set_event_loop(loop_)
|
|
424
|
+
loop_.run_until_complete(step_callback(step_details))
|
|
425
|
+
loop_.close()
|
|
426
|
+
except Exception as e:
|
|
427
|
+
logger.error(f"Error in step_callback_sync: {e}", exc_info=True)
|
|
428
|
+
|
|
429
|
+
agent = Agent(
|
|
430
|
+
name=role_name,
|
|
431
|
+
role=role_filled,
|
|
432
|
+
goal=goal_filled,
|
|
433
|
+
backstory=backstory_filled,
|
|
434
|
+
llm=details.get('llm', 'gpt-5-nano'),
|
|
435
|
+
verbose=True,
|
|
436
|
+
allow_delegation=details.get('allow_delegation', False),
|
|
437
|
+
max_iter=details.get('max_iter', 15),
|
|
438
|
+
max_rpm=details.get('max_rpm'),
|
|
439
|
+
max_execution_time=details.get('max_execution_time'),
|
|
440
|
+
cache=details.get('cache', True),
|
|
441
|
+
step_callback=step_callback_sync,
|
|
442
|
+
self_reflect=details.get('self_reflect', False)
|
|
443
|
+
)
|
|
444
|
+
agents_map[role] = agent
|
|
445
|
+
|
|
446
|
+
# Create tasks
|
|
447
|
+
for role, details in config['roles'].items():
|
|
448
|
+
agent = agents_map[role]
|
|
449
|
+
role_name = agent.name
|
|
450
|
+
|
|
451
|
+
# -------------------------------------------------------------
|
|
452
|
+
# FIX: Skip empty or invalid tool names to avoid null tool objects
|
|
453
|
+
# -------------------------------------------------------------
|
|
454
|
+
role_tools = []
|
|
455
|
+
task_tools = [] # Initialize task_tools outside the loop
|
|
456
|
+
|
|
457
|
+
for tool_name in details.get('tools', []):
|
|
458
|
+
if not tool_name or not tool_name.strip():
|
|
459
|
+
logger.warning("Skipping empty tool name.")
|
|
460
|
+
continue
|
|
461
|
+
if tool_name in tools_dict:
|
|
462
|
+
# Create a copy of the tool definition
|
|
463
|
+
tool_def = tools_dict[tool_name].copy()
|
|
464
|
+
# Store the callable separately and remove from definition
|
|
465
|
+
callable_func = tool_def.pop("callable")
|
|
466
|
+
# Add callable to role_tools for task execution
|
|
467
|
+
role_tools.append(callable_func)
|
|
468
|
+
# Add API tool definition to task's tools
|
|
469
|
+
task_tools.append(tool_def)
|
|
470
|
+
else:
|
|
471
|
+
logger.warning(f"Tool '{tool_name}' not found. Skipping.")
|
|
472
|
+
|
|
473
|
+
# Set the agent's tools after collecting all tools
|
|
474
|
+
if role_tools:
|
|
475
|
+
agent.tools = role_tools
|
|
476
|
+
|
|
477
|
+
for tname, tdetails in details.get('tasks', {}).items():
|
|
478
|
+
description_filled = tdetails['description'].format(topic=topic)
|
|
479
|
+
expected_output_filled = tdetails['expected_output'].format(topic=topic)
|
|
480
|
+
|
|
481
|
+
def task_callback_sync(task_output):
|
|
482
|
+
try:
|
|
483
|
+
loop_ = asyncio.new_event_loop()
|
|
484
|
+
asyncio.set_event_loop(loop_)
|
|
485
|
+
loop_.run_until_complete(task_callback(task_output))
|
|
486
|
+
loop_.close()
|
|
487
|
+
except Exception as e:
|
|
488
|
+
logger.error(f"Error in task_callback_sync: {e}", exc_info=True)
|
|
489
|
+
|
|
490
|
+
task = Task(
|
|
491
|
+
description=description_filled,
|
|
492
|
+
expected_output=expected_output_filled,
|
|
493
|
+
agent=agent,
|
|
494
|
+
tools=task_tools, # Pass API tool definitions
|
|
495
|
+
async_execution=True,
|
|
496
|
+
context=[],
|
|
497
|
+
config=tdetails.get('config', {}),
|
|
498
|
+
output_json=tdetails.get('output_json'),
|
|
499
|
+
output_pydantic=tdetails.get('output_pydantic'),
|
|
500
|
+
output_file=tdetails.get('output_file', ""),
|
|
501
|
+
callback=task_callback_sync,
|
|
502
|
+
create_directory=tdetails.get('create_directory', False)
|
|
503
|
+
)
|
|
504
|
+
tasks.append(task)
|
|
505
|
+
tasks_dict[tname] = task
|
|
506
|
+
|
|
507
|
+
# Build context links
|
|
508
|
+
for role, details in config['roles'].items():
|
|
509
|
+
for tname, tdetails in details.get('tasks', {}).items():
|
|
510
|
+
if tname not in tasks_dict:
|
|
511
|
+
continue
|
|
512
|
+
task = tasks_dict[tname]
|
|
513
|
+
context_tasks = [
|
|
514
|
+
tasks_dict[ctx]
|
|
515
|
+
for ctx in tdetails.get('context', [])
|
|
516
|
+
if ctx in tasks_dict
|
|
517
|
+
]
|
|
518
|
+
task.context = context_tasks
|
|
519
|
+
|
|
520
|
+
await cl.Message(content="Starting PraisonAI agents execution...", author="System").send()
|
|
521
|
+
|
|
522
|
+
# Decide how to process tasks
|
|
523
|
+
if config.get('process') == 'hierarchical':
|
|
524
|
+
prai_agents = PraisonAIAgents(
|
|
525
|
+
agents=list(agents_map.values()),
|
|
526
|
+
tasks=tasks,
|
|
527
|
+
verbose=True,
|
|
528
|
+
process="hierarchical",
|
|
529
|
+
manager_llm=config.get('manager_llm', 'gpt-5-nano')
|
|
530
|
+
)
|
|
531
|
+
else:
|
|
532
|
+
prai_agents = PraisonAIAgents(
|
|
533
|
+
agents=list(agents_map.values()),
|
|
534
|
+
tasks=tasks,
|
|
535
|
+
verbose=2
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
cl.user_session.set("agents", prai_agents)
|
|
539
|
+
|
|
540
|
+
loop = asyncio.get_event_loop()
|
|
541
|
+
response = await loop.run_in_executor(None, prai_agents.start)
|
|
542
|
+
|
|
543
|
+
if hasattr(response, 'raw'):
|
|
544
|
+
result = response.raw
|
|
545
|
+
elif hasattr(response, 'content'):
|
|
546
|
+
result = response.content
|
|
547
|
+
else:
|
|
548
|
+
result = str(response)
|
|
549
|
+
|
|
550
|
+
await cl.Message(content="PraisonAI agents execution completed.", author="System").send()
|
|
551
|
+
await asyncio.sleep(1)
|
|
552
|
+
queue_processor.cancel()
|
|
553
|
+
return result
|
|
554
|
+
|
|
555
|
+
except Exception as e:
|
|
556
|
+
error_msg = f"Error in ui_run_praisonai: {str(e)}"
|
|
557
|
+
logger.error(error_msg, exc_info=True)
|
|
558
|
+
await cl.Message(content=error_msg, author="System").send()
|
|
559
|
+
raise
|
|
560
|
+
|
|
561
|
+
# -----------------------------------------------------------------------------
|
|
562
|
+
# Chainlit Handlers + logic
|
|
563
|
+
# -----------------------------------------------------------------------------
|
|
564
|
+
|
|
565
|
+
tools_dict = load_tools_from_tools_py()
|
|
566
|
+
print(f"[DEBUG] tools_dict: {tools_dict}")
|
|
567
|
+
|
|
568
|
+
# Load agent config (default) from 'agents.yaml'
|
|
569
|
+
with open(agent_file, 'r') as f:
|
|
570
|
+
config = yaml.safe_load(f)
|
|
571
|
+
|
|
572
|
+
AUTH_PASSWORD_ENABLED = os.getenv("AUTH_PASSWORD_ENABLED", "true").lower() == "true"
|
|
573
|
+
CHAINLIT_AUTH_SECRET = os.getenv("CHAINLIT_AUTH_SECRET")
|
|
574
|
+
if not CHAINLIT_AUTH_SECRET:
|
|
575
|
+
os.environ["CHAINLIT_AUTH_SECRET"] = "p8BPhQChpg@J>jBz$wGxqLX2V>yTVgP*7Ky9H$aV:axW~ANNX-7_T:o@lnyCBu^U"
|
|
576
|
+
|
|
577
|
+
username_env = os.getenv("CHAINLIT_USERNAME", "admin")
|
|
578
|
+
password_env = os.getenv("CHAINLIT_PASSWORD", "admin")
|
|
579
|
+
|
|
580
|
+
def simple_auth_callback(u: str, p: str):
|
|
581
|
+
if (u, p) == (username_env, password_env):
|
|
582
|
+
return cl.User(identifier=u, metadata={"role": "ADMIN", "provider": "credentials"})
|
|
583
|
+
return None
|
|
584
|
+
|
|
585
|
+
if AUTH_PASSWORD_ENABLED:
|
|
586
|
+
auth_callback = cl.password_auth_callback(simple_auth_callback)
|
|
587
|
+
|
|
588
|
+
@cl.set_chat_profiles
|
|
589
|
+
async def set_profiles(current_user: cl.User):
|
|
590
|
+
return [
|
|
591
|
+
cl.ChatProfile(
|
|
592
|
+
name="Auto",
|
|
593
|
+
markdown_description=(
|
|
594
|
+
"Automatically generate agents and tasks based on your input."
|
|
595
|
+
),
|
|
596
|
+
starters=[
|
|
597
|
+
cl.Starter(
|
|
598
|
+
label="Create a movie script",
|
|
599
|
+
message=(
|
|
600
|
+
"Create a movie script about a futuristic society where AI "
|
|
601
|
+
"and humans coexist, focusing on the conflict and resolution "
|
|
602
|
+
"between them. Start with an intriguing opening scene."
|
|
603
|
+
),
|
|
604
|
+
icon="/public/movie.svg",
|
|
605
|
+
),
|
|
606
|
+
cl.Starter(
|
|
607
|
+
label="Design a fantasy world",
|
|
608
|
+
message=(
|
|
609
|
+
"Design a detailed fantasy world with unique geography, "
|
|
610
|
+
"cultures, and magical systems. Start by describing the main "
|
|
611
|
+
"continent and its inhabitants."
|
|
612
|
+
),
|
|
613
|
+
icon="/public/fantasy.svg",
|
|
614
|
+
),
|
|
615
|
+
cl.Starter(
|
|
616
|
+
label="Write a futuristic political thriller",
|
|
617
|
+
message=(
|
|
618
|
+
"Write a futuristic political thriller involving a conspiracy "
|
|
619
|
+
"within a global government. Start with a high-stakes meeting "
|
|
620
|
+
"that sets the plot in motion."
|
|
621
|
+
),
|
|
622
|
+
icon="/public/thriller.svg",
|
|
623
|
+
),
|
|
624
|
+
cl.Starter(
|
|
625
|
+
label="Develop a new board game",
|
|
626
|
+
message=(
|
|
627
|
+
"Develop a new, innovative board game. Describe the game's "
|
|
628
|
+
"objective, rules, and unique mechanics. Create a scenario to "
|
|
629
|
+
"illustrate gameplay."
|
|
630
|
+
),
|
|
631
|
+
icon="/public/game.svg",
|
|
632
|
+
),
|
|
633
|
+
],
|
|
634
|
+
),
|
|
635
|
+
cl.ChatProfile(
|
|
636
|
+
name="Manual",
|
|
637
|
+
markdown_description="Manually define your agents and tasks using a YAML file.",
|
|
638
|
+
),
|
|
639
|
+
]
|
|
640
|
+
|
|
641
|
+
@cl.on_chat_start
|
|
642
|
+
async def start_chat():
|
|
643
|
+
try:
|
|
644
|
+
model_name = load_setting("model_name") or os.getenv("MODEL_NAME", "gpt-5-nano")
|
|
645
|
+
cl.user_session.set("model_name", model_name)
|
|
646
|
+
logger.debug(f"Model name: {model_name}")
|
|
647
|
+
|
|
648
|
+
cl.user_session.set(
|
|
649
|
+
"message_history",
|
|
650
|
+
[{"role": "system", "content": "You are a helpful assistant."}],
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
if not os.path.exists("tools.py"):
|
|
654
|
+
with open("tools.py", "w") as f:
|
|
655
|
+
f.write("# Add your custom tools here\n")
|
|
656
|
+
|
|
657
|
+
if not os.path.exists("agents.yaml"):
|
|
658
|
+
with open("agents.yaml", "w") as f:
|
|
659
|
+
f.write("# Add your custom agents here\n")
|
|
660
|
+
|
|
661
|
+
settings = await cl.ChatSettings(
|
|
662
|
+
[
|
|
663
|
+
TextInput(id="Model", label="OpenAI - Model", initial=model_name),
|
|
664
|
+
TextInput(id="BaseUrl", label="OpenAI - Base URL", initial=config_list[0]['base_url']),
|
|
665
|
+
TextInput(id="ApiKey", label="OpenAI - API Key", initial=config_list[0]['api_key']),
|
|
666
|
+
Select(
|
|
667
|
+
id="Framework",
|
|
668
|
+
label="Framework",
|
|
669
|
+
values=["praisonai", "crewai", "autogen"],
|
|
670
|
+
initial_index=0,
|
|
671
|
+
),
|
|
672
|
+
]
|
|
673
|
+
).send()
|
|
674
|
+
cl.user_session.set("settings", settings)
|
|
675
|
+
chat_profile = cl.user_session.get("chat_profile")
|
|
676
|
+
|
|
677
|
+
if chat_profile == "Manual":
|
|
678
|
+
agent_file = "agents.yaml"
|
|
679
|
+
full_agent_file_path = os.path.abspath(agent_file)
|
|
680
|
+
if os.path.exists(full_agent_file_path):
|
|
681
|
+
with open(full_agent_file_path, 'r') as f:
|
|
682
|
+
yaml_content = f.read()
|
|
683
|
+
msg = cl.Message(content=yaml_content, language="yaml")
|
|
684
|
+
await msg.send()
|
|
685
|
+
|
|
686
|
+
full_tools_file_path = os.path.abspath("tools.py")
|
|
687
|
+
if os.path.exists(full_tools_file_path):
|
|
688
|
+
with open(full_tools_file_path, 'r') as f:
|
|
689
|
+
tools_content = f.read()
|
|
690
|
+
msg = cl.Message(content=tools_content, language="python")
|
|
691
|
+
await msg.send()
|
|
692
|
+
|
|
693
|
+
settings = await cl.ChatSettings(
|
|
694
|
+
[
|
|
695
|
+
TextInput(id="Model", label="OpenAI - Model", initial=model_name),
|
|
696
|
+
TextInput(id="BaseUrl", label="OpenAI - Base URL", initial=config_list[0]['base_url']),
|
|
697
|
+
TextInput(id="ApiKey", label="OpenAI - API Key", initial=config_list[0]['api_key']),
|
|
698
|
+
Select(
|
|
699
|
+
id="Framework",
|
|
700
|
+
label="Framework",
|
|
701
|
+
values=["praisonai", "crewai", "autogen"],
|
|
702
|
+
initial_index=0,
|
|
703
|
+
),
|
|
704
|
+
TextInput(id="agents", label="agents.yaml", initial=yaml_content, multiline=True),
|
|
705
|
+
TextInput(id="tools", label="tools.py", initial=tools_content, multiline=True),
|
|
706
|
+
]
|
|
707
|
+
).send()
|
|
708
|
+
cl.user_session.set("settings", settings)
|
|
709
|
+
|
|
710
|
+
res = await cl.AskActionMessage(
|
|
711
|
+
content="Pick an action!",
|
|
712
|
+
actions=actions,
|
|
713
|
+
).send()
|
|
714
|
+
if res and res.get("value") == "modify":
|
|
715
|
+
await cl.Message(content="Modify the agents and tools from below settings", actions=actions).send()
|
|
716
|
+
elif res and res.get("value") == "run":
|
|
717
|
+
await main(cl.Message(content="", actions=actions))
|
|
718
|
+
|
|
719
|
+
await on_settings_update(settings)
|
|
720
|
+
except Exception as e:
|
|
721
|
+
logger.error(f"Error in start_chat: {str(e)}")
|
|
722
|
+
await cl.Message(content=f"An error occurred while starting the chat: {str(e)}").send()
|
|
723
|
+
|
|
724
|
+
@cl.on_chat_resume
|
|
725
|
+
async def on_chat_resume(thread: ThreadDict):
|
|
726
|
+
try:
|
|
727
|
+
message_history = cl.user_session.get("message_history", [])
|
|
728
|
+
root_messages = [m for m in thread["steps"] if m["parentId"] is None]
|
|
729
|
+
for message in root_messages:
|
|
730
|
+
if message["type"] == "user_message":
|
|
731
|
+
message_history.append({"role": "user", "content": message["output"]})
|
|
732
|
+
elif message["type"] == "ai_message":
|
|
733
|
+
message_history.append({"role": "assistant", "content": message["content"]})
|
|
734
|
+
cl.user_session.set("message_history", message_history)
|
|
735
|
+
except Exception as e:
|
|
736
|
+
logger.error(f"Error in on_chat_resume: {str(e)}")
|
|
737
|
+
|
|
738
|
+
@cl.on_message
|
|
739
|
+
async def main(message: cl.Message):
|
|
740
|
+
try:
|
|
741
|
+
logger.info(f"User message: {message.content}")
|
|
742
|
+
msg = cl.Message(content="")
|
|
743
|
+
await msg.stream_token(f"🔄 Processing your request: {message.content}...")
|
|
744
|
+
|
|
745
|
+
# Run PraisonAI
|
|
746
|
+
result = await ui_run_praisonai(config, message.content, tools_dict)
|
|
747
|
+
|
|
748
|
+
message_history = cl.user_session.get("message_history", [])
|
|
749
|
+
message_history.append({"role": "user", "content": message.content})
|
|
750
|
+
message_history.append({"role": "assistant", "content": str(result)})
|
|
751
|
+
cl.user_session.set("message_history", message_history)
|
|
752
|
+
await msg.send()
|
|
753
|
+
except Exception as e:
|
|
754
|
+
error_msg = f"Error running PraisonAI agents: {str(e)}"
|
|
755
|
+
logger.error(error_msg, exc_info=True)
|
|
756
|
+
await cl.Message(content=error_msg, author="System").send()
|
|
757
|
+
|
|
758
|
+
@cl.on_settings_update
|
|
759
|
+
async def on_settings_update(settings):
|
|
760
|
+
try:
|
|
761
|
+
global config_list, framework
|
|
762
|
+
config_list[0]['model'] = settings["Model"]
|
|
763
|
+
config_list[0]['base_url'] = settings["BaseUrl"]
|
|
764
|
+
config_list[0]['api_key'] = settings["ApiKey"]
|
|
765
|
+
|
|
766
|
+
for attempt in range(MAX_RETRIES):
|
|
767
|
+
try:
|
|
768
|
+
await save_setting_with_retry("model_name", config_list[0]['model'])
|
|
769
|
+
await save_setting_with_retry("base_url", config_list[0]['base_url'])
|
|
770
|
+
await save_setting_with_retry("api_key", config_list[0]['api_key'])
|
|
771
|
+
break
|
|
772
|
+
except sqlite3.OperationalError as e:
|
|
773
|
+
if "database is locked" in str(e) and attempt < MAX_RETRIES - 1:
|
|
774
|
+
await asyncio.sleep(RETRY_DELAY)
|
|
775
|
+
continue
|
|
776
|
+
raise
|
|
777
|
+
|
|
778
|
+
os.environ["OPENAI_API_KEY"] = config_list[0]['api_key']
|
|
779
|
+
os.environ["OPENAI_MODEL_NAME"] = config_list[0]['model']
|
|
780
|
+
os.environ["OPENAI_API_BASE"] = config_list[0]['base_url']
|
|
781
|
+
os.environ["MODEL_NAME"] = config_list[0]['model']
|
|
782
|
+
framework = settings["Framework"]
|
|
783
|
+
os.environ["FRAMEWORK"] = framework
|
|
784
|
+
|
|
785
|
+
if "agents" in settings:
|
|
786
|
+
with open("agents.yaml", "w") as f:
|
|
787
|
+
f.write(settings["agents"])
|
|
788
|
+
if "tools" in settings:
|
|
789
|
+
with open("tools.py", "w") as f:
|
|
790
|
+
f.write(settings["tools"])
|
|
791
|
+
|
|
792
|
+
thread_id = cl.user_session.get("thread_id")
|
|
793
|
+
if thread_id:
|
|
794
|
+
for attempt in range(MAX_RETRIES):
|
|
795
|
+
try:
|
|
796
|
+
thread = await cl_data.get_thread(thread_id)
|
|
797
|
+
if thread:
|
|
798
|
+
metadata = thread.get("metadata", {})
|
|
799
|
+
if isinstance(metadata, str):
|
|
800
|
+
try:
|
|
801
|
+
import json
|
|
802
|
+
metadata = json.loads(metadata)
|
|
803
|
+
except json.JSONDecodeError:
|
|
804
|
+
metadata = {}
|
|
805
|
+
metadata["model_name"] = config_list[0]['model']
|
|
806
|
+
await cl_data.update_thread(thread_id, metadata=metadata)
|
|
807
|
+
cl.user_session.set("metadata", metadata)
|
|
808
|
+
break
|
|
809
|
+
except sqlite3.OperationalError as e:
|
|
810
|
+
if "database is locked" in str(e) and attempt < MAX_RETRIES - 1:
|
|
811
|
+
await asyncio.sleep(RETRY_DELAY)
|
|
812
|
+
continue
|
|
813
|
+
raise
|
|
814
|
+
|
|
815
|
+
logger.info("Settings updated successfully")
|
|
816
|
+
except Exception as e:
|
|
817
|
+
logger.error(f"Error updating settings: {str(e)}")
|
|
818
|
+
await cl.Message(content=f"An error occurred while updating settings: {str(e)}. Retrying...").send()
|
|
819
|
+
try:
|
|
820
|
+
await asyncio.sleep(RETRY_DELAY * 2)
|
|
821
|
+
await on_settings_update(settings)
|
|
822
|
+
except Exception as e:
|
|
823
|
+
logger.error(f"Final retry failed: {str(e)}")
|
|
824
|
+
await cl.Message(content=f"Failed to update settings after retries: {str(e)}").send()
|