@scemoon/cdh 1.0.6 → 1.0.7
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.
- package/package.json +2 -2
- package/ai-dlc-skill/SKILL.md +0 -77
- package/ai-dlc-skill/agents/deliver-agent.md +0 -24
- package/ai-dlc-skill/agents/master.md +0 -50
- package/ai-dlc-skill/agents/plan-agent.md +0 -23
- package/ai-dlc-skill/agents/understand-agent.md +0 -23
- package/ai-dlc-skill/agents/verify-agent.md +0 -27
- package/ai-dlc-skill/architecture/project-structure.md +0 -78
- package/ai-dlc-skill/brownfield/README.md +0 -30
- package/ai-dlc-skill/brownfield/generate-context.sh +0 -64
- package/ai-dlc-skill/brownfield/scripts/deps.sh +0 -27
- package/ai-dlc-skill/brownfield/scripts/discover.sh +0 -34
- package/ai-dlc-skill/brownfield/scripts/extract-api.sh +0 -45
- package/ai-dlc-skill/components/backend.md +0 -34
- package/ai-dlc-skill/components/desktop.md +0 -30
- package/ai-dlc-skill/components/mya.md +0 -29
- package/ai-dlc-skill/components/native.md +0 -30
- package/ai-dlc-skill/components/tta.md +0 -29
- package/ai-dlc-skill/components/web.md +0 -31
- package/ai-dlc-skill/components/wxa.md +0 -29
- package/ai-dlc-skill/contracts/README.md +0 -25
- package/ai-dlc-skill/core/adaptive-flow.md +0 -36
- package/ai-dlc-skill/core/security.md +0 -29
- package/ai-dlc-skill/cross-tool/cline/export.sh +0 -31
- package/ai-dlc-skill/cross-tool/copilot/export.sh +0 -47
- package/ai-dlc-skill/cross-tool/cursor/export.sh +0 -32
- package/ai-dlc-skill/cross-tool/export.sh +0 -22
- package/ai-dlc-skill/cross-tool/onecode/config.yaml.tpl +0 -17
- package/ai-dlc-skill/cross-tool/onecode/export-skills.sh +0 -35
- package/ai-dlc-skill/cross-tool/onecode/export.sh +0 -54
- package/ai-dlc-skill/phases/deliver/entry.md +0 -26
- package/ai-dlc-skill/phases/deliver/lifecycle.md +0 -185
- package/ai-dlc-skill/phases/deliver/prompt.md +0 -40
- package/ai-dlc-skill/phases/deliver/rules.md +0 -61
- package/ai-dlc-skill/phases/plan/entry.md +0 -25
- package/ai-dlc-skill/phases/plan/lifecycle.md +0 -204
- package/ai-dlc-skill/phases/plan/prompt.md +0 -38
- package/ai-dlc-skill/phases/plan/rules.md +0 -25
- package/ai-dlc-skill/phases/understand/entry.md +0 -26
- package/ai-dlc-skill/phases/understand/lifecycle.md +0 -184
- package/ai-dlc-skill/phases/understand/prompt.md +0 -41
- package/ai-dlc-skill/phases/understand/rules.md +0 -37
- package/ai-dlc-skill/phases/verify/entry.md +0 -26
- package/ai-dlc-skill/phases/verify/lifecycle.md +0 -192
- package/ai-dlc-skill/phases/verify/prompt.md +0 -43
- package/ai-dlc-skill/phases/verify/rules.md +0 -73
- package/ai-dlc-skill/practices/bdd.md +0 -91
- package/ai-dlc-skill/practices/sdd.md +0 -84
- package/ai-dlc-skill/practices/tdd.md +0 -95
- package/ai-dlc-skill/providers/README.md +0 -114
- package/ai-dlc-skill/providers/aliyun/deployment.yaml +0 -66
- package/ai-dlc-skill/providers/aliyun/preview.yaml +0 -84
- package/ai-dlc-skill/providers/aliyun/provider.yaml +0 -160
- package/ai-dlc-skill/providers/tcb/deployment.yaml +0 -69
- package/ai-dlc-skill/providers/tcb/preview.yaml +0 -83
- package/ai-dlc-skill/providers/tcb/provider.yaml +0 -172
- package/ai-dlc-skill/requirements.md +0 -140
- package/ai-dlc-skill/skill.yaml +0 -147
- package/ai-dlc-skill/templates/integration/CHANGELOG.md +0 -41
- package/ai-dlc-skill/templates/integration/asyncapi.yaml +0 -43
- package/ai-dlc-skill/templates/integration/contract-diff.md +0 -63
- package/ai-dlc-skill/templates/integration/contract-spec.md +0 -49
- package/ai-dlc-skill/templates/integration/openapi.yaml +0 -18
- package/ai-dlc-skill/templates/integration/runtime-contract.yaml +0 -36
- package/ai-dlc-skill/templates/plan/design.md +0 -49
- package/ai-dlc-skill/templates/plan/task-list.md +0 -32
- package/ai-dlc-skill/templates/project/README.md +0 -126
- package/ai-dlc-skill/templates/project/template.md +0 -119
- package/ai-dlc-skill/templates/understand/feature.feature +0 -23
- package/ai-dlc-skill/templates/understand/intent.md +0 -29
- package/ai-dlc-skill/templates/understand/spec-delta.md +0 -47
- package/ai-dlc-skill/templates/verify/test-unit.py +0 -27
- package/ai-dlc-skill/walkthrough/collect.sh +0 -50
- package/ai-dlc-skill/walkthrough/template.md +0 -54
- package/ai-dlc-skill/workflows/ai-dlc.yaml +0 -110
- package/ai-dlc-skill/workflows/deployment.yaml +0 -199
- package/cdh/__init__.py +0 -0
- package/cdh/__main__.py +0 -4
- package/cdh/__pycache__/__init__.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_mcp_injector.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_mcp_loader.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_mcp_manager.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_session_aggregator.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_skill_loader.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_skill_manager.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cli.cpython-313.pyc +0 -0
- package/cdh/__pycache__/cli.cpython-314.pyc +0 -0
- package/cdh/__pycache__/scaffold.cpython-314.pyc +0 -0
- package/cdh/cdh_mcp_injector.py +0 -115
- package/cdh/cdh_mcp_loader.py +0 -67
- package/cdh/cdh_mcp_manager.py +0 -73
- package/cdh/cdh_session_aggregator.py +0 -258
- package/cdh/cdh_skill_manager.py +0 -105
- package/cdh/cli.py +0 -807
- package/cdh/scaffold.py +0 -728
- package/onecode/__init__.py +0 -1
- package/onecode/__main__.py +0 -4
- package/onecode/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/__pycache__/cli.cpython-314.pyc +0 -0
- package/onecode/__pycache__/commands.cpython-314.pyc +0 -0
- package/onecode/__pycache__/config.cpython-314.pyc +0 -0
- package/onecode/__pycache__/config_screen.cpython-314.pyc +0 -0
- package/onecode/__pycache__/migrate.cpython-314.pyc +0 -0
- package/onecode/agent/__init__.py +0 -0
- package/onecode/agent/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/cdh_loader.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/context.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/engine.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/hooks.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/onecode_agent_acp.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/permissions.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/permissions_store.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/project_doc.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/session.cpython-314.pyc +0 -0
- package/onecode/agent/agents/__init__.py +0 -29
- package/onecode/agent/agents/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/agent/agents/__pycache__/types.cpython-314.pyc +0 -0
- package/onecode/agent/agents/types.py +0 -589
- package/onecode/agent/attachments.py +0 -112
- package/onecode/agent/cdh_loader.py +0 -267
- package/onecode/agent/context.py +0 -343
- package/onecode/agent/engine.py +0 -2700
- package/onecode/agent/hooks.py +0 -86
- package/onecode/agent/onecode_agent_acp.py +0 -2743
- package/onecode/agent/onecode_agent_acp.py.bak +0 -2447
- package/onecode/agent/permissions.py +0 -150
- package/onecode/agent/permissions_store.py +0 -62
- package/onecode/agent/project_doc.py +0 -19
- package/onecode/agent/session.py +0 -195
- package/onecode/agent/snapshot.py +0 -144
- package/onecode/agent/tools/__init__.py +0 -17
- package/onecode/agent/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/agent_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/apply_patch_tool.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/bash_tool.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/communication_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/config_tool.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/cron_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/errors.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/file_ops.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/file_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/git_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/lsp_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/mcp_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/permission_handler.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/permissions.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/protocol.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/registry.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/sandbox.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/schema_validation.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/skill_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/todo_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/web_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/agent_tools.py +0 -132
- package/onecode/agent/tools/apply_patch_tool.py +0 -138
- package/onecode/agent/tools/bash_tool.py +0 -111
- package/onecode/agent/tools/communication_tools.py +0 -148
- package/onecode/agent/tools/config_tool.py +0 -130
- package/onecode/agent/tools/cron_tools.py +0 -178
- package/onecode/agent/tools/errors.py +0 -17
- package/onecode/agent/tools/file_ops.py +0 -221
- package/onecode/agent/tools/file_tools.py +0 -225
- package/onecode/agent/tools/git_tools.py +0 -88
- package/onecode/agent/tools/lsp_tools.py +0 -404
- package/onecode/agent/tools/mcp_tools.py +0 -127
- package/onecode/agent/tools/permission_handler.py +0 -46
- package/onecode/agent/tools/permissions.py +0 -77
- package/onecode/agent/tools/protocol.py +0 -20
- package/onecode/agent/tools/registry.py +0 -201
- package/onecode/agent/tools/sandbox.py +0 -358
- package/onecode/agent/tools/schema_validation.py +0 -100
- package/onecode/agent/tools/skill_tools.py +0 -52
- package/onecode/agent/tools/todo_tools.py +0 -207
- package/onecode/agent/tools/web_tools.py +0 -169
- package/onecode/builtin_skills/agent-browser/SKILL.md +0 -212
- package/onecode/builtin_skills/agent-browser/skill.yaml +0 -8
- package/onecode/builtin_skills/git/SKILL.md +0 -64
- package/onecode/builtin_skills/git/skill.yaml +0 -7
- package/onecode/builtin_skills/shell/SKILL.md +0 -58
- package/onecode/builtin_skills/shell/skill.yaml +0 -8
- package/onecode/builtin_skills/skill-creator/SKILL.md +0 -106
- package/onecode/builtin_skills/skill-creator/skill.yaml +0 -8
- package/onecode/cli.py +0 -935
- package/onecode/cloud/__init__.py +0 -0
- package/onecode/cloud/base.py +0 -20
- package/onecode/codebase/__init__.py +0 -69
- package/onecode/codebase/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/codebase/__pycache__/chunker.cpython-314.pyc +0 -0
- package/onecode/codebase/__pycache__/indexer.cpython-314.pyc +0 -0
- package/onecode/codebase/__pycache__/retriever.cpython-314.pyc +0 -0
- package/onecode/codebase/__pycache__/storage.cpython-314.pyc +0 -0
- package/onecode/codebase/__pycache__/tools.cpython-314.pyc +0 -0
- package/onecode/codebase/chunker.py +0 -125
- package/onecode/codebase/indexer.py +0 -149
- package/onecode/codebase/retriever.py +0 -203
- package/onecode/codebase/storage.py +0 -131
- package/onecode/codebase/tools.py +0 -59
- package/onecode/commands.py +0 -596
- package/onecode/config.py +0 -314
- package/onecode/config_screen.py +0 -600
- package/onecode/cron/__init__.py +0 -3
- package/onecode/lsp/__init__.py +0 -3
- package/onecode/mcp/__init__.py +0 -4
- package/onecode/mcp/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/mcp/__pycache__/client.cpython-314.pyc +0 -0
- package/onecode/mcp/__pycache__/manager.cpython-314.pyc +0 -0
- package/onecode/mcp/client.py +0 -193
- package/onecode/mcp/manager.py +0 -190
- package/onecode/memory/__init__.py +0 -49
- package/onecode/memory/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/backend.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/offload.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/pyramid.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/recall.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/symbolic.cpython-314.pyc +0 -0
- package/onecode/memory/backend.py +0 -159
- package/onecode/memory/pyramid.py +0 -133
- package/onecode/memory/recall.py +0 -203
- package/onecode/migrate.py +0 -122
- package/onecode/models/__init__.py +0 -4
- package/onecode/models/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/models/__pycache__/errors.cpython-314.pyc +0 -0
- package/onecode/models/__pycache__/messages.cpython-314.pyc +0 -0
- package/onecode/models/__pycache__/provider.cpython-314.pyc +0 -0
- package/onecode/models/__pycache__/registry.cpython-314.pyc +0 -0
- package/onecode/models/errors.py +0 -104
- package/onecode/models/messages.py +0 -739
- package/onecode/models/provider.py +0 -699
- package/onecode/models/providers/__init__.py +0 -17
- package/onecode/models/providers/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/anthropic_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/deepseek_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/glm_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/minimax_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/minimaxi_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/ollama_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/openai_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/anthropic_provider.py +0 -273
- package/onecode/models/providers/deepseek_provider.py +0 -212
- package/onecode/models/providers/glm_provider.py +0 -195
- package/onecode/models/providers/minimax_provider.py +0 -158
- package/onecode/models/providers/minimaxi_provider.py +0 -417
- package/onecode/models/providers/ollama_provider.py +0 -100
- package/onecode/models/providers/openai_provider.py +0 -237
- package/onecode/models/registry.py +0 -212
- package/onecode/server/__init__.py +0 -3
- package/onecode/server/app.py +0 -224
- package/onecode/skills/__init__.py +0 -11
- package/onecode/skills/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/argument_substitution.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/bootstrap.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/create.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/frontmatter.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/loader.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/manager.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/model.cpython-314.pyc +0 -0
- package/onecode/skills/argument_substitution.py +0 -38
- package/onecode/skills/bootstrap.py +0 -124
- package/onecode/skills/create.py +0 -56
- package/onecode/skills/frontmatter.py +0 -33
- package/onecode/skills/loader.py +0 -139
- package/onecode/skills/manager.py +0 -75
- package/onecode/skills/model.py +0 -43
- package/onecode/storage/__init__.py +0 -0
- package/onecode/storage/activity.py +0 -63
- package/onecode/storage/project.py +0 -54
- package/onecode/storage/session.py +0 -112
- package/onecode/tasks/__init__.py +0 -26
- package/onecode/tasks/manager.py +0 -232
- package/onecode/tasks/models.py +0 -240
- package/onecode/trace/__init__.py +0 -0
- package/onecode/trace/tracer.py +0 -173
- package/onecode/utils/__init__.py +0 -0
- package/onecode/utils/helpers.py +0 -20
- package/pyproject.toml +0 -74
- package/tui/__init__.py +0 -46
- package/tui/__main__.py +0 -4
- package/tui/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tui/__pycache__/_loop.cpython-314.pyc +0 -0
- package/tui/__pycache__/_path_fuzzy_search.cpython-314.pyc +0 -0
- package/tui/__pycache__/_path_match.cpython-314.pyc +0 -0
- package/tui/__pycache__/about.cpython-314.pyc +0 -0
- package/tui/__pycache__/agent.cpython-314.pyc +0 -0
- package/tui/__pycache__/agent_schema.cpython-314.pyc +0 -0
- package/tui/__pycache__/agents.cpython-314.pyc +0 -0
- package/tui/__pycache__/answer.cpython-314.pyc +0 -0
- package/tui/__pycache__/app.cpython-313.pyc +0 -0
- package/tui/__pycache__/app.cpython-314.pyc +0 -0
- package/tui/__pycache__/atomic.cpython-314.pyc +0 -0
- package/tui/__pycache__/complete.cpython-314.pyc +0 -0
- package/tui/__pycache__/constants.cpython-314.pyc +0 -0
- package/tui/__pycache__/conversation_markdown.cpython-314.pyc +0 -0
- package/tui/__pycache__/danger.cpython-314.pyc +0 -0
- package/tui/__pycache__/db.cpython-314.pyc +0 -0
- package/tui/__pycache__/dec.cpython-314.pyc +0 -0
- package/tui/__pycache__/directory.cpython-314.pyc +0 -0
- package/tui/__pycache__/directory_suggester.cpython-314.pyc +0 -0
- package/tui/__pycache__/directory_watcher.cpython-314.pyc +0 -0
- package/tui/__pycache__/format_path.cpython-314.pyc +0 -0
- package/tui/__pycache__/fuzzy.cpython-314.pyc +0 -0
- package/tui/__pycache__/fuzzy_index.cpython-314.pyc +0 -0
- package/tui/__pycache__/history.cpython-314.pyc +0 -0
- package/tui/__pycache__/jsonrpc.cpython-314.pyc +0 -0
- package/tui/__pycache__/menus.cpython-314.pyc +0 -0
- package/tui/__pycache__/message_log.cpython-314.pyc +0 -0
- package/tui/__pycache__/messages.cpython-314.pyc +0 -0
- package/tui/__pycache__/path_complete.cpython-314.pyc +0 -0
- package/tui/__pycache__/path_filter.cpython-314.pyc +0 -0
- package/tui/__pycache__/paths.cpython-314.pyc +0 -0
- package/tui/__pycache__/pill.cpython-314.pyc +0 -0
- package/tui/__pycache__/platform_commands.cpython-314.pyc +0 -0
- package/tui/__pycache__/protocol.cpython-314.pyc +0 -0
- package/tui/__pycache__/session_tracker.cpython-314.pyc +0 -0
- package/tui/__pycache__/settings.cpython-314.pyc +0 -0
- package/tui/__pycache__/settings_schema.cpython-314.pyc +0 -0
- package/tui/__pycache__/shell.cpython-314.pyc +0 -0
- package/tui/__pycache__/shell_read.cpython-314.pyc +0 -0
- package/tui/__pycache__/slash_command.cpython-314.pyc +0 -0
- package/tui/__pycache__/version.cpython-314.pyc +0 -0
- package/tui/_loop.py +0 -86
- package/tui/_path_fuzzy_search.py +0 -48
- package/tui/_path_match.py +0 -141
- package/tui/about.py +0 -95
- package/tui/acp/__pycache__/agent.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/api.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/encode_tool_call_id.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/messages.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/prompt.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/protocol.cpython-314.pyc +0 -0
- package/tui/acp/agent.py +0 -1002
- package/tui/acp/api.py +0 -73
- package/tui/acp/encode_tool_call_id.py +0 -12
- package/tui/acp/messages.py +0 -203
- package/tui/acp/prompt.py +0 -53
- package/tui/acp/protocol.py +0 -499
- package/tui/agent.py +0 -70
- package/tui/agent_schema.py +0 -76
- package/tui/agents.py +0 -46
- package/tui/ansi/__init__.py +0 -1
- package/tui/ansi/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tui/ansi/__pycache__/_ansi.cpython-314.pyc +0 -0
- package/tui/ansi/__pycache__/_ansi_colors.cpython-314.pyc +0 -0
- package/tui/ansi/__pycache__/_control_codes.cpython-314.pyc +0 -0
- package/tui/ansi/__pycache__/_keys.cpython-314.pyc +0 -0
- package/tui/ansi/__pycache__/_sgr_styles.cpython-314.pyc +0 -0
- package/tui/ansi/__pycache__/_stream_parser.cpython-314.pyc +0 -0
- package/tui/ansi/_ansi.py +0 -1689
- package/tui/ansi/_ansi_colors.py +0 -264
- package/tui/ansi/_control_codes.py +0 -37
- package/tui/ansi/_keys.py +0 -251
- package/tui/ansi/_sgr_styles.py +0 -64
- package/tui/ansi/_stream_parser.py +0 -418
- package/tui/answer.py +0 -12
- package/tui/app.py +0 -1266
- package/tui/atomic.py +0 -37
- package/tui/cli.py +0 -313
- package/tui/code_analyze.py +0 -27
- package/tui/complete.py +0 -38
- package/tui/constants.py +0 -61
- package/tui/conversation_markdown.py +0 -30
- package/tui/danger.py +0 -372
- package/tui/data/agents/ampcode.com.toml +0 -52
- package/tui/data/agents/augmentcode.com.toml +0 -41
- package/tui/data/agents/cdh.cloud-dev-harness.toml +0 -23
- package/tui/data/agents/claude.com.toml +0 -41
- package/tui/data/agents/copilot.github.com.toml +0 -29
- package/tui/data/agents/crowai.dev.toml +0 -36
- package/tui/data/agents/cursor.com.toml +0 -38
- package/tui/data/agents/deepagents.langchain.com +0 -34
- package/tui/data/agents/docker.com.toml +0 -60
- package/tui/data/agents/geminicli.com.toml +0 -28
- package/tui/data/agents/goose.ai.toml +0 -52
- package/tui/data/agents/inference.huggingface.co.toml +0 -41
- package/tui/data/agents/kimi.com.toml +0 -35
- package/tui/data/agents/kiro.dev.toml +0 -43
- package/tui/data/agents/onecode.dev.toml +0 -22
- package/tui/data/agents/openai.com.toml +0 -53
- package/tui/data/agents/openclaw.ai.toml +0 -37
- package/tui/data/agents/opencode.ai.toml +0 -39
- package/tui/data/agents/openhands.dev.toml +0 -46
- package/tui/data/agents/stakpak.dev.toml +0 -62
- package/tui/data/agents/vibe.mistral.ai.toml +0 -31
- package/tui/data/agents/vtcode.dev.toml +0 -63
- package/tui/data/images/frog.png +0 -0
- package/tui/data/sounds/question.wav +0 -0
- package/tui/data/sounds/turn-over.wav +0 -0
- package/tui/db.py +0 -213
- package/tui/dec.py +0 -332
- package/tui/directory.py +0 -234
- package/tui/directory_suggester.py +0 -93
- package/tui/directory_watcher.py +0 -231
- package/tui/format_path.py +0 -25
- package/tui/fuzzy.py +0 -140
- package/tui/fuzzy_index.py +0 -275
- package/tui/gist.py +0 -33
- package/tui/history.py +0 -138
- package/tui/jsonrpc.py +0 -578
- package/tui/menus.py +0 -14
- package/tui/message_log.py +0 -69
- package/tui/messages.py +0 -144
- package/tui/option_content.py +0 -51
- package/tui/os.py +0 -0
- package/tui/path_complete.py +0 -217
- package/tui/path_filter.py +0 -124
- package/tui/paths.py +0 -87
- package/tui/pill.py +0 -23
- package/tui/platform_commands.py +0 -52
- package/tui/prompt/__pycache__/extract.cpython-314.pyc +0 -0
- package/tui/prompt/__pycache__/resource.cpython-314.pyc +0 -0
- package/tui/prompt/extract.py +0 -19
- package/tui/prompt/resource.py +0 -68
- package/tui/protocol.py +0 -28
- package/tui/screens/__init__.py +0 -0
- package/tui/screens/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/agent_modal.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/component_picker.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/diff_screen.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/log.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/main.cpython-313.pyc +0 -0
- package/tui/screens/__pycache__/main.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/permissions.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/projects_app.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/projects_screen.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/sessions.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/store.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/tool_content_screen.cpython-314.pyc +0 -0
- package/tui/screens/action_modal.py +0 -94
- package/tui/screens/agent_modal.py +0 -175
- package/tui/screens/command_edit_modal.py +0 -74
- package/tui/screens/component_picker.py +0 -168
- package/tui/screens/diff_screen.py +0 -84
- package/tui/screens/log.py +0 -553
- package/tui/screens/main.py +0 -385
- package/tui/screens/main.tcss +0 -655
- package/tui/screens/permissions.py +0 -404
- package/tui/screens/permissions.tcss +0 -72
- package/tui/screens/projects.tcss +0 -80
- package/tui/screens/projects_app.py +0 -347
- package/tui/screens/projects_screen.py +0 -284
- package/tui/screens/session_resume_modal.py +0 -172
- package/tui/screens/session_resume_modal.tcss +0 -35
- package/tui/screens/sessions.py +0 -164
- package/tui/screens/sessions.tcss +0 -119
- package/tui/screens/settings.py +0 -254
- package/tui/screens/settings.tcss +0 -101
- package/tui/screens/store.py +0 -500
- package/tui/screens/store.tcss +0 -274
- package/tui/screens/tool_content_screen.py +0 -59
- package/tui/session_tracker.py +0 -123
- package/tui/settings.py +0 -353
- package/tui/settings_schema.py +0 -431
- package/tui/shell.py +0 -304
- package/tui/shell_read.py +0 -42
- package/tui/slash_command.py +0 -34
- package/tui/tui.tcss +0 -246
- package/tui/twitter.py +0 -38
- package/tui/version.py +0 -80
- package/tui/visuals/__init__.py +0 -0
- package/tui/visuals/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tui/visuals/__pycache__/columns.cpython-314.pyc +0 -0
- package/tui/visuals/columns.py +0 -272
- package/tui/widgets/__init__.py +0 -0
- package/tui/widgets/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/acp_content.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/agent_response.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/agent_thought.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/ask_user.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/condensed_path.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/context_stats.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/conversation.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/diff_view.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/directory_input.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/flash.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/future_text.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/grid_select.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/highlighted_textarea.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/load_more.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/mandelbrot.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/markdown_note.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/menu.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/modified_files.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/non_selectable_label.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/note.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/path_search.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/plan.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/project_directory_tree.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/project_grid_select.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/project_summary.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/prompt.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/question.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/session_grid_select.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/session_summary.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/session_tabs.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/shell_result.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/shell_terminal.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/side_bar.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/slash_complete.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/strike_text.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/subagent.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/subagent_screen.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/terminal.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/throbber.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/tool_call.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/user_input.cpython-314.pyc +0 -0
- package/tui/widgets/acp_content.py +0 -70
- package/tui/widgets/agent_response.py +0 -76
- package/tui/widgets/agent_thought.py +0 -167
- package/tui/widgets/ask_user.py +0 -264
- package/tui/widgets/command_pane.py +0 -223
- package/tui/widgets/condensed_path.py +0 -115
- package/tui/widgets/context_stats.py +0 -66
- package/tui/widgets/conversation.py +0 -2641
- package/tui/widgets/danger_warning.py +0 -65
- package/tui/widgets/diff_view.py +0 -69
- package/tui/widgets/directory_input.py +0 -8
- package/tui/widgets/flash.py +0 -81
- package/tui/widgets/future_text.py +0 -126
- package/tui/widgets/grid_select.py +0 -234
- package/tui/widgets/highlighted_textarea.py +0 -180
- package/tui/widgets/load_more.py +0 -24
- package/tui/widgets/mandelbrot.py +0 -294
- package/tui/widgets/markdown_note.py +0 -13
- package/tui/widgets/menu.py +0 -147
- package/tui/widgets/modified_files.py +0 -306
- package/tui/widgets/non_selectable_label.py +0 -5
- package/tui/widgets/note.py +0 -18
- package/tui/widgets/path_search.py +0 -492
- package/tui/widgets/plan.py +0 -285
- package/tui/widgets/project_directory_tree.py +0 -102
- package/tui/widgets/project_grid_select.py +0 -64
- package/tui/widgets/project_summary.py +0 -33
- package/tui/widgets/prompt.py +0 -795
- package/tui/widgets/question.py +0 -360
- package/tui/widgets/session_grid_select.py +0 -230
- package/tui/widgets/session_summary.py +0 -115
- package/tui/widgets/session_tabs.py +0 -188
- package/tui/widgets/shell_result.py +0 -35
- package/tui/widgets/shell_terminal.py +0 -40
- package/tui/widgets/side_bar.py +0 -124
- package/tui/widgets/slash_complete.py +0 -235
- package/tui/widgets/strike_text.py +0 -70
- package/tui/widgets/subagent.py +0 -287
- package/tui/widgets/subagent_screen.py +0 -709
- package/tui/widgets/terminal.py +0 -576
- package/tui/widgets/terminal_tool.py +0 -360
- package/tui/widgets/throbber.py +0 -100
- package/tui/widgets/tool_call.py +0 -398
- package/tui/widgets/user_input.py +0 -23
- package/tui/widgets/version.py +0 -5
- package/tui/widgets/welcome.py +0 -31
|
@@ -1,699 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import asyncio
|
|
6
|
-
import logging
|
|
7
|
-
from abc import ABC, abstractmethod
|
|
8
|
-
from dataclasses import dataclass, field
|
|
9
|
-
from enum import Enum
|
|
10
|
-
from typing import Any, AsyncIterator, Callable, Optional
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@dataclass(frozen=True)
|
|
14
|
-
class ChatResponse:
|
|
15
|
-
"""Structured response from a streaming provider call (Clawd-Code style).
|
|
16
|
-
|
|
17
|
-
Unlike ModelResponse (which uses ContentBlock list), ChatResponse provides
|
|
18
|
-
a flat structure for the agent loop: content text + structured tool_uses + usage.
|
|
19
|
-
"""
|
|
20
|
-
content: str = ""
|
|
21
|
-
tool_uses: list[dict[str, Any]] = field(default_factory=list)
|
|
22
|
-
usage: dict[str, int] = field(default_factory=dict)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class ContentBlockType(str, Enum):
|
|
26
|
-
TEXT = "text"
|
|
27
|
-
IMAGE = "image"
|
|
28
|
-
THINKING = "thinking"
|
|
29
|
-
TOOL_USE = "tool_use"
|
|
30
|
-
TOOL_RESULT = "tool_result"
|
|
31
|
-
SERVER_TOOL_USE = "server_tool_use"
|
|
32
|
-
TOOL_SEARCH_RESULT = "tool_search_tool_result"
|
|
33
|
-
CODE_EXECUTION_RESULT = "code_execution_tool_result"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@dataclass
|
|
37
|
-
class ToolUse:
|
|
38
|
-
id: str
|
|
39
|
-
name: str
|
|
40
|
-
input: dict[str, Any]
|
|
41
|
-
caller: str = "agent"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@dataclass
|
|
45
|
-
class ServerToolUse:
|
|
46
|
-
id: str
|
|
47
|
-
name: str
|
|
48
|
-
input: dict[str, Any]
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
@dataclass
|
|
52
|
-
class ToolSearchResult:
|
|
53
|
-
tool_use_id: str
|
|
54
|
-
content: Any # Can be dict with search results
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@dataclass
|
|
58
|
-
class CodeExecutionResult:
|
|
59
|
-
tool_use_id: str
|
|
60
|
-
content: Any # Can be dict with execution output
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@dataclass
|
|
64
|
-
class ImageContent:
|
|
65
|
-
data: str # base64-encoded image data
|
|
66
|
-
mime_type: str = "image/png"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@dataclass
|
|
70
|
-
class ToolResult:
|
|
71
|
-
tool_use_id: str
|
|
72
|
-
content: str
|
|
73
|
-
is_error: bool = False
|
|
74
|
-
content_blocks: list["ContentBlock"] = field(default_factory=list)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
@dataclass
|
|
78
|
-
class ContentBlock:
|
|
79
|
-
type: ContentBlockType
|
|
80
|
-
text: Optional[str] = None
|
|
81
|
-
thinking: Optional[str] = None
|
|
82
|
-
image: Optional[ImageContent] = None
|
|
83
|
-
tool_use: Optional[ToolUse] = None
|
|
84
|
-
tool_result: Optional[ToolResult] = None
|
|
85
|
-
server_tool_use: Optional[ServerToolUse] = None
|
|
86
|
-
tool_search_result: Optional[ToolSearchResult] = None
|
|
87
|
-
code_execution_result: Optional[CodeExecutionResult] = None
|
|
88
|
-
cache_control: Optional[str] = None
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@dataclass
|
|
92
|
-
class Message:
|
|
93
|
-
role: str # user | assistant | system | tool
|
|
94
|
-
content: list[ContentBlock] = field(default_factory=list)
|
|
95
|
-
name: Optional[str] = None
|
|
96
|
-
|
|
97
|
-
def __init__(self, role: str, content: str | list[ContentBlock] = "", name: Optional[str] = None):
|
|
98
|
-
self.role = role
|
|
99
|
-
self.name = name
|
|
100
|
-
if isinstance(content, str):
|
|
101
|
-
if role == "tool" and name:
|
|
102
|
-
self.content = [ContentBlock(
|
|
103
|
-
type=ContentBlockType.TOOL_RESULT,
|
|
104
|
-
tool_result=ToolResult(tool_use_id=name, content=content),
|
|
105
|
-
)]
|
|
106
|
-
else:
|
|
107
|
-
self.content = [ContentBlock(type=ContentBlockType.TEXT, text=content)] if content else []
|
|
108
|
-
else:
|
|
109
|
-
self.content = content
|
|
110
|
-
|
|
111
|
-
def add_text(self, text: str) -> None:
|
|
112
|
-
self.content.append(ContentBlock(type=ContentBlockType.TEXT, text=text))
|
|
113
|
-
|
|
114
|
-
def add_image(self, data: str, mime_type: str = "image/png") -> None:
|
|
115
|
-
self.content.append(ContentBlock(
|
|
116
|
-
type=ContentBlockType.IMAGE,
|
|
117
|
-
image=ImageContent(data=data, mime_type=mime_type),
|
|
118
|
-
))
|
|
119
|
-
|
|
120
|
-
def add_thinking(self, thinking: str) -> None:
|
|
121
|
-
self.content.append(ContentBlock(type=ContentBlockType.THINKING, thinking=thinking))
|
|
122
|
-
|
|
123
|
-
def add_tool_use(self, tool_use: ToolUse) -> None:
|
|
124
|
-
self.content.append(ContentBlock(type=ContentBlockType.TOOL_USE, tool_use=tool_use))
|
|
125
|
-
|
|
126
|
-
def add_tool_result(self, tool_result: ToolResult) -> None:
|
|
127
|
-
self.content.append(ContentBlock(type=ContentBlockType.TOOL_RESULT, tool_result=tool_result))
|
|
128
|
-
|
|
129
|
-
def to_api_content(self) -> str:
|
|
130
|
-
"""Serialize text/thinking content for API calls.
|
|
131
|
-
|
|
132
|
-
For image blocks, includes a placeholder describing the image
|
|
133
|
-
so non-vision providers still get context about attached images.
|
|
134
|
-
"""
|
|
135
|
-
parts = []
|
|
136
|
-
for cb in self.content:
|
|
137
|
-
if cb.type == ContentBlockType.TEXT and cb.text:
|
|
138
|
-
parts.append(cb.text)
|
|
139
|
-
elif cb.type == ContentBlockType.THINKING and cb.thinking:
|
|
140
|
-
parts.append(cb.thinking)
|
|
141
|
-
elif cb.type == ContentBlockType.IMAGE and cb.image:
|
|
142
|
-
parts.append(f"[Image: {cb.image.mime_type}]")
|
|
143
|
-
return "\n".join(parts)
|
|
144
|
-
|
|
145
|
-
def to_api_dict(self) -> dict:
|
|
146
|
-
"""Serialize to OpenAI-compatible message dict (text-only).
|
|
147
|
-
|
|
148
|
-
For vision-capable providers, use to_multimodal_dict() to include
|
|
149
|
-
image content as content parts.
|
|
150
|
-
"""
|
|
151
|
-
text = self.to_api_content()
|
|
152
|
-
msg: dict = {"role": self.role}
|
|
153
|
-
msg["content"] = text if text else None
|
|
154
|
-
|
|
155
|
-
tool_calls = []
|
|
156
|
-
for cb in self.content:
|
|
157
|
-
if cb.type == ContentBlockType.TOOL_USE and cb.tool_use:
|
|
158
|
-
tc = {
|
|
159
|
-
"id": cb.tool_use.id,
|
|
160
|
-
"type": "function",
|
|
161
|
-
"function": {
|
|
162
|
-
"name": cb.tool_use.name,
|
|
163
|
-
"arguments": cb.tool_use.input if isinstance(cb.tool_use.input, str) else __import__("json").dumps(cb.tool_use.input),
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
tool_calls.append(tc)
|
|
167
|
-
if cb.type == ContentBlockType.TOOL_RESULT and cb.tool_result:
|
|
168
|
-
msg["role"] = "tool"
|
|
169
|
-
msg["tool_call_id"] = cb.tool_result.tool_use_id
|
|
170
|
-
msg["content"] = cb.tool_result.content
|
|
171
|
-
|
|
172
|
-
if tool_calls:
|
|
173
|
-
msg["tool_calls"] = tool_calls
|
|
174
|
-
|
|
175
|
-
return msg
|
|
176
|
-
|
|
177
|
-
def to_multimodal_dict(self) -> dict:
|
|
178
|
-
"""Serialize to OpenAI-compatible message dict with image content.
|
|
179
|
-
|
|
180
|
-
Returns content as a list of parts (text + image_url) for vision
|
|
181
|
-
API calls. Falls back to to_api_dict() when no images are present.
|
|
182
|
-
"""
|
|
183
|
-
has_image = any(cb.type == ContentBlockType.IMAGE and cb.image for cb in self.content)
|
|
184
|
-
if not has_image:
|
|
185
|
-
return self.to_api_dict()
|
|
186
|
-
|
|
187
|
-
content_parts: list[dict] = []
|
|
188
|
-
for cb in self.content:
|
|
189
|
-
if cb.type == ContentBlockType.TEXT and cb.text:
|
|
190
|
-
content_parts.append({"type": "text", "text": cb.text})
|
|
191
|
-
elif cb.type == ContentBlockType.IMAGE and cb.image:
|
|
192
|
-
content_parts.append({
|
|
193
|
-
"type": "image_url",
|
|
194
|
-
"image_url": {
|
|
195
|
-
"url": f"data:{cb.image.mime_type};base64,{cb.image.data}",
|
|
196
|
-
},
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
msg: dict = self.to_api_dict()
|
|
200
|
-
msg["content"] = content_parts
|
|
201
|
-
return msg
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
@dataclass
|
|
205
|
-
class ModelResponse:
|
|
206
|
-
content: list[ContentBlock] = field(default_factory=list)
|
|
207
|
-
model: str = ""
|
|
208
|
-
usage: dict[str, Any] = field(default_factory=dict)
|
|
209
|
-
raw: Any = None
|
|
210
|
-
|
|
211
|
-
def __init__(self, content: str | list[ContentBlock | dict] = "", model: str = "", usage: dict[str, Any] = None, raw: Any = None):
|
|
212
|
-
self.model = model
|
|
213
|
-
self.usage = usage or {}
|
|
214
|
-
self.raw = raw
|
|
215
|
-
if isinstance(content, str):
|
|
216
|
-
self.content = [ContentBlock(type=ContentBlockType.TEXT, text=content)] if content else []
|
|
217
|
-
elif isinstance(content, list):
|
|
218
|
-
self.content = []
|
|
219
|
-
for item in content:
|
|
220
|
-
if isinstance(item, ContentBlock):
|
|
221
|
-
self.content.append(item)
|
|
222
|
-
elif isinstance(item, dict):
|
|
223
|
-
cb_type = ContentBlockType(item.get("type", "text"))
|
|
224
|
-
if cb_type == ContentBlockType.TEXT:
|
|
225
|
-
self.content.append(ContentBlock(type=cb_type, text=item.get("text", "")))
|
|
226
|
-
elif cb_type == ContentBlockType.IMAGE:
|
|
227
|
-
source = item.get("source", {})
|
|
228
|
-
self.content.append(ContentBlock(
|
|
229
|
-
type=cb_type,
|
|
230
|
-
image=ImageContent(
|
|
231
|
-
data=source.get("data", item.get("data", "")),
|
|
232
|
-
mime_type=source.get("media_type", item.get("mimeType", "image/png")),
|
|
233
|
-
),
|
|
234
|
-
))
|
|
235
|
-
elif cb_type == ContentBlockType.THINKING:
|
|
236
|
-
self.content.append(ContentBlock(type=cb_type, thinking=item.get("thinking", "")))
|
|
237
|
-
elif cb_type == ContentBlockType.TOOL_USE:
|
|
238
|
-
self.content.append(ContentBlock(
|
|
239
|
-
type=cb_type,
|
|
240
|
-
tool_use=ToolUse(
|
|
241
|
-
id=item.get("id", ""),
|
|
242
|
-
name=item.get("name", ""),
|
|
243
|
-
input=item.get("input", {}),
|
|
244
|
-
caller=item.get("caller", "agent"),
|
|
245
|
-
)
|
|
246
|
-
))
|
|
247
|
-
elif cb_type == ContentBlockType.TOOL_RESULT:
|
|
248
|
-
self.content.append(ContentBlock(
|
|
249
|
-
type=cb_type,
|
|
250
|
-
tool_result=ToolResult(
|
|
251
|
-
tool_use_id=item.get("tool_use_id", ""),
|
|
252
|
-
content=item.get("content", ""),
|
|
253
|
-
is_error=item.get("is_error", False),
|
|
254
|
-
)
|
|
255
|
-
))
|
|
256
|
-
elif cb_type == ContentBlockType.SERVER_TOOL_USE:
|
|
257
|
-
self.content.append(ContentBlock(
|
|
258
|
-
type=cb_type,
|
|
259
|
-
server_tool_use=ServerToolUse(
|
|
260
|
-
id=item.get("id", ""),
|
|
261
|
-
name=item.get("name", ""),
|
|
262
|
-
input=item.get("input", {}),
|
|
263
|
-
)
|
|
264
|
-
))
|
|
265
|
-
elif cb_type == ContentBlockType.TOOL_SEARCH_RESULT:
|
|
266
|
-
self.content.append(ContentBlock(
|
|
267
|
-
type=cb_type,
|
|
268
|
-
tool_search_result=ToolSearchResult(
|
|
269
|
-
tool_use_id=item.get("tool_use_id", ""),
|
|
270
|
-
content=item.get("content", {}),
|
|
271
|
-
)
|
|
272
|
-
))
|
|
273
|
-
elif cb_type == ContentBlockType.CODE_EXECUTION_RESULT:
|
|
274
|
-
self.content.append(ContentBlock(
|
|
275
|
-
type=cb_type,
|
|
276
|
-
code_execution_result=CodeExecutionResult(
|
|
277
|
-
tool_use_id=item.get("tool_use_id", ""),
|
|
278
|
-
content=item.get("content", {}),
|
|
279
|
-
)
|
|
280
|
-
))
|
|
281
|
-
else:
|
|
282
|
-
self.content.append(ContentBlock(type=cb_type, text=str(item)))
|
|
283
|
-
else:
|
|
284
|
-
self.content.append(ContentBlock(type=ContentBlockType.TEXT, text=str(item)))
|
|
285
|
-
else:
|
|
286
|
-
self.content = []
|
|
287
|
-
|
|
288
|
-
def get_text(self) -> str:
|
|
289
|
-
return "".join(cb.text or "" for cb in self.content if cb.type == ContentBlockType.TEXT)
|
|
290
|
-
|
|
291
|
-
def get_thinking(self) -> Optional[str]:
|
|
292
|
-
for cb in self.content:
|
|
293
|
-
if cb.type == ContentBlockType.THINKING:
|
|
294
|
-
return cb.thinking
|
|
295
|
-
return None
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
class Provider(ABC):
|
|
299
|
-
name: str = ""
|
|
300
|
-
|
|
301
|
-
_prep_logger = logging.getLogger("onecode.provider.prepare")
|
|
302
|
-
|
|
303
|
-
@abstractmethod
|
|
304
|
-
async def chat(
|
|
305
|
-
self, messages: list[Message], model: str, **kwargs
|
|
306
|
-
) -> ModelResponse:
|
|
307
|
-
...
|
|
308
|
-
|
|
309
|
-
@abstractmethod
|
|
310
|
-
async def chat_stream(
|
|
311
|
-
self, messages: list[Message], model: str, **kwargs
|
|
312
|
-
) -> AsyncIterator[str]:
|
|
313
|
-
...
|
|
314
|
-
yield ""
|
|
315
|
-
|
|
316
|
-
def resolve_api_key(self, key: Optional[str]) -> str:
|
|
317
|
-
from onecode.config import resolve_env
|
|
318
|
-
|
|
319
|
-
if key and key.startswith("${"):
|
|
320
|
-
return resolve_env(key)
|
|
321
|
-
return key or ""
|
|
322
|
-
|
|
323
|
-
def prepare_messages(self, messages: list["Message"]) -> list[dict]:
|
|
324
|
-
"""Prepare messages for API call. Handles tool, tool_use, tool_result blocks properly.
|
|
325
|
-
|
|
326
|
-
Hardening applied before serialization:
|
|
327
|
-
1. Drop ``tool`` messages with empty content and no ``tool_call_id``
|
|
328
|
-
(the OpenAI-compatible API rejects these with HTTP 400).
|
|
329
|
-
2. Coalesce runs of consecutive identical ``user`` content into one
|
|
330
|
-
message (some MiniMax gateways reject back-to-back duplicates).
|
|
331
|
-
3. Cap the combined system-prompt length at 32 KiB, keeping the
|
|
332
|
-
highest-priority blocks (``AGENT_CONFIG`` → ``REACT_PHASE`` →
|
|
333
|
-
newest skills) and dropping older skill bodies first.
|
|
334
|
-
4. Drop ``tool`` messages whose ``tool_call_id`` is not present in
|
|
335
|
-
the immediately preceding assistant message's ``tool_calls`` —
|
|
336
|
-
the MiniMax gateway returns ``(2013) tool call result does not
|
|
337
|
-
follow tool call`` otherwise.
|
|
338
|
-
5. When a tool message is dropped (empty or orphan), also strip
|
|
339
|
-
the matching ``tool_call`` entry from the *owning* assistant
|
|
340
|
-
message — i.e. the assistant that originally emitted that
|
|
341
|
-
``tool_call.id``. Without this, the request still contains
|
|
342
|
-
an ``assistant.tool_calls`` reference with no following
|
|
343
|
-
``tool`` result, which MiniMax rejects with the same (2013)
|
|
344
|
-
error (``tool call and result not match`` in the stricter
|
|
345
|
-
validator). Tracking is per-id, not per-last-assistant, so
|
|
346
|
-
drops that span multiple turns (e.g. an empty result from
|
|
347
|
-
turn 1 and a legitimate one from turn 2) are each routed
|
|
348
|
-
back to the right assistant.
|
|
349
|
-
"""
|
|
350
|
-
_SYSTEM_CAP_BYTES = 32 * 1024
|
|
351
|
-
log = Provider._prep_logger
|
|
352
|
-
dropped_empty_tools = 0
|
|
353
|
-
dropped_orphan_tools = 0
|
|
354
|
-
coalesced_user_msgs = 0
|
|
355
|
-
|
|
356
|
-
system_parts: list[tuple[str, str]] = [] # (marker, body)
|
|
357
|
-
non_system: list[dict] = []
|
|
358
|
-
|
|
359
|
-
for m in messages:
|
|
360
|
-
if m.role == "system":
|
|
361
|
-
body = m.to_api_content()
|
|
362
|
-
marker = ""
|
|
363
|
-
if "<!-- AGENT_CONFIG -->" in body:
|
|
364
|
-
marker = "AGENT_CONFIG"
|
|
365
|
-
elif "<!-- REACT_PHASE -->" in body:
|
|
366
|
-
marker = "REACT_PHASE"
|
|
367
|
-
elif "<!-- SKILL:" in body:
|
|
368
|
-
marker = "SKILL"
|
|
369
|
-
elif "<!-- CDH_PROJECT -->" in body:
|
|
370
|
-
marker = "CDH_PROJECT"
|
|
371
|
-
if body:
|
|
372
|
-
system_parts.append((marker, body))
|
|
373
|
-
elif m.role == "tool":
|
|
374
|
-
api = m.to_api_dict()
|
|
375
|
-
has_id = bool(api.get("tool_call_id"))
|
|
376
|
-
has_content = bool((api.get("content") or "").strip())
|
|
377
|
-
if not has_id or not has_content:
|
|
378
|
-
dropped_empty_tools += 1
|
|
379
|
-
log.debug(
|
|
380
|
-
"prepare_messages: dropped empty tool message (id=%r content_len=%d)",
|
|
381
|
-
api.get("tool_call_id"),
|
|
382
|
-
len(api.get("content") or ""),
|
|
383
|
-
)
|
|
384
|
-
continue
|
|
385
|
-
non_system.append(api)
|
|
386
|
-
else:
|
|
387
|
-
non_system.append(m.to_api_dict())
|
|
388
|
-
|
|
389
|
-
coalesced: list[dict] = []
|
|
390
|
-
for msg in non_system:
|
|
391
|
-
if (
|
|
392
|
-
msg.get("role") == "user"
|
|
393
|
-
and coalesced
|
|
394
|
-
and coalesced[-1].get("role") == "user"
|
|
395
|
-
and (msg.get("content") or "") == (coalesced[-1].get("content") or "")
|
|
396
|
-
and not msg.get("tool_calls")
|
|
397
|
-
and not coalesced[-1].get("tool_calls")
|
|
398
|
-
):
|
|
399
|
-
coalesced_user_msgs += 1
|
|
400
|
-
continue
|
|
401
|
-
coalesced.append(msg)
|
|
402
|
-
non_system = coalesced
|
|
403
|
-
|
|
404
|
-
# First pass: walk messages, drop orphan tool messages, and build
|
|
405
|
-
# a per-id map of ``tc_id -> owning assistant index`` so we know
|
|
406
|
-
# exactly which assistant emitted each ``tool_call`` entry.
|
|
407
|
-
linked: list[dict] = []
|
|
408
|
-
last_assistant_tc_ids: set[str] = set()
|
|
409
|
-
# Maps tool_call.id -> index in ``linked`` of the assistant that
|
|
410
|
-
# currently owns it. Rebuilt whenever a new assistant message
|
|
411
|
-
# is appended (matches the same reset semantics as
|
|
412
|
-
# ``last_assistant_tc_ids``).
|
|
413
|
-
tc_owner: dict[str, int] = {}
|
|
414
|
-
for msg in non_system:
|
|
415
|
-
if msg.get("role") == "tool":
|
|
416
|
-
tcid = msg.get("tool_call_id") or ""
|
|
417
|
-
if tcid not in last_assistant_tc_ids:
|
|
418
|
-
dropped_orphan_tools += 1
|
|
419
|
-
log.debug(
|
|
420
|
-
"prepare_messages: dropped orphan tool message "
|
|
421
|
-
"(tool_call_id=%r not in preceding assistant.tool_calls)",
|
|
422
|
-
tcid,
|
|
423
|
-
)
|
|
424
|
-
continue
|
|
425
|
-
linked.append(msg)
|
|
426
|
-
continue
|
|
427
|
-
new_index = len(linked)
|
|
428
|
-
linked.append(msg)
|
|
429
|
-
if msg.get("role") == "assistant":
|
|
430
|
-
last_assistant_tc_ids = {
|
|
431
|
-
tc.get("id") for tc in (msg.get("tool_calls") or []) if tc.get("id")
|
|
432
|
-
}
|
|
433
|
-
for tc in msg.get("tool_calls") or []:
|
|
434
|
-
tcid = tc.get("id")
|
|
435
|
-
if tcid:
|
|
436
|
-
tc_owner[tcid] = new_index
|
|
437
|
-
else:
|
|
438
|
-
last_assistant_tc_ids = set()
|
|
439
|
-
non_system = linked
|
|
440
|
-
|
|
441
|
-
# Second pass: any tool_call whose matching tool message was
|
|
442
|
-
# dropped (empty content caught above, or otherwise missing)
|
|
443
|
-
# must be removed from the *owning* assistant message — not just
|
|
444
|
-
# the last assistant. We do this in two steps:
|
|
445
|
-
# 1. Walk the full list and collect every tool_call_id that
|
|
446
|
-
# appears in any tool message — these are the "seen" ids.
|
|
447
|
-
# 2. Walk again and, for each assistant message, find any
|
|
448
|
-
# tool_call.id that is NOT in the seen set. Those are
|
|
449
|
-
# dangling entries that must be stripped.
|
|
450
|
-
seen_tool_ids: set[str] = set()
|
|
451
|
-
for msg in non_system:
|
|
452
|
-
if msg.get("role") == "tool":
|
|
453
|
-
tcid = msg.get("tool_call_id") or ""
|
|
454
|
-
if tcid:
|
|
455
|
-
seen_tool_ids.add(tcid)
|
|
456
|
-
|
|
457
|
-
dangling: dict[int, set[str]] = {}
|
|
458
|
-
for msg in non_system:
|
|
459
|
-
if msg.get("role") != "assistant":
|
|
460
|
-
continue
|
|
461
|
-
tcs = msg.get("tool_calls") or []
|
|
462
|
-
if not tcs:
|
|
463
|
-
continue
|
|
464
|
-
for tc in tcs:
|
|
465
|
-
tcid = tc.get("id") or ""
|
|
466
|
-
if tcid and tcid not in seen_tool_ids:
|
|
467
|
-
owner_idx = tc_owner.get(tcid)
|
|
468
|
-
if owner_idx is not None:
|
|
469
|
-
dangling.setdefault(owner_idx, set()).add(tcid)
|
|
470
|
-
# Also strip tool_call entries with an empty id — those can
|
|
471
|
-
# never be matched to a tool result and would be silently
|
|
472
|
-
# dangling. This mirrors the upstream filter in the first-pass
|
|
473
|
-
# tool tracking (``if tc.get("id")`` / ``if tcid``).
|
|
474
|
-
for msg in non_system:
|
|
475
|
-
if msg.get("role") != "assistant":
|
|
476
|
-
continue
|
|
477
|
-
tcs = msg.get("tool_calls") or []
|
|
478
|
-
if not tcs:
|
|
479
|
-
continue
|
|
480
|
-
kept = [tc for tc in tcs if tc.get("id")]
|
|
481
|
-
if len(kept) < len(tcs):
|
|
482
|
-
log.debug(
|
|
483
|
-
"prepare_messages: stripped %d empty-id tool_call entr%s "
|
|
484
|
-
"from assistant message",
|
|
485
|
-
len(tcs) - len(kept),
|
|
486
|
-
"y" if len(tcs) - len(kept) == 1 else "ies",
|
|
487
|
-
)
|
|
488
|
-
if kept:
|
|
489
|
-
msg["tool_calls"] = kept
|
|
490
|
-
else:
|
|
491
|
-
msg.pop("tool_calls", None)
|
|
492
|
-
|
|
493
|
-
# Apply the cleanup: strip dangling tool_call entries. Empty
|
|
494
|
-
# ``tool_calls`` lists are removed entirely.
|
|
495
|
-
if dangling:
|
|
496
|
-
total_stripped = 0
|
|
497
|
-
for idx, ids in dangling.items():
|
|
498
|
-
assistant_msg = non_system[idx]
|
|
499
|
-
tcs = assistant_msg.get("tool_calls") or []
|
|
500
|
-
kept = [tc for tc in tcs if (tc.get("id") or "") not in ids]
|
|
501
|
-
stripped = len(tcs) - len(kept)
|
|
502
|
-
if stripped:
|
|
503
|
-
total_stripped += stripped
|
|
504
|
-
log.debug(
|
|
505
|
-
"prepare_messages: stripped %d dangling tool_call entr%s "
|
|
506
|
-
"from assistant message idx=%d (ids=%r)",
|
|
507
|
-
stripped,
|
|
508
|
-
"y" if stripped == 1 else "ies",
|
|
509
|
-
idx,
|
|
510
|
-
sorted(ids),
|
|
511
|
-
)
|
|
512
|
-
if kept:
|
|
513
|
-
assistant_msg["tool_calls"] = kept
|
|
514
|
-
else:
|
|
515
|
-
assistant_msg.pop("tool_calls", None)
|
|
516
|
-
if total_stripped:
|
|
517
|
-
log.info(
|
|
518
|
-
"prepare_messages(%s): stripped %d dangling tool_call entr%s across %d assistant message(s)",
|
|
519
|
-
self.name or "?",
|
|
520
|
-
total_stripped,
|
|
521
|
-
"y" if total_stripped == 1 else "ies",
|
|
522
|
-
len(dangling),
|
|
523
|
-
)
|
|
524
|
-
|
|
525
|
-
if dropped_empty_tools or dropped_orphan_tools or coalesced_user_msgs:
|
|
526
|
-
log.info(
|
|
527
|
-
"prepare_messages(%s): dropped empty_tools=%d orphan_tools=%d "
|
|
528
|
-
"coalesced_user=%d (input=%d output=%d)",
|
|
529
|
-
self.name or "?",
|
|
530
|
-
dropped_empty_tools,
|
|
531
|
-
dropped_orphan_tools,
|
|
532
|
-
coalesced_user_msgs,
|
|
533
|
-
len(messages),
|
|
534
|
-
len(non_system) + (1 if system_parts else 0),
|
|
535
|
-
)
|
|
536
|
-
|
|
537
|
-
combined_system = ""
|
|
538
|
-
if system_parts:
|
|
539
|
-
combined_parts = [body for _, body in system_parts]
|
|
540
|
-
combined_system = "\n\n".join(combined_parts)
|
|
541
|
-
if len(combined_system.encode("utf-8")) > _SYSTEM_CAP_BYTES:
|
|
542
|
-
priority = {"AGENT_CONFIG": 0, "REACT_PHASE": 1, "CDH_PROJECT": 2, "SKILL": 3}
|
|
543
|
-
ordered = sorted(
|
|
544
|
-
range(len(system_parts)),
|
|
545
|
-
key=lambda i: (priority.get(system_parts[i][0], 9), -i),
|
|
546
|
-
)
|
|
547
|
-
kept_indices: set[int] = set()
|
|
548
|
-
running = 0
|
|
549
|
-
for idx in ordered:
|
|
550
|
-
marker, body = system_parts[idx]
|
|
551
|
-
size = len(body.encode("utf-8")) + 2
|
|
552
|
-
if marker == "AGENT_CONFIG":
|
|
553
|
-
kept_indices.add(idx)
|
|
554
|
-
running = max(running, size)
|
|
555
|
-
continue
|
|
556
|
-
if running + size <= _SYSTEM_CAP_BYTES:
|
|
557
|
-
kept_indices.add(idx)
|
|
558
|
-
running += size
|
|
559
|
-
kept = [body for i, body in enumerate(combined_parts) if i in kept_indices]
|
|
560
|
-
if not any("<!-- AGENT_CONFIG -->" in b for b in kept) and system_parts:
|
|
561
|
-
kept.insert(0, system_parts[0][1])
|
|
562
|
-
combined_system = "\n\n".join(kept).encode("utf-8")[:_SYSTEM_CAP_BYTES].decode("utf-8", errors="ignore")
|
|
563
|
-
|
|
564
|
-
if combined_system:
|
|
565
|
-
non_system.insert(0, {"role": "system", "content": combined_system})
|
|
566
|
-
|
|
567
|
-
return non_system
|
|
568
|
-
|
|
569
|
-
def parse_response_tool_calls(self, response_data: dict) -> list[dict]:
|
|
570
|
-
"""Extract tool calls from a non-streaming API response."""
|
|
571
|
-
choice = response_data.get("choices", [{}])[0]
|
|
572
|
-
msg = choice.get("message", {})
|
|
573
|
-
tool_calls = msg.get("tool_calls", [])
|
|
574
|
-
result = []
|
|
575
|
-
for tc in tool_calls:
|
|
576
|
-
func = tc.get("function", {})
|
|
577
|
-
try:
|
|
578
|
-
args = __import__("json").loads(func.get("arguments", "{}"))
|
|
579
|
-
except Exception:
|
|
580
|
-
args = {"raw": func.get("arguments", "")}
|
|
581
|
-
if not isinstance(args, dict):
|
|
582
|
-
args = {"raw": args}
|
|
583
|
-
result.append({
|
|
584
|
-
"id": tc.get("id", ""),
|
|
585
|
-
"name": func.get("name", ""),
|
|
586
|
-
"input": args,
|
|
587
|
-
})
|
|
588
|
-
return result
|
|
589
|
-
|
|
590
|
-
# ── Clawd-Code style structured streaming ──
|
|
591
|
-
|
|
592
|
-
def is_anthropic_style(self) -> bool:
|
|
593
|
-
"""Whether this provider uses Anthropic-style 'system' kwarg."""
|
|
594
|
-
return False
|
|
595
|
-
|
|
596
|
-
def supports_native_tools(self) -> bool:
|
|
597
|
-
"""Whether this provider accepts structured tools param in API calls.
|
|
598
|
-
|
|
599
|
-
When True, TOOL_DESCRIPTIONS prose in the system prompt is skipped
|
|
600
|
-
because the provider's native tool schema (passed via the ``tools``
|
|
601
|
-
kwarg to ``chat_stream_response``) provides equivalent information.
|
|
602
|
-
"""
|
|
603
|
-
return True
|
|
604
|
-
|
|
605
|
-
async def chat_stream_response(
|
|
606
|
-
self,
|
|
607
|
-
messages: list[Message],
|
|
608
|
-
model: str,
|
|
609
|
-
on_text_chunk: Optional[Callable[[str], None]] = None,
|
|
610
|
-
on_tool_call_delta: Optional[Callable[[str, str, str], None]] = None,
|
|
611
|
-
**kwargs,
|
|
612
|
-
) -> ChatResponse:
|
|
613
|
-
"""Stream with structured ChatResponse return (Clawd-Code style).
|
|
614
|
-
|
|
615
|
-
Default implementation: falls back to chat() for providers that
|
|
616
|
-
don't implement structured streaming.
|
|
617
|
-
"""
|
|
618
|
-
logging.getLogger("onecode.models.provider").warning(
|
|
619
|
-
"[PROVIDER-STREAM] %s does not override chat_stream_response — "
|
|
620
|
-
"falling back to non-streaming chat() (on_text_chunk will NOT fire)",
|
|
621
|
-
type(self).__name__,
|
|
622
|
-
)
|
|
623
|
-
response = await asyncio.wait_for(
|
|
624
|
-
self.chat(messages, model=model, **kwargs),
|
|
625
|
-
timeout=300,
|
|
626
|
-
)
|
|
627
|
-
return ChatResponse(
|
|
628
|
-
content=response.get_text(),
|
|
629
|
-
tool_uses=[
|
|
630
|
-
{"id": tu.id, "name": tu.name, "input": tu.input}
|
|
631
|
-
for cb in response.content
|
|
632
|
-
if cb.type == ContentBlockType.TOOL_USE and cb.tool_use
|
|
633
|
-
for tu in [cb.tool_use]
|
|
634
|
-
],
|
|
635
|
-
usage=response.usage,
|
|
636
|
-
)
|
|
637
|
-
|
|
638
|
-
# ── Error classification ────────────────────────────────────────
|
|
639
|
-
|
|
640
|
-
@staticmethod
|
|
641
|
-
def classify_http_error(
|
|
642
|
-
status_code: int,
|
|
643
|
-
body: str,
|
|
644
|
-
*,
|
|
645
|
-
retry_after: Optional[float] = None,
|
|
646
|
-
) -> "ProviderError": # noqa: F821
|
|
647
|
-
"""Convert an upstream HTTP error into the most specific
|
|
648
|
-
:class:`ProviderError` subclass.
|
|
649
|
-
|
|
650
|
-
The 4 KiB body cap is applied here too so callers don't have to
|
|
651
|
-
remember to truncate before constructing the exception.
|
|
652
|
-
"""
|
|
653
|
-
from onecode.models.errors import (
|
|
654
|
-
AuthError,
|
|
655
|
-
ContextLengthError,
|
|
656
|
-
ProviderError,
|
|
657
|
-
RateLimitError,
|
|
658
|
-
TransientProviderError,
|
|
659
|
-
)
|
|
660
|
-
snippet = (body or "").strip()
|
|
661
|
-
body_excerpt = snippet[:200] if snippet else ""
|
|
662
|
-
if status_code == 429:
|
|
663
|
-
return RateLimitError(
|
|
664
|
-
"rate limit exceeded", status_code=status_code,
|
|
665
|
-
retry_after=retry_after, body=snippet,
|
|
666
|
-
)
|
|
667
|
-
if status_code in (401, 403):
|
|
668
|
-
return AuthError(
|
|
669
|
-
"authentication failed", status_code=status_code, body=snippet,
|
|
670
|
-
)
|
|
671
|
-
if status_code == 400 and "context" in snippet.lower() and "length" in snippet.lower():
|
|
672
|
-
return ContextLengthError(
|
|
673
|
-
"context length exceeded", status_code=status_code, body=snippet,
|
|
674
|
-
)
|
|
675
|
-
if 500 <= status_code < 600:
|
|
676
|
-
msg = f"upstream {status_code}: {body_excerpt}" if body_excerpt else f"upstream {status_code}"
|
|
677
|
-
return TransientProviderError(
|
|
678
|
-
msg, status_code=status_code, body=snippet,
|
|
679
|
-
)
|
|
680
|
-
msg = f"upstream error {status_code}: {body_excerpt}" if body_excerpt else f"upstream error {status_code}"
|
|
681
|
-
return ProviderError(
|
|
682
|
-
msg, status_code=status_code, body=snippet,
|
|
683
|
-
)
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
class ProviderRegistry:
|
|
687
|
-
_providers: dict[str, type[Provider]] = {}
|
|
688
|
-
|
|
689
|
-
@classmethod
|
|
690
|
-
def register(cls, name: str, provider_cls: type[Provider]):
|
|
691
|
-
cls._providers[name] = provider_cls
|
|
692
|
-
|
|
693
|
-
@classmethod
|
|
694
|
-
def get(cls, name: str) -> Optional[type[Provider]]:
|
|
695
|
-
return cls._providers.get(name)
|
|
696
|
-
|
|
697
|
-
@classmethod
|
|
698
|
-
def list(cls) -> list[str]:
|
|
699
|
-
return list(cls._providers.keys())
|