Implemented the next parity slice: prompt-budget preflight and context collapse.
Core changes:
- Added claw-code/src/token_budget.py for projected prompt size, chat-framing overhead, output reserve, and soft/hard input limits.
- Wired preflight prompt-length validation and auto-compact/context collapse into claw-code/src/agent_runtime.py.
- Extended claw-code/src/compact.py so compaction reports usage back to the runtime.
- Added inspection surfaces in claw-code/src/agent_slash_commands.py and claw-code/src/main.py:
- /token-budget and /budget
- token-budget
- Hardened claw-code/src/tokenizer_runtime.py so arbitrary simple model names fall back cleanly instead of trying a slow Transformers
lookup.
- Exported the new helpers in claw-code/src/__init__.py.
Docs and tracking:
- Updated claw-code/PARITY_CHECKLIST.md to mark prompt-length validation, token-budget calculation, and auto-compact/context collapse as
done.
- Updated claw-code/README.md and claw-code/TESTING_GUIDE.md with the new commands and behavior.
Tests:
- Added claw-code/tests/test_token_budget.py.
- Updated claw-code/tests/test_agent_runtime.py, claw-code/tests/test_agent_slash_commands.py, claw-code/tests/test_main.py, and claw-code/
tests/test_agent_context_usage.py.
- Verified with:
- /data/fs201059/aa17626/miniconda3/bin/python3 -m compileall src tests
- /data/fs201059/aa17626/miniconda3/bin/python3 -m unittest -v tests.test_token_budget
tests.test_agent_runtime.AgentRuntimeTests.test_agent_rejects_prompt_before_backend_when_preflight_input_budget_is_exceeded
tests.test_agent_runtime.AgentRuntimeTests.test_agent_auto_compacts_context_before_next_model_call tests.test_agent_slash_commands
tests.test_main tests.test_compact tests.test_tokenizer_runtime tests.test_agent_context_usage
- Result: 71 tests, OK
This commit is contained in:
@@ -109,6 +109,11 @@ def get_slash_command_specs() -> tuple[SlashCommandSpec, ...]:
|
||||
description='Show the raw environment, user context, and system context snapshot.',
|
||||
handler=_handle_context_raw,
|
||||
),
|
||||
SlashCommandSpec(
|
||||
names=('token-budget', 'budget'),
|
||||
description='Show the current token-budget window, reserves, and prompt-length limits.',
|
||||
handler=_handle_token_budget,
|
||||
),
|
||||
SlashCommandSpec(
|
||||
names=('mcp',),
|
||||
description='Show discovered local MCP manifests and resource counts.',
|
||||
@@ -154,6 +159,11 @@ def get_slash_command_specs() -> tuple[SlashCommandSpec, ...]:
|
||||
description='Show local config runtime state, effective config, config sources, or a config value.',
|
||||
handler=_handle_config,
|
||||
),
|
||||
SlashCommandSpec(
|
||||
names=('lsp',),
|
||||
description='Show local LSP runtime status or run document symbols, definition, references, hover, call hierarchy, and diagnostics queries.',
|
||||
handler=_handle_lsp,
|
||||
),
|
||||
SlashCommandSpec(
|
||||
names=('remotes',),
|
||||
description='List configured local remote profiles.',
|
||||
@@ -395,6 +405,10 @@ def _handle_context_raw(agent: 'LocalCodingAgent', _args: str, input_text: str)
|
||||
return _local_result(input_text, agent.render_context_snapshot_report())
|
||||
|
||||
|
||||
def _handle_token_budget(agent: 'LocalCodingAgent', _args: str, input_text: str) -> SlashCommandResult:
|
||||
return _local_result(input_text, agent.render_token_budget_report())
|
||||
|
||||
|
||||
def _handle_mcp(agent: 'LocalCodingAgent', args: str, input_text: str) -> SlashCommandResult:
|
||||
command = args.strip()
|
||||
if not command:
|
||||
@@ -521,6 +535,81 @@ def _handle_config(agent: 'LocalCodingAgent', args: str, input_text: str) -> Sla
|
||||
)
|
||||
|
||||
|
||||
def _handle_lsp(agent: 'LocalCodingAgent', args: str, input_text: str) -> SlashCommandResult:
|
||||
command = args.strip()
|
||||
if not command:
|
||||
return _local_result(input_text, agent.render_lsp_report())
|
||||
if command == 'diagnostics':
|
||||
return _local_result(input_text, agent.render_lsp_diagnostics_report())
|
||||
if command.startswith('diagnostics '):
|
||||
file_path = command.split(' ', 1)[1].strip()
|
||||
if not file_path:
|
||||
return _local_result(input_text, 'Usage: /lsp diagnostics [file-path]')
|
||||
return _local_result(input_text, agent.render_lsp_diagnostics_report(file_path))
|
||||
if command.startswith('symbols '):
|
||||
file_path = command.split(' ', 1)[1].strip()
|
||||
if not file_path:
|
||||
return _local_result(input_text, 'Usage: /lsp symbols <file-path>')
|
||||
return _local_result(input_text, agent.render_lsp_document_symbols_report(file_path))
|
||||
if command.startswith('workspace '):
|
||||
query = command.split(' ', 1)[1].strip()
|
||||
if not query:
|
||||
return _local_result(input_text, 'Usage: /lsp workspace <query>')
|
||||
return _local_result(input_text, agent.render_lsp_workspace_symbols_report(query))
|
||||
parts = command.split()
|
||||
if len(parts) == 4 and parts[0] in {
|
||||
'definition',
|
||||
'references',
|
||||
'hover',
|
||||
'hierarchy',
|
||||
'incoming',
|
||||
'outgoing',
|
||||
}:
|
||||
subcommand, file_path, line_text, character_text = parts
|
||||
try:
|
||||
line = int(line_text)
|
||||
character = int(character_text)
|
||||
except ValueError:
|
||||
return _local_result(
|
||||
input_text,
|
||||
f'Usage: /lsp {subcommand} <file-path> <line> <character>',
|
||||
)
|
||||
if subcommand == 'definition':
|
||||
return _local_result(
|
||||
input_text,
|
||||
agent.render_lsp_definition_report(file_path, line, character),
|
||||
)
|
||||
if subcommand == 'references':
|
||||
return _local_result(
|
||||
input_text,
|
||||
agent.render_lsp_references_report(file_path, line, character),
|
||||
)
|
||||
if subcommand == 'hover':
|
||||
return _local_result(
|
||||
input_text,
|
||||
agent.render_lsp_hover_report(file_path, line, character),
|
||||
)
|
||||
if subcommand == 'hierarchy':
|
||||
return _local_result(
|
||||
input_text,
|
||||
agent.render_lsp_prepare_call_hierarchy_report(file_path, line, character),
|
||||
)
|
||||
if subcommand == 'incoming':
|
||||
return _local_result(
|
||||
input_text,
|
||||
agent.render_lsp_incoming_calls_report(file_path, line, character),
|
||||
)
|
||||
if subcommand == 'outgoing':
|
||||
return _local_result(
|
||||
input_text,
|
||||
agent.render_lsp_outgoing_calls_report(file_path, line, character),
|
||||
)
|
||||
return _local_result(
|
||||
input_text,
|
||||
'Usage: /lsp [symbols <file>|workspace <query>|definition <file> <line> <character>|references <file> <line> <character>|hover <file> <line> <character>|hierarchy <file> <line> <character>|incoming <file> <line> <character>|outgoing <file> <line> <character>|diagnostics [file]]',
|
||||
)
|
||||
|
||||
|
||||
def _handle_remotes(agent: 'LocalCodingAgent', args: str, input_text: str) -> SlashCommandResult:
|
||||
query = args or None
|
||||
return _local_result(input_text, agent.render_remote_profiles_report(query))
|
||||
|
||||
Reference in New Issue
Block a user