klaude-code 1.9.0__py3-none-any.whl → 2.0.1__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/base.py +2 -6
- klaude_code/cli/auth_cmd.py +4 -4
- klaude_code/cli/cost_cmd.py +1 -1
- klaude_code/cli/list_model.py +1 -1
- klaude_code/cli/main.py +1 -1
- klaude_code/cli/runtime.py +7 -5
- klaude_code/cli/self_update.py +1 -1
- klaude_code/cli/session_cmd.py +1 -1
- klaude_code/command/clear_cmd.py +6 -2
- klaude_code/command/command_abc.py +2 -2
- klaude_code/command/debug_cmd.py +4 -4
- klaude_code/command/export_cmd.py +2 -2
- klaude_code/command/export_online_cmd.py +12 -12
- klaude_code/command/fork_session_cmd.py +29 -23
- klaude_code/command/help_cmd.py +4 -4
- klaude_code/command/model_cmd.py +4 -4
- klaude_code/command/model_select.py +1 -1
- klaude_code/command/prompt-commit.md +11 -2
- klaude_code/command/prompt_command.py +3 -3
- klaude_code/command/refresh_cmd.py +2 -2
- klaude_code/command/registry.py +7 -5
- klaude_code/command/release_notes_cmd.py +4 -4
- klaude_code/command/resume_cmd.py +15 -11
- klaude_code/command/status_cmd.py +4 -4
- klaude_code/command/terminal_setup_cmd.py +8 -8
- klaude_code/command/thinking_cmd.py +4 -4
- klaude_code/config/assets/builtin_config.yaml +20 -0
- klaude_code/config/builtin_config.py +16 -5
- klaude_code/config/config.py +7 -2
- klaude_code/const.py +147 -91
- klaude_code/core/agent.py +3 -12
- klaude_code/core/executor.py +18 -39
- klaude_code/core/manager/sub_agent_manager.py +71 -7
- klaude_code/core/prompts/prompt-sub-agent-image-gen.md +1 -0
- klaude_code/core/prompts/prompt-sub-agent-web.md +27 -1
- klaude_code/core/reminders.py +88 -69
- klaude_code/core/task.py +44 -45
- klaude_code/core/tool/file/apply_patch_tool.py +9 -9
- klaude_code/core/tool/file/diff_builder.py +3 -5
- klaude_code/core/tool/file/edit_tool.py +23 -23
- klaude_code/core/tool/file/move_tool.py +43 -43
- klaude_code/core/tool/file/read_tool.py +44 -39
- klaude_code/core/tool/file/write_tool.py +14 -14
- klaude_code/core/tool/report_back_tool.py +4 -4
- klaude_code/core/tool/shell/bash_tool.py +23 -23
- klaude_code/core/tool/skill/skill_tool.py +7 -7
- klaude_code/core/tool/sub_agent_tool.py +38 -9
- klaude_code/core/tool/todo/todo_write_tool.py +9 -10
- klaude_code/core/tool/todo/update_plan_tool.py +6 -6
- klaude_code/core/tool/tool_abc.py +2 -2
- klaude_code/core/tool/tool_context.py +27 -0
- klaude_code/core/tool/tool_runner.py +88 -42
- klaude_code/core/tool/truncation.py +38 -20
- klaude_code/core/tool/web/mermaid_tool.py +6 -7
- klaude_code/core/tool/web/web_fetch_tool.py +68 -30
- klaude_code/core/tool/web/web_search_tool.py +15 -17
- klaude_code/core/turn.py +120 -73
- klaude_code/llm/anthropic/client.py +79 -44
- klaude_code/llm/anthropic/input.py +116 -108
- klaude_code/llm/bedrock/client.py +8 -5
- klaude_code/llm/claude/client.py +18 -8
- klaude_code/llm/client.py +4 -3
- klaude_code/llm/codex/client.py +15 -9
- klaude_code/llm/google/client.py +122 -60
- klaude_code/llm/google/input.py +94 -108
- klaude_code/llm/image.py +123 -0
- klaude_code/llm/input_common.py +136 -189
- klaude_code/llm/openai_compatible/client.py +17 -7
- klaude_code/llm/openai_compatible/input.py +36 -66
- klaude_code/llm/openai_compatible/stream.py +119 -67
- klaude_code/llm/openai_compatible/tool_call_accumulator.py +23 -11
- klaude_code/llm/openrouter/client.py +34 -9
- klaude_code/llm/openrouter/input.py +63 -64
- klaude_code/llm/openrouter/reasoning.py +22 -24
- klaude_code/llm/registry.py +20 -17
- klaude_code/llm/responses/client.py +107 -45
- klaude_code/llm/responses/input.py +115 -98
- klaude_code/llm/usage.py +52 -25
- klaude_code/protocol/__init__.py +1 -0
- klaude_code/protocol/events.py +16 -12
- klaude_code/protocol/llm_param.py +20 -2
- klaude_code/protocol/message.py +250 -0
- klaude_code/protocol/model.py +95 -285
- klaude_code/protocol/op.py +2 -15
- klaude_code/protocol/op_handler.py +0 -5
- klaude_code/protocol/sub_agent/__init__.py +1 -0
- klaude_code/protocol/sub_agent/explore.py +10 -0
- klaude_code/protocol/sub_agent/image_gen.py +119 -0
- klaude_code/protocol/sub_agent/task.py +10 -0
- klaude_code/protocol/sub_agent/web.py +10 -0
- klaude_code/session/codec.py +6 -6
- klaude_code/session/export.py +261 -62
- klaude_code/session/selector.py +7 -24
- klaude_code/session/session.py +126 -54
- klaude_code/session/store.py +5 -32
- klaude_code/session/templates/export_session.html +1 -1
- klaude_code/session/templates/mermaid_viewer.html +1 -1
- klaude_code/trace/log.py +11 -6
- klaude_code/ui/core/input.py +1 -1
- klaude_code/ui/core/stage_manager.py +1 -8
- klaude_code/ui/modes/debug/display.py +2 -2
- klaude_code/ui/modes/repl/clipboard.py +2 -2
- klaude_code/ui/modes/repl/completers.py +18 -10
- klaude_code/ui/modes/repl/event_handler.py +138 -132
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +1 -1
- klaude_code/ui/modes/repl/key_bindings.py +136 -2
- klaude_code/ui/modes/repl/renderer.py +107 -15
- klaude_code/ui/renderers/assistant.py +2 -2
- klaude_code/ui/renderers/bash_syntax.py +36 -4
- klaude_code/ui/renderers/common.py +70 -10
- klaude_code/ui/renderers/developer.py +7 -6
- klaude_code/ui/renderers/diffs.py +11 -11
- klaude_code/ui/renderers/mermaid_viewer.py +49 -2
- klaude_code/ui/renderers/metadata.py +33 -5
- klaude_code/ui/renderers/sub_agent.py +57 -16
- klaude_code/ui/renderers/thinking.py +37 -2
- klaude_code/ui/renderers/tools.py +188 -178
- klaude_code/ui/rich/live.py +3 -1
- klaude_code/ui/rich/markdown.py +39 -7
- klaude_code/ui/rich/quote.py +76 -1
- klaude_code/ui/rich/status.py +14 -8
- klaude_code/ui/rich/theme.py +20 -14
- klaude_code/ui/terminal/image.py +34 -0
- klaude_code/ui/terminal/notifier.py +2 -1
- klaude_code/ui/terminal/progress_bar.py +4 -4
- klaude_code/ui/terminal/selector.py +22 -4
- klaude_code/ui/utils/common.py +11 -2
- {klaude_code-1.9.0.dist-info → klaude_code-2.0.1.dist-info}/METADATA +4 -2
- klaude_code-2.0.1.dist-info/RECORD +229 -0
- klaude_code-1.9.0.dist-info/RECORD +0 -224
- {klaude_code-1.9.0.dist-info → klaude_code-2.0.1.dist-info}/WHEEL +0 -0
- {klaude_code-1.9.0.dist-info → klaude_code-2.0.1.dist-info}/entry_points.txt +0 -0
klaude_code/protocol/op.py
CHANGED
|
@@ -14,7 +14,7 @@ from uuid import uuid4
|
|
|
14
14
|
from pydantic import BaseModel, Field
|
|
15
15
|
|
|
16
16
|
from klaude_code.protocol.llm_param import Thinking
|
|
17
|
-
from klaude_code.protocol.
|
|
17
|
+
from klaude_code.protocol.message import UserInputPayload
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
20
20
|
from klaude_code.protocol.op_handler import OperationHandler
|
|
@@ -23,7 +23,6 @@ if TYPE_CHECKING:
|
|
|
23
23
|
class OperationType(Enum):
|
|
24
24
|
"""Enumeration of supported operation types."""
|
|
25
25
|
|
|
26
|
-
USER_INPUT = "user_input"
|
|
27
26
|
RUN_AGENT = "run_agent"
|
|
28
27
|
CHANGE_MODEL = "change_model"
|
|
29
28
|
CHANGE_THINKING = "change_thinking"
|
|
@@ -46,18 +45,6 @@ class Operation(BaseModel):
|
|
|
46
45
|
raise NotImplementedError("Subclasses must implement execute()")
|
|
47
46
|
|
|
48
47
|
|
|
49
|
-
class UserInputOperation(Operation):
|
|
50
|
-
"""Operation for handling user input (text and optional images) that should be processed by an agent."""
|
|
51
|
-
|
|
52
|
-
type: OperationType = OperationType.USER_INPUT
|
|
53
|
-
input: UserInputPayload
|
|
54
|
-
session_id: str | None = None
|
|
55
|
-
|
|
56
|
-
async def execute(self, handler: OperationHandler) -> None:
|
|
57
|
-
"""Execute user input by running it through an agent."""
|
|
58
|
-
await handler.handle_user_input(self)
|
|
59
|
-
|
|
60
|
-
|
|
61
48
|
class RunAgentOperation(Operation):
|
|
62
49
|
"""Operation for launching an agent task for a given session."""
|
|
63
50
|
|
|
@@ -84,7 +71,7 @@ class ChangeModelOperation(Operation):
|
|
|
84
71
|
# This is useful for in-prompt model switching where extra output is noisy.
|
|
85
72
|
emit_welcome_event: bool = True
|
|
86
73
|
|
|
87
|
-
# When False, do not emit the "Switched to:
|
|
74
|
+
# When False, do not emit the "Switched to: …" developer message.
|
|
88
75
|
# This is useful for in-prompt model switching where extra output is noisy.
|
|
89
76
|
emit_switch_message: bool = True
|
|
90
77
|
|
|
@@ -18,17 +18,12 @@ if TYPE_CHECKING:
|
|
|
18
18
|
InterruptOperation,
|
|
19
19
|
ResumeSessionOperation,
|
|
20
20
|
RunAgentOperation,
|
|
21
|
-
UserInputOperation,
|
|
22
21
|
)
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
class OperationHandler(Protocol):
|
|
26
25
|
"""Protocol defining the interface for handling operations."""
|
|
27
26
|
|
|
28
|
-
async def handle_user_input(self, operation: UserInputOperation) -> None:
|
|
29
|
-
"""Handle a user input operation."""
|
|
30
|
-
...
|
|
31
|
-
|
|
32
27
|
async def handle_run_agent(self, operation: RunAgentOperation) -> None:
|
|
33
28
|
"""Handle a run agent operation."""
|
|
34
29
|
...
|
|
@@ -112,5 +112,6 @@ def sub_agent_tool_names(enabled_only: bool = False, model_name: str | None = No
|
|
|
112
112
|
|
|
113
113
|
# Import sub-agent modules to trigger registration
|
|
114
114
|
from klaude_code.protocol.sub_agent import explore as explore # noqa: E402
|
|
115
|
+
from klaude_code.protocol.sub_agent import image_gen as image_gen # noqa: E402
|
|
115
116
|
from klaude_code.protocol.sub_agent import task as task # noqa: E402
|
|
116
117
|
from klaude_code.protocol.sub_agent import web as web # noqa: E402
|
|
@@ -14,11 +14,21 @@ Always spawn multiple search agents in parallel to maximise speed.
|
|
|
14
14
|
Structured output:
|
|
15
15
|
- Provide an `output_format` (JSON Schema) parameter for structured data back from the sub-agent
|
|
16
16
|
- Example: `output_format={"type": "object", "properties": {"files": {"type": "array", "items": {"type": "string"}, "description": "List of file paths that match the search criteria, e.g. ['src/main.py', 'src/utils/helper.py']"}}, "required": ["files"]}`\
|
|
17
|
+
|
|
18
|
+
- Agents can be resumed using the `resume` parameter by passing the agent ID from a previous invocation. When resumed, the agent
|
|
19
|
+
continues with its full previous context preserved. When NOT resuming, each invocation starts fresh and you should provide a detailed
|
|
20
|
+
task description with all necessary context.
|
|
21
|
+
- When the agent is done, it will return a single message back to you along with its agent ID. You can use this ID to resume the agent
|
|
22
|
+
later if needed for follow-up work.
|
|
17
23
|
"""
|
|
18
24
|
|
|
19
25
|
EXPLORE_PARAMETERS = {
|
|
20
26
|
"type": "object",
|
|
21
27
|
"properties": {
|
|
28
|
+
"resume": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript.",
|
|
31
|
+
},
|
|
22
32
|
"description": {
|
|
23
33
|
"type": "string",
|
|
24
34
|
"description": "Short (3-5 words) label for the exploration goal",
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, cast
|
|
4
|
+
|
|
5
|
+
from klaude_code.protocol.sub_agent import SubAgentProfile, register_sub_agent
|
|
6
|
+
|
|
7
|
+
IMAGE_GEN_DESCRIPTION = """\
|
|
8
|
+
Generate one or more images from a text prompt.
|
|
9
|
+
|
|
10
|
+
This tool invokes an Image Gen model to generate images. The generated image paths are automatically \
|
|
11
|
+
returned in the response.
|
|
12
|
+
|
|
13
|
+
Inputs:
|
|
14
|
+
- `prompt`: The main instruction describing the desired image.
|
|
15
|
+
- `image_paths` (optional): Local image file paths to use as references for editing or style guidance.
|
|
16
|
+
- `generation` (optional): Per-call image generation settings (aspect ratio / size).
|
|
17
|
+
|
|
18
|
+
Notes:
|
|
19
|
+
- Provide a short textual description of the generated image(s).
|
|
20
|
+
- Do NOT include base64 image data in text output.
|
|
21
|
+
- When providing multiple input images, describe each image's characteristics and purpose in the prompt, \
|
|
22
|
+
not just "image 1, image 2" - the image model cannot distinguish image order. \
|
|
23
|
+
For example: "Edit the first image (a photo of a cat sitting on a windowsill) to match the style of \
|
|
24
|
+
the second image (Van Gogh's Starry Night painting with swirling blue brushstrokes)."
|
|
25
|
+
|
|
26
|
+
Multi-turn image editing:
|
|
27
|
+
- Use `resume` to continue editing a previously generated image. The agent preserves its full context \
|
|
28
|
+
including the generated image, so you don't need to pass `image_paths` again.
|
|
29
|
+
- Example workflow:
|
|
30
|
+
1. Call ImageGen with prompt="Generate a watercolor painting of a mountain lake" -> returns agent_id
|
|
31
|
+
2. Call ImageGen with resume=agent_id, prompt="Add a wooden cabin on the shore" -> edits the previous image
|
|
32
|
+
3. Call ImageGen with resume=agent_id, prompt="Change to sunset lighting" -> continues editing
|
|
33
|
+
|
|
34
|
+
- Agents can be resumed using the `resume` parameter by passing the agent ID from a previous invocation. When resumed, the agent
|
|
35
|
+
continues with its full previous context preserved. When NOT resuming, each invocation starts fresh and you should provide a detailed
|
|
36
|
+
task description with all necessary context.
|
|
37
|
+
- When the agent is done, it will return a single message back to you along with its agent ID. You can use this ID to resume the agent
|
|
38
|
+
later if needed for follow-up work.
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
IMAGE_GEN_PARAMETERS: dict[str, Any] = {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"properties": {
|
|
46
|
+
"resume": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript.",
|
|
49
|
+
},
|
|
50
|
+
"description": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "A short (3-5 word) description of the request.",
|
|
53
|
+
},
|
|
54
|
+
"prompt": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"description": "Text prompt for image generation.",
|
|
57
|
+
},
|
|
58
|
+
"image_paths": {
|
|
59
|
+
"type": "array",
|
|
60
|
+
"items": {"type": "string"},
|
|
61
|
+
"description": "Optional local image file paths used as references.",
|
|
62
|
+
},
|
|
63
|
+
"generation": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"description": "Optional per-call image generation settings.",
|
|
66
|
+
"properties": {
|
|
67
|
+
"aspect_ratio": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"description": "Aspect ratio, e.g. '16:9', '1:1', '9:16'.",
|
|
70
|
+
},
|
|
71
|
+
"image_size": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"enum": ["1K", "2K", "4K"],
|
|
74
|
+
"description": "Output size for Nano Banana Pro (must use uppercase K).",
|
|
75
|
+
},
|
|
76
|
+
"extra": {
|
|
77
|
+
"type": "object",
|
|
78
|
+
"description": "Provider/model-specific extra parameters (future-proofing).",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
"additionalProperties": False,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
"required": ["prompt"],
|
|
85
|
+
"additionalProperties": False,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _quote_at_pattern_path(path: str) -> str:
|
|
90
|
+
if any(ch.isspace() for ch in path) or '"' in path:
|
|
91
|
+
escaped = path.replace('"', '\\"')
|
|
92
|
+
return f'@"{escaped}"'
|
|
93
|
+
return f"@{path}"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _build_image_gen_prompt(args: dict[str, Any]) -> str:
|
|
97
|
+
prompt = str(args.get("prompt") or "").strip()
|
|
98
|
+
image_paths = args.get("image_paths")
|
|
99
|
+
|
|
100
|
+
lines: list[str] = ["Generate images: " + prompt]
|
|
101
|
+
if isinstance(image_paths, list) and image_paths:
|
|
102
|
+
referenced = [str(p) for p in cast(list[object], image_paths) if str(p).strip()]
|
|
103
|
+
if referenced:
|
|
104
|
+
lines.append("\n# Reference images\n" + "\n".join(_quote_at_pattern_path(p) for p in referenced))
|
|
105
|
+
|
|
106
|
+
return "\n".join(lines).strip()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
register_sub_agent(
|
|
110
|
+
SubAgentProfile(
|
|
111
|
+
name="ImageGen",
|
|
112
|
+
description=IMAGE_GEN_DESCRIPTION,
|
|
113
|
+
parameters=IMAGE_GEN_PARAMETERS,
|
|
114
|
+
prompt_file="prompts/prompt-sub-agent-image-gen.md",
|
|
115
|
+
tool_set=(),
|
|
116
|
+
prompt_builder=_build_image_gen_prompt,
|
|
117
|
+
active_form="Generating Image",
|
|
118
|
+
)
|
|
119
|
+
)
|
|
@@ -24,11 +24,21 @@ Usage notes:
|
|
|
24
24
|
Structured output:
|
|
25
25
|
- Provide an `output_format` (JSON Schema) parameter for structured data back from the agent
|
|
26
26
|
- Example: `output_format={"type": "object", "properties": {"files": {"type": "array", "items": {"type": "string"}, "description": "List of file paths that match the search criteria, e.g. ['src/main.py', 'src/utils/helper.py']"}}, "required": ["files"]}`\
|
|
27
|
+
|
|
28
|
+
- Agents can be resumed using the `resume` parameter by passing the agent ID from a previous invocation. When resumed, the agent
|
|
29
|
+
continues with its full previous context preserved. When NOT resuming, each invocation starts fresh and you should provide a detailed
|
|
30
|
+
task description with all necessary context.
|
|
31
|
+
- When the agent is done, it will return a single message back to you along with its agent ID. You can use this ID to resume the agent
|
|
32
|
+
later if needed for follow-up work.
|
|
27
33
|
"""
|
|
28
34
|
|
|
29
35
|
TASK_PARAMETERS = {
|
|
30
36
|
"type": "object",
|
|
31
37
|
"properties": {
|
|
38
|
+
"resume": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript.",
|
|
41
|
+
},
|
|
32
42
|
"description": {
|
|
33
43
|
"type": "string",
|
|
34
44
|
"description": "A short (3-5 word) description of the task",
|
|
@@ -29,11 +29,21 @@ What you receive:
|
|
|
29
29
|
- With `output_format`, you receive structured JSON matching your schema
|
|
30
30
|
- The response is the agent's analysis, not raw web content
|
|
31
31
|
- Web content is saved to local files (paths included in Sources) - read them directly if you need full content\
|
|
32
|
+
|
|
33
|
+
- Agents can be resumed using the `resume` parameter by passing the agent ID from a previous invocation. When resumed, the agent
|
|
34
|
+
continues with its full previous context preserved. When NOT resuming, each invocation starts fresh and you should provide a detailed
|
|
35
|
+
task description with all necessary context.
|
|
36
|
+
- When the agent is done, it will return a single message back to you along with its agent ID. You can use this ID to resume the agent
|
|
37
|
+
later if needed for follow-up work.
|
|
32
38
|
"""
|
|
33
39
|
|
|
34
40
|
WEB_AGENT_PARAMETERS = {
|
|
35
41
|
"type": "object",
|
|
36
42
|
"properties": {
|
|
43
|
+
"resume": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript.",
|
|
46
|
+
},
|
|
37
47
|
"description": {
|
|
38
48
|
"type": "string",
|
|
39
49
|
"description": "A short (3-5 word) description of the task",
|
klaude_code/session/codec.py
CHANGED
|
@@ -5,7 +5,7 @@ from typing import Any, TypeGuard, cast, get_args
|
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel
|
|
7
7
|
|
|
8
|
-
from klaude_code.protocol import
|
|
8
|
+
from klaude_code.protocol import message
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def _is_basemodel_subclass(tp: object) -> TypeGuard[type[BaseModel]]:
|
|
@@ -24,7 +24,7 @@ def _flatten_union(tp: object) -> list[object]:
|
|
|
24
24
|
|
|
25
25
|
def _build_type_registry() -> dict[str, type[BaseModel]]:
|
|
26
26
|
registry: dict[str, type[BaseModel]] = {}
|
|
27
|
-
for tp in _flatten_union(
|
|
27
|
+
for tp in _flatten_union(message.HistoryEvent):
|
|
28
28
|
if not _is_basemodel_subclass(tp):
|
|
29
29
|
continue
|
|
30
30
|
registry[tp.__name__] = tp
|
|
@@ -34,11 +34,11 @@ def _build_type_registry() -> dict[str, type[BaseModel]]:
|
|
|
34
34
|
_CONVERSATION_ITEM_TYPES: dict[str, type[BaseModel]] = _build_type_registry()
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def encode_conversation_item(item:
|
|
37
|
+
def encode_conversation_item(item: message.HistoryEvent) -> dict[str, Any]:
|
|
38
38
|
return {"type": item.__class__.__name__, "data": item.model_dump(mode="json")}
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
def decode_conversation_item(obj: dict[str, Any]) ->
|
|
41
|
+
def decode_conversation_item(obj: dict[str, Any]) -> message.HistoryEvent | None:
|
|
42
42
|
t = obj.get("type")
|
|
43
43
|
data = obj.get("data", {})
|
|
44
44
|
if not isinstance(t, str) or not isinstance(data, dict):
|
|
@@ -54,11 +54,11 @@ def decode_conversation_item(obj: dict[str, Any]) -> model.ConversationItem | No
|
|
|
54
54
|
return item # type: ignore[return-value]
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def encode_jsonl_line(item:
|
|
57
|
+
def encode_jsonl_line(item: message.HistoryEvent) -> str:
|
|
58
58
|
return json.dumps(encode_conversation_item(item), ensure_ascii=False) + "\n"
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
def decode_jsonl_line(line: str) ->
|
|
61
|
+
def decode_jsonl_line(line: str) -> message.HistoryEvent | None:
|
|
62
62
|
line = line.strip()
|
|
63
63
|
if not line:
|
|
64
64
|
return None
|