polyrob 0.5.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.
- agents/README.md +453 -0
- agents/__init__.py +164 -0
- agents/base_agent.py +743 -0
- agents/personality/character.py +119 -0
- agents/personality/character_manager.py +309 -0
- agents/personality/characters/rob.character.json +56 -0
- agents/personality/characters/trump.character.json +63 -0
- agents/personality/persona_render.py +78 -0
- agents/personality/persona_resolver.py +115 -0
- agents/prompt/__init__.py +58 -0
- agents/prompt/base_prompt.py +92 -0
- agents/prompt/system.py +417 -0
- agents/task/README_PATH_MANAGEMENT.md +141 -0
- agents/task/__init__.py +162 -0
- agents/task/agent/__init__.py +70 -0
- agents/task/agent/agent_state.py +408 -0
- agents/task/agent/async_delegation.py +274 -0
- agents/task/agent/autonomy_state.py +275 -0
- agents/task/agent/conversation.py +75 -0
- agents/task/agent/core/__init__.py +6 -0
- agents/task/agent/core/aux_metering.py +61 -0
- agents/task/agent/core/background_review.py +157 -0
- agents/task/agent/core/construction.py +1179 -0
- agents/task/agent/core/conversational_exit.py +49 -0
- agents/task/agent/core/correspondent_gate.py +227 -0
- agents/task/agent/core/curator.py +246 -0
- agents/task/agent/core/episodic_digest.py +103 -0
- agents/task/agent/core/error_recovery.py +480 -0
- agents/task/agent/core/history_io.py +257 -0
- agents/task/agent/core/llm_provisioning.py +287 -0
- agents/task/agent/core/llm_runner.py +547 -0
- agents/task/agent/core/logging_io.py +276 -0
- agents/task/agent/core/loop_detection.py +262 -0
- agents/task/agent/core/memory_prefetch.py +325 -0
- agents/task/agent/core/memory_writer.py +667 -0
- agents/task/agent/core/model_introspection.py +82 -0
- agents/task/agent/core/model_swap.py +177 -0
- agents/task/agent/core/next_action_internal.py +1331 -0
- agents/task/agent/core/output_validation.py +303 -0
- agents/task/agent/core/project_context.py +272 -0
- agents/task/agent/core/resources.py +93 -0
- agents/task/agent/core/result_processing.py +389 -0
- agents/task/agent/core/run_loop.py +661 -0
- agents/task/agent/core/safety_lifecycle.py +228 -0
- agents/task/agent/core/secret_guard.py +293 -0
- agents/task/agent/core/self_wake.py +280 -0
- agents/task/agent/core/session_metadata.py +104 -0
- agents/task/agent/core/step.py +781 -0
- agents/task/agent/core/step_execution.py +398 -0
- agents/task/agent/core/step_telemetry.py +222 -0
- agents/task/agent/core/test_aux_metering.py +50 -0
- agents/task/agent/core/test_billing_failover.py +68 -0
- agents/task/agent/core/test_fatal_error_classifier.py +38 -0
- agents/task/agent/core/test_judge_validation.py +275 -0
- agents/task/agent/core/test_model_swap.py +190 -0
- agents/task/agent/core/test_reflection_offload.py +54 -0
- agents/task/agent/core/turn_input.py +8 -0
- agents/task/agent/core/untrusted_wrap.py +103 -0
- agents/task/agent/core/user_ingress.py +228 -0
- agents/task/agent/hitl_manager.py +396 -0
- agents/task/agent/log_sanitize.py +36 -0
- agents/task/agent/message_manager/config.py +97 -0
- agents/task/agent/message_manager/service.py +758 -0
- agents/task/agent/message_manager/tests.py +305 -0
- agents/task/agent/message_manager/tool_call_builder.py +539 -0
- agents/task/agent/message_manager/tool_message_repair.py +364 -0
- agents/task/agent/message_manager/views.py +143 -0
- agents/task/agent/messages/__init__.py +8 -0
- agents/task/agent/messages/builders.py +309 -0
- agents/task/agent/messages/compactor.py +611 -0
- agents/task/agent/messages/context_references.py +271 -0
- agents/task/agent/messages/filters.py +333 -0
- agents/task/agent/messages/guidance.py +328 -0
- agents/task/agent/messages/persistence.py +448 -0
- agents/task/agent/messages/retrieval.py +506 -0
- agents/task/agent/messages/sqlite_persistence.py +85 -0
- agents/task/agent/messages/test_compaction_upgrade.py +349 -0
- agents/task/agent/messages/test_persistence_subdir_fix.py +28 -0
- agents/task/agent/messages/test_sqlite_persistence.py +54 -0
- agents/task/agent/messages/token_counter.py +532 -0
- agents/task/agent/orchestrator.py +776 -0
- agents/task/agent/profile_manager.py +105 -0
- agents/task/agent/profile_registry.py +173 -0
- agents/task/agent/prompts.py +1090 -0
- agents/task/agent/scenario_registry.py +226 -0
- agents/task/agent/service.py +509 -0
- agents/task/agent/session.py +984 -0
- agents/task/agent/skill_discovery.py +110 -0
- agents/task/agent/skill_frontmatter.py +94 -0
- agents/task/agent/skill_manager.py +1201 -0
- agents/task/agent/skill_store.py +627 -0
- agents/task/agent/skill_validation.py +56 -0
- agents/task/agent/skill_writer.py +492 -0
- agents/task/agent/sub_agent_manager.py +1137 -0
- agents/task/agent/test_session_disk_recovery.py +34 -0
- agents/task/agent/tests.py +513 -0
- agents/task/agent/tool_call_tracker.py +511 -0
- agents/task/agent/views.py +469 -0
- agents/task/config.py +432 -0
- agents/task/constants.py +1384 -0
- agents/task/flag_defaults.py +63 -0
- agents/task/goals/__init__.py +1 -0
- agents/task/goals/autonomy_marker.py +28 -0
- agents/task/goals/board.py +690 -0
- agents/task/goals/completion_judge.py +247 -0
- agents/task/goals/context.py +62 -0
- agents/task/goals/dispatcher.py +848 -0
- agents/task/goals/escalation.py +102 -0
- agents/task/goals/planner.py +114 -0
- agents/task/logging_config.py +155 -0
- agents/task/path.py +991 -0
- agents/task/robust_parse_config.py +400 -0
- agents/task/runtime/__init__.py +0 -0
- agents/task/runtime/run_as_session.py +130 -0
- agents/task/runtime_safety.py +308 -0
- agents/task/session/__init__.py +5 -0
- agents/task/session/browser_pool.py +130 -0
- agents/task/session/cleanup.py +612 -0
- agents/task/session/execution.py +397 -0
- agents/task/session/feed.py +200 -0
- agents/task/session/hitl_ingress.py +201 -0
- agents/task/session/hooks.py +83 -0
- agents/task/session/multi_agent.py +243 -0
- agents/task/session/workspace.py +91 -0
- agents/task/session_registry.py +91 -0
- agents/task/session_route.py +35 -0
- agents/task/sqlite_session_registry.py +261 -0
- agents/task/surface_config.py +185 -0
- agents/task/telemetry/__init__.py +58 -0
- agents/task/telemetry/event_log.py +180 -0
- agents/task/telemetry/formatters.py +642 -0
- agents/task/telemetry/manager.py +571 -0
- agents/task/telemetry/memory_events.py +66 -0
- agents/task/telemetry/self_events.py +53 -0
- agents/task/telemetry/sequence.py +160 -0
- agents/task/telemetry/service.py +1265 -0
- agents/task/telemetry/views.py +882 -0
- agents/task/templates.py +98 -0
- agents/task/test_aux_chain.py +151 -0
- agents/task/test_aux_router.py +41 -0
- agents/task/tests/test_parse_robustness.py +170 -0
- agents/task/tool_defaults.py +110 -0
- agents/task/utils.py +762 -0
- agents/task/utils_json.py +768 -0
- agents/task/utils_webview.py +86 -0
- agents/task/workspace_context.py +229 -0
- agents/task_agent_lite.py +2398 -0
- api/README.md +683 -0
- api/TASK_API_DOCS.md +151 -0
- api/__init__.py +0 -0
- api/a2a/__init__.py +37 -0
- api/a2a/agent_card.py +337 -0
- api/a2a/client.py +657 -0
- api/a2a/endpoints.py +375 -0
- api/a2a/models.py +268 -0
- api/a2a/streaming.py +419 -0
- api/a2a/task_handler.py +929 -0
- api/admin_endpoints.py +1584 -0
- api/app.py +923 -0
- api/auth_constants.py +28 -0
- api/auth_endpoints.py +402 -0
- api/auth_state.py +36 -0
- api/chat_via_task.py +42 -0
- api/conversation_manager.py +231 -0
- api/dependencies.py +186 -0
- api/eip8004_endpoints.py +452 -0
- api/hyperliquid_models.py +174 -0
- api/hyperliquid_routes.py +450 -0
- api/interfaces.py +207 -0
- api/jwt_middleware.py +83 -0
- api/kb/__init__.py +1 -0
- api/kb/endpoints.py +314 -0
- api/mcp_models.py +279 -0
- api/mcp_routes.py +438 -0
- api/middleware.py +526 -0
- api/models.py +176 -0
- api/openai_compat/__init__.py +0 -0
- api/openai_compat/model_map.py +25 -0
- api/openai_compat/models.py +59 -0
- api/openai_compat/router.py +140 -0
- api/payment_endpoints.py +444 -0
- api/payment_verification.py +138 -0
- api/polymarket_models.py +155 -0
- api/polymarket_routes.py +501 -0
- api/pricing_endpoints.py +89 -0
- api/session_routing.py +54 -0
- api/skill_endpoints.py +453 -0
- api/task_http_api.py +1856 -0
- api/webhooks.py +52 -0
- api/x402_endpoints.py +280 -0
- avatar/README.md +44 -0
- avatar/__init__.py +1 -0
- avatar/config/rob.json +7 -0
- avatar/mindprint.js +395 -0
- avatar/renders/rob.meta.json +20 -0
- avatar/renders/rob.png +0 -0
- avatar/studio.html +373 -0
- avatar/webview/avatar-live.js +44 -0
- cli/README.md +89 -0
- cli/__init__.py +0 -0
- cli/cli_surface.py +67 -0
- cli/commands/__init__.py +0 -0
- cli/commands/_bootstrap.py +49 -0
- cli/commands/_errors.py +53 -0
- cli/commands/chat.py +973 -0
- cli/commands/config.py +100 -0
- cli/commands/dashboard.py +103 -0
- cli/commands/doctor.py +222 -0
- cli/commands/email.py +174 -0
- cli/commands/gateway.py +346 -0
- cli/commands/goals.py +509 -0
- cli/commands/init.py +276 -0
- cli/commands/journey.py +30 -0
- cli/commands/kb.py +280 -0
- cli/commands/model.py +107 -0
- cli/commands/owner.py +431 -0
- cli/commands/pfp.py +232 -0
- cli/commands/run.py +414 -0
- cli/commands/serve.py +41 -0
- cli/commands/session.py +684 -0
- cli/commands/skill_install.py +786 -0
- cli/commands/skills.py +128 -0
- cli/commands/subagents.py +100 -0
- cli/commands/surface.py +81 -0
- cli/commands/telegram.py +185 -0
- cli/commands/todos.py +209 -0
- cli/commands/tools.py +102 -0
- cli/commands/update.py +316 -0
- cli/commands/whatsapp.py +175 -0
- cli/config_store.py +276 -0
- cli/gitignore.py +43 -0
- cli/inventory.py +46 -0
- cli/keys.py +101 -0
- cli/persona.py +17 -0
- cli/polyrob.py +137 -0
- cli/ui/__init__.py +64 -0
- cli/ui/activity.py +141 -0
- cli/ui/app.py +538 -0
- cli/ui/banner.py +233 -0
- cli/ui/blocks.py +534 -0
- cli/ui/bootstrap_notice.py +47 -0
- cli/ui/commands/__init__.py +41 -0
- cli/ui/commands/h_cron.py +64 -0
- cli/ui/commands/h_journey.py +164 -0
- cli/ui/commands/h_kb.py +121 -0
- cli/ui/commands/h_learn.py +100 -0
- cli/ui/commands/h_mcp.py +215 -0
- cli/ui/commands/h_self.py +113 -0
- cli/ui/commands/h_skills.py +314 -0
- cli/ui/commands/handlers.py +1672 -0
- cli/ui/commands/registry.py +426 -0
- cli/ui/dialog.py +425 -0
- cli/ui/event_registry.py +110 -0
- cli/ui/event_specs.py +45 -0
- cli/ui/events.py +433 -0
- cli/ui/identity.py +15 -0
- cli/ui/lifecycle.py +159 -0
- cli/ui/live_hooks.py +43 -0
- cli/ui/model_selector.py +532 -0
- cli/ui/persistent_loop.py +181 -0
- cli/ui/pick.py +113 -0
- cli/ui/plain_renderer.py +351 -0
- cli/ui/renderer.py +384 -0
- cli/ui/rich_renderer.py +533 -0
- cli/ui/secrets.py +115 -0
- cli/ui/state.py +376 -0
- cli/ui/statusbar.py +231 -0
- cli/ui/streaming.py +254 -0
- cli/ui/terminal_render.py +105 -0
- cli/ui/theme.py +132 -0
- cli/update/__init__.py +6 -0
- cli/update/context.py +121 -0
- cli/update/detect.py +176 -0
- cli/update/engine.py +95 -0
- cli/update/migrate_guarded.py +55 -0
- cli/update/process_guard.py +215 -0
- cli/update/runners.py +78 -0
- cli/update/snapshot.py +270 -0
- cli/update/versions.py +205 -0
- core/README.md +377 -0
- core/__init__.py +174 -0
- core/assets.py +87 -0
- core/async_bridge.py +76 -0
- core/autonomy_runtime.py +259 -0
- core/base_component.py +302 -0
- core/bootstrap.py +681 -0
- core/bot.py +310 -0
- core/config.py +833 -0
- core/constants.py +126 -0
- core/container.py +462 -0
- core/db_manifest.py +101 -0
- core/embedding.py +57 -0
- core/env.py +39 -0
- core/exceptions.py +287 -0
- core/flags.py +144 -0
- core/flags_catalog.py +345 -0
- core/home_migration.py +62 -0
- core/identity.py +118 -0
- core/initialization.py +1004 -0
- core/instance.py +537 -0
- core/interactive_gate.py +96 -0
- core/logging.py +436 -0
- core/owner_doc_writer.py +176 -0
- core/pairing.py +193 -0
- core/path_safety.py +20 -0
- core/paths.py +27 -0
- core/payment_config.py +21 -0
- core/permissions.py +322 -0
- core/runtime_config.py +96 -0
- core/runtime_paths.py +135 -0
- core/seams.py +35 -0
- core/secret_scan.py +25 -0
- core/secret_scrub.py +71 -0
- core/secrets.py +35 -0
- core/security_logging_filter.py +148 -0
- core/self_context_writer.py +333 -0
- core/self_evolution.py +294 -0
- core/session_context.py +47 -0
- core/sqlite_util.py +59 -0
- core/surfaces/__init__.py +16 -0
- core/surfaces/access.py +118 -0
- core/surfaces/binding.py +88 -0
- core/surfaces/bootstrap.py +78 -0
- core/surfaces/circuit.py +168 -0
- core/surfaces/continuity.py +77 -0
- core/surfaces/correspondents.py +228 -0
- core/surfaces/dispatcher.py +216 -0
- core/surfaces/envelopes.py +75 -0
- core/surfaces/gc.py +30 -0
- core/surfaces/idempotency.py +57 -0
- core/surfaces/inbound_webhook.py +124 -0
- core/surfaces/media.py +32 -0
- core/surfaces/message_router.py +91 -0
- core/surfaces/outbound_allowlist.py +80 -0
- core/surfaces/outbound_dispatcher.py +110 -0
- core/surfaces/outbound_mirror.py +37 -0
- core/surfaces/outbound_queue.py +107 -0
- core/surfaces/outbound_target.py +12 -0
- core/surfaces/owner_admin.py +29 -0
- core/surfaces/proactive.py +78 -0
- core/surfaces/progress.py +108 -0
- core/surfaces/rate_bucket.py +21 -0
- core/surfaces/registry.py +51 -0
- core/surfaces/rendering.py +53 -0
- core/surfaces/send_policy.py +14 -0
- core/surfaces/serialize.py +24 -0
- core/surfaces/session_chat_registry.py +112 -0
- core/surfaces/session_policy.py +60 -0
- core/surfaces/surface.py +288 -0
- core/surfaces/transcription.py +88 -0
- core/surfaces/voice_echo.py +26 -0
- core/surfaces/voice_guard.py +23 -0
- core/tickers.py +174 -0
- core/tool_catalog.py +192 -0
- core/version.py +75 -0
- core/wallet/__init__.py +5 -0
- core/wallet/agent_wallet.py +59 -0
- core/wallet/audit_sink.py +61 -0
- core/wallet/config.py +70 -0
- core/wallet/factory.py +87 -0
- core/wallet/policy.py +104 -0
- core/wallet/signer.py +51 -0
- cron/README.md +65 -0
- cron/__init__.py +0 -0
- cron/delivery.py +277 -0
- cron/digest.py +124 -0
- cron/jobs.py +207 -0
- cron/runner.py +297 -0
- cron/schedule.py +194 -0
- cron/scheduler.py +201 -0
- cron/service.py +57 -0
- cron/wake_gate.py +223 -0
- data/__init__.py +1 -0
- data/prompts/autov2_prompts.json +87 -0
- data/prompts/skills/browser-automation/SKILL.md +43 -0
- data/prompts/skills/coding-workflow/SKILL.md +36 -0
- data/prompts/skills/crypto-trading-safety/SKILL.md +53 -0
- data/prompts/skills/document-writing/SKILL.md +33 -0
- data/prompts/skills/email-comms/SKILL.md +33 -0
- data/prompts/skills/file-data-ops/SKILL.md +35 -0
- data/prompts/skills/hyperliquid-account-review/SKILL.md +48 -0
- data/prompts/skills/hyperliquid-market-data/SKILL.md +52 -0
- data/prompts/skills/hyperliquid-trading/SKILL.md +59 -0
- data/prompts/skills/lead-research/SKILL.md +86 -0
- data/prompts/skills/market-research-brief/SKILL.md +104 -0
- data/prompts/skills/person-analyzer/SKILL.md +124 -0
- data/prompts/skills/polymarket-market-research/SKILL.md +54 -0
- data/prompts/skills/polymarket-portfolio-review/SKILL.md +50 -0
- data/prompts/skills/polymarket-trading/SKILL.md +55 -0
- data/prompts/skills/presentation-creator/SKILL.md +36 -0
- data/prompts/skills/project-analyzer/SKILL.md +169 -0
- data/prompts/skills/rules.json +268 -0
- data/prompts/skills/secret-handling/SKILL.md +20 -0
- data/prompts/skills/skill-authoring/SKILL.md +31 -0
- data/prompts/skills/skill-security-review/SKILL.md +27 -0
- data/prompts/skills/social-discovery/SKILL.md +57 -0
- data/prompts/skills/task-planning/SKILL.md +33 -0
- data/prompts/skills/web-research/SKILL.md +39 -0
- data/prompts/skills/web-scraping/SKILL.md +73 -0
- data/prompts/skills/x-engagement/SKILL.md +69 -0
- data/prompts/system_prompts.json +4 -0
- data/subscription_channels.json +5 -0
- modules/README.md +503 -0
- modules/__init__.py +124 -0
- modules/auth/__init__.py +13 -0
- modules/auth/api_key_manager.py +157 -0
- modules/auth/identity_mapper.py +380 -0
- modules/auth/siwe_auth.py +273 -0
- modules/auth/tier_manager.py +173 -0
- modules/base_module.py +190 -0
- modules/credits/__init__.py +29 -0
- modules/credits/balance_manager.py +237 -0
- modules/credits/cost_utils.py +129 -0
- modules/credits/pricing.py +139 -0
- modules/credits/unified_ledger.py +148 -0
- modules/credits/usage_meter.py +310 -0
- modules/credits/usage_tracker.py +674 -0
- modules/database/__init__.py +33 -0
- modules/database/audit_log.py +322 -0
- modules/database/auth_tables.py +276 -0
- modules/database/connection.py +644 -0
- modules/database/connection_pool.py +305 -0
- modules/database/conversation_contexts.py +446 -0
- modules/database/database_manager.py +237 -0
- modules/database/hyperliquid.py +375 -0
- modules/database/polymarket.py +574 -0
- modules/database/user_mcp_servers.py +728 -0
- modules/database/user_profiles.py +562 -0
- modules/database/utils.py +107 -0
- modules/database/x402_tables.py +92 -0
- modules/eip8004/README.md +1069 -0
- modules/eip8004/__init__.py +65 -0
- modules/eip8004/contracts.py +498 -0
- modules/eip8004/models.py +271 -0
- modules/eip8004/registration.py +148 -0
- modules/eip8004/reputation.py +367 -0
- modules/eip8004/validation.py +277 -0
- modules/llm/__init__.py +97 -0
- modules/llm/adapters.py +963 -0
- modules/llm/anthropic_client.py +831 -0
- modules/llm/available_models.py +106 -0
- modules/llm/brain_scrubber.py +137 -0
- modules/llm/cache_hints.py +162 -0
- modules/llm/deepseek_client.py +562 -0
- modules/llm/gemini_client.py +1367 -0
- modules/llm/llm_client.py +1139 -0
- modules/llm/llm_client_registry.py +188 -0
- modules/llm/llm_factory.py +237 -0
- modules/llm/llm_manager.py +1036 -0
- modules/llm/messages.py +374 -0
- modules/llm/model_registry.py +1935 -0
- modules/llm/nvidia_client.py +125 -0
- modules/llm/openai_client.py +800 -0
- modules/llm/openrouter_client.py +969 -0
- modules/llm/profiles.py +178 -0
- modules/llm/test_anthropic_cache_capture.py +35 -0
- modules/llm/test_cached_pricing_derivation.py +24 -0
- modules/llm/think_scrubber.py +444 -0
- modules/llm/token_counter.py +268 -0
- modules/memory/__init__.py +35 -0
- modules/memory/backend_factory.py +83 -0
- modules/memory/cache_manager.py +186 -0
- modules/memory/episodic.py +108 -0
- modules/memory/local_vector_memory_provider.py +552 -0
- modules/memory/memory_manager.py +225 -0
- modules/memory/models.py +312 -0
- modules/memory/provider.py +210 -0
- modules/memory/registry.py +205 -0
- modules/memory/sqlite_memory_provider.py +767 -0
- modules/memory/task/__init__.py +52 -0
- modules/memory/task/compaction_manager.py +157 -0
- modules/memory/task/context_retriever.py +809 -0
- modules/memory/task/hierarchical_memory.py +1002 -0
- modules/memory/task/lexical_retriever.py +71 -0
- modules/memory/task/null_context_manager.py +213 -0
- modules/memory/task/phase_manager.py +625 -0
- modules/memory/task/reflection_service.py +157 -0
- modules/memory/task/semantic_retriever.py +218 -0
- modules/memory/task/task_context_manager.py +1165 -0
- modules/memory/task/test_forgetting_engages.py +24 -0
- modules/memory/task/test_reflection_llm.py +39 -0
- modules/memory/task/test_threat_scan.py +53 -0
- modules/memory/task/threat_scan.py +90 -0
- modules/memory/test_sqlite_memory_provider.py +106 -0
- modules/memory/user_profile_manager.py +527 -0
- modules/payments/__init__.py +11 -0
- modules/payments/deposit_monitor.py +478 -0
- modules/payments/price_oracle.py +70 -0
- modules/payments/treasury_sweeper.py +401 -0
- modules/payments/wallet_generator.py +145 -0
- modules/pfp/__init__.py +1 -0
- modules/pfp/config.py +92 -0
- modules/pfp/mesh.py +565 -0
- modules/pfp/push.py +69 -0
- modules/pfp/renderer.py +85 -0
- modules/pfp/store.py +82 -0
- modules/skills/__init__.py +1 -0
- modules/skills/skill_usage.py +248 -0
- modules/transcription/__init__.py +24 -0
- modules/transcription/base.py +20 -0
- modules/transcription/faster_whisper_transcriber.py +55 -0
- modules/x402/README.md +249 -0
- modules/x402/__init__.py +22 -0
- modules/x402/invoicing.py +416 -0
- modules/x402/middleware.py +364 -0
- modules/x402/settlement_watcher.py +145 -0
- modules/x402/x402_integration.py +344 -0
- polyrob-0.5.0.dist-info/METADATA +482 -0
- polyrob-0.5.0.dist-info/RECORD +745 -0
- polyrob-0.5.0.dist-info/WHEEL +5 -0
- polyrob-0.5.0.dist-info/entry_points.txt +2 -0
- polyrob-0.5.0.dist-info/licenses/LICENSE +21 -0
- polyrob-0.5.0.dist-info/top_level.txt +12 -0
- surfaces/README.md +70 -0
- surfaces/__init__.py +0 -0
- surfaces/email/__init__.py +0 -0
- surfaces/email/dedup.py +44 -0
- surfaces/email/harness.py +187 -0
- surfaces/email/inbound.py +159 -0
- surfaces/email/seed.py +60 -0
- surfaces/email/surface.py +105 -0
- surfaces/telegram/__init__.py +0 -0
- surfaces/telegram/dedup.py +83 -0
- surfaces/telegram/harness.py +886 -0
- surfaces/telegram/inbound.py +145 -0
- surfaces/telegram/interactive_tools.py +49 -0
- surfaces/telegram/markdown.py +347 -0
- surfaces/telegram/rate_limit.py +167 -0
- surfaces/telegram/surface.py +187 -0
- surfaces/telegram/voice.py +52 -0
- surfaces/whatsapp/__init__.py +0 -0
- surfaces/whatsapp/client.py +51 -0
- surfaces/whatsapp/harness.py +62 -0
- surfaces/whatsapp/inbound.py +132 -0
- surfaces/whatsapp/surface.py +64 -0
- surfaces/whatsapp/window.py +34 -0
- tools/README.md +578 -0
- tools/__init__.py +424 -0
- tools/alchemy/__init__.py +5 -0
- tools/alchemy/alchemy_tool.py +389 -0
- tools/anysite/__init__.py +17 -0
- tools/anysite/client.py +88 -0
- tools/anysite/tool.py +89 -0
- tools/base_tool.py +468 -0
- tools/browser/__init__.py +22 -0
- tools/browser/actions.py +118 -0
- tools/browser/browser.py +1457 -0
- tools/browser/browser_manager.py +690 -0
- tools/browser/context.py +1748 -0
- tools/browser/playwright_utils.py +137 -0
- tools/browser/views.py +56 -0
- tools/code_exec/SANDBOX_SECURITY.md +144 -0
- tools/code_exec/__init__.py +178 -0
- tools/code_exec/backend.py +73 -0
- tools/code_exec/backends/__init__.py +1 -0
- tools/code_exec/backends/docker.py +779 -0
- tools/code_exec/backends/local_subprocess.py +154 -0
- tools/code_exec/env_policy.py +45 -0
- tools/code_exec/result.py +42 -0
- tools/code_exec/sandbox_guard.py +59 -0
- tools/code_exec/tool.py +227 -0
- tools/coding/__init__.py +50 -0
- tools/coding/edit.py +107 -0
- tools/coding/search.py +113 -0
- tools/coding/tool.py +353 -0
- tools/collabland/__init__.py +5 -0
- tools/collabland/collabland_tool.py +471 -0
- tools/controller/__init__.py +0 -0
- tools/controller/_helpers.py +165 -0
- tools/controller/action_registration.py +1765 -0
- tools/controller/approval.py +244 -0
- tools/controller/approval_interactive.py +90 -0
- tools/controller/delegation.py +195 -0
- tools/controller/execution.py +797 -0
- tools/controller/execution_context.py +141 -0
- tools/controller/hooks.py +135 -0
- tools/controller/introspection.py +344 -0
- tools/controller/mcp_registrar.py +190 -0
- tools/controller/message_send.py +31 -0
- tools/controller/registry/__init__.py +4 -0
- tools/controller/registry/schema_generators.py +395 -0
- tools/controller/registry/schema_sanitizer.py +406 -0
- tools/controller/registry/service.py +1266 -0
- tools/controller/registry/views.py +151 -0
- tools/controller/service.py +409 -0
- tools/controller/tool_management.py +334 -0
- tools/controller/types.py +28 -0
- tools/controller/views.py +450 -0
- tools/cronjob_tools.py +166 -0
- tools/crypto_trade_gate.py +59 -0
- tools/descriptors.py +452 -0
- tools/dom/__init__.py +0 -0
- tools/dom/history_tree_processor/service.py +244 -0
- tools/dom/history_tree_processor/view.py +99 -0
- tools/dom/service.py +471 -0
- tools/dom/views.py +346 -0
- tools/email_tool.py +403 -0
- tools/exceptions.py +169 -0
- tools/filesystem.py +1038 -0
- tools/filesystem_docproc.py +331 -0
- tools/filesystem_pdf.py +755 -0
- tools/git/__init__.py +42 -0
- tools/git/tool.py +258 -0
- tools/github/__init__.py +33 -0
- tools/github/client.py +94 -0
- tools/github/tool.py +237 -0
- tools/goal_tools.py +309 -0
- tools/hyperliquid/__init__.py +56 -0
- tools/hyperliquid/models.py +233 -0
- tools/hyperliquid/service.py +1680 -0
- tools/knowledge_ingest.py +950 -0
- tools/mcp/README.md +535 -0
- tools/mcp/__init__.py +52 -0
- tools/mcp/catalog.py +115 -0
- tools/mcp/config.py +337 -0
- tools/mcp/mcp_tool.py +1415 -0
- tools/mcp/param_coercion.py +289 -0
- tools/mcp/protocol.py +1520 -0
- tools/mcp/rate_limit.py +50 -0
- tools/mcp/security.py +424 -0
- tools/mcp/self_install.py +111 -0
- tools/mcp/server_manager.py +829 -0
- tools/mcp/subscriptions.py +72 -0
- tools/mcp/user_mcp_service.py +899 -0
- tools/mcp/validation_tracker.py +89 -0
- tools/mcp/views.py +346 -0
- tools/oauth/__init__.py +17 -0
- tools/oauth/manager.py +75 -0
- tools/oauth/provider.py +69 -0
- tools/oauth/providers/__init__.py +1 -0
- tools/oauth/providers/generic_oauth2.py +96 -0
- tools/perplexity_tool.py +280 -0
- tools/polymarket/__init__.py +41 -0
- tools/polymarket/clob_adapter.py +76 -0
- tools/polymarket/models.py +182 -0
- tools/polymarket/service.py +2082 -0
- tools/self_env/__init__.py +61 -0
- tools/self_env/tool.py +274 -0
- tools/shell/__init__.py +80 -0
- tools/shell/backend_pool.py +118 -0
- tools/shell/discipline.py +62 -0
- tools/shell/executor.py +157 -0
- tools/shell/loopback_allow.py +87 -0
- tools/shell/process_registry.py +113 -0
- tools/shell/process_tool.py +131 -0
- tools/shell/state.py +129 -0
- tools/shell/tool.py +151 -0
- tools/task_tool.py +637 -0
- tools/twitter_tool.py +2086 -0
- tools/user_directory.py +179 -0
- tools/web_fetch/__init__.py +3 -0
- tools/web_fetch/fetcher.py +144 -0
- tools/web_fetch/render.py +44 -0
- tools/web_fetch/tool.py +60 -0
- tools/x402/__init__.py +60 -0
- tools/x402/client.py +47 -0
- tools/x402/invoice_tool.py +143 -0
- tools/x402/real_client.py +250 -0
- tools/x402/service.py +131 -0
- utils/README.md +498 -0
- utils/__init__.py +46 -0
- utils/auth_utils.py +114 -0
- utils/bounded_collections.py +162 -0
- utils/circuit_breaker.py +389 -0
- utils/gif_utils.py +435 -0
- utils/markdown_utils.py +291 -0
- utils/message_utils.py +147 -0
- utils/metrics.py +36 -0
- utils/path_validator.py +227 -0
- utils/rate_limit_manager.py +399 -0
- utils/result_size.py +296 -0
- utils/time_utils.py +206 -0
- utils/user_utils.py +188 -0
- webview/README.md +111 -0
- webview/RENAME_AND_ALIGN_HANDOFF.md +200 -0
- webview/__init__.py +8 -0
- webview/activity.py +636 -0
- webview/owner_auth.py +108 -0
- webview/pages.py +448 -0
- webview/repair_sessions.py +399 -0
- webview/server.py +4323 -0
- webview/server_launcher.py +112 -0
- webview/static/css/activity.css +89 -0
- webview/static/css/chat.css +1772 -0
- webview/static/css/components.css +1190 -0
- webview/static/css/config-panel.css +651 -0
- webview/static/css/pages/admin.css +766 -0
- webview/static/css/pages/profile.css +231 -0
- webview/static/css/pages/settings.css +297 -0
- webview/static/css/pages/signin.css +316 -0
- webview/static/css/style.css +4250 -0
- webview/static/css/variables.css +147 -0
- webview/static/css/workspace-fullwidth-fix.css +109 -0
- webview/static/img/favicon.ico +0 -0
- webview/static/js/activity.js +394 -0
- webview/static/js/admin/activity.js +186 -0
- webview/static/js/admin/dashboard.js +163 -0
- webview/static/js/admin/user_detail.js +400 -0
- webview/static/js/admin/users.js +174 -0
- webview/static/js/admin/utils.js +214 -0
- webview/static/js/chat.js +3178 -0
- webview/static/js/config-panel.js +522 -0
- webview/static/js/constants.js +20 -0
- webview/static/js/error-handler.js +226 -0
- webview/static/js/ethers.min.js +1 -0
- webview/static/js/event-filter.js +137 -0
- webview/static/js/event-store.js +351 -0
- webview/static/js/file-attachments.js +374 -0
- webview/static/js/file-loader.js +163 -0
- webview/static/js/index.js +103 -0
- webview/static/js/performance-utils.js +196 -0
- webview/static/js/profile.js +396 -0
- webview/static/js/screenshot.js +386 -0
- webview/static/js/session.js +1527 -0
- webview/static/js/settings.js +1288 -0
- webview/static/js/sidebar-data.js +766 -0
- webview/static/js/sidebar-toggle.js +296 -0
- webview/static/js/socket.io.min.js +7 -0
- webview/static/js/stats.js +355 -0
- webview/static/js/ui-utils.js +420 -0
- webview/static/js/workspace.js +966 -0
- webview/stats_service.py +613 -0
- webview/templates/__init__.py +1 -0
- webview/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- webview/templates/activity.html +36 -0
- webview/templates/admin/activity.html +122 -0
- webview/templates/admin/dashboard.html +112 -0
- webview/templates/admin/user_detail.html +228 -0
- webview/templates/admin/users.html +105 -0
- webview/templates/autonomy.html +60 -0
- webview/templates/error.html +133 -0
- webview/templates/finance.html +90 -0
- webview/templates/identity.html +73 -0
- webview/templates/index.html +42 -0
- webview/templates/layout.html +125 -0
- webview/templates/memory.html +59 -0
- webview/templates/owner_login.html +15 -0
- webview/templates/profile.html +121 -0
- webview/templates/session.html +312 -0
- webview/templates/settings.html +534 -0
- webview/templates/sidebar.html +112 -0
- webview/templates/signin.html +213 -0
- webview/templates/status.html +14 -0
- webview/templates/system.html +45 -0
- webview/webgate.py +196 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
"""Error-recovery + provider-failover mixin (roadmap P9; code-motion from llm_runner.py).
|
|
2
|
+
|
|
3
|
+
Step-error handling, progress recovery, LLM provider fallback, and the
|
|
4
|
+
provider-failure / fallback-success telemetry. Split out of llm_runner.py so that
|
|
5
|
+
file owns LLM *invocation* and this one owns *error recovery / failover*. ``Agent``
|
|
6
|
+
composes ``ErrorRecoveryMixin``; call sites (step.py, get_next_action) unchanged
|
|
7
|
+
via MRO.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
import logging
|
|
13
|
+
import time
|
|
14
|
+
import traceback
|
|
15
|
+
from typing import Any, List, Optional
|
|
16
|
+
|
|
17
|
+
from pydantic import ValidationError
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from google.api_core.exceptions import ResourceExhausted
|
|
21
|
+
except ImportError: # google-api-core not installed
|
|
22
|
+
class ResourceExhausted(Exception):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
# NOTE: AgentError is imported from views (not core.exceptions) because only views'
|
|
26
|
+
# AgentError exposes format_error(), which _handle_step_error uses at its first line.
|
|
27
|
+
# core.exceptions.AgentError has no such method (importing it there crashes on any error).
|
|
28
|
+
from agents.task.agent.views import ActionResult, AgentError
|
|
29
|
+
from agents.task.telemetry.views import ProviderFailureEvent, ProviderFallbackSuccessEvent
|
|
30
|
+
from agents.task.utils import time_execution_async
|
|
31
|
+
from core.env import bool_env as _bool_env
|
|
32
|
+
from core.exceptions import InsufficientCreditsError
|
|
33
|
+
from core.exceptions import (
|
|
34
|
+
RateLimitError,
|
|
35
|
+
LLMError,
|
|
36
|
+
LLMRateLimitError,
|
|
37
|
+
LLMAuthenticationError,
|
|
38
|
+
LLMConnectionError,
|
|
39
|
+
LLMContextLengthError,
|
|
40
|
+
LLMResponseError,
|
|
41
|
+
LLMPermanentError,
|
|
42
|
+
LLMProviderExhaustedError,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _billing_failover_enabled() -> bool:
|
|
47
|
+
"""Whether billing errors should attempt provider fallback (default off)."""
|
|
48
|
+
return _bool_env("BILLING_FAILOVER_ENABLED", False)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ErrorRecoveryMixin:
|
|
52
|
+
"""Step-error handling + LLM provider fallback + failover telemetry for Agent."""
|
|
53
|
+
|
|
54
|
+
async def _recover_from_error(self, error: Exception) -> None:
|
|
55
|
+
"""Comprehensive error recovery with state reset and resource cleanup.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
error: The exception that triggered recovery
|
|
59
|
+
"""
|
|
60
|
+
self.logger.info("Starting comprehensive error recovery")
|
|
61
|
+
|
|
62
|
+
# 1. Clear any pending operations
|
|
63
|
+
if hasattr(self, '_pending_operations'):
|
|
64
|
+
self._pending_operations = []
|
|
65
|
+
|
|
66
|
+
# 2. Reset approval state if stuck
|
|
67
|
+
# NOTE: Approval system simplified - no state tracking needed
|
|
68
|
+
if hasattr(self, 'hitl_manager'):
|
|
69
|
+
self.logger.debug("Approval system simplified - no state reset needed")
|
|
70
|
+
|
|
71
|
+
# 3. Clean up browser state if browser-related error
|
|
72
|
+
if 'browser' in str(error).lower() or 'playwright' in str(error).lower():
|
|
73
|
+
browser_context = await self.get_browser_context()
|
|
74
|
+
if browser_context:
|
|
75
|
+
try:
|
|
76
|
+
# Navigate to safe page
|
|
77
|
+
pages = browser_context.pages
|
|
78
|
+
if pages:
|
|
79
|
+
await pages[0].goto('about:blank', timeout=5000)
|
|
80
|
+
self.logger.info("Reset browser to blank page")
|
|
81
|
+
except Exception as browser_error:
|
|
82
|
+
self.logger.warning(f"Could not reset browser: {browser_error}")
|
|
83
|
+
|
|
84
|
+
# 4. Clear any cached results that might be corrupt
|
|
85
|
+
if hasattr(self, '_last_result'):
|
|
86
|
+
self._last_result = None
|
|
87
|
+
|
|
88
|
+
# 6. Reset consecutive failure counter if it's getting too high
|
|
89
|
+
if self.state.consecutive_failures >= self.max_failures - 1:
|
|
90
|
+
# Last chance - checkpoint and warn
|
|
91
|
+
self.logger.warning("Near max failures - checkpointing state")
|
|
92
|
+
self.message_manager.checkpoint_history()
|
|
93
|
+
|
|
94
|
+
# Force garbage collection
|
|
95
|
+
import gc
|
|
96
|
+
gc.collect()
|
|
97
|
+
|
|
98
|
+
# 7. Clear any tool-specific state
|
|
99
|
+
if self.controller:
|
|
100
|
+
# Reset any tool state that might be stuck
|
|
101
|
+
if hasattr(self.controller, 'reset_tool_state'):
|
|
102
|
+
try:
|
|
103
|
+
await self.controller.reset_tool_state()
|
|
104
|
+
except Exception:
|
|
105
|
+
pass # Best effort
|
|
106
|
+
|
|
107
|
+
# 8. Log recovery completion
|
|
108
|
+
self.logger.info("Error recovery completed")
|
|
109
|
+
|
|
110
|
+
async def _handle_step_error(self, error: Exception) -> list[ActionResult]:
|
|
111
|
+
"""Enhanced error handling with LLM fallback support (Dec 2025).
|
|
112
|
+
|
|
113
|
+
UPGRADED: Now triggers LLM fallback for recoverable errors instead of
|
|
114
|
+
just waiting and retrying with the same broken client.
|
|
115
|
+
"""
|
|
116
|
+
include_trace = self.logger.isEnabledFor(logging.DEBUG)
|
|
117
|
+
error_msg = AgentError.format_error(error, include_trace=include_trace)
|
|
118
|
+
prefix = f'❌ Result failed {self.state.consecutive_failures + 1}/{self.max_failures} times:\n '
|
|
119
|
+
error_str_lower = str(error).lower()
|
|
120
|
+
error_type = type(error).__name__
|
|
121
|
+
|
|
122
|
+
# === CONTEXT-LENGTH OVERFLOW - COMPACT, DON'T FALLBACK (CO-F6) ===
|
|
123
|
+
# A context-overflow needs a SMALLER prompt, not a different provider — retrying
|
|
124
|
+
# on a fallback provider with the same oversized history just overflows again.
|
|
125
|
+
# Must run BEFORE the generic is_llm_error block below (which would otherwise
|
|
126
|
+
# route this typed error into a blind provider-fallback retry).
|
|
127
|
+
if isinstance(error, LLMContextLengthError):
|
|
128
|
+
self.logger.warning("Context-length overflow — checkpoint + emergency prune, then retry")
|
|
129
|
+
self.message_manager.checkpoint_history()
|
|
130
|
+
self.message_manager.emergency_context_prune()
|
|
131
|
+
self.state.consecutive_failures += 1
|
|
132
|
+
return [] # retry next step with pruned history
|
|
133
|
+
|
|
134
|
+
# === PERMANENT/CRITICAL ERRORS - HALT IMMEDIATELY ===
|
|
135
|
+
# These errors indicate account-level issues that won't be resolved by fallback
|
|
136
|
+
is_permanent = (
|
|
137
|
+
isinstance(error, LLMPermanentError) or
|
|
138
|
+
isinstance(error, LLMAuthenticationError) or
|
|
139
|
+
'insufficient_quota' in error_str_lower or
|
|
140
|
+
'billing' in error_str_lower or
|
|
141
|
+
'invalid_api_key' in error_str_lower or
|
|
142
|
+
'account_deactivated' in error_str_lower
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if is_permanent:
|
|
146
|
+
import os
|
|
147
|
+
is_billing = ('insufficient_quota' in error_str_lower or 'billing' in error_str_lower)
|
|
148
|
+
if is_billing and _billing_failover_enabled():
|
|
149
|
+
self.logger.warning("💳 Billing error with BILLING_FAILOVER_ENABLED — attempting provider fallback")
|
|
150
|
+
# HIGH-2: record the exhausted provider BEFORE failover so repeated billing
|
|
151
|
+
# errors accumulate the failed-set (prevents A↔B ping-pong / step-budget burn).
|
|
152
|
+
# reset_llm_errors() below clears only error-type counts, not this set.
|
|
153
|
+
self.state.track_llm_error("billing", self._get_provider_from_model(self.model_name))
|
|
154
|
+
if await self._attempt_llm_fallback_in_handler("billing"):
|
|
155
|
+
self.logger.info("✅ Billing failover succeeded; continuing on fallback provider")
|
|
156
|
+
self.state.reset_llm_errors()
|
|
157
|
+
return []
|
|
158
|
+
self.logger.error("❌ Billing failover found no alternative provider; halting")
|
|
159
|
+
self.logger.error(f"❌ PERMANENT ERROR - Session halted: {error}")
|
|
160
|
+
self.state.consecutive_failures = self.max_failures
|
|
161
|
+
self.state.stopped = True
|
|
162
|
+
return [ActionResult(
|
|
163
|
+
error=f"PERMANENT ERROR: {str(error)[:400]}. Session halted. Please check API configuration.",
|
|
164
|
+
include_in_memory=True
|
|
165
|
+
)]
|
|
166
|
+
|
|
167
|
+
# === PROVIDER EXHAUSTED - All fallbacks failed ===
|
|
168
|
+
if isinstance(error, LLMProviderExhaustedError):
|
|
169
|
+
self.logger.error(f"❌ ALL LLM PROVIDERS EXHAUSTED: {error}")
|
|
170
|
+
self.state.consecutive_failures = self.max_failures
|
|
171
|
+
self.state.stopped = True
|
|
172
|
+
providers = getattr(error, 'providers_tried', [])
|
|
173
|
+
return [ActionResult(
|
|
174
|
+
error=f"All LLM providers failed. Tried: {providers}. Session halted.",
|
|
175
|
+
include_in_memory=True
|
|
176
|
+
)]
|
|
177
|
+
|
|
178
|
+
# CRITICAL: State recovery and cleanup (only for non-critical errors)
|
|
179
|
+
try:
|
|
180
|
+
await self._recover_from_error(error)
|
|
181
|
+
except Exception as recovery_error:
|
|
182
|
+
self.logger.error(f"Failed to recover from error: {recovery_error}", exc_info=True)
|
|
183
|
+
|
|
184
|
+
# Enhanced error message creation with better context
|
|
185
|
+
if not error_msg or error_msg.strip() == '':
|
|
186
|
+
error_msg = f"Unknown error of type {error_type}"
|
|
187
|
+
if hasattr(error, "__str__"):
|
|
188
|
+
error_detail = str(error)
|
|
189
|
+
if error_detail:
|
|
190
|
+
error_msg += f": {error_detail}"
|
|
191
|
+
|
|
192
|
+
# Add context information for debugging
|
|
193
|
+
if hasattr(error, '__dict__'):
|
|
194
|
+
error_context = {k: v for k, v in error.__dict__.items() if not k.startswith('_')}
|
|
195
|
+
if error_context:
|
|
196
|
+
error_msg += f"\nError context: {error_context}"
|
|
197
|
+
|
|
198
|
+
if include_trace and hasattr(error, "__traceback__"):
|
|
199
|
+
import traceback
|
|
200
|
+
error_msg += f"\n\nTraceback:\n{traceback.format_tb(error.__traceback__)}"
|
|
201
|
+
|
|
202
|
+
# === LLM ERRORS - TRY FALLBACK (Dec 2025 upgrade) ===
|
|
203
|
+
is_llm_error = isinstance(error, (
|
|
204
|
+
LLMError, LLMRateLimitError, LLMConnectionError, LLMContextLengthError
|
|
205
|
+
)) or any(x in error_str_lower for x in ['rate_limit', 'rate limit', '429', 'connection', 'timeout', 'llm'])
|
|
206
|
+
|
|
207
|
+
if is_llm_error:
|
|
208
|
+
# Track error for circuit breaker
|
|
209
|
+
current_provider = self._get_provider_from_model(self.model_name)
|
|
210
|
+
circuit_tripped = self.state.track_llm_error(error_type, current_provider)
|
|
211
|
+
|
|
212
|
+
if circuit_tripped:
|
|
213
|
+
self.logger.error(f"🚨 CIRCUIT BREAKER TRIPPED: Same error {error_type} repeated {self.state.CIRCUIT_BREAKER_THRESHOLD}+ times")
|
|
214
|
+
# Try one last fallback before giving up
|
|
215
|
+
fallback_result = await self._attempt_llm_fallback_in_handler(error_type)
|
|
216
|
+
if fallback_result:
|
|
217
|
+
self.logger.info(f"✅ Circuit breaker: Fallback successful, continuing with new provider")
|
|
218
|
+
self.state.reset_llm_errors(reset_circuit_breaker=True)
|
|
219
|
+
return [] # Empty result = continue execution
|
|
220
|
+
else:
|
|
221
|
+
self.logger.error(f"❌ Circuit breaker: No fallback available, halting")
|
|
222
|
+
self.state.stopped = True
|
|
223
|
+
self.state.consecutive_failures = self.max_failures
|
|
224
|
+
# Raise typed exception for proper handling upstream
|
|
225
|
+
raise LLMProviderExhaustedError(
|
|
226
|
+
f"All LLM providers exhausted after {error_type}",
|
|
227
|
+
providers_tried=list(self.state.llm_providers_failed)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# First occurrence or not yet at threshold - try immediate fallback
|
|
231
|
+
self.logger.warning(f'{prefix}LLM Error ({error_type}): {error_msg[:200]}')
|
|
232
|
+
|
|
233
|
+
if self.state.consecutive_failures >= 1: # Already failed once, try fallback
|
|
234
|
+
fallback_result = await self._attempt_llm_fallback_in_handler(error_type)
|
|
235
|
+
if fallback_result:
|
|
236
|
+
self.logger.info(f"✅ Fallback successful after {error_type}")
|
|
237
|
+
self.state.reset_llm_errors()
|
|
238
|
+
return [] # Continue with new LLM
|
|
239
|
+
|
|
240
|
+
# Backoff before retry with current provider
|
|
241
|
+
import random
|
|
242
|
+
base_delay = self.retry_delay * (2 ** self.state.consecutive_failures)
|
|
243
|
+
jitter = random.uniform(0.8, 1.2)
|
|
244
|
+
delay = min(base_delay * jitter, 120)
|
|
245
|
+
self.logger.info(f'Waiting {delay:.1f}s before retry...')
|
|
246
|
+
await asyncio.sleep(delay)
|
|
247
|
+
self.state.consecutive_failures += 1
|
|
248
|
+
|
|
249
|
+
return [ActionResult(error=error_msg, include_in_memory=True)]
|
|
250
|
+
|
|
251
|
+
# === VALIDATION/PARSE ERRORS ===
|
|
252
|
+
if isinstance(error, (ValidationError, ValueError)):
|
|
253
|
+
self.logger.error(f'{prefix}{error_msg}', exc_info=True)
|
|
254
|
+
if 'Max token limit reached' in error_msg or 'context length' in error_msg.lower():
|
|
255
|
+
# Token limit exceeded - checkpoint and warn
|
|
256
|
+
self.logger.warning(f'Token limit exceeded - consider checkpointing or using smaller context')
|
|
257
|
+
self.message_manager.checkpoint_history()
|
|
258
|
+
|
|
259
|
+
elif 'Could not parse response' in error_msg or 'json' in error_msg.lower():
|
|
260
|
+
# Enhanced JSON parsing error recovery
|
|
261
|
+
error_msg += '\n\nIMPORTANT: Return a valid JSON object with the required fields. Ensure all JSON is properly formatted and escaped.'
|
|
262
|
+
|
|
263
|
+
# Try to recover by reducing complexity
|
|
264
|
+
if hasattr(self, 'max_actions_per_step') and self.max_actions_per_step > 1:
|
|
265
|
+
self.max_actions_per_step = max(1, self.max_actions_per_step - 1)
|
|
266
|
+
self.logger.info(f'Reducing max actions per step to {self.max_actions_per_step} due to parse errors')
|
|
267
|
+
|
|
268
|
+
# Smart backoff for parse errors
|
|
269
|
+
delay = min(self.retry_delay * (1.5 ** self.state.consecutive_failures), 30)
|
|
270
|
+
self.logger.info(f'Waiting {delay:.1f}s before retry due to parse error...')
|
|
271
|
+
await asyncio.sleep(delay)
|
|
272
|
+
|
|
273
|
+
self.state.consecutive_failures += 1
|
|
274
|
+
|
|
275
|
+
elif isinstance(error, RateLimitError) or isinstance(error, ResourceExhausted):
|
|
276
|
+
# IMPORTANT: Rate limits due to quota issues are critical
|
|
277
|
+
if 'quota' in error_str_lower or 'billing' in error_str_lower:
|
|
278
|
+
self.logger.error(f"❌ CRITICAL: Rate limit due to quota/billing issue")
|
|
279
|
+
self.state.consecutive_failures = self.max_failures
|
|
280
|
+
self.state.stopped = True
|
|
281
|
+
return [ActionResult(
|
|
282
|
+
error=f"CRITICAL: {str(error)[:400]}. Session halted due to quota/billing issue.",
|
|
283
|
+
include_in_memory=True
|
|
284
|
+
)]
|
|
285
|
+
|
|
286
|
+
# Regular rate limit - can retry with fallback
|
|
287
|
+
self.logger.warning(f'{prefix}{error_msg}')
|
|
288
|
+
|
|
289
|
+
# Track for circuit breaker and try fallback
|
|
290
|
+
current_provider = self._get_provider_from_model(self.model_name)
|
|
291
|
+
self.state.track_llm_error('RateLimitError', current_provider)
|
|
292
|
+
|
|
293
|
+
if self.state.consecutive_failures >= 1:
|
|
294
|
+
fallback_result = await self._attempt_llm_fallback_in_handler('RateLimitError')
|
|
295
|
+
if fallback_result:
|
|
296
|
+
self.logger.info(f"✅ Switched to fallback provider after rate limit")
|
|
297
|
+
self.state.reset_llm_errors()
|
|
298
|
+
return []
|
|
299
|
+
|
|
300
|
+
# Enhanced rate limit handling with jitter
|
|
301
|
+
import random
|
|
302
|
+
base_delay = self.retry_delay * (2 ** self.state.consecutive_failures)
|
|
303
|
+
jitter = random.uniform(0.8, 1.2)
|
|
304
|
+
delay = min(base_delay * jitter, 120)
|
|
305
|
+
self.logger.info(f'Rate limited - waiting {delay:.1f}s before retry (with jitter)')
|
|
306
|
+
await asyncio.sleep(delay)
|
|
307
|
+
self.state.consecutive_failures += 1
|
|
308
|
+
|
|
309
|
+
elif isinstance(error, ConnectionError) or 'connection' in error_msg.lower():
|
|
310
|
+
self.logger.warning(f'{prefix}Connection error: {error_msg}')
|
|
311
|
+
|
|
312
|
+
# Track for circuit breaker and try fallback
|
|
313
|
+
current_provider = self._get_provider_from_model(self.model_name)
|
|
314
|
+
self.state.track_llm_error('ConnectionError', current_provider)
|
|
315
|
+
|
|
316
|
+
if self.state.consecutive_failures >= 1:
|
|
317
|
+
fallback_result = await self._attempt_llm_fallback_in_handler('ConnectionError')
|
|
318
|
+
if fallback_result:
|
|
319
|
+
self.logger.info(f"✅ Switched to fallback provider after connection error")
|
|
320
|
+
self.state.reset_llm_errors()
|
|
321
|
+
return []
|
|
322
|
+
|
|
323
|
+
# Progressive backoff for connection errors
|
|
324
|
+
delay = min(5 * (1.5 ** self.state.consecutive_failures), 60)
|
|
325
|
+
self.logger.info(f'Connection error - waiting {delay:.1f}s before retry')
|
|
326
|
+
await asyncio.sleep(delay)
|
|
327
|
+
self.state.consecutive_failures += 1
|
|
328
|
+
|
|
329
|
+
else:
|
|
330
|
+
self.logger.error(f'{prefix}{error_msg}', exc_info=True)
|
|
331
|
+
# For unknown errors, add a small delay to prevent rapid retries
|
|
332
|
+
if self.state.consecutive_failures > 0:
|
|
333
|
+
delay = min(2 * self.state.consecutive_failures, 10)
|
|
334
|
+
self.logger.info(f'Unknown error - brief delay of {delay}s before retry')
|
|
335
|
+
await asyncio.sleep(delay)
|
|
336
|
+
self.state.consecutive_failures += 1
|
|
337
|
+
|
|
338
|
+
return [ActionResult(error=error_msg, include_in_memory=True)]
|
|
339
|
+
|
|
340
|
+
async def _attempt_llm_fallback_in_handler(self, original_error_type: str) -> bool:
|
|
341
|
+
"""Attempt to switch to a fallback LLM provider.
|
|
342
|
+
|
|
343
|
+
Called from _handle_step_error when LLM errors occur.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
original_error_type: The error type that triggered fallback
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
True if fallback successful, False otherwise
|
|
350
|
+
"""
|
|
351
|
+
try:
|
|
352
|
+
current_provider = self._get_provider_from_model(self.model_name)
|
|
353
|
+
exclude_providers = list(self.state.llm_providers_failed) + [current_provider]
|
|
354
|
+
|
|
355
|
+
self.logger.info(f"🔄 Attempting LLM fallback (excluding: {exclude_providers})")
|
|
356
|
+
|
|
357
|
+
fallback_llm = await self._get_fallback_llm(
|
|
358
|
+
exclude_providers=exclude_providers,
|
|
359
|
+
original_model=self.model_name
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
if fallback_llm:
|
|
363
|
+
# Store original for telemetry
|
|
364
|
+
original_model = self.model_name
|
|
365
|
+
|
|
366
|
+
# Switch to fallback
|
|
367
|
+
self.llm = fallback_llm
|
|
368
|
+
self.model_name = getattr(fallback_llm, 'model_name', 'fallback')
|
|
369
|
+
|
|
370
|
+
self.logger.info(f"✅ Switched LLM: {original_model} → {self.model_name}")
|
|
371
|
+
|
|
372
|
+
# Emit telemetry
|
|
373
|
+
self._emit_fallback_success_telemetry(
|
|
374
|
+
original_provider=current_provider,
|
|
375
|
+
original_model=original_model,
|
|
376
|
+
fallback_provider=self._get_provider_from_model(self.model_name),
|
|
377
|
+
fallback_model=self.model_name,
|
|
378
|
+
original_error_type=original_error_type,
|
|
379
|
+
total_attempts=len(self.state.llm_providers_failed) + 1,
|
|
380
|
+
total_time=0.0
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
return True
|
|
384
|
+
else:
|
|
385
|
+
self.logger.warning(f"❌ No fallback LLM available")
|
|
386
|
+
return False
|
|
387
|
+
|
|
388
|
+
except Exception as e:
|
|
389
|
+
self.logger.error(f"Fallback attempt failed: {e}")
|
|
390
|
+
return False
|
|
391
|
+
|
|
392
|
+
async def _get_fallback_llm(
|
|
393
|
+
self,
|
|
394
|
+
exclude_providers: list,
|
|
395
|
+
original_model: str
|
|
396
|
+
) -> Optional[Any]:
|
|
397
|
+
"""Get a fallback LLM from LLMManager.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
exclude_providers: List of providers to exclude (already failed)
|
|
401
|
+
original_model: Original model name for context
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
Chat model or None if no fallback available
|
|
405
|
+
"""
|
|
406
|
+
try:
|
|
407
|
+
llm_manager = self.container.get_service('llm') if self.container else None
|
|
408
|
+
if not llm_manager:
|
|
409
|
+
self.logger.warning("LLMManager not available - cannot get fallback")
|
|
410
|
+
return None
|
|
411
|
+
|
|
412
|
+
# Use LLMManager's fallback method
|
|
413
|
+
if hasattr(llm_manager, 'get_fallback_chat_model'):
|
|
414
|
+
return await llm_manager.get_fallback_chat_model(
|
|
415
|
+
exclude_providers=exclude_providers,
|
|
416
|
+
original_model=original_model,
|
|
417
|
+
temperature=0.0
|
|
418
|
+
)
|
|
419
|
+
else:
|
|
420
|
+
self.logger.warning("LLMManager does not support get_fallback_chat_model")
|
|
421
|
+
return None
|
|
422
|
+
|
|
423
|
+
except Exception as e:
|
|
424
|
+
self.logger.error(f"Error getting fallback LLM: {e}")
|
|
425
|
+
return None
|
|
426
|
+
|
|
427
|
+
def _emit_provider_failure_telemetry(
|
|
428
|
+
self,
|
|
429
|
+
failed_provider: str,
|
|
430
|
+
failed_model: str,
|
|
431
|
+
error_type: str,
|
|
432
|
+
error_message: str,
|
|
433
|
+
attempt_number: int
|
|
434
|
+
) -> None:
|
|
435
|
+
"""Emit telemetry event for provider failure."""
|
|
436
|
+
try:
|
|
437
|
+
if self.telemetry_manager:
|
|
438
|
+
event = ProviderFailureEvent(
|
|
439
|
+
failed_provider=failed_provider,
|
|
440
|
+
failed_model=failed_model,
|
|
441
|
+
error_type=error_type,
|
|
442
|
+
error_message=error_message[:500],
|
|
443
|
+
attempt_number=attempt_number,
|
|
444
|
+
session_id=self.session_id,
|
|
445
|
+
agent_id=self.agent_id,
|
|
446
|
+
step=self.state.n_steps if hasattr(self, 'state') else None
|
|
447
|
+
)
|
|
448
|
+
self.telemetry_manager.capture_event(event)
|
|
449
|
+
except Exception as e:
|
|
450
|
+
self.logger.debug(f"Failed to emit provider failure telemetry: {e}")
|
|
451
|
+
|
|
452
|
+
def _emit_fallback_success_telemetry(
|
|
453
|
+
self,
|
|
454
|
+
original_provider: str,
|
|
455
|
+
original_model: str,
|
|
456
|
+
fallback_provider: str,
|
|
457
|
+
fallback_model: str,
|
|
458
|
+
original_error_type: str,
|
|
459
|
+
total_attempts: int,
|
|
460
|
+
total_time: float
|
|
461
|
+
) -> None:
|
|
462
|
+
"""Emit telemetry event for successful fallback."""
|
|
463
|
+
try:
|
|
464
|
+
if self.telemetry_manager:
|
|
465
|
+
event = ProviderFallbackSuccessEvent(
|
|
466
|
+
original_provider=original_provider,
|
|
467
|
+
original_model=original_model,
|
|
468
|
+
fallback_provider=fallback_provider,
|
|
469
|
+
fallback_model=fallback_model,
|
|
470
|
+
original_error_type=original_error_type,
|
|
471
|
+
total_attempts=total_attempts,
|
|
472
|
+
total_fallback_time_seconds=total_time,
|
|
473
|
+
session_id=self.session_id,
|
|
474
|
+
agent_id=self.agent_id,
|
|
475
|
+
step=self.state.n_steps if hasattr(self, 'state') else None
|
|
476
|
+
)
|
|
477
|
+
self.telemetry_manager.capture_event(event)
|
|
478
|
+
except Exception as e:
|
|
479
|
+
self.logger.debug(f"Failed to emit fallback success telemetry: {e}")
|
|
480
|
+
|