klaude-code 2.5.1__py3-none-any.whl → 2.5.3__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/.DS_Store +0 -0
- klaude_code/cli/auth_cmd.py +2 -13
- klaude_code/cli/cost_cmd.py +10 -10
- klaude_code/cli/list_model.py +8 -0
- klaude_code/cli/main.py +41 -8
- klaude_code/cli/session_cmd.py +2 -11
- klaude_code/config/assets/builtin_config.yaml +45 -26
- klaude_code/config/config.py +30 -7
- klaude_code/config/model_matcher.py +3 -3
- klaude_code/config/sub_agent_model_helper.py +1 -1
- klaude_code/const.py +2 -1
- klaude_code/core/agent_profile.py +1 -0
- klaude_code/core/executor.py +4 -0
- klaude_code/core/loaded_skills.py +36 -0
- klaude_code/core/tool/context.py +1 -3
- klaude_code/core/tool/file/edit_tool.py +1 -1
- klaude_code/core/tool/file/read_tool.py +2 -2
- klaude_code/core/tool/file/write_tool.py +1 -1
- klaude_code/core/turn.py +19 -7
- klaude_code/llm/anthropic/client.py +97 -60
- klaude_code/llm/anthropic/input.py +20 -9
- klaude_code/llm/google/client.py +223 -148
- klaude_code/llm/google/input.py +44 -36
- klaude_code/llm/openai_compatible/stream.py +109 -99
- klaude_code/llm/openrouter/reasoning.py +4 -29
- klaude_code/llm/partial_message.py +2 -32
- klaude_code/llm/responses/client.py +99 -81
- klaude_code/llm/responses/input.py +11 -25
- klaude_code/llm/stream_parts.py +94 -0
- klaude_code/log.py +57 -0
- klaude_code/protocol/events/system.py +3 -0
- klaude_code/protocol/llm_param.py +1 -0
- klaude_code/session/export.py +259 -91
- klaude_code/session/templates/export_session.html +141 -59
- klaude_code/skill/.DS_Store +0 -0
- klaude_code/skill/assets/.DS_Store +0 -0
- klaude_code/skill/loader.py +1 -0
- klaude_code/tui/command/fork_session_cmd.py +14 -23
- klaude_code/tui/command/model_picker.py +2 -17
- klaude_code/tui/command/refresh_cmd.py +2 -0
- klaude_code/tui/command/resume_cmd.py +2 -18
- klaude_code/tui/command/sub_agent_model_cmd.py +5 -19
- klaude_code/tui/command/thinking_cmd.py +2 -14
- klaude_code/tui/components/common.py +1 -1
- klaude_code/tui/components/metadata.py +22 -21
- klaude_code/tui/components/rich/markdown.py +8 -0
- klaude_code/tui/components/rich/quote.py +36 -8
- klaude_code/tui/components/rich/theme.py +2 -0
- klaude_code/tui/components/welcome.py +32 -0
- klaude_code/tui/input/prompt_toolkit.py +3 -1
- klaude_code/tui/machine.py +19 -1
- klaude_code/tui/renderer.py +3 -4
- klaude_code/tui/terminal/selector.py +174 -31
- {klaude_code-2.5.1.dist-info → klaude_code-2.5.3.dist-info}/METADATA +1 -1
- {klaude_code-2.5.1.dist-info → klaude_code-2.5.3.dist-info}/RECORD +57 -53
- klaude_code/skill/assets/jj-workspace/SKILL.md +0 -20
- {klaude_code-2.5.1.dist-info → klaude_code-2.5.3.dist-info}/WHEEL +0 -0
- {klaude_code-2.5.1.dist-info → klaude_code-2.5.3.dist-info}/entry_points.txt +0 -0
|
@@ -33,8 +33,13 @@ from klaude_code.const import (
|
|
|
33
33
|
from klaude_code.llm.anthropic.input import convert_history_to_input, convert_system_to_input, convert_tool_schema
|
|
34
34
|
from klaude_code.llm.client import LLMClientABC, LLMStreamABC
|
|
35
35
|
from klaude_code.llm.input_common import apply_config_defaults
|
|
36
|
-
from klaude_code.llm.partial_message import degrade_thinking_to_text
|
|
37
36
|
from klaude_code.llm.registry import register
|
|
37
|
+
from klaude_code.llm.stream_parts import (
|
|
38
|
+
append_text_part,
|
|
39
|
+
append_thinking_text_part,
|
|
40
|
+
build_partial_message,
|
|
41
|
+
build_partial_parts,
|
|
42
|
+
)
|
|
38
43
|
from klaude_code.llm.usage import MetadataTracker, error_llm_stream
|
|
39
44
|
from klaude_code.log import DebugType, log_debug
|
|
40
45
|
from klaude_code.protocol import llm_param, message, model
|
|
@@ -64,11 +69,10 @@ class AnthropicStreamStateManager:
|
|
|
64
69
|
|
|
65
70
|
def __init__(self, model_id: str) -> None:
|
|
66
71
|
self.model_id = model_id
|
|
67
|
-
self.
|
|
68
|
-
self.accumulated_content: list[str] = []
|
|
69
|
-
self.parts: list[message.Part] = []
|
|
72
|
+
self.assistant_parts: list[message.Part] = []
|
|
70
73
|
self.response_id: str | None = None
|
|
71
|
-
self.
|
|
74
|
+
self._pending_signature: str | None = None
|
|
75
|
+
self._pending_signature_thinking_index: int | None = None
|
|
72
76
|
self.stop_reason: model.StopReason | None = None
|
|
73
77
|
|
|
74
78
|
# Tool call state
|
|
@@ -80,34 +84,59 @@ class AnthropicStreamStateManager:
|
|
|
80
84
|
self.input_token: int = 0
|
|
81
85
|
self.cached_token: int = 0
|
|
82
86
|
|
|
83
|
-
def
|
|
84
|
-
"""
|
|
85
|
-
|
|
87
|
+
def append_thinking_text(self, text: str) -> None:
|
|
88
|
+
"""Append thinking text, merging with the previous ThinkingTextPart when possible."""
|
|
89
|
+
index = append_thinking_text_part(self.assistant_parts, text, model_id=self.model_id)
|
|
90
|
+
if index is not None:
|
|
91
|
+
self._pending_signature_thinking_index = index
|
|
92
|
+
|
|
93
|
+
def append_text(self, text: str) -> None:
|
|
94
|
+
"""Append assistant text, merging with the previous TextPart when possible."""
|
|
95
|
+
append_text_part(self.assistant_parts, text)
|
|
96
|
+
|
|
97
|
+
def set_pending_signature(self, signature: str) -> None:
|
|
98
|
+
if signature:
|
|
99
|
+
self._pending_signature = signature
|
|
100
|
+
|
|
101
|
+
def flush_pending_signature(self) -> None:
|
|
102
|
+
"""Attach any pending signature to the most recent thinking segment.
|
|
103
|
+
|
|
104
|
+
Anthropic's signature is semantically tied to its thinking content. The
|
|
105
|
+
signature delta may arrive slightly after thinking text, so we insert the
|
|
106
|
+
signature part adjacent to the thinking part it signs.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
if not self._pending_signature:
|
|
110
|
+
return
|
|
111
|
+
if self._pending_signature_thinking_index is None:
|
|
112
|
+
# No thinking part seen for this signature; drop it.
|
|
113
|
+
self._pending_signature = None
|
|
86
114
|
return
|
|
87
|
-
full_thinking = "".join(self.accumulated_thinking)
|
|
88
|
-
self.parts.append(message.ThinkingTextPart(text=full_thinking, model_id=self.model_id))
|
|
89
|
-
if self.pending_signature:
|
|
90
|
-
self.parts.append(
|
|
91
|
-
message.ThinkingSignaturePart(
|
|
92
|
-
signature=self.pending_signature,
|
|
93
|
-
model_id=self.model_id,
|
|
94
|
-
format="anthropic",
|
|
95
|
-
)
|
|
96
|
-
)
|
|
97
|
-
self.accumulated_thinking.clear()
|
|
98
|
-
self.pending_signature = None
|
|
99
115
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if
|
|
116
|
+
insert_at = self._pending_signature_thinking_index + 1
|
|
117
|
+
# Avoid inserting duplicates if flush is called multiple times.
|
|
118
|
+
if insert_at < len(self.assistant_parts) and isinstance(
|
|
119
|
+
self.assistant_parts[insert_at], message.ThinkingSignaturePart
|
|
120
|
+
):
|
|
121
|
+
self._pending_signature = None
|
|
103
122
|
return
|
|
104
|
-
|
|
105
|
-
self.
|
|
123
|
+
|
|
124
|
+
self.assistant_parts.insert(
|
|
125
|
+
insert_at,
|
|
126
|
+
message.ThinkingSignaturePart(
|
|
127
|
+
signature=self._pending_signature,
|
|
128
|
+
model_id=self.model_id,
|
|
129
|
+
format="anthropic",
|
|
130
|
+
),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
self._pending_signature = None
|
|
134
|
+
self._pending_signature_thinking_index = None
|
|
106
135
|
|
|
107
136
|
def flush_tool_call(self) -> None:
|
|
108
137
|
"""Flush current tool call into parts."""
|
|
109
138
|
if self.current_tool_name and self.current_tool_call_id:
|
|
110
|
-
self.
|
|
139
|
+
self.assistant_parts.append(
|
|
111
140
|
message.ToolCallPart(
|
|
112
141
|
call_id=self.current_tool_call_id,
|
|
113
142
|
tool_name=self.current_tool_name,
|
|
@@ -119,11 +148,17 @@ class AnthropicStreamStateManager:
|
|
|
119
148
|
self.current_tool_inputs = None
|
|
120
149
|
|
|
121
150
|
def flush_all(self) -> list[message.Part]:
|
|
122
|
-
"""Flush all
|
|
123
|
-
self.
|
|
124
|
-
self.flush_content()
|
|
151
|
+
"""Flush all pending content in order and return parts."""
|
|
152
|
+
self.flush_pending_signature()
|
|
125
153
|
self.flush_tool_call()
|
|
126
|
-
return list(self.
|
|
154
|
+
return list(self.assistant_parts)
|
|
155
|
+
|
|
156
|
+
def get_partial_parts(self) -> list[message.Part]:
|
|
157
|
+
"""Get accumulated parts excluding tool calls, with thinking degraded.
|
|
158
|
+
|
|
159
|
+
Filters out ToolCallPart and applies degrade_thinking_to_text.
|
|
160
|
+
"""
|
|
161
|
+
return build_partial_parts(self.assistant_parts)
|
|
127
162
|
|
|
128
163
|
def get_partial_message(self) -> message.AssistantMessage | None:
|
|
129
164
|
"""Build a partial AssistantMessage from accumulated state.
|
|
@@ -131,16 +166,7 @@ class AnthropicStreamStateManager:
|
|
|
131
166
|
Flushes all accumulated content and returns the message with
|
|
132
167
|
stop_reason="aborted". Returns None if no content has been accumulated.
|
|
133
168
|
"""
|
|
134
|
-
self.
|
|
135
|
-
self.flush_content()
|
|
136
|
-
parts = degrade_thinking_to_text(list(self.parts))
|
|
137
|
-
if not parts:
|
|
138
|
-
return None
|
|
139
|
-
return message.AssistantMessage(
|
|
140
|
-
parts=parts,
|
|
141
|
-
response_id=self.response_id,
|
|
142
|
-
stop_reason="aborted",
|
|
143
|
-
)
|
|
169
|
+
return build_partial_message(self.assistant_parts, response_id=self.response_id)
|
|
144
170
|
|
|
145
171
|
|
|
146
172
|
def build_payload(
|
|
@@ -226,17 +252,18 @@ async def parse_anthropic_stream(
|
|
|
226
252
|
case BetaThinkingDelta() as delta:
|
|
227
253
|
if delta.thinking:
|
|
228
254
|
metadata_tracker.record_token()
|
|
229
|
-
state.
|
|
255
|
+
state.append_thinking_text(delta.thinking)
|
|
230
256
|
yield message.ThinkingTextDelta(
|
|
231
257
|
content=delta.thinking,
|
|
232
258
|
response_id=state.response_id,
|
|
233
259
|
)
|
|
234
260
|
case BetaSignatureDelta() as delta:
|
|
235
|
-
state.
|
|
261
|
+
state.set_pending_signature(delta.signature)
|
|
236
262
|
case BetaTextDelta() as delta:
|
|
237
263
|
if delta.text:
|
|
238
264
|
metadata_tracker.record_token()
|
|
239
|
-
state.
|
|
265
|
+
state.flush_pending_signature()
|
|
266
|
+
state.append_text(delta.text)
|
|
240
267
|
yield message.AssistantTextDelta(
|
|
241
268
|
content=delta.text,
|
|
242
269
|
response_id=state.response_id,
|
|
@@ -251,6 +278,7 @@ async def parse_anthropic_stream(
|
|
|
251
278
|
match event.content_block:
|
|
252
279
|
case BetaToolUseBlock() as block:
|
|
253
280
|
metadata_tracker.record_token()
|
|
281
|
+
state.flush_pending_signature()
|
|
254
282
|
yield message.ToolCallStartDelta(
|
|
255
283
|
response_id=state.response_id,
|
|
256
284
|
call_id=block.id,
|
|
@@ -262,12 +290,7 @@ async def parse_anthropic_stream(
|
|
|
262
290
|
case _:
|
|
263
291
|
pass
|
|
264
292
|
case BetaRawContentBlockStopEvent():
|
|
265
|
-
|
|
266
|
-
metadata_tracker.record_token()
|
|
267
|
-
state.flush_thinking()
|
|
268
|
-
if state.accumulated_content:
|
|
269
|
-
metadata_tracker.record_token()
|
|
270
|
-
state.flush_content()
|
|
293
|
+
state.flush_pending_signature()
|
|
271
294
|
if state.current_tool_name and state.current_tool_call_id:
|
|
272
295
|
metadata_tracker.record_token()
|
|
273
296
|
state.flush_tool_call()
|
|
@@ -282,8 +305,6 @@ async def parse_anthropic_stream(
|
|
|
282
305
|
max_tokens=param.max_tokens,
|
|
283
306
|
)
|
|
284
307
|
)
|
|
285
|
-
metadata_tracker.set_model_name(str(param.model_id))
|
|
286
|
-
metadata_tracker.set_response_id(state.response_id)
|
|
287
308
|
raw_stop_reason = getattr(event, "stop_reason", None)
|
|
288
309
|
if isinstance(raw_stop_reason, str):
|
|
289
310
|
state.stop_reason = _map_anthropic_stop_reason(raw_stop_reason)
|
|
@@ -293,6 +314,8 @@ async def parse_anthropic_stream(
|
|
|
293
314
|
parts = state.flush_all()
|
|
294
315
|
if parts:
|
|
295
316
|
metadata_tracker.record_token()
|
|
317
|
+
metadata_tracker.set_model_name(str(param.model_id))
|
|
318
|
+
metadata_tracker.set_response_id(state.response_id)
|
|
296
319
|
metadata = metadata_tracker.finalize()
|
|
297
320
|
yield message.AssistantMessage(
|
|
298
321
|
parts=parts,
|
|
@@ -322,15 +345,29 @@ class AnthropicLLMStream(LLMStreamABC):
|
|
|
322
345
|
return self._iterate()
|
|
323
346
|
|
|
324
347
|
async def _iterate(self) -> AsyncGenerator[message.LLMStreamItem]:
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
348
|
+
try:
|
|
349
|
+
async for item in parse_anthropic_stream(
|
|
350
|
+
self._stream,
|
|
351
|
+
self._param,
|
|
352
|
+
self._metadata_tracker,
|
|
353
|
+
self._state,
|
|
354
|
+
):
|
|
355
|
+
if isinstance(item, message.AssistantMessage):
|
|
356
|
+
self._completed = True
|
|
357
|
+
yield item
|
|
358
|
+
except (anthropic.AnthropicError, httpx.HTTPError) as e:
|
|
359
|
+
yield message.StreamErrorItem(error=f"{e.__class__.__name__} {e!s}")
|
|
360
|
+
self._metadata_tracker.set_model_name(str(self._param.model_id))
|
|
361
|
+
self._metadata_tracker.set_response_id(self._state.response_id)
|
|
362
|
+
metadata = self._metadata_tracker.finalize()
|
|
363
|
+
# Use accumulated parts for potential prefill on retry
|
|
364
|
+
self._state.flush_all()
|
|
365
|
+
yield message.AssistantMessage(
|
|
366
|
+
parts=self._state.get_partial_parts(),
|
|
367
|
+
response_id=self._state.response_id,
|
|
368
|
+
usage=metadata,
|
|
369
|
+
stop_reason="error",
|
|
370
|
+
)
|
|
334
371
|
|
|
335
372
|
def get_partial_message(self) -> message.AssistantMessage | None:
|
|
336
373
|
if self._completed:
|
|
@@ -109,19 +109,34 @@ def _tool_blocks_to_message(blocks: list[BetaToolResultBlockParam]) -> BetaMessa
|
|
|
109
109
|
def _assistant_message_to_message(msg: message.AssistantMessage, model_name: str | None) -> BetaMessageParam:
|
|
110
110
|
content: list[BetaContentBlockParam] = []
|
|
111
111
|
current_thinking_content: str | None = None
|
|
112
|
-
native_thinking_parts,
|
|
112
|
+
native_thinking_parts, _ = split_thinking_parts(msg, model_name)
|
|
113
113
|
native_thinking_ids = {id(part) for part in native_thinking_parts}
|
|
114
114
|
|
|
115
|
-
def
|
|
115
|
+
def _degraded_thinking_block(text: str) -> BetaTextBlockParam | None:
|
|
116
|
+
stripped = text.strip()
|
|
117
|
+
if not stripped:
|
|
118
|
+
return None
|
|
119
|
+
return cast(
|
|
120
|
+
BetaTextBlockParam,
|
|
121
|
+
{
|
|
122
|
+
"type": "text",
|
|
123
|
+
"text": f"<thinking>\n{stripped}\n</thinking>",
|
|
124
|
+
},
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def _flush_thinking_as_text_block() -> None:
|
|
116
128
|
nonlocal current_thinking_content
|
|
117
129
|
if current_thinking_content is None:
|
|
118
130
|
return
|
|
119
|
-
|
|
131
|
+
if block := _degraded_thinking_block(current_thinking_content):
|
|
132
|
+
content.append(block)
|
|
120
133
|
current_thinking_content = None
|
|
121
134
|
|
|
122
135
|
for part in msg.parts:
|
|
123
136
|
if isinstance(part, message.ThinkingTextPart):
|
|
124
137
|
if id(part) not in native_thinking_ids:
|
|
138
|
+
if block := _degraded_thinking_block(part.text):
|
|
139
|
+
content.append(block)
|
|
125
140
|
continue
|
|
126
141
|
current_thinking_content = part.text
|
|
127
142
|
continue
|
|
@@ -142,7 +157,7 @@ def _assistant_message_to_message(msg: message.AssistantMessage, model_name: str
|
|
|
142
157
|
current_thinking_content = None
|
|
143
158
|
continue
|
|
144
159
|
|
|
145
|
-
|
|
160
|
+
_flush_thinking_as_text_block()
|
|
146
161
|
if isinstance(part, message.TextPart):
|
|
147
162
|
content.append(cast(BetaTextBlockParam, {"type": "text", "text": part.text}))
|
|
148
163
|
elif isinstance(part, message.ToolCallPart):
|
|
@@ -166,11 +181,7 @@ def _assistant_message_to_message(msg: message.AssistantMessage, model_name: str
|
|
|
166
181
|
)
|
|
167
182
|
)
|
|
168
183
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if degraded_thinking_texts:
|
|
172
|
-
degraded_text = "<thinking>\n" + "\n".join(degraded_thinking_texts) + "\n</thinking>"
|
|
173
|
-
content.insert(0, cast(BetaTextBlockParam, {"type": "text", "text": degraded_text}))
|
|
184
|
+
_flush_thinking_as_text_block()
|
|
174
185
|
|
|
175
186
|
return {"role": "assistant", "content": content}
|
|
176
187
|
|