klaude-code 1.2.6__py3-none-any.whl → 1.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- klaude_code/auth/__init__.py +24 -0
- klaude_code/auth/codex/__init__.py +20 -0
- klaude_code/auth/codex/exceptions.py +17 -0
- klaude_code/auth/codex/jwt_utils.py +45 -0
- klaude_code/auth/codex/oauth.py +229 -0
- klaude_code/auth/codex/token_manager.py +84 -0
- klaude_code/cli/auth_cmd.py +73 -0
- klaude_code/cli/config_cmd.py +91 -0
- klaude_code/cli/cost_cmd.py +338 -0
- klaude_code/cli/debug.py +78 -0
- klaude_code/cli/list_model.py +307 -0
- klaude_code/cli/main.py +233 -134
- klaude_code/cli/runtime.py +309 -117
- klaude_code/{version.py → cli/self_update.py} +114 -5
- klaude_code/cli/session_cmd.py +37 -21
- klaude_code/command/__init__.py +88 -27
- klaude_code/command/clear_cmd.py +8 -7
- klaude_code/command/command_abc.py +31 -31
- klaude_code/command/debug_cmd.py +79 -0
- klaude_code/command/export_cmd.py +19 -53
- klaude_code/command/export_online_cmd.py +154 -0
- klaude_code/command/fork_session_cmd.py +267 -0
- klaude_code/command/help_cmd.py +7 -8
- klaude_code/command/model_cmd.py +60 -10
- klaude_code/command/model_select.py +84 -0
- klaude_code/command/prompt-jj-describe.md +32 -0
- klaude_code/command/prompt_command.py +19 -11
- klaude_code/command/refresh_cmd.py +8 -10
- klaude_code/command/registry.py +139 -40
- klaude_code/command/release_notes_cmd.py +84 -0
- klaude_code/command/resume_cmd.py +111 -0
- klaude_code/command/status_cmd.py +104 -60
- klaude_code/command/terminal_setup_cmd.py +7 -9
- klaude_code/command/thinking_cmd.py +98 -0
- klaude_code/config/__init__.py +14 -6
- klaude_code/config/assets/__init__.py +1 -0
- klaude_code/config/assets/builtin_config.yaml +303 -0
- klaude_code/config/builtin_config.py +38 -0
- klaude_code/config/config.py +378 -109
- klaude_code/config/select_model.py +117 -53
- klaude_code/config/thinking.py +269 -0
- klaude_code/{const/__init__.py → const.py} +50 -19
- klaude_code/core/agent.py +20 -28
- klaude_code/core/executor.py +327 -112
- klaude_code/core/manager/__init__.py +2 -4
- klaude_code/core/manager/llm_clients.py +1 -15
- klaude_code/core/manager/llm_clients_builder.py +10 -11
- klaude_code/core/manager/sub_agent_manager.py +37 -6
- klaude_code/core/prompt.py +63 -44
- klaude_code/core/prompts/prompt-claude-code.md +2 -13
- klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +117 -0
- klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +117 -0
- klaude_code/core/prompts/prompt-codex.md +9 -42
- klaude_code/core/prompts/prompt-minimal.md +12 -0
- klaude_code/core/prompts/{prompt-subagent-explore.md → prompt-sub-agent-explore.md} +16 -3
- klaude_code/core/prompts/{prompt-subagent-oracle.md → prompt-sub-agent-oracle.md} +1 -2
- klaude_code/core/prompts/prompt-sub-agent-web.md +51 -0
- klaude_code/core/reminders.py +283 -95
- klaude_code/core/task.py +113 -75
- klaude_code/core/tool/__init__.py +24 -31
- klaude_code/core/tool/file/_utils.py +36 -0
- klaude_code/core/tool/file/apply_patch.py +17 -25
- klaude_code/core/tool/file/apply_patch_tool.py +57 -77
- klaude_code/core/tool/file/diff_builder.py +151 -0
- klaude_code/core/tool/file/edit_tool.py +50 -63
- klaude_code/core/tool/file/move_tool.md +41 -0
- klaude_code/core/tool/file/move_tool.py +435 -0
- klaude_code/core/tool/file/read_tool.md +1 -1
- klaude_code/core/tool/file/read_tool.py +86 -86
- klaude_code/core/tool/file/write_tool.py +59 -69
- klaude_code/core/tool/report_back_tool.py +84 -0
- klaude_code/core/tool/shell/bash_tool.py +265 -22
- klaude_code/core/tool/shell/command_safety.py +3 -6
- klaude_code/core/tool/{memory → skill}/skill_tool.py +16 -26
- klaude_code/core/tool/sub_agent_tool.py +13 -2
- klaude_code/core/tool/todo/todo_write_tool.md +0 -157
- klaude_code/core/tool/todo/todo_write_tool.py +1 -1
- klaude_code/core/tool/todo/todo_write_tool_raw.md +182 -0
- klaude_code/core/tool/todo/update_plan_tool.py +1 -1
- klaude_code/core/tool/tool_abc.py +18 -0
- klaude_code/core/tool/tool_context.py +27 -12
- klaude_code/core/tool/tool_registry.py +7 -7
- klaude_code/core/tool/tool_runner.py +44 -36
- klaude_code/core/tool/truncation.py +29 -14
- klaude_code/core/tool/web/mermaid_tool.md +43 -0
- klaude_code/core/tool/web/mermaid_tool.py +2 -5
- klaude_code/core/tool/web/web_fetch_tool.md +1 -1
- klaude_code/core/tool/web/web_fetch_tool.py +112 -22
- klaude_code/core/tool/web/web_search_tool.md +23 -0
- klaude_code/core/tool/web/web_search_tool.py +130 -0
- klaude_code/core/turn.py +168 -66
- klaude_code/llm/__init__.py +2 -10
- klaude_code/llm/anthropic/client.py +190 -178
- klaude_code/llm/anthropic/input.py +39 -15
- klaude_code/llm/bedrock/__init__.py +3 -0
- klaude_code/llm/bedrock/client.py +60 -0
- klaude_code/llm/client.py +7 -21
- klaude_code/llm/codex/__init__.py +5 -0
- klaude_code/llm/codex/client.py +149 -0
- klaude_code/llm/google/__init__.py +3 -0
- klaude_code/llm/google/client.py +309 -0
- klaude_code/llm/google/input.py +215 -0
- klaude_code/llm/input_common.py +3 -9
- klaude_code/llm/openai_compatible/client.py +72 -164
- klaude_code/llm/openai_compatible/input.py +6 -4
- klaude_code/llm/openai_compatible/stream.py +273 -0
- klaude_code/llm/openai_compatible/tool_call_accumulator.py +17 -1
- klaude_code/llm/openrouter/client.py +89 -160
- klaude_code/llm/openrouter/input.py +18 -30
- klaude_code/llm/openrouter/reasoning.py +118 -0
- klaude_code/llm/registry.py +39 -7
- klaude_code/llm/responses/client.py +184 -171
- klaude_code/llm/responses/input.py +20 -1
- klaude_code/llm/usage.py +17 -12
- klaude_code/protocol/commands.py +17 -1
- klaude_code/protocol/events.py +31 -4
- klaude_code/protocol/llm_param.py +13 -10
- klaude_code/protocol/model.py +232 -29
- klaude_code/protocol/op.py +90 -1
- klaude_code/protocol/op_handler.py +35 -1
- klaude_code/protocol/sub_agent/__init__.py +117 -0
- klaude_code/protocol/sub_agent/explore.py +63 -0
- klaude_code/protocol/sub_agent/oracle.py +91 -0
- klaude_code/protocol/sub_agent/task.py +61 -0
- klaude_code/protocol/sub_agent/web.py +79 -0
- klaude_code/protocol/tools.py +4 -2
- klaude_code/session/__init__.py +2 -2
- klaude_code/session/codec.py +71 -0
- klaude_code/session/export.py +293 -86
- klaude_code/session/selector.py +89 -67
- klaude_code/session/session.py +320 -309
- klaude_code/session/store.py +220 -0
- klaude_code/session/templates/export_session.html +595 -83
- klaude_code/session/templates/mermaid_viewer.html +926 -0
- klaude_code/skill/__init__.py +27 -0
- klaude_code/skill/assets/deslop/SKILL.md +17 -0
- klaude_code/skill/assets/dev-docs/SKILL.md +108 -0
- klaude_code/skill/assets/handoff/SKILL.md +39 -0
- klaude_code/skill/assets/jj-workspace/SKILL.md +20 -0
- klaude_code/skill/assets/skill-creator/SKILL.md +139 -0
- klaude_code/{core/tool/memory/skill_loader.py → skill/loader.py} +55 -15
- klaude_code/skill/manager.py +70 -0
- klaude_code/skill/system_skills.py +192 -0
- klaude_code/trace/__init__.py +20 -2
- klaude_code/trace/log.py +150 -5
- klaude_code/ui/__init__.py +4 -9
- klaude_code/ui/core/input.py +1 -1
- klaude_code/ui/core/stage_manager.py +7 -7
- klaude_code/ui/modes/debug/display.py +2 -1
- klaude_code/ui/modes/repl/__init__.py +3 -48
- klaude_code/ui/modes/repl/clipboard.py +5 -5
- klaude_code/ui/modes/repl/completers.py +487 -123
- klaude_code/ui/modes/repl/display.py +5 -4
- klaude_code/ui/modes/repl/event_handler.py +370 -117
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +552 -105
- klaude_code/ui/modes/repl/key_bindings.py +146 -23
- klaude_code/ui/modes/repl/renderer.py +189 -99
- klaude_code/ui/renderers/assistant.py +9 -2
- klaude_code/ui/renderers/bash_syntax.py +178 -0
- klaude_code/ui/renderers/common.py +78 -0
- klaude_code/ui/renderers/developer.py +104 -48
- klaude_code/ui/renderers/diffs.py +87 -6
- klaude_code/ui/renderers/errors.py +11 -6
- klaude_code/ui/renderers/mermaid_viewer.py +57 -0
- klaude_code/ui/renderers/metadata.py +112 -76
- klaude_code/ui/renderers/sub_agent.py +92 -7
- klaude_code/ui/renderers/thinking.py +40 -18
- klaude_code/ui/renderers/tools.py +405 -227
- klaude_code/ui/renderers/user_input.py +73 -13
- klaude_code/ui/rich/__init__.py +10 -1
- klaude_code/ui/rich/cjk_wrap.py +228 -0
- klaude_code/ui/rich/code_panel.py +131 -0
- klaude_code/ui/rich/live.py +17 -0
- klaude_code/ui/rich/markdown.py +305 -170
- klaude_code/ui/rich/searchable_text.py +10 -13
- klaude_code/ui/rich/status.py +190 -49
- klaude_code/ui/rich/theme.py +135 -39
- klaude_code/ui/terminal/__init__.py +55 -0
- klaude_code/ui/terminal/color.py +1 -1
- klaude_code/ui/terminal/control.py +13 -22
- klaude_code/ui/terminal/notifier.py +44 -4
- klaude_code/ui/terminal/selector.py +658 -0
- klaude_code/ui/utils/common.py +0 -18
- klaude_code-1.8.0.dist-info/METADATA +377 -0
- klaude_code-1.8.0.dist-info/RECORD +219 -0
- {klaude_code-1.2.6.dist-info → klaude_code-1.8.0.dist-info}/entry_points.txt +1 -0
- klaude_code/command/diff_cmd.py +0 -138
- klaude_code/command/prompt-dev-docs-update.md +0 -56
- klaude_code/command/prompt-dev-docs.md +0 -46
- klaude_code/config/list_model.py +0 -162
- klaude_code/core/manager/agent_manager.py +0 -127
- klaude_code/core/prompts/prompt-subagent-webfetch.md +0 -46
- klaude_code/core/tool/file/multi_edit_tool.md +0 -42
- klaude_code/core/tool/file/multi_edit_tool.py +0 -199
- klaude_code/core/tool/memory/memory_tool.md +0 -16
- klaude_code/core/tool/memory/memory_tool.py +0 -462
- klaude_code/llm/openrouter/reasoning_handler.py +0 -209
- klaude_code/protocol/sub_agent.py +0 -348
- klaude_code/ui/utils/debouncer.py +0 -42
- klaude_code-1.2.6.dist-info/METADATA +0 -178
- klaude_code-1.2.6.dist-info/RECORD +0 -167
- /klaude_code/core/prompts/{prompt-subagent.md → prompt-sub-agent.md} +0 -0
- /klaude_code/core/tool/{memory → skill}/__init__.py +0 -0
- /klaude_code/core/tool/{memory → skill}/skill_tool.md +0 -0
- {klaude_code-1.2.6.dist-info → klaude_code-1.8.0.dist-info}/WHEEL +0 -0
klaude_code/core/turn.py
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from collections.abc import AsyncGenerator
|
|
4
|
-
from dataclasses import dataclass
|
|
3
|
+
from collections.abc import AsyncGenerator
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from klaude_code.core.tool import ToolABC, tool_context
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from klaude_code.core.task import SessionContext
|
|
5
11
|
|
|
6
|
-
from klaude_code.core.tool import TodoContext, ToolABC, tool_context
|
|
7
12
|
from klaude_code.core.tool.tool_runner import (
|
|
8
13
|
ToolExecutionCallStarted,
|
|
9
14
|
ToolExecutionResult,
|
|
@@ -12,7 +17,7 @@ from klaude_code.core.tool.tool_runner import (
|
|
|
12
17
|
ToolExecutorEvent,
|
|
13
18
|
)
|
|
14
19
|
from klaude_code.llm import LLMClientABC
|
|
15
|
-
from klaude_code.protocol import events, llm_param, model
|
|
20
|
+
from klaude_code.protocol import events, llm_param, model, tools
|
|
16
21
|
from klaude_code.trace import DebugType, log_debug
|
|
17
22
|
|
|
18
23
|
|
|
@@ -26,16 +31,22 @@ class TurnError(Exception):
|
|
|
26
31
|
class TurnExecutionContext:
|
|
27
32
|
"""Execution context required to run a single turn."""
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
get_conversation_history: Callable[[], list[model.ConversationItem]]
|
|
31
|
-
append_history: Callable[[Sequence[model.ConversationItem]], None]
|
|
34
|
+
session_ctx: SessionContext
|
|
32
35
|
llm_client: LLMClientABC
|
|
33
36
|
system_prompt: str | None
|
|
34
37
|
tools: list[llm_param.ToolSchema]
|
|
35
38
|
tool_registry: dict[str, type[ToolABC]]
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class TurnResult:
|
|
43
|
+
"""Aggregated state produced while executing a turn."""
|
|
44
|
+
|
|
45
|
+
reasoning_items: list[model.ReasoningTextItem | model.ReasoningEncryptedItem]
|
|
46
|
+
assistant_message: model.AssistantMessageItem | None
|
|
47
|
+
tool_calls: list[model.ToolCallItem]
|
|
48
|
+
stream_error: model.StreamErrorItem | None
|
|
49
|
+
report_back_result: str | None = field(default=None)
|
|
39
50
|
|
|
40
51
|
|
|
41
52
|
def build_events_from_tool_executor_event(session_id: str, event: ToolExecutorEvent) -> list[events.Event]:
|
|
@@ -64,6 +75,7 @@ def build_events_from_tool_executor_event(session_id: str, event: ToolExecutorEv
|
|
|
64
75
|
result=tool_result.output or "",
|
|
65
76
|
ui_extra=tool_result.ui_extra,
|
|
66
77
|
status=tool_result.status,
|
|
78
|
+
task_metadata=tool_result.task_metadata,
|
|
67
79
|
)
|
|
68
80
|
)
|
|
69
81
|
case ToolExecutionTodoChange(todos=todos):
|
|
@@ -87,45 +99,110 @@ class TurnExecutor:
|
|
|
87
99
|
def __init__(self, context: TurnExecutionContext) -> None:
|
|
88
100
|
self._context = context
|
|
89
101
|
self._tool_executor: ToolExecutor | None = None
|
|
90
|
-
self.
|
|
102
|
+
self._turn_result: TurnResult | None = None
|
|
103
|
+
self._assistant_delta_buffer: list[str] = []
|
|
104
|
+
self._assistant_response_id: str | None = None
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def report_back_result(self) -> str | None:
|
|
108
|
+
return self._turn_result.report_back_result if self._turn_result else None
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def task_finished(self) -> bool:
|
|
112
|
+
"""Check if this turn indicates the task should end.
|
|
113
|
+
|
|
114
|
+
Task ends when there are no tool calls or report_back was called.
|
|
115
|
+
"""
|
|
116
|
+
if self._turn_result is None:
|
|
117
|
+
return True
|
|
118
|
+
if not self._turn_result.tool_calls:
|
|
119
|
+
return True
|
|
120
|
+
return self._turn_result.report_back_result is not None
|
|
91
121
|
|
|
92
122
|
@property
|
|
93
|
-
def
|
|
94
|
-
|
|
123
|
+
def task_result(self) -> str:
|
|
124
|
+
"""Get the task result from this turn.
|
|
125
|
+
|
|
126
|
+
Returns report_back result if available, otherwise returns
|
|
127
|
+
the assistant message content.
|
|
128
|
+
"""
|
|
129
|
+
if self._turn_result is not None and self._turn_result.report_back_result is not None:
|
|
130
|
+
return self._turn_result.report_back_result
|
|
131
|
+
if self._turn_result is not None and self._turn_result.assistant_message is not None:
|
|
132
|
+
return self._turn_result.assistant_message.content or ""
|
|
133
|
+
return ""
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def has_structured_output(self) -> bool:
|
|
137
|
+
"""Check if the task result is structured output from report_back."""
|
|
138
|
+
return bool(self._turn_result and self._turn_result.report_back_result)
|
|
95
139
|
|
|
96
140
|
def cancel(self) -> list[events.Event]:
|
|
97
141
|
"""Cancel running tools and return any resulting events."""
|
|
98
142
|
ui_events: list[events.Event] = []
|
|
143
|
+
self._persist_partial_assistant_on_cancel()
|
|
99
144
|
if self._tool_executor is not None:
|
|
100
145
|
for exec_event in self._tool_executor.cancel():
|
|
101
|
-
for ui_event in build_events_from_tool_executor_event(self._context.session_id, exec_event):
|
|
146
|
+
for ui_event in build_events_from_tool_executor_event(self._context.session_ctx.session_id, exec_event):
|
|
102
147
|
ui_events.append(ui_event)
|
|
103
148
|
self._tool_executor = None
|
|
104
149
|
return ui_events
|
|
105
150
|
|
|
106
|
-
async def run(self) -> AsyncGenerator[events.Event
|
|
151
|
+
async def run(self) -> AsyncGenerator[events.Event]:
|
|
107
152
|
"""Execute the turn, yielding events as they occur.
|
|
108
153
|
|
|
109
154
|
Raises:
|
|
110
155
|
TurnError: If the turn fails (stream error or non-completed status).
|
|
111
156
|
"""
|
|
112
157
|
ctx = self._context
|
|
158
|
+
session_ctx = ctx.session_ctx
|
|
159
|
+
|
|
160
|
+
yield events.TurnStartEvent(session_id=session_ctx.session_id)
|
|
161
|
+
|
|
162
|
+
self._turn_result = TurnResult(
|
|
163
|
+
reasoning_items=[],
|
|
164
|
+
assistant_message=None,
|
|
165
|
+
tool_calls=[],
|
|
166
|
+
stream_error=None,
|
|
167
|
+
)
|
|
113
168
|
|
|
114
|
-
|
|
169
|
+
async for event in self._consume_llm_stream(self._turn_result):
|
|
170
|
+
yield event
|
|
115
171
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
error_message: str | None = None
|
|
172
|
+
if self._turn_result.stream_error is not None:
|
|
173
|
+
session_ctx.append_history([self._turn_result.stream_error])
|
|
174
|
+
yield events.TurnEndEvent(session_id=session_ctx.session_id)
|
|
175
|
+
raise TurnError(self._turn_result.stream_error.error)
|
|
121
176
|
|
|
177
|
+
self._append_success_history(self._turn_result)
|
|
178
|
+
|
|
179
|
+
if self._turn_result.tool_calls:
|
|
180
|
+
# Check for report_back before running tools
|
|
181
|
+
self._detect_report_back(self._turn_result)
|
|
182
|
+
|
|
183
|
+
async for ui_event in self._run_tool_executor(self._turn_result.tool_calls):
|
|
184
|
+
yield ui_event
|
|
185
|
+
|
|
186
|
+
yield events.TurnEndEvent(session_id=session_ctx.session_id)
|
|
187
|
+
|
|
188
|
+
def _detect_report_back(self, turn_result: TurnResult) -> None:
|
|
189
|
+
"""Detect report_back tool call and store its arguments as JSON string."""
|
|
190
|
+
for tool_call in turn_result.tool_calls:
|
|
191
|
+
if tool_call.name == tools.REPORT_BACK:
|
|
192
|
+
turn_result.report_back_result = tool_call.arguments
|
|
193
|
+
break
|
|
194
|
+
|
|
195
|
+
async def _consume_llm_stream(self, turn_result: TurnResult) -> AsyncGenerator[events.Event]:
|
|
196
|
+
"""Stream events from LLM and update turn_result in place."""
|
|
197
|
+
|
|
198
|
+
ctx = self._context
|
|
199
|
+
session_ctx = ctx.session_ctx
|
|
122
200
|
async for response_item in ctx.llm_client.call(
|
|
123
201
|
llm_param.LLMCallParameter(
|
|
124
|
-
input=
|
|
202
|
+
input=session_ctx.get_conversation_history(),
|
|
125
203
|
system=ctx.system_prompt,
|
|
126
204
|
tools=ctx.tools,
|
|
127
|
-
|
|
128
|
-
session_id=ctx.session_id,
|
|
205
|
+
session_id=session_ctx.session_id,
|
|
129
206
|
)
|
|
130
207
|
):
|
|
131
208
|
log_debug(
|
|
@@ -136,41 +213,45 @@ class TurnExecutor:
|
|
|
136
213
|
)
|
|
137
214
|
match response_item:
|
|
138
215
|
case model.StartItem():
|
|
139
|
-
|
|
216
|
+
continue
|
|
140
217
|
case model.ReasoningTextItem() as item:
|
|
141
|
-
|
|
218
|
+
turn_result.reasoning_items.append(item)
|
|
142
219
|
yield events.ThinkingEvent(
|
|
143
220
|
content=item.content,
|
|
144
221
|
response_id=item.response_id,
|
|
145
|
-
session_id=
|
|
222
|
+
session_id=session_ctx.session_id,
|
|
146
223
|
)
|
|
147
224
|
case model.ReasoningEncryptedItem() as item:
|
|
148
|
-
|
|
225
|
+
turn_result.reasoning_items.append(item)
|
|
226
|
+
case model.ReasoningTextDelta() as item:
|
|
227
|
+
yield events.ThinkingDeltaEvent(
|
|
228
|
+
content=item.content,
|
|
229
|
+
response_id=item.response_id,
|
|
230
|
+
session_id=session_ctx.session_id,
|
|
231
|
+
)
|
|
149
232
|
case model.AssistantMessageDelta() as item:
|
|
233
|
+
if item.response_id:
|
|
234
|
+
self._assistant_response_id = item.response_id
|
|
235
|
+
self._assistant_delta_buffer.append(item.content)
|
|
150
236
|
yield events.AssistantMessageDeltaEvent(
|
|
151
237
|
content=item.content,
|
|
152
238
|
response_id=item.response_id,
|
|
153
|
-
session_id=
|
|
239
|
+
session_id=session_ctx.session_id,
|
|
154
240
|
)
|
|
155
241
|
case model.AssistantMessageItem() as item:
|
|
156
|
-
|
|
242
|
+
turn_result.assistant_message = item
|
|
157
243
|
yield events.AssistantMessageEvent(
|
|
158
244
|
content=item.content or "",
|
|
159
245
|
response_id=item.response_id,
|
|
160
|
-
session_id=
|
|
246
|
+
session_id=session_ctx.session_id,
|
|
161
247
|
)
|
|
162
248
|
case model.ResponseMetadataItem() as item:
|
|
163
249
|
yield events.ResponseMetadataEvent(
|
|
164
|
-
session_id=
|
|
250
|
+
session_id=session_ctx.session_id,
|
|
165
251
|
metadata=item,
|
|
166
252
|
)
|
|
167
|
-
status = item.status
|
|
168
|
-
if status is not None and status != "completed":
|
|
169
|
-
response_failed = True
|
|
170
|
-
error_message = f"Response status: {status}"
|
|
171
253
|
case model.StreamErrorItem() as item:
|
|
172
|
-
|
|
173
|
-
error_message = item.error
|
|
254
|
+
turn_result.stream_error = item
|
|
174
255
|
log_debug(
|
|
175
256
|
"[StreamError]",
|
|
176
257
|
item.error,
|
|
@@ -179,42 +260,63 @@ class TurnExecutor:
|
|
|
179
260
|
)
|
|
180
261
|
case model.ToolCallStartItem() as item:
|
|
181
262
|
yield events.TurnToolCallStartEvent(
|
|
182
|
-
session_id=
|
|
263
|
+
session_id=session_ctx.session_id,
|
|
183
264
|
response_id=item.response_id,
|
|
184
265
|
tool_call_id=item.call_id,
|
|
185
266
|
tool_name=item.name,
|
|
186
267
|
arguments="",
|
|
187
268
|
)
|
|
188
269
|
case model.ToolCallItem() as item:
|
|
189
|
-
|
|
270
|
+
turn_result.tool_calls.append(item)
|
|
190
271
|
case _:
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if
|
|
199
|
-
|
|
200
|
-
if
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
self._has_tool_call = True
|
|
205
|
-
|
|
206
|
-
# Execute tools
|
|
207
|
-
if turn_tool_calls:
|
|
208
|
-
with tool_context(ctx.file_tracker, ctx.todo_context):
|
|
209
|
-
executor = ToolExecutor(
|
|
210
|
-
registry=ctx.tool_registry,
|
|
211
|
-
append_history=ctx.append_history,
|
|
212
|
-
)
|
|
213
|
-
self._tool_executor = executor
|
|
272
|
+
continue
|
|
273
|
+
|
|
274
|
+
def _append_success_history(self, turn_result: TurnResult) -> None:
|
|
275
|
+
"""Persist successful turn artifacts to conversation history."""
|
|
276
|
+
session_ctx = self._context.session_ctx
|
|
277
|
+
if turn_result.reasoning_items:
|
|
278
|
+
session_ctx.append_history(turn_result.reasoning_items)
|
|
279
|
+
if turn_result.assistant_message:
|
|
280
|
+
session_ctx.append_history([turn_result.assistant_message])
|
|
281
|
+
if turn_result.tool_calls:
|
|
282
|
+
session_ctx.append_history(turn_result.tool_calls)
|
|
283
|
+
self._assistant_delta_buffer.clear()
|
|
284
|
+
self._assistant_response_id = None
|
|
214
285
|
|
|
215
|
-
|
|
216
|
-
|
|
286
|
+
async def _run_tool_executor(self, tool_calls: list[model.ToolCallItem]) -> AsyncGenerator[events.Event]:
|
|
287
|
+
"""Run tools for the turn and translate executor events to UI events."""
|
|
288
|
+
|
|
289
|
+
ctx = self._context
|
|
290
|
+
session_ctx = ctx.session_ctx
|
|
291
|
+
with tool_context(session_ctx.file_tracker, session_ctx.todo_context):
|
|
292
|
+
executor = ToolExecutor(
|
|
293
|
+
registry=ctx.tool_registry,
|
|
294
|
+
append_history=session_ctx.append_history,
|
|
295
|
+
)
|
|
296
|
+
self._tool_executor = executor
|
|
297
|
+
try:
|
|
298
|
+
async for exec_event in executor.run_tools(tool_calls):
|
|
299
|
+
for ui_event in build_events_from_tool_executor_event(session_ctx.session_id, exec_event):
|
|
217
300
|
yield ui_event
|
|
301
|
+
finally:
|
|
218
302
|
self._tool_executor = None
|
|
219
303
|
|
|
220
|
-
|
|
304
|
+
def _persist_partial_assistant_on_cancel(self) -> None:
|
|
305
|
+
"""Persist streamed assistant text when a turn is interrupted.
|
|
306
|
+
|
|
307
|
+
Reasoning and tool calls are intentionally discarded on interrupt; only
|
|
308
|
+
the assistant message text collected so far is saved so it appears in
|
|
309
|
+
subsequent history/context.
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
if not self._assistant_delta_buffer:
|
|
313
|
+
return
|
|
314
|
+
partial_text = "".join(self._assistant_delta_buffer) + "<system interrupted by user>"
|
|
315
|
+
if not partial_text:
|
|
316
|
+
return
|
|
317
|
+
message_item = model.AssistantMessageItem(
|
|
318
|
+
content=partial_text,
|
|
319
|
+
response_id=self._assistant_response_id,
|
|
320
|
+
)
|
|
321
|
+
self._context.session_ctx.append_history([message_item])
|
|
322
|
+
self._assistant_delta_buffer.clear()
|
klaude_code/llm/__init__.py
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
"""LLM package init.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
LLM clients are lazily loaded to avoid heavy imports at module load time.
|
|
4
|
+
Only LLMClientABC and create_llm_client are exposed.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from .anthropic import AnthropicClient
|
|
8
7
|
from .client import LLMClientABC
|
|
9
|
-
from .openai_compatible import OpenAICompatibleClient
|
|
10
|
-
from .openrouter import OpenRouterClient
|
|
11
8
|
from .registry import create_llm_client
|
|
12
|
-
from .responses import ResponsesClient
|
|
13
9
|
|
|
14
10
|
__all__ = [
|
|
15
11
|
"LLMClientABC",
|
|
16
|
-
"ResponsesClient",
|
|
17
|
-
"OpenAICompatibleClient",
|
|
18
|
-
"OpenRouterClient",
|
|
19
|
-
"AnthropicClient",
|
|
20
12
|
"create_llm_client",
|
|
21
13
|
]
|