klaude-code 1.2.23__tar.gz → 1.2.25__tar.gz
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.
- {klaude_code-1.2.23 → klaude_code-1.2.25}/PKG-INFO +2 -1
- {klaude_code-1.2.23 → klaude_code-1.2.25}/pyproject.toml +2 -1
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/cli/runtime.py +17 -1
- klaude_code-1.2.25/src/klaude_code/command/prompt-jj-describe.md +32 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/thinking_cmd.py +37 -28
- klaude_code-1.2.23/src/klaude_code/const/__init__.py → klaude_code-1.2.25/src/klaude_code/const.py +7 -6
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/executor.py +46 -3
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/read_tool.py +23 -1
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/write_tool.py +7 -3
- klaude_code-1.2.25/src/klaude_code/llm/openai_compatible/client.py +119 -0
- klaude_code-1.2.25/src/klaude_code/llm/openai_compatible/stream.py +272 -0
- klaude_code-1.2.25/src/klaude_code/llm/openrouter/client.py +129 -0
- klaude_code-1.2.23/src/klaude_code/llm/openrouter/reasoning_handler.py → klaude_code-1.2.25/src/klaude_code/llm/openrouter/reasoning.py +24 -2
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/model.py +13 -1
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/op.py +11 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/op_handler.py +5 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/core/stage_manager.py +0 -3
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/repl/display.py +2 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/repl/event_handler.py +97 -57
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/repl/input_prompt_toolkit.py +25 -4
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/repl/renderer.py +119 -25
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/renderers/assistant.py +1 -1
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/renderers/metadata.py +2 -6
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/renderers/sub_agent.py +28 -5
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/renderers/thinking.py +16 -10
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/renderers/tools.py +26 -2
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/rich/code_panel.py +24 -5
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/rich/live.py +17 -0
- klaude_code-1.2.25/src/klaude_code/ui/rich/markdown.py +391 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/rich/status.py +19 -17
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/rich/theme.py +63 -12
- klaude_code-1.2.23/src/klaude_code/llm/openai_compatible/client.py +0 -192
- klaude_code-1.2.23/src/klaude_code/llm/openai_compatible/stream_processor.py +0 -83
- klaude_code-1.2.23/src/klaude_code/llm/openrouter/client.py +0 -209
- klaude_code-1.2.23/src/klaude_code/ui/rich/markdown.py +0 -313
- {klaude_code-1.2.23 → klaude_code-1.2.25}/README.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/auth/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/auth/codex/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/auth/codex/exceptions.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/auth/codex/jwt_utils.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/auth/codex/oauth.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/auth/codex/token_manager.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/cli/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/cli/auth_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/cli/config_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/cli/debug.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/cli/list_model.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/cli/main.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/cli/self_update.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/cli/session_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/clear_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/command_abc.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/debug_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/export_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/export_online_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/help_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/model_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/prompt-init.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/prompt_command.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/refresh_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/registry.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/release_notes_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/status_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/command/terminal_setup_cmd.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/config/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/config/config.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/config/select_model.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/agent.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/manager/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/manager/llm_clients.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/manager/llm_clients_builder.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/manager/sub_agent_manager.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/prompt.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/prompts/prompt-codex.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/prompts/prompt-gemini.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/prompts/prompt-minimal.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/prompts/prompt-sub-agent-explore.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/prompts/prompt-sub-agent-oracle.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/prompts/prompt-sub-agent-web.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/prompts/prompt-sub-agent.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/reminders.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/task.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/_utils.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/apply_patch_tool.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/diff_builder.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/edit_tool.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/read_tool.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/file/write_tool.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/report_back_tool.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/shell/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/shell/bash_tool.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/skill/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/skill/skill_tool.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/skill/skill_tool.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/sub_agent_tool.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/todo/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/todo/todo_write_tool.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/todo/todo_write_tool_raw.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/todo/update_plan_tool.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/tool_abc.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/tool_context.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/tool_registry.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/tool_runner.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/truncation.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/web/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/web/mermaid_tool.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/web/web_fetch_tool.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/web/web_fetch_tool.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/web/web_search_tool.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/tool/web/web_search_tool.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/core/turn.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/anthropic/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/anthropic/client.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/anthropic/input.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/client.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/codex/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/codex/client.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/input_common.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/openai_compatible/input.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/openai_compatible/tool_call_accumulator.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/openrouter/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/openrouter/input.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/registry.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/responses/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/responses/client.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/responses/input.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/llm/usage.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/commands.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/events.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/llm_param.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/sub_agent/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/sub_agent/explore.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/sub_agent/oracle.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/sub_agent/task.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/sub_agent/web.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/protocol/tools.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/session/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/session/codec.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/session/export.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/session/selector.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/session/session.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/session/store.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/session/templates/export_session.html +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/skill/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/skill/assets/deslop/SKILL.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/skill/assets/dev-docs/SKILL.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/skill/assets/handoff/SKILL.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/skill/assets/jj-workspace/SKILL.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/skill/assets/skill-creator/SKILL.md +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/skill/loader.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/skill/manager.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/skill/system_skills.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/trace/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/trace/log.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/core/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/core/display.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/core/input.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/debug/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/debug/display.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/exec/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/exec/display.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/repl/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/repl/clipboard.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/repl/completers.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/modes/repl/key_bindings.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/renderers/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/renderers/common.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/renderers/developer.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/renderers/diffs.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/renderers/errors.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/renderers/user_input.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/rich/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/rich/cjk_wrap.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/rich/quote.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/rich/searchable_text.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/terminal/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/terminal/color.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/terminal/control.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/terminal/notifier.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/terminal/progress_bar.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/utils/__init__.py +0 -0
- {klaude_code-1.2.23 → klaude_code-1.2.25}/src/klaude_code/ui/utils/common.py +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: klaude-code
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.25
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Dist: anthropic>=0.66.0
|
|
6
6
|
Requires-Dist: chardet>=5.2.0
|
|
7
7
|
Requires-Dist: ddgs>=9.9.3
|
|
8
8
|
Requires-Dist: diff-match-patch>=20241021
|
|
9
|
+
Requires-Dist: markdown-it-py>=4.0.0
|
|
9
10
|
Requires-Dist: openai>=1.102.0
|
|
10
11
|
Requires-Dist: pillow>=12.0.0
|
|
11
12
|
Requires-Dist: prompt-toolkit>=3.0.52
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "klaude-code"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.25"
|
|
8
8
|
description = "Add your description here"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -13,6 +13,7 @@ dependencies = [
|
|
|
13
13
|
"chardet>=5.2.0",
|
|
14
14
|
"ddgs>=9.9.3",
|
|
15
15
|
"diff-match-patch>=20241021",
|
|
16
|
+
"markdown-it-py>=4.0.0",
|
|
16
17
|
"openai>=1.102.0",
|
|
17
18
|
"pillow>=12.0.0",
|
|
18
19
|
"prompt-toolkit>=3.0.52",
|
|
@@ -265,7 +265,23 @@ async def run_interactive(init_config: AppInitConfig, session_id: str | None = N
|
|
|
265
265
|
)
|
|
266
266
|
|
|
267
267
|
# Set up input provider for interactive mode
|
|
268
|
-
|
|
268
|
+
def _stop_rich_bottom_ui() -> None:
|
|
269
|
+
display = components.display
|
|
270
|
+
if isinstance(display, ui.REPLDisplay):
|
|
271
|
+
display.renderer.spinner_stop()
|
|
272
|
+
display.renderer.stop_bottom_live()
|
|
273
|
+
elif (
|
|
274
|
+
isinstance(display, ui.DebugEventDisplay)
|
|
275
|
+
and display.wrapped_display
|
|
276
|
+
and isinstance(display.wrapped_display, ui.REPLDisplay)
|
|
277
|
+
):
|
|
278
|
+
display.wrapped_display.renderer.spinner_stop()
|
|
279
|
+
display.wrapped_display.renderer.stop_bottom_live()
|
|
280
|
+
|
|
281
|
+
input_provider: ui.InputProviderABC = ui.PromptToolkitInput(
|
|
282
|
+
status_provider=_status_provider,
|
|
283
|
+
pre_prompt=_stop_rich_bottom_ui,
|
|
284
|
+
)
|
|
269
285
|
|
|
270
286
|
# --- Custom Ctrl+C handler: double-press within 2s to exit, single press shows toast ---
|
|
271
287
|
def _show_toast_once() -> None:
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Add description for current jj change
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Run `jj status` and `jj diff --git` to see the current changes and add a description for the it.
|
|
6
|
+
|
|
7
|
+
In order to ensure good formatting, ALWAYS pass the commit message via a HEREDOC, a la this example:<example>
|
|
8
|
+
jj describe -m "$(cat <<'EOF'
|
|
9
|
+
Commit message here.
|
|
10
|
+
EOF
|
|
11
|
+
)"
|
|
12
|
+
</example>
|
|
13
|
+
|
|
14
|
+
Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification:
|
|
15
|
+
```
|
|
16
|
+
<type>(<scope>): <description>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Types:
|
|
20
|
+
- `feat`: New feature
|
|
21
|
+
- `fix`: Bug fix
|
|
22
|
+
- `docs`: Documentation changes
|
|
23
|
+
- `style`: Code style changes (formatting, no logic change)
|
|
24
|
+
- `refactor`: Code refactoring (no feature or fix)
|
|
25
|
+
- `test`: Adding or updating tests
|
|
26
|
+
- `chore`: Build process, dependencies, or tooling changes
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
- `feat(cli): add --verbose flag for debug output`
|
|
30
|
+
- `fix(llm): handle API timeout errors gracefully`
|
|
31
|
+
- `docs(readme): update installation instructions`
|
|
32
|
+
- `refactor(core): simplify session state management`
|
|
@@ -56,6 +56,14 @@ def _is_gemini_flash_model(model_name: str | None) -> bool:
|
|
|
56
56
|
return "gemini-3-flash" in model_name.lower()
|
|
57
57
|
|
|
58
58
|
|
|
59
|
+
def should_auto_trigger_thinking(model_name: str | None) -> bool:
|
|
60
|
+
"""Check if model should auto-trigger thinking selection on switch."""
|
|
61
|
+
if not model_name:
|
|
62
|
+
return False
|
|
63
|
+
model_lower = model_name.lower()
|
|
64
|
+
return "gpt-5" in model_lower or "gemini-3" in model_lower or "opus" in model_lower
|
|
65
|
+
|
|
66
|
+
|
|
59
67
|
def _get_levels_for_responses(model_name: str | None) -> list[str]:
|
|
60
68
|
"""Get thinking levels for responses protocol."""
|
|
61
69
|
if _is_codex_max_model(model_name):
|
|
@@ -69,7 +77,7 @@ def _get_levels_for_responses(model_name: str | None) -> list[str]:
|
|
|
69
77
|
return RESPONSES_LEVELS
|
|
70
78
|
|
|
71
79
|
|
|
72
|
-
def
|
|
80
|
+
def format_current_thinking(config: llm_param.LLMConfigParameter) -> str:
|
|
73
81
|
"""Format the current thinking configuration for display."""
|
|
74
82
|
thinking = config.thinking
|
|
75
83
|
if not thinking:
|
|
@@ -164,6 +172,31 @@ def _select_anthropic_thinking_sync() -> llm_param.Thinking | None:
|
|
|
164
172
|
return None
|
|
165
173
|
|
|
166
174
|
|
|
175
|
+
async def select_thinking_for_protocol(config: llm_param.LLMConfigParameter) -> llm_param.Thinking | None:
|
|
176
|
+
"""Select thinking configuration based on the LLM protocol.
|
|
177
|
+
|
|
178
|
+
Returns the selected Thinking config, or None if user cancelled.
|
|
179
|
+
"""
|
|
180
|
+
protocol = config.protocol
|
|
181
|
+
model_name = config.model
|
|
182
|
+
|
|
183
|
+
if protocol in (llm_param.LLMClientProtocol.RESPONSES, llm_param.LLMClientProtocol.CODEX):
|
|
184
|
+
return await asyncio.to_thread(_select_responses_thinking_sync, model_name)
|
|
185
|
+
|
|
186
|
+
if protocol == llm_param.LLMClientProtocol.ANTHROPIC:
|
|
187
|
+
return await asyncio.to_thread(_select_anthropic_thinking_sync)
|
|
188
|
+
|
|
189
|
+
if protocol == llm_param.LLMClientProtocol.OPENROUTER:
|
|
190
|
+
if _is_openrouter_model_with_reasoning_effort(model_name):
|
|
191
|
+
return await asyncio.to_thread(_select_responses_thinking_sync, model_name)
|
|
192
|
+
return await asyncio.to_thread(_select_anthropic_thinking_sync)
|
|
193
|
+
|
|
194
|
+
if protocol == llm_param.LLMClientProtocol.OPENAI:
|
|
195
|
+
return await asyncio.to_thread(_select_anthropic_thinking_sync)
|
|
196
|
+
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
|
|
167
200
|
class ThinkingCommand(CommandABC):
|
|
168
201
|
"""Configure model thinking/reasoning level."""
|
|
169
202
|
|
|
@@ -185,40 +218,16 @@ class ThinkingCommand(CommandABC):
|
|
|
185
218
|
return self._no_change_result(agent, "No profile configured")
|
|
186
219
|
|
|
187
220
|
config = agent.profile.llm_client.get_llm_config()
|
|
188
|
-
|
|
189
|
-
model_name = config.model
|
|
190
|
-
|
|
191
|
-
current = _format_current_thinking(config)
|
|
192
|
-
|
|
193
|
-
# Select new thinking configuration based on protocol
|
|
194
|
-
new_thinking: llm_param.Thinking | None = None
|
|
195
|
-
|
|
196
|
-
if protocol in (llm_param.LLMClientProtocol.RESPONSES, llm_param.LLMClientProtocol.CODEX):
|
|
197
|
-
new_thinking = await asyncio.to_thread(_select_responses_thinking_sync, model_name)
|
|
198
|
-
|
|
199
|
-
elif protocol == llm_param.LLMClientProtocol.ANTHROPIC:
|
|
200
|
-
new_thinking = await asyncio.to_thread(_select_anthropic_thinking_sync)
|
|
201
|
-
|
|
202
|
-
elif protocol == llm_param.LLMClientProtocol.OPENROUTER:
|
|
203
|
-
if _is_openrouter_model_with_reasoning_effort(model_name):
|
|
204
|
-
new_thinking = await asyncio.to_thread(_select_responses_thinking_sync, model_name)
|
|
205
|
-
else:
|
|
206
|
-
new_thinking = await asyncio.to_thread(_select_anthropic_thinking_sync)
|
|
207
|
-
|
|
208
|
-
elif protocol == llm_param.LLMClientProtocol.OPENAI:
|
|
209
|
-
# openai_compatible uses anthropic style
|
|
210
|
-
new_thinking = await asyncio.to_thread(_select_anthropic_thinking_sync)
|
|
211
|
-
|
|
212
|
-
else:
|
|
213
|
-
return self._no_change_result(agent, f"Unsupported protocol: {protocol}")
|
|
221
|
+
current = format_current_thinking(config)
|
|
214
222
|
|
|
223
|
+
new_thinking = await select_thinking_for_protocol(config)
|
|
215
224
|
if new_thinking is None:
|
|
216
225
|
return self._no_change_result(agent, "(no change)")
|
|
217
226
|
|
|
218
227
|
# Apply the new thinking configuration
|
|
219
228
|
config.thinking = new_thinking
|
|
220
229
|
agent.session.model_thinking = new_thinking
|
|
221
|
-
new_status =
|
|
230
|
+
new_status = format_current_thinking(config)
|
|
222
231
|
|
|
223
232
|
return CommandResult(
|
|
224
233
|
events=[
|
klaude_code-1.2.23/src/klaude_code/const/__init__.py → klaude_code-1.2.25/src/klaude_code/const.py
RENAMED
|
@@ -93,7 +93,7 @@ TRUNCATE_DISPLAY_MAX_LINE_LENGTH = 1000
|
|
|
93
93
|
TRUNCATE_DISPLAY_MAX_LINES = 8
|
|
94
94
|
|
|
95
95
|
# Maximum lines for sub-agent result display
|
|
96
|
-
SUB_AGENT_RESULT_MAX_LINES =
|
|
96
|
+
SUB_AGENT_RESULT_MAX_LINES = 50
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
# UI refresh rate (frames per second) for debounced content streaming
|
|
@@ -111,19 +111,20 @@ MARKDOWN_RIGHT_MARGIN = 2
|
|
|
111
111
|
# Status hint text shown after spinner status
|
|
112
112
|
STATUS_HINT_TEXT = " (esc to interrupt)"
|
|
113
113
|
|
|
114
|
+
# Default spinner status text when idle/thinking
|
|
115
|
+
STATUS_DEFAULT_TEXT = "Thinking …"
|
|
116
|
+
|
|
114
117
|
# Status shimmer animation
|
|
115
118
|
# Horizontal padding used when computing shimmer band position
|
|
116
119
|
STATUS_SHIMMER_PADDING = 10
|
|
117
|
-
# Duration in seconds for one full shimmer sweep across the text
|
|
118
|
-
STATUS_SHIMMER_SWEEP_SECONDS = 2
|
|
119
120
|
# Half-width of the shimmer band in characters
|
|
120
121
|
STATUS_SHIMMER_BAND_HALF_WIDTH = 5.0
|
|
121
122
|
# Scale factor applied to shimmer intensity when blending colors
|
|
122
123
|
STATUS_SHIMMER_ALPHA_SCALE = 0.7
|
|
123
124
|
|
|
124
|
-
# Spinner breathing animation
|
|
125
|
-
# Duration in seconds for one full breathe-in + breathe-out cycle
|
|
126
|
-
#
|
|
125
|
+
# Spinner breathing and shimmer animation period
|
|
126
|
+
# Duration in seconds for one full breathe-in + breathe-out cycle (breathing)
|
|
127
|
+
# and one full shimmer sweep across the text (shimmer)
|
|
127
128
|
SPINNER_BREATH_PERIOD_SECONDS: float = 2.0
|
|
128
129
|
|
|
129
130
|
|
|
@@ -14,6 +14,11 @@ from dataclasses import dataclass
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
16
|
from klaude_code.command import dispatch_command
|
|
17
|
+
from klaude_code.command.thinking_cmd import (
|
|
18
|
+
format_current_thinking,
|
|
19
|
+
select_thinking_for_protocol,
|
|
20
|
+
should_auto_trigger_thinking,
|
|
21
|
+
)
|
|
17
22
|
from klaude_code.config import load_config
|
|
18
23
|
from klaude_code.core.agent import Agent, DefaultModelProfileProvider, ModelProfileProvider
|
|
19
24
|
from klaude_code.core.manager import LLMClients, SubAgentManager
|
|
@@ -235,17 +240,55 @@ class ExecutorContext:
|
|
|
235
240
|
agent.session.model_thinking = llm_config.thinking
|
|
236
241
|
|
|
237
242
|
developer_item = model.DeveloperMessageItem(
|
|
238
|
-
content=f"
|
|
243
|
+
content=f"Switched to: {llm_config.model}",
|
|
239
244
|
command_output=model.CommandOutput(command_name=commands.CommandName.MODEL),
|
|
240
245
|
)
|
|
241
246
|
agent.session.append_history([developer_item])
|
|
242
247
|
|
|
243
248
|
await self.emit_event(events.DeveloperMessageEvent(session_id=agent.session.id, item=developer_item))
|
|
244
|
-
await self.emit_event(events.WelcomeEvent(llm_config=llm_config, work_dir=str(agent.session.work_dir)))
|
|
245
249
|
|
|
246
250
|
if self._on_model_change is not None:
|
|
247
251
|
self._on_model_change(llm_client.model_name)
|
|
248
252
|
|
|
253
|
+
if should_auto_trigger_thinking(llm_config.model):
|
|
254
|
+
thinking_op = op.ChangeThinkingOperation(session_id=operation.session_id)
|
|
255
|
+
await thinking_op.execute(handler=self)
|
|
256
|
+
# WelcomeEvent is already handled by the thinking change
|
|
257
|
+
else:
|
|
258
|
+
await self.emit_event(events.WelcomeEvent(llm_config=llm_config, work_dir=str(agent.session.work_dir)))
|
|
259
|
+
|
|
260
|
+
async def handle_change_thinking(self, operation: op.ChangeThinkingOperation) -> None:
|
|
261
|
+
"""Handle a change thinking operation by prompting user to select thinking level."""
|
|
262
|
+
agent = await self._ensure_agent(operation.session_id)
|
|
263
|
+
if not agent.profile:
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
config = agent.profile.llm_client.get_llm_config()
|
|
267
|
+
current = format_current_thinking(config)
|
|
268
|
+
|
|
269
|
+
new_thinking = await select_thinking_for_protocol(config)
|
|
270
|
+
|
|
271
|
+
if new_thinking is None:
|
|
272
|
+
developer_item = model.DeveloperMessageItem(
|
|
273
|
+
content="(thinking unchanged)",
|
|
274
|
+
command_output=model.CommandOutput(command_name=commands.CommandName.THINKING),
|
|
275
|
+
)
|
|
276
|
+
await self.emit_event(events.DeveloperMessageEvent(session_id=agent.session.id, item=developer_item))
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
config.thinking = new_thinking
|
|
280
|
+
agent.session.model_thinking = new_thinking
|
|
281
|
+
new_status = format_current_thinking(config)
|
|
282
|
+
|
|
283
|
+
developer_item = model.DeveloperMessageItem(
|
|
284
|
+
content=f"Thinking changed: {current} -> {new_status}",
|
|
285
|
+
command_output=model.CommandOutput(command_name=commands.CommandName.THINKING),
|
|
286
|
+
)
|
|
287
|
+
agent.session.append_history([developer_item])
|
|
288
|
+
|
|
289
|
+
await self.emit_event(events.DeveloperMessageEvent(session_id=agent.session.id, item=developer_item))
|
|
290
|
+
await self.emit_event(events.WelcomeEvent(work_dir=str(agent.session.work_dir), llm_config=config))
|
|
291
|
+
|
|
249
292
|
async def handle_clear_session(self, operation: op.ClearSessionOperation) -> None:
|
|
250
293
|
agent = await self._ensure_agent(operation.session_id)
|
|
251
294
|
new_session = Session.create(work_dir=agent.session.work_dir)
|
|
@@ -327,7 +370,7 @@ class ExecutorContext:
|
|
|
327
370
|
log_debug(traceback.format_exc(), style="red", debug_type=DebugType.EXECUTION)
|
|
328
371
|
await self.emit_event(
|
|
329
372
|
events.ErrorEvent(
|
|
330
|
-
error_message=f"Agent task failed: [{e.__class__.__name__}] {e!s}",
|
|
373
|
+
error_message=f"Agent task failed: [{e.__class__.__name__}] {e!s} {traceback.format_exc()}",
|
|
331
374
|
can_retry=False,
|
|
332
375
|
)
|
|
333
376
|
)
|
|
@@ -25,6 +25,18 @@ _IMAGE_MIME_TYPES: dict[str, str] = {
|
|
|
25
25
|
".webp": "image/webp",
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
_BINARY_CHECK_SIZE = 8192
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _is_binary_file(file_path: str) -> bool:
|
|
32
|
+
"""Check if a file is binary by looking for null bytes in the first chunk."""
|
|
33
|
+
try:
|
|
34
|
+
with open(file_path, "rb") as f:
|
|
35
|
+
chunk = f.read(_BINARY_CHECK_SIZE)
|
|
36
|
+
return b"\x00" in chunk
|
|
37
|
+
except OSError:
|
|
38
|
+
return False
|
|
39
|
+
|
|
28
40
|
|
|
29
41
|
def _format_numbered_line(line_no: int, content: str) -> str:
|
|
30
42
|
# 6-width right-aligned line number followed by a right arrow
|
|
@@ -218,12 +230,22 @@ class ReadTool(ToolABC):
|
|
|
218
230
|
),
|
|
219
231
|
)
|
|
220
232
|
|
|
233
|
+
is_image_file = _is_supported_image_file(file_path)
|
|
234
|
+
# Check for binary files (skip for images which are handled separately)
|
|
235
|
+
if not is_image_file and _is_binary_file(file_path):
|
|
236
|
+
return model.ToolResultItem(
|
|
237
|
+
status="error",
|
|
238
|
+
output=(
|
|
239
|
+
"<tool_use_error>This appears to be a binary file and cannot be read as text. "
|
|
240
|
+
"Use appropriate tools or libraries to handle binary files.</tool_use_error>"
|
|
241
|
+
),
|
|
242
|
+
)
|
|
243
|
+
|
|
221
244
|
try:
|
|
222
245
|
size_bytes = Path(file_path).stat().st_size
|
|
223
246
|
except OSError:
|
|
224
247
|
size_bytes = 0
|
|
225
248
|
|
|
226
|
-
is_image_file = _is_supported_image_file(file_path)
|
|
227
249
|
if is_image_file:
|
|
228
250
|
if size_bytes > const.READ_MAX_IMAGE_BYTES:
|
|
229
251
|
size_mb = size_bytes / (1024 * 1024)
|
|
@@ -124,9 +124,13 @@ class WriteTool(ToolABC):
|
|
|
124
124
|
is_memory=is_mem,
|
|
125
125
|
)
|
|
126
126
|
|
|
127
|
-
#
|
|
128
|
-
|
|
129
|
-
ui_extra
|
|
127
|
+
# For markdown files, use MarkdownDocUIExtra to render content as markdown
|
|
128
|
+
# Otherwise, build diff between previous and new content
|
|
129
|
+
ui_extra: model.ToolResultUIExtra | None
|
|
130
|
+
if file_path.endswith(".md"):
|
|
131
|
+
ui_extra = model.MarkdownDocUIExtra(file_path=file_path, content=args.content)
|
|
132
|
+
else:
|
|
133
|
+
ui_extra = build_structured_diff(before, args.content, file_path=file_path)
|
|
130
134
|
|
|
131
135
|
message = f"File {'overwritten' if exists else 'created'} successfully at: {file_path}"
|
|
132
136
|
return model.ToolResultItem(status="success", output=message, ui_extra=ui_extra)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from collections.abc import AsyncGenerator
|
|
3
|
+
from typing import Any, override
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
import openai
|
|
7
|
+
from openai.types.chat.completion_create_params import CompletionCreateParamsStreaming
|
|
8
|
+
|
|
9
|
+
from klaude_code.llm.client import LLMClientABC
|
|
10
|
+
from klaude_code.llm.input_common import apply_config_defaults
|
|
11
|
+
from klaude_code.llm.openai_compatible.input import convert_history_to_input, convert_tool_schema
|
|
12
|
+
from klaude_code.llm.openai_compatible.stream import DefaultReasoningHandler, parse_chat_completions_stream
|
|
13
|
+
from klaude_code.llm.registry import register
|
|
14
|
+
from klaude_code.llm.usage import MetadataTracker
|
|
15
|
+
from klaude_code.protocol import llm_param, model
|
|
16
|
+
from klaude_code.trace import DebugType, log_debug
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def build_payload(param: llm_param.LLMCallParameter) -> tuple[CompletionCreateParamsStreaming, dict[str, object]]:
|
|
20
|
+
"""Build OpenAI API request parameters."""
|
|
21
|
+
messages = convert_history_to_input(param.input, param.system, param.model)
|
|
22
|
+
tools = convert_tool_schema(param.tools)
|
|
23
|
+
|
|
24
|
+
extra_body: dict[str, object] = {}
|
|
25
|
+
|
|
26
|
+
if param.thinking and param.thinking.type == "enabled":
|
|
27
|
+
extra_body["thinking"] = {
|
|
28
|
+
"type": param.thinking.type,
|
|
29
|
+
"budget": param.thinking.budget_tokens,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
payload: CompletionCreateParamsStreaming = {
|
|
33
|
+
"model": str(param.model),
|
|
34
|
+
"tool_choice": "auto",
|
|
35
|
+
"parallel_tool_calls": True,
|
|
36
|
+
"stream": True,
|
|
37
|
+
"messages": messages,
|
|
38
|
+
"temperature": param.temperature,
|
|
39
|
+
"max_tokens": param.max_tokens,
|
|
40
|
+
"tools": tools,
|
|
41
|
+
"reasoning_effort": param.thinking.reasoning_effort if param.thinking else None,
|
|
42
|
+
"verbosity": param.verbosity,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return payload, extra_body
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@register(llm_param.LLMClientProtocol.OPENAI)
|
|
49
|
+
class OpenAICompatibleClient(LLMClientABC):
|
|
50
|
+
def __init__(self, config: llm_param.LLMConfigParameter):
|
|
51
|
+
super().__init__(config)
|
|
52
|
+
if config.is_azure:
|
|
53
|
+
if not config.base_url:
|
|
54
|
+
raise ValueError("Azure endpoint is required")
|
|
55
|
+
client = openai.AsyncAzureOpenAI(
|
|
56
|
+
api_key=config.api_key,
|
|
57
|
+
azure_endpoint=str(config.base_url),
|
|
58
|
+
api_version=config.azure_api_version,
|
|
59
|
+
timeout=httpx.Timeout(300.0, connect=15.0, read=285.0),
|
|
60
|
+
)
|
|
61
|
+
else:
|
|
62
|
+
client = openai.AsyncOpenAI(
|
|
63
|
+
api_key=config.api_key,
|
|
64
|
+
base_url=config.base_url,
|
|
65
|
+
timeout=httpx.Timeout(300.0, connect=15.0, read=285.0),
|
|
66
|
+
)
|
|
67
|
+
self.client: openai.AsyncAzureOpenAI | openai.AsyncOpenAI = client
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
@override
|
|
71
|
+
def create(cls, config: llm_param.LLMConfigParameter) -> "LLMClientABC":
|
|
72
|
+
return cls(config)
|
|
73
|
+
|
|
74
|
+
@override
|
|
75
|
+
async def call(self, param: llm_param.LLMCallParameter) -> AsyncGenerator[model.ConversationItem]:
|
|
76
|
+
param = apply_config_defaults(param, self.get_llm_config())
|
|
77
|
+
|
|
78
|
+
metadata_tracker = MetadataTracker(cost_config=self.get_llm_config().cost)
|
|
79
|
+
|
|
80
|
+
payload, extra_body = build_payload(param)
|
|
81
|
+
extra_headers: dict[str, str] = {"extra": json.dumps({"session_id": param.session_id}, sort_keys=True)}
|
|
82
|
+
|
|
83
|
+
log_debug(
|
|
84
|
+
json.dumps({**payload, **extra_body}, ensure_ascii=False, default=str),
|
|
85
|
+
style="yellow",
|
|
86
|
+
debug_type=DebugType.LLM_PAYLOAD,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
stream = await self.client.chat.completions.create(
|
|
91
|
+
**payload,
|
|
92
|
+
extra_body=extra_body,
|
|
93
|
+
extra_headers=extra_headers,
|
|
94
|
+
)
|
|
95
|
+
except (openai.OpenAIError, httpx.HTTPError) as e:
|
|
96
|
+
yield model.StreamErrorItem(error=f"{e.__class__.__name__} {e!s}")
|
|
97
|
+
yield metadata_tracker.finalize()
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
reasoning_handler = DefaultReasoningHandler(
|
|
101
|
+
param_model=str(param.model),
|
|
102
|
+
response_id=None,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def on_event(event: Any) -> None:
|
|
106
|
+
log_debug(
|
|
107
|
+
event.model_dump_json(exclude_none=True),
|
|
108
|
+
style="blue",
|
|
109
|
+
debug_type=DebugType.LLM_STREAM,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
async for item in parse_chat_completions_stream(
|
|
113
|
+
stream,
|
|
114
|
+
param=param,
|
|
115
|
+
metadata_tracker=metadata_tracker,
|
|
116
|
+
reasoning_handler=reasoning_handler,
|
|
117
|
+
on_event=on_event,
|
|
118
|
+
):
|
|
119
|
+
yield item
|