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,534 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI Compare Feature for PraisonAI.
|
|
3
|
+
|
|
4
|
+
Compares different CLI command modes without performance impact.
|
|
5
|
+
Only loaded when --compare flag is used.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Dict, Any, List, Optional
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
COMPARE_MODES = {
|
|
18
|
+
"basic": {},
|
|
19
|
+
"tools": {"tools": "calculator,internet_search"},
|
|
20
|
+
"research": {"research": True},
|
|
21
|
+
"planning": {"planning": True},
|
|
22
|
+
"memory": {"memory": True},
|
|
23
|
+
"router": {"router": True},
|
|
24
|
+
"web_search": {"web_search": True},
|
|
25
|
+
"web_fetch": {"web_fetch": True},
|
|
26
|
+
"query_rewrite": {"query_rewrite": True},
|
|
27
|
+
"expand_prompt": {"expand_prompt": True},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_mode_config(mode_name: str, custom_args: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
32
|
+
"""
|
|
33
|
+
Get configuration for a comparison mode.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
mode_name: Name of the mode
|
|
37
|
+
custom_args: Optional custom arguments to merge
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Configuration dictionary for the mode
|
|
41
|
+
"""
|
|
42
|
+
config = COMPARE_MODES.get(mode_name, {}).copy()
|
|
43
|
+
if custom_args:
|
|
44
|
+
config.update(custom_args)
|
|
45
|
+
return config
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def list_available_modes() -> List[str]:
|
|
49
|
+
"""
|
|
50
|
+
List all available comparison modes.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
List of mode names
|
|
54
|
+
"""
|
|
55
|
+
return list(COMPARE_MODES.keys())
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def parse_modes(modes_str: str) -> List[str]:
|
|
59
|
+
"""
|
|
60
|
+
Parse comma-separated modes string.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
modes_str: Comma-separated mode names
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
List of mode names
|
|
67
|
+
"""
|
|
68
|
+
if not modes_str or not modes_str.strip():
|
|
69
|
+
return ["basic"]
|
|
70
|
+
|
|
71
|
+
modes = [m.strip() for m in modes_str.split(",")]
|
|
72
|
+
return [m for m in modes if m]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class ModeResult:
|
|
77
|
+
"""Result from running a single mode."""
|
|
78
|
+
mode: str
|
|
79
|
+
output: str
|
|
80
|
+
execution_time_ms: float
|
|
81
|
+
model_used: str
|
|
82
|
+
tokens: Optional[Dict[str, int]] = None
|
|
83
|
+
cost: Optional[float] = None
|
|
84
|
+
tools_used: List[str] = field(default_factory=list)
|
|
85
|
+
error: Optional[str] = None
|
|
86
|
+
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
|
|
87
|
+
|
|
88
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
89
|
+
"""Convert to dictionary."""
|
|
90
|
+
return {
|
|
91
|
+
"mode": self.mode,
|
|
92
|
+
"output_preview": self.output[:200] + "..." if len(self.output) > 200 else self.output,
|
|
93
|
+
"execution_time_ms": self.execution_time_ms,
|
|
94
|
+
"model_used": self.model_used,
|
|
95
|
+
"tokens": self.tokens,
|
|
96
|
+
"cost": self.cost,
|
|
97
|
+
"tools_used": self.tools_used,
|
|
98
|
+
"error": self.error,
|
|
99
|
+
"timestamp": self.timestamp,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class CompareResult:
|
|
105
|
+
"""Result from comparing multiple modes."""
|
|
106
|
+
query: str
|
|
107
|
+
comparisons: List[ModeResult] = field(default_factory=list)
|
|
108
|
+
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
|
|
109
|
+
|
|
110
|
+
def get_summary(self) -> Dict[str, Any]:
|
|
111
|
+
"""Generate summary statistics."""
|
|
112
|
+
if not self.comparisons:
|
|
113
|
+
return {}
|
|
114
|
+
|
|
115
|
+
times = [(c.mode, c.execution_time_ms) for c in self.comparisons if c.error is None]
|
|
116
|
+
if not times:
|
|
117
|
+
return {"error": "All modes failed"}
|
|
118
|
+
|
|
119
|
+
sorted_by_time = sorted(times, key=lambda x: x[1])
|
|
120
|
+
|
|
121
|
+
summary = {
|
|
122
|
+
"fastest": sorted_by_time[0][0],
|
|
123
|
+
"slowest": sorted_by_time[-1][0],
|
|
124
|
+
"fastest_time_ms": sorted_by_time[0][1],
|
|
125
|
+
"slowest_time_ms": sorted_by_time[-1][1],
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
total_tokens = 0
|
|
129
|
+
total_cost = 0.0
|
|
130
|
+
for c in self.comparisons:
|
|
131
|
+
if c.tokens:
|
|
132
|
+
total_tokens += c.tokens.get("input", 0) + c.tokens.get("output", 0)
|
|
133
|
+
if c.cost:
|
|
134
|
+
total_cost += c.cost
|
|
135
|
+
|
|
136
|
+
if total_tokens > 0:
|
|
137
|
+
summary["total_tokens"] = total_tokens
|
|
138
|
+
if total_cost > 0:
|
|
139
|
+
summary["total_cost"] = total_cost
|
|
140
|
+
|
|
141
|
+
return summary
|
|
142
|
+
|
|
143
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
144
|
+
"""Convert to dictionary."""
|
|
145
|
+
return {
|
|
146
|
+
"query": self.query,
|
|
147
|
+
"timestamp": self.timestamp,
|
|
148
|
+
"comparisons": [c.to_dict() for c in self.comparisons],
|
|
149
|
+
"summary": self.get_summary(),
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
def to_json(self) -> str:
|
|
153
|
+
"""Convert to JSON string."""
|
|
154
|
+
return json.dumps(self.to_dict(), indent=2)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def save_compare_result(result: CompareResult, path: str) -> bool:
|
|
158
|
+
"""
|
|
159
|
+
Save comparison result to file.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
result: CompareResult to save
|
|
163
|
+
path: File path
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
True if successful
|
|
167
|
+
"""
|
|
168
|
+
try:
|
|
169
|
+
from pathlib import Path
|
|
170
|
+
|
|
171
|
+
file_path = Path(path)
|
|
172
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
173
|
+
file_path.write_text(result.to_json())
|
|
174
|
+
return True
|
|
175
|
+
except Exception as e:
|
|
176
|
+
logger.error(f"Failed to save comparison result: {e}")
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def format_comparison_table(result: CompareResult, show_responses: bool = True) -> str:
|
|
181
|
+
"""
|
|
182
|
+
Format comparison result as a table string with optional response display.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
result: CompareResult to format
|
|
186
|
+
show_responses: Whether to show actual responses from each mode
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Formatted table string
|
|
190
|
+
"""
|
|
191
|
+
try:
|
|
192
|
+
from rich.console import Console
|
|
193
|
+
from rich.table import Table
|
|
194
|
+
from rich.panel import Panel
|
|
195
|
+
from io import StringIO
|
|
196
|
+
|
|
197
|
+
console = Console(file=StringIO(), force_terminal=True)
|
|
198
|
+
|
|
199
|
+
# Main comparison table
|
|
200
|
+
table = Table(title=f"Comparison: {result.query[:50]}...")
|
|
201
|
+
table.add_column("Mode", style="cyan")
|
|
202
|
+
table.add_column("Time (ms)", style="green")
|
|
203
|
+
table.add_column("Model", style="yellow")
|
|
204
|
+
table.add_column("Tools", style="magenta")
|
|
205
|
+
table.add_column("Status", style="bold")
|
|
206
|
+
|
|
207
|
+
for c in result.comparisons:
|
|
208
|
+
status = "✅" if c.error is None else f"❌ {c.error[:20]}"
|
|
209
|
+
tools = ", ".join(c.tools_used) if c.tools_used else "-"
|
|
210
|
+
table.add_row(
|
|
211
|
+
c.mode,
|
|
212
|
+
f"{c.execution_time_ms:.1f}",
|
|
213
|
+
c.model_used,
|
|
214
|
+
tools,
|
|
215
|
+
status
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
summary = result.get_summary()
|
|
219
|
+
if summary.get("fastest"):
|
|
220
|
+
table.add_section()
|
|
221
|
+
table.add_row(
|
|
222
|
+
"Summary",
|
|
223
|
+
f"Fastest: {summary['fastest']}",
|
|
224
|
+
"",
|
|
225
|
+
"",
|
|
226
|
+
f"Δ {summary['slowest_time_ms'] - summary['fastest_time_ms']:.1f}ms"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
console.print(table)
|
|
230
|
+
|
|
231
|
+
# Show responses if enabled
|
|
232
|
+
if show_responses:
|
|
233
|
+
console.print("\n[bold cyan]📝 Responses:[/bold cyan]")
|
|
234
|
+
for c in result.comparisons:
|
|
235
|
+
if c.error is None:
|
|
236
|
+
# Truncate long responses
|
|
237
|
+
response_preview = c.output[:500] + "..." if len(c.output) > 500 else c.output
|
|
238
|
+
console.print(Panel(
|
|
239
|
+
response_preview,
|
|
240
|
+
title=f"[bold]{c.mode}[/bold] ({c.execution_time_ms:.0f}ms)",
|
|
241
|
+
border_style="cyan"
|
|
242
|
+
))
|
|
243
|
+
else:
|
|
244
|
+
console.print(Panel(
|
|
245
|
+
f"[red]Error: {c.error}[/red]",
|
|
246
|
+
title=f"[bold]{c.mode}[/bold]",
|
|
247
|
+
border_style="red"
|
|
248
|
+
))
|
|
249
|
+
|
|
250
|
+
return console.file.getvalue()
|
|
251
|
+
except ImportError:
|
|
252
|
+
lines = [f"Comparison: {result.query}"]
|
|
253
|
+
lines.append("-" * 60)
|
|
254
|
+
for c in result.comparisons:
|
|
255
|
+
status = "OK" if c.error is None else f"ERROR: {c.error}"
|
|
256
|
+
lines.append(f"{c.mode}: {c.execution_time_ms:.1f}ms - {status}")
|
|
257
|
+
if show_responses and c.error is None:
|
|
258
|
+
lines.append(f" Response: {c.output[:200]}...")
|
|
259
|
+
return "\n".join(lines)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class CompareHandler:
|
|
263
|
+
"""
|
|
264
|
+
Handler for CLI compare feature.
|
|
265
|
+
|
|
266
|
+
Compares different CLI command modes and generates comparison reports.
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
def __init__(self, verbose: bool = False):
|
|
270
|
+
"""
|
|
271
|
+
Initialize the compare handler.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
verbose: Enable verbose output
|
|
275
|
+
"""
|
|
276
|
+
self.verbose = verbose
|
|
277
|
+
self.results: List[ModeResult] = []
|
|
278
|
+
|
|
279
|
+
def compare(
|
|
280
|
+
self,
|
|
281
|
+
query: str,
|
|
282
|
+
modes: List[str],
|
|
283
|
+
model: Optional[str] = None,
|
|
284
|
+
**kwargs
|
|
285
|
+
) -> CompareResult:
|
|
286
|
+
"""
|
|
287
|
+
Compare query execution across different modes.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
query: The query to execute
|
|
291
|
+
modes: List of mode names to compare
|
|
292
|
+
model: Optional model override
|
|
293
|
+
**kwargs: Additional arguments
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
CompareResult with all comparisons
|
|
297
|
+
"""
|
|
298
|
+
result = CompareResult(query=query)
|
|
299
|
+
|
|
300
|
+
for mode in modes:
|
|
301
|
+
if self.verbose:
|
|
302
|
+
logger.info(f"Running mode: {mode}")
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
mode_result = self._run_mode(query, mode, model=model, **kwargs)
|
|
306
|
+
result.comparisons.append(mode_result)
|
|
307
|
+
except Exception as e:
|
|
308
|
+
logger.error(f"Error running mode {mode}: {e}")
|
|
309
|
+
result.comparisons.append(ModeResult(
|
|
310
|
+
mode=mode,
|
|
311
|
+
output="",
|
|
312
|
+
execution_time_ms=0,
|
|
313
|
+
model_used=model or "unknown",
|
|
314
|
+
error=str(e)
|
|
315
|
+
))
|
|
316
|
+
|
|
317
|
+
return result
|
|
318
|
+
|
|
319
|
+
def _run_mode(
|
|
320
|
+
self,
|
|
321
|
+
query: str,
|
|
322
|
+
mode: str,
|
|
323
|
+
model: Optional[str] = None,
|
|
324
|
+
**kwargs
|
|
325
|
+
) -> ModeResult:
|
|
326
|
+
"""
|
|
327
|
+
Run a single mode and capture results.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
query: The query to execute
|
|
331
|
+
mode: Mode name
|
|
332
|
+
model: Optional model override
|
|
333
|
+
**kwargs: Additional arguments
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
ModeResult with execution details
|
|
337
|
+
"""
|
|
338
|
+
config = get_mode_config(mode, kwargs)
|
|
339
|
+
|
|
340
|
+
if model:
|
|
341
|
+
config["llm"] = model
|
|
342
|
+
|
|
343
|
+
start_time = time.perf_counter()
|
|
344
|
+
output = ""
|
|
345
|
+
tokens = None
|
|
346
|
+
cost = None
|
|
347
|
+
tools_used = []
|
|
348
|
+
error = None
|
|
349
|
+
model_used = model or "gpt-4o-mini"
|
|
350
|
+
|
|
351
|
+
try:
|
|
352
|
+
from praisonaiagents import Agent
|
|
353
|
+
|
|
354
|
+
agent_config = {
|
|
355
|
+
"name": f"CompareAgent_{mode}",
|
|
356
|
+
"role": "Assistant",
|
|
357
|
+
"goal": "Complete the given task",
|
|
358
|
+
"backstory": "You are a helpful AI assistant",
|
|
359
|
+
"verbose": False,
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if config.get("llm"):
|
|
363
|
+
agent_config["llm"] = config["llm"]
|
|
364
|
+
model_used = config["llm"]
|
|
365
|
+
|
|
366
|
+
if config.get("tools"):
|
|
367
|
+
tools_list = self._load_tools(config["tools"])
|
|
368
|
+
if tools_list:
|
|
369
|
+
agent_config["tools"] = tools_list
|
|
370
|
+
|
|
371
|
+
if config.get("planning"):
|
|
372
|
+
agent_config["planning"] = True
|
|
373
|
+
|
|
374
|
+
if config.get("memory"):
|
|
375
|
+
agent_config["memory"] = True
|
|
376
|
+
|
|
377
|
+
if config.get("web_search"):
|
|
378
|
+
agent_config["web_search"] = True
|
|
379
|
+
|
|
380
|
+
if config.get("web_fetch"):
|
|
381
|
+
agent_config["web_fetch"] = True
|
|
382
|
+
|
|
383
|
+
agent = Agent(**agent_config)
|
|
384
|
+
result = agent.start(query)
|
|
385
|
+
|
|
386
|
+
if hasattr(result, 'raw'):
|
|
387
|
+
output = str(result.raw)
|
|
388
|
+
else:
|
|
389
|
+
output = str(result)
|
|
390
|
+
|
|
391
|
+
if hasattr(agent, 'tools') and agent.tools:
|
|
392
|
+
for tool in agent.tools:
|
|
393
|
+
if hasattr(tool, '__name__'):
|
|
394
|
+
tools_used.append(tool.__name__)
|
|
395
|
+
elif hasattr(tool, 'name'):
|
|
396
|
+
tools_used.append(tool.name)
|
|
397
|
+
|
|
398
|
+
except Exception as e:
|
|
399
|
+
error = str(e)
|
|
400
|
+
logger.error(f"Mode {mode} failed: {e}")
|
|
401
|
+
|
|
402
|
+
end_time = time.perf_counter()
|
|
403
|
+
execution_time_ms = (end_time - start_time) * 1000
|
|
404
|
+
|
|
405
|
+
return ModeResult(
|
|
406
|
+
mode=mode,
|
|
407
|
+
output=output,
|
|
408
|
+
execution_time_ms=execution_time_ms,
|
|
409
|
+
model_used=model_used,
|
|
410
|
+
tokens=tokens,
|
|
411
|
+
cost=cost,
|
|
412
|
+
tools_used=tools_used,
|
|
413
|
+
error=error
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
def _load_tools(self, tools_str: str) -> List:
|
|
417
|
+
"""
|
|
418
|
+
Load tools from string specification.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
tools_str: Comma-separated tool names
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
List of tool functions
|
|
425
|
+
"""
|
|
426
|
+
tools = []
|
|
427
|
+
tool_names = [t.strip() for t in tools_str.split(",")]
|
|
428
|
+
|
|
429
|
+
for name in tool_names:
|
|
430
|
+
try:
|
|
431
|
+
if name == "internet_search":
|
|
432
|
+
from praisonaiagents.tools import internet_search
|
|
433
|
+
tools.append(internet_search)
|
|
434
|
+
elif name == "calculator":
|
|
435
|
+
tools.append(_calculator_tool)
|
|
436
|
+
except ImportError:
|
|
437
|
+
logger.warning(f"Could not load tool: {name}")
|
|
438
|
+
|
|
439
|
+
return tools
|
|
440
|
+
|
|
441
|
+
def print_result(self, result: CompareResult) -> None:
|
|
442
|
+
"""
|
|
443
|
+
Print comparison result to console.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
result: CompareResult to print
|
|
447
|
+
"""
|
|
448
|
+
table_str = format_comparison_table(result)
|
|
449
|
+
print(table_str)
|
|
450
|
+
|
|
451
|
+
def execute(
|
|
452
|
+
self,
|
|
453
|
+
query: str,
|
|
454
|
+
modes_str: str,
|
|
455
|
+
model: Optional[str] = None,
|
|
456
|
+
output_path: Optional[str] = None,
|
|
457
|
+
**kwargs
|
|
458
|
+
) -> CompareResult:
|
|
459
|
+
"""
|
|
460
|
+
Execute comparison from CLI arguments.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
query: The query to execute
|
|
464
|
+
modes_str: Comma-separated mode names
|
|
465
|
+
model: Optional model override
|
|
466
|
+
output_path: Optional path to save results
|
|
467
|
+
**kwargs: Additional arguments
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
CompareResult with all comparisons
|
|
471
|
+
"""
|
|
472
|
+
modes = parse_modes(modes_str)
|
|
473
|
+
|
|
474
|
+
if self.verbose:
|
|
475
|
+
print(f"[bold cyan]Comparing modes: {', '.join(modes)}[/bold cyan]")
|
|
476
|
+
|
|
477
|
+
result = self.compare(query, modes, model=model, **kwargs)
|
|
478
|
+
|
|
479
|
+
self.print_result(result)
|
|
480
|
+
|
|
481
|
+
if output_path:
|
|
482
|
+
if save_compare_result(result, output_path):
|
|
483
|
+
print(f"[green]Results saved to: {output_path}[/green]")
|
|
484
|
+
else:
|
|
485
|
+
print(f"[red]Failed to save results to: {output_path}[/red]")
|
|
486
|
+
|
|
487
|
+
return result
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def _calculator_tool(expression: str) -> str:
|
|
491
|
+
"""
|
|
492
|
+
Evaluate a mathematical expression safely.
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
expression: Mathematical expression to evaluate (e.g., "2+2", "15*3", "100/4")
|
|
496
|
+
|
|
497
|
+
Returns:
|
|
498
|
+
The result of the calculation as a string
|
|
499
|
+
"""
|
|
500
|
+
import ast
|
|
501
|
+
import operator
|
|
502
|
+
|
|
503
|
+
# Safe operators
|
|
504
|
+
operators = {
|
|
505
|
+
ast.Add: operator.add,
|
|
506
|
+
ast.Sub: operator.sub,
|
|
507
|
+
ast.Mult: operator.mul,
|
|
508
|
+
ast.Div: operator.truediv,
|
|
509
|
+
ast.Pow: operator.pow,
|
|
510
|
+
ast.USub: operator.neg,
|
|
511
|
+
ast.UAdd: operator.pos,
|
|
512
|
+
ast.Mod: operator.mod,
|
|
513
|
+
ast.FloorDiv: operator.floordiv,
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
def _eval(node):
|
|
517
|
+
if isinstance(node, ast.Constant):
|
|
518
|
+
return node.value
|
|
519
|
+
elif isinstance(node, ast.BinOp):
|
|
520
|
+
left = _eval(node.left)
|
|
521
|
+
right = _eval(node.right)
|
|
522
|
+
return operators[type(node.op)](left, right)
|
|
523
|
+
elif isinstance(node, ast.UnaryOp):
|
|
524
|
+
operand = _eval(node.operand)
|
|
525
|
+
return operators[type(node.op)](operand)
|
|
526
|
+
else:
|
|
527
|
+
raise ValueError(f"Unsupported operation: {type(node)}")
|
|
528
|
+
|
|
529
|
+
try:
|
|
530
|
+
tree = ast.parse(expression, mode='eval')
|
|
531
|
+
result = _eval(tree.body)
|
|
532
|
+
return str(result)
|
|
533
|
+
except Exception as e:
|
|
534
|
+
return f"Error evaluating '{expression}': {e}"
|