klaude-code 1.2.11__py3-none-any.whl → 1.2.12__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/command/__init__.py +29 -26
- klaude_code/command/clear_cmd.py +0 -2
- klaude_code/command/diff_cmd.py +0 -2
- klaude_code/command/export_cmd.py +0 -2
- klaude_code/command/help_cmd.py +0 -2
- klaude_code/command/model_cmd.py +0 -2
- klaude_code/command/refresh_cmd.py +0 -2
- klaude_code/command/registry.py +4 -8
- klaude_code/command/release_notes_cmd.py +0 -2
- klaude_code/command/status_cmd.py +2 -4
- klaude_code/command/terminal_setup_cmd.py +0 -2
- klaude_code/command/thinking_cmd.py +227 -0
- klaude_code/config/select_model.py +5 -15
- klaude_code/const/__init__.py +1 -1
- klaude_code/core/agent.py +1 -8
- klaude_code/core/prompt.py +1 -1
- klaude_code/core/task.py +2 -3
- klaude_code/core/turn.py +0 -1
- klaude_code/llm/anthropic/client.py +56 -47
- klaude_code/llm/client.py +1 -19
- klaude_code/llm/codex/client.py +49 -30
- klaude_code/llm/openai_compatible/client.py +52 -34
- klaude_code/llm/openrouter/client.py +63 -41
- klaude_code/llm/responses/client.py +56 -39
- klaude_code/llm/usage.py +1 -49
- klaude_code/protocol/commands.py +1 -0
- klaude_code/protocol/llm_param.py +1 -9
- klaude_code/protocol/model.py +3 -5
- klaude_code/session/export.py +1 -8
- klaude_code/session/selector.py +12 -7
- klaude_code/ui/modes/repl/completers.py +3 -3
- klaude_code/ui/renderers/metadata.py +1 -12
- {klaude_code-1.2.11.dist-info → klaude_code-1.2.12.dist-info}/METADATA +1 -1
- {klaude_code-1.2.11.dist-info → klaude_code-1.2.12.dist-info}/RECORD +36 -35
- {klaude_code-1.2.11.dist-info → klaude_code-1.2.12.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.11.dist-info → klaude_code-1.2.12.dist-info}/entry_points.txt +0 -0
klaude_code/command/__init__.py
CHANGED
|
@@ -5,7 +5,7 @@ from .registry import (
|
|
|
5
5
|
has_interactive_command,
|
|
6
6
|
is_slash_command_name,
|
|
7
7
|
load_prompt_commands,
|
|
8
|
-
|
|
8
|
+
register,
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
# Lazy load commands to avoid heavy imports at module load time
|
|
@@ -17,38 +17,41 @@ def ensure_commands_loaded() -> None:
|
|
|
17
17
|
|
|
18
18
|
This function is called internally by registry functions like get_commands(),
|
|
19
19
|
dispatch_command(), etc. It can also be called explicitly if early loading is desired.
|
|
20
|
+
|
|
21
|
+
Commands are registered in display order - the order here determines
|
|
22
|
+
the order shown in slash command completion.
|
|
20
23
|
"""
|
|
21
24
|
global _commands_loaded
|
|
22
25
|
if _commands_loaded:
|
|
23
26
|
return
|
|
24
27
|
_commands_loaded = True
|
|
25
28
|
|
|
26
|
-
# Import
|
|
27
|
-
from . import
|
|
28
|
-
from . import
|
|
29
|
-
from . import
|
|
30
|
-
from . import
|
|
31
|
-
from . import
|
|
32
|
-
from . import
|
|
33
|
-
from . import
|
|
34
|
-
from . import
|
|
35
|
-
from . import
|
|
36
|
-
|
|
37
|
-
# Suppress unused variable warnings
|
|
38
|
-
_ = (
|
|
39
|
-
_clear_cmd,
|
|
40
|
-
_diff_cmd,
|
|
41
|
-
_export_cmd,
|
|
42
|
-
_help_cmd,
|
|
43
|
-
_model_cmd,
|
|
44
|
-
_refresh_cmd,
|
|
45
|
-
_release_notes_cmd,
|
|
46
|
-
_status_cmd,
|
|
47
|
-
_terminal_setup_cmd,
|
|
48
|
-
)
|
|
29
|
+
# Import and register commands in display order
|
|
30
|
+
from .clear_cmd import ClearCommand
|
|
31
|
+
from .model_cmd import ModelCommand
|
|
32
|
+
from .status_cmd import StatusCommand
|
|
33
|
+
from .diff_cmd import DiffCommand
|
|
34
|
+
from .export_cmd import ExportCommand
|
|
35
|
+
from .thinking_cmd import ThinkingCommand
|
|
36
|
+
from .help_cmd import HelpCommand
|
|
37
|
+
from .refresh_cmd import RefreshTerminalCommand
|
|
38
|
+
from .terminal_setup_cmd import TerminalSetupCommand
|
|
39
|
+
from .release_notes_cmd import ReleaseNotesCommand
|
|
49
40
|
|
|
50
|
-
#
|
|
41
|
+
# Register in desired display order
|
|
42
|
+
register(ExportCommand())
|
|
43
|
+
register(RefreshTerminalCommand())
|
|
44
|
+
register(ThinkingCommand())
|
|
45
|
+
register(ModelCommand())
|
|
51
46
|
load_prompt_commands()
|
|
47
|
+
register(ClearCommand())
|
|
48
|
+
register(StatusCommand())
|
|
49
|
+
register(DiffCommand())
|
|
50
|
+
register(HelpCommand())
|
|
51
|
+
register(ReleaseNotesCommand())
|
|
52
|
+
register(TerminalSetupCommand())
|
|
53
|
+
|
|
54
|
+
# Load prompt-based commands (appended after built-in commands)
|
|
52
55
|
|
|
53
56
|
|
|
54
57
|
# Lazy accessors for command classes
|
|
@@ -63,6 +66,7 @@ def __getattr__(name: str) -> object:
|
|
|
63
66
|
"ReleaseNotesCommand": "release_notes_cmd",
|
|
64
67
|
"StatusCommand": "status_cmd",
|
|
65
68
|
"TerminalSetupCommand": "terminal_setup_cmd",
|
|
69
|
+
"ThinkingCommand": "thinking_cmd",
|
|
66
70
|
}
|
|
67
71
|
if name in _commands_map:
|
|
68
72
|
import importlib
|
|
@@ -77,7 +81,6 @@ __all__ = [
|
|
|
77
81
|
# "ClearCommand", "DiffCommand", "HelpCommand", "ModelCommand",
|
|
78
82
|
# "ExportCommand", "RefreshTerminalCommand", "ReleaseNotesCommand",
|
|
79
83
|
# "StatusCommand", "TerminalSetupCommand",
|
|
80
|
-
"register_command",
|
|
81
84
|
"CommandABC",
|
|
82
85
|
"CommandResult",
|
|
83
86
|
"InputAction",
|
klaude_code/command/clear_cmd.py
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING
|
|
2
2
|
|
|
3
3
|
from klaude_code.command.command_abc import CommandABC, CommandResult, InputAction
|
|
4
|
-
from klaude_code.command.registry import register_command
|
|
5
4
|
from klaude_code.protocol import commands
|
|
6
5
|
|
|
7
6
|
if TYPE_CHECKING:
|
|
8
7
|
from klaude_code.core.agent import Agent
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
@register_command
|
|
12
10
|
class ClearCommand(CommandABC):
|
|
13
11
|
"""Clear current session and start a new conversation"""
|
|
14
12
|
|
klaude_code/command/diff_cmd.py
CHANGED
|
@@ -3,14 +3,12 @@ from pathlib import Path
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
6
|
-
from klaude_code.command.registry import register_command
|
|
7
6
|
from klaude_code.protocol import commands, events, model
|
|
8
7
|
|
|
9
8
|
if TYPE_CHECKING:
|
|
10
9
|
from klaude_code.core.agent import Agent
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
@register_command
|
|
14
12
|
class DiffCommand(CommandABC):
|
|
15
13
|
"""Show git diff for the current repository."""
|
|
16
14
|
|
|
@@ -5,7 +5,6 @@ from pathlib import Path
|
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
8
|
-
from klaude_code.command.registry import register_command
|
|
9
8
|
from klaude_code.protocol import commands, events, model
|
|
10
9
|
from klaude_code.session.export import build_export_html, get_default_export_path
|
|
11
10
|
|
|
@@ -13,7 +12,6 @@ if TYPE_CHECKING:
|
|
|
13
12
|
from klaude_code.core.agent import Agent
|
|
14
13
|
|
|
15
14
|
|
|
16
|
-
@register_command
|
|
17
15
|
class ExportCommand(CommandABC):
|
|
18
16
|
"""Export the current session into a standalone HTML transcript."""
|
|
19
17
|
|
klaude_code/command/help_cmd.py
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING
|
|
2
2
|
|
|
3
3
|
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
4
|
-
from klaude_code.command.registry import register_command
|
|
5
4
|
from klaude_code.protocol import commands, events, model
|
|
6
5
|
|
|
7
6
|
if TYPE_CHECKING:
|
|
8
7
|
from klaude_code.core.agent import Agent
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
@register_command
|
|
12
10
|
class HelpCommand(CommandABC):
|
|
13
11
|
"""Display help information for all available slash commands."""
|
|
14
12
|
|
klaude_code/command/model_cmd.py
CHANGED
|
@@ -2,7 +2,6 @@ import asyncio
|
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from klaude_code.command.command_abc import CommandABC, CommandResult, InputAction
|
|
5
|
-
from klaude_code.command.registry import register_command
|
|
6
5
|
from klaude_code.config import select_model_from_config
|
|
7
6
|
from klaude_code.protocol import commands, events, model
|
|
8
7
|
|
|
@@ -10,7 +9,6 @@ if TYPE_CHECKING:
|
|
|
10
9
|
from klaude_code.core.agent import Agent
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
@register_command
|
|
14
12
|
class ModelCommand(CommandABC):
|
|
15
13
|
"""Display or change the model configuration."""
|
|
16
14
|
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING
|
|
2
2
|
|
|
3
3
|
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
4
|
-
from klaude_code.command.registry import register_command
|
|
5
4
|
from klaude_code.protocol import commands, events
|
|
6
5
|
|
|
7
6
|
if TYPE_CHECKING:
|
|
8
7
|
from klaude_code.core.agent import Agent
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
@register_command
|
|
12
10
|
class RefreshTerminalCommand(CommandABC):
|
|
13
11
|
"""Refresh terminal display"""
|
|
14
12
|
|
klaude_code/command/registry.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from importlib.resources import files
|
|
2
|
-
from typing import TYPE_CHECKING
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from klaude_code.command.command_abc import CommandResult, InputAction
|
|
5
5
|
from klaude_code.command.prompt_command import PromptCommand
|
|
@@ -13,14 +13,10 @@ if TYPE_CHECKING:
|
|
|
13
13
|
|
|
14
14
|
_COMMANDS: dict[commands.CommandName | str, "CommandABC"] = {}
|
|
15
15
|
|
|
16
|
-
T = TypeVar("T", bound="CommandABC")
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
instance = cls()
|
|
22
|
-
_COMMANDS[instance.name] = instance
|
|
23
|
-
return cls
|
|
17
|
+
def register(cmd: "CommandABC") -> None:
|
|
18
|
+
"""Register a command instance. Order of registration determines display order."""
|
|
19
|
+
_COMMANDS[cmd.name] = cmd
|
|
24
20
|
|
|
25
21
|
|
|
26
22
|
def load_prompt_commands():
|
|
@@ -2,7 +2,6 @@ from pathlib import Path
|
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
5
|
-
from klaude_code.command.registry import register_command
|
|
6
5
|
from klaude_code.protocol import commands, events, model
|
|
7
6
|
|
|
8
7
|
if TYPE_CHECKING:
|
|
@@ -62,7 +61,6 @@ def _extract_releases(changelog: str, count: int = 1) -> str:
|
|
|
62
61
|
return "\n".join("\n".join(release) for release in releases).strip()
|
|
63
62
|
|
|
64
63
|
|
|
65
|
-
@register_command
|
|
66
64
|
class ReleaseNotesCommand(CommandABC):
|
|
67
65
|
"""Display the latest release notes from CHANGELOG.md."""
|
|
68
66
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING
|
|
2
2
|
|
|
3
3
|
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
4
|
-
from klaude_code.command.registry import register_command
|
|
5
4
|
from klaude_code.protocol import commands, events, model
|
|
6
5
|
from klaude_code.session.session import Session
|
|
7
6
|
|
|
@@ -60,8 +59,8 @@ def accumulate_session_usage(session: Session) -> AggregatedUsage:
|
|
|
60
59
|
total.cache_read_cost = (total.cache_read_cost or 0.0) + usage.cache_read_cost
|
|
61
60
|
|
|
62
61
|
# Track peak context window size (max across all tasks)
|
|
63
|
-
if usage.
|
|
64
|
-
total.
|
|
62
|
+
if usage.context_size is not None:
|
|
63
|
+
total.context_size = usage.context_size
|
|
65
64
|
|
|
66
65
|
# Keep the latest context_limit for computed context_usage_percent
|
|
67
66
|
if usage.context_limit is not None:
|
|
@@ -127,7 +126,6 @@ def format_status_content(aggregated: AggregatedUsage) -> str:
|
|
|
127
126
|
return "\n".join(lines)
|
|
128
127
|
|
|
129
128
|
|
|
130
|
-
@register_command
|
|
131
129
|
class StatusCommand(CommandABC):
|
|
132
130
|
"""Display session usage statistics."""
|
|
133
131
|
|
|
@@ -4,14 +4,12 @@ from pathlib import Path
|
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
6
|
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
7
|
-
from klaude_code.command.registry import register_command
|
|
8
7
|
from klaude_code.protocol import commands, events, model
|
|
9
8
|
|
|
10
9
|
if TYPE_CHECKING:
|
|
11
10
|
from klaude_code.core.agent import Agent
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
@register_command
|
|
15
13
|
class TerminalSetupCommand(CommandABC):
|
|
16
14
|
"""Setup shift+enter newline functionality in terminal"""
|
|
17
15
|
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
import questionary
|
|
5
|
+
|
|
6
|
+
from klaude_code.command.command_abc import CommandABC, CommandResult
|
|
7
|
+
from klaude_code.protocol import commands, events, llm_param, model
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from klaude_code.core.agent import Agent
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Thinking level options for different protocols
|
|
14
|
+
RESPONSES_LEVELS = ["minimal", "low", "medium", "high"]
|
|
15
|
+
RESPONSES_GPT51_LEVELS = ["none", "minimal", "low", "medium", "high"]
|
|
16
|
+
RESPONSES_CODEX_MAX_LEVELS = ["medium", "high", "xhigh"]
|
|
17
|
+
|
|
18
|
+
ANTHROPIC_LEVELS: list[tuple[str, int | None]] = [
|
|
19
|
+
("off", 0),
|
|
20
|
+
("low (2048 tokens)", 2048),
|
|
21
|
+
("medium (8192 tokens)", 8192),
|
|
22
|
+
("high (31999 tokens)", 31999),
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _is_openrouter_model_with_reasoning_effort(model_name: str | None) -> bool:
|
|
27
|
+
"""Check if the model is GPT series, Grok or Gemini 3."""
|
|
28
|
+
if not model_name:
|
|
29
|
+
return False
|
|
30
|
+
model_lower = model_name.lower()
|
|
31
|
+
return model_lower.startswith(("openai/gpt-", "x-ai/grok-", "google/gemini-3"))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _is_gpt51_model(model_name: str | None) -> bool:
|
|
35
|
+
"""Check if the model is GPT-5.1."""
|
|
36
|
+
if not model_name:
|
|
37
|
+
return False
|
|
38
|
+
return model_name.lower() in ["gpt5.1", "openai/gpt-5.1", "gpt-5.1-codex-2025-11-13"]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _is_codex_max_model(model_name: str | None) -> bool:
|
|
42
|
+
"""Check if the model is GPT-5.1-codex-max."""
|
|
43
|
+
if not model_name:
|
|
44
|
+
return False
|
|
45
|
+
return "codex-max" in model_name.lower()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _get_levels_for_responses(model_name: str | None) -> list[str]:
|
|
49
|
+
"""Get thinking levels for responses protocol."""
|
|
50
|
+
if _is_codex_max_model(model_name):
|
|
51
|
+
return RESPONSES_CODEX_MAX_LEVELS
|
|
52
|
+
if _is_gpt51_model(model_name):
|
|
53
|
+
return RESPONSES_GPT51_LEVELS
|
|
54
|
+
return RESPONSES_LEVELS
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _format_current_thinking(config: llm_param.LLMConfigParameter) -> str:
|
|
58
|
+
"""Format the current thinking configuration for display."""
|
|
59
|
+
thinking = config.thinking
|
|
60
|
+
if not thinking:
|
|
61
|
+
return "not configured"
|
|
62
|
+
|
|
63
|
+
protocol = config.protocol
|
|
64
|
+
|
|
65
|
+
if protocol in (llm_param.LLMClientProtocol.RESPONSES, llm_param.LLMClientProtocol.CODEX):
|
|
66
|
+
if thinking.reasoning_effort:
|
|
67
|
+
return f"reasoning_effort={thinking.reasoning_effort}"
|
|
68
|
+
return "not set"
|
|
69
|
+
|
|
70
|
+
if protocol == llm_param.LLMClientProtocol.ANTHROPIC:
|
|
71
|
+
if thinking.type == "disabled":
|
|
72
|
+
return "off"
|
|
73
|
+
if thinking.type == "enabled":
|
|
74
|
+
return f"enabled (budget_tokens={thinking.budget_tokens})"
|
|
75
|
+
return "not set"
|
|
76
|
+
|
|
77
|
+
if protocol == llm_param.LLMClientProtocol.OPENROUTER:
|
|
78
|
+
if _is_openrouter_model_with_reasoning_effort(config.model):
|
|
79
|
+
if thinking.reasoning_effort:
|
|
80
|
+
return f"reasoning_effort={thinking.reasoning_effort}"
|
|
81
|
+
else:
|
|
82
|
+
if thinking.type == "disabled":
|
|
83
|
+
return "off"
|
|
84
|
+
if thinking.type == "enabled":
|
|
85
|
+
return f"enabled (budget_tokens={thinking.budget_tokens})"
|
|
86
|
+
return "not set"
|
|
87
|
+
|
|
88
|
+
if protocol == llm_param.LLMClientProtocol.OPENAI:
|
|
89
|
+
if thinking.type == "disabled":
|
|
90
|
+
return "off"
|
|
91
|
+
if thinking.type == "enabled":
|
|
92
|
+
return f"enabled (budget_tokens={thinking.budget_tokens})"
|
|
93
|
+
return "not set"
|
|
94
|
+
|
|
95
|
+
return "unknown protocol"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
SELECT_STYLE = questionary.Style([
|
|
99
|
+
("instruction", "ansibrightblack"),
|
|
100
|
+
("pointer", "ansicyan"),
|
|
101
|
+
("highlighted", "ansicyan"),
|
|
102
|
+
("text", "ansibrightblack"),
|
|
103
|
+
])
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _select_responses_thinking_sync(model_name: str | None) -> llm_param.Thinking | None:
|
|
107
|
+
"""Select thinking level for responses/codex protocol (sync version)."""
|
|
108
|
+
levels = _get_levels_for_responses(model_name)
|
|
109
|
+
choices: list[questionary.Choice] = [questionary.Choice(title=level, value=level) for level in levels]
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
result = questionary.select(
|
|
113
|
+
message="Select reasoning effort:",
|
|
114
|
+
choices=choices,
|
|
115
|
+
pointer="→",
|
|
116
|
+
instruction="Use arrow keys to move, Enter to select",
|
|
117
|
+
use_jk_keys=False,
|
|
118
|
+
style=SELECT_STYLE,
|
|
119
|
+
).ask()
|
|
120
|
+
|
|
121
|
+
if result is None:
|
|
122
|
+
return None
|
|
123
|
+
return llm_param.Thinking(reasoning_effort=result)
|
|
124
|
+
except KeyboardInterrupt:
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _select_anthropic_thinking_sync() -> llm_param.Thinking | None:
|
|
129
|
+
"""Select thinking level for anthropic/openai_compatible protocol (sync version)."""
|
|
130
|
+
choices: list[questionary.Choice] = [
|
|
131
|
+
questionary.Choice(title=label, value=tokens) for label, tokens in ANTHROPIC_LEVELS
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
result = questionary.select(
|
|
136
|
+
message="Select thinking level:",
|
|
137
|
+
choices=choices,
|
|
138
|
+
pointer="→",
|
|
139
|
+
instruction="Use arrow keys to move, Enter to select",
|
|
140
|
+
use_jk_keys=False,
|
|
141
|
+
style=SELECT_STYLE,
|
|
142
|
+
).ask()
|
|
143
|
+
if result is None:
|
|
144
|
+
return llm_param.Thinking(type="disabled", budget_tokens=0)
|
|
145
|
+
return llm_param.Thinking(type="enabled", budget_tokens=result or 0)
|
|
146
|
+
except KeyboardInterrupt:
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class ThinkingCommand(CommandABC):
|
|
151
|
+
"""Configure model thinking/reasoning level."""
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def name(self) -> commands.CommandName:
|
|
155
|
+
return commands.CommandName.THINKING
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def summary(self) -> str:
|
|
159
|
+
return "Configure model thinking/reasoning level"
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def is_interactive(self) -> bool:
|
|
163
|
+
return True
|
|
164
|
+
|
|
165
|
+
async def run(self, raw: str, agent: "Agent") -> CommandResult:
|
|
166
|
+
if not agent.profile:
|
|
167
|
+
return self._no_change_result(agent, "No profile configured")
|
|
168
|
+
|
|
169
|
+
config = agent.profile.llm_client.get_llm_config()
|
|
170
|
+
protocol = config.protocol
|
|
171
|
+
model_name = config.model
|
|
172
|
+
|
|
173
|
+
current = _format_current_thinking(config)
|
|
174
|
+
|
|
175
|
+
# Select new thinking configuration based on protocol
|
|
176
|
+
new_thinking: llm_param.Thinking | None = None
|
|
177
|
+
|
|
178
|
+
if protocol in (llm_param.LLMClientProtocol.RESPONSES, llm_param.LLMClientProtocol.CODEX):
|
|
179
|
+
new_thinking = await asyncio.to_thread(_select_responses_thinking_sync, model_name)
|
|
180
|
+
|
|
181
|
+
elif protocol == llm_param.LLMClientProtocol.ANTHROPIC:
|
|
182
|
+
new_thinking = await asyncio.to_thread(_select_anthropic_thinking_sync)
|
|
183
|
+
|
|
184
|
+
elif protocol == llm_param.LLMClientProtocol.OPENROUTER:
|
|
185
|
+
if _is_openrouter_model_with_reasoning_effort(model_name):
|
|
186
|
+
new_thinking = await asyncio.to_thread(_select_responses_thinking_sync, model_name)
|
|
187
|
+
else:
|
|
188
|
+
new_thinking = await asyncio.to_thread(_select_anthropic_thinking_sync)
|
|
189
|
+
|
|
190
|
+
elif protocol == llm_param.LLMClientProtocol.OPENAI:
|
|
191
|
+
# openai_compatible uses anthropic style
|
|
192
|
+
new_thinking = await asyncio.to_thread(_select_anthropic_thinking_sync)
|
|
193
|
+
|
|
194
|
+
else:
|
|
195
|
+
return self._no_change_result(agent, f"Unsupported protocol: {protocol}")
|
|
196
|
+
|
|
197
|
+
if new_thinking is None:
|
|
198
|
+
return self._no_change_result(agent, "(no change)")
|
|
199
|
+
|
|
200
|
+
# Apply the new thinking configuration
|
|
201
|
+
config.thinking = new_thinking
|
|
202
|
+
new_status = _format_current_thinking(config)
|
|
203
|
+
|
|
204
|
+
return CommandResult(
|
|
205
|
+
events=[
|
|
206
|
+
events.DeveloperMessageEvent(
|
|
207
|
+
session_id=agent.session.id,
|
|
208
|
+
item=model.DeveloperMessageItem(
|
|
209
|
+
content=f"Thinking changed: {current} -> {new_status}",
|
|
210
|
+
command_output=model.CommandOutput(command_name=self.name),
|
|
211
|
+
),
|
|
212
|
+
)
|
|
213
|
+
]
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def _no_change_result(self, agent: "Agent", message: str) -> CommandResult:
|
|
217
|
+
return CommandResult(
|
|
218
|
+
events=[
|
|
219
|
+
events.DeveloperMessageEvent(
|
|
220
|
+
session_id=agent.session.id,
|
|
221
|
+
item=model.DeveloperMessageItem(
|
|
222
|
+
content=message,
|
|
223
|
+
command_output=model.CommandOutput(command_name=self.name),
|
|
224
|
+
),
|
|
225
|
+
)
|
|
226
|
+
]
|
|
227
|
+
)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from klaude_code.config.config import load_config
|
|
2
2
|
from klaude_code.trace import log
|
|
3
|
-
from klaude_code.ui.rich.searchable_text import SearchableFormattedList
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
def select_model_from_config(preferred: str | None = None) -> str | None:
|
|
@@ -16,9 +15,6 @@ def select_model_from_config(preferred: str | None = None) -> str | None:
|
|
|
16
15
|
raise ValueError("No models configured. Please update your config.yaml")
|
|
17
16
|
|
|
18
17
|
names: list[str] = [m.model_name for m in models]
|
|
19
|
-
default_name: str | None = (
|
|
20
|
-
preferred if preferred in names else (config.main_model if config.main_model in names else None)
|
|
21
|
-
)
|
|
22
18
|
|
|
23
19
|
try:
|
|
24
20
|
import questionary
|
|
@@ -28,29 +24,23 @@ def select_model_from_config(preferred: str | None = None) -> str | None:
|
|
|
28
24
|
max_model_name_length = max(len(m.model_name) for m in models)
|
|
29
25
|
for m in models:
|
|
30
26
|
star = "★ " if m.model_name == config.main_model else " "
|
|
31
|
-
|
|
32
|
-
("class:t", f"{star}{m.model_name:<{max_model_name_length}} → "),
|
|
33
|
-
("class:b", m.model_params.model or "N/A"),
|
|
34
|
-
("class:d", f" {m.provider}"),
|
|
35
|
-
]
|
|
36
|
-
# Provide a formatted title for display and a plain text for search.
|
|
37
|
-
title = SearchableFormattedList(fragments)
|
|
27
|
+
title = f"{star}{m.model_name:<{max_model_name_length}} → {m.model_params.model or 'N/A'} @ {m.provider}"
|
|
38
28
|
choices.append(questionary.Choice(title=title, value=m.model_name))
|
|
39
29
|
|
|
40
30
|
try:
|
|
41
31
|
result = questionary.select(
|
|
42
32
|
message="Select a model:",
|
|
43
33
|
choices=choices,
|
|
44
|
-
default=default_name,
|
|
45
34
|
pointer="→",
|
|
46
35
|
instruction="↑↓ to move • Enter to select",
|
|
47
36
|
use_jk_keys=False,
|
|
48
37
|
use_search_filter=True,
|
|
49
38
|
style=questionary.Style(
|
|
50
39
|
[
|
|
51
|
-
("
|
|
52
|
-
("
|
|
53
|
-
("
|
|
40
|
+
("instruction", "ansibrightblack"),
|
|
41
|
+
("pointer", "ansicyan"),
|
|
42
|
+
("highlighted", "ansicyan"),
|
|
43
|
+
("text", "ansibrightblack"),
|
|
54
44
|
# search filter colors at the bottom
|
|
55
45
|
("search_success", "noinherit fg:ansigreen"),
|
|
56
46
|
("search_none", "noinherit fg:ansired"),
|
klaude_code/const/__init__.py
CHANGED
|
@@ -62,7 +62,7 @@ BASH_DEFAULT_TIMEOUT_MS = 120000
|
|
|
62
62
|
|
|
63
63
|
# -- Tool Output --
|
|
64
64
|
# Maximum length for tool output before truncation
|
|
65
|
-
TOOL_OUTPUT_MAX_LENGTH =
|
|
65
|
+
TOOL_OUTPUT_MAX_LENGTH = 40000
|
|
66
66
|
|
|
67
67
|
# Characters to show from the beginning of truncated output
|
|
68
68
|
TOOL_OUTPUT_DISPLAY_HEAD = 10000
|
klaude_code/core/agent.py
CHANGED
|
@@ -4,7 +4,7 @@ from collections.abc import AsyncGenerator, Callable, Iterable
|
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from typing import TYPE_CHECKING, Protocol
|
|
6
6
|
|
|
7
|
-
from klaude_code.core.prompt import
|
|
7
|
+
from klaude_code.core.prompt import load_system_prompt
|
|
8
8
|
from klaude_code.core.reminders import Reminder, load_agent_reminders
|
|
9
9
|
from klaude_code.core.task import SessionContext, TaskExecutionContext, TaskExecutor
|
|
10
10
|
from klaude_code.core.tool import build_todo_context, get_registry, load_agent_tools
|
|
@@ -122,7 +122,6 @@ class Agent:
|
|
|
122
122
|
self.session: Session = session
|
|
123
123
|
self.profile: AgentProfile = profile
|
|
124
124
|
self._current_task: TaskExecutor | None = None
|
|
125
|
-
self._prev_context_token: int = 0 # Track context size from previous task for delta calculation
|
|
126
125
|
if not self.session.model_name and model_name:
|
|
127
126
|
self.session.model_name = model_name
|
|
128
127
|
|
|
@@ -170,12 +169,6 @@ class Agent:
|
|
|
170
169
|
|
|
171
170
|
try:
|
|
172
171
|
async for event in task.run(user_input):
|
|
173
|
-
# Compute context_delta for TaskMetadataEvent
|
|
174
|
-
if isinstance(event, events.TaskMetadataEvent):
|
|
175
|
-
usage = event.metadata.main.usage
|
|
176
|
-
if usage is not None and usage.context_token is not None:
|
|
177
|
-
usage.context_delta = usage.context_token - self._prev_context_token
|
|
178
|
-
self._prev_context_token = usage.context_token
|
|
179
172
|
yield event
|
|
180
173
|
finally:
|
|
181
174
|
self._current_task = None
|
klaude_code/core/prompt.py
CHANGED
|
@@ -84,7 +84,7 @@ def _build_env_info(model_name: str) -> str:
|
|
|
84
84
|
return "\n".join(env_lines)
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
def
|
|
87
|
+
def load_system_prompt(model_name: str, sub_agent_type: str | None = None) -> str:
|
|
88
88
|
"""Get system prompt content for the given model and sub-agent type."""
|
|
89
89
|
file_key = _get_file_key(model_name, sub_agent_type)
|
|
90
90
|
base_prompt = _load_base_prompt(file_key)
|
klaude_code/core/task.py
CHANGED
|
@@ -45,11 +45,10 @@ class MetadataAccumulator:
|
|
|
45
45
|
acc_usage.cached_tokens += usage.cached_tokens
|
|
46
46
|
acc_usage.reasoning_tokens += usage.reasoning_tokens
|
|
47
47
|
acc_usage.output_tokens += usage.output_tokens
|
|
48
|
-
acc_usage.last_turn_output_token = usage.output_tokens
|
|
49
48
|
acc_usage.currency = usage.currency
|
|
50
49
|
|
|
51
|
-
if usage.
|
|
52
|
-
acc_usage.
|
|
50
|
+
if usage.context_size is not None:
|
|
51
|
+
acc_usage.context_size = usage.context_size
|
|
53
52
|
if usage.context_limit is not None:
|
|
54
53
|
acc_usage.context_limit = usage.context_limit
|
|
55
54
|
|