mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
- Increase headroom wrap startup timeout from 15s to 45s (#114) ML components (Kompress, Magika, Tree-sitter) need 20-30s on slower machines - Detect Strands SDK toolUse/toolResult blocks in find_tool_units (#116) Strands wraps tools as {"toolUse": {"toolUseId": ...}} not {"type": "tool_use"} Without this, tool pairs aren't grouped and dropping one breaks Anthropic validation - Match CCR marker content format to conversation style (#117) Strands expects list-of-blocks content, not plain strings Detect format from existing messages and match it
This commit is contained in:
parent
9c51f63c48
commit
d7122d6bab
4 changed files with 50 additions and 13 deletions
|
|
@ -108,8 +108,10 @@ def _start_proxy(
|
|||
env=proxy_env,
|
||||
)
|
||||
|
||||
# Wait for proxy to be ready (up to 15 seconds)
|
||||
for _i in range(15):
|
||||
# Wait for proxy to be ready (up to 45 seconds).
|
||||
# ML components (Kompress, Magika, Tree-sitter) load synchronously before
|
||||
# uvicorn binds the port. On slower machines this can take 20-30 seconds.
|
||||
for _i in range(45):
|
||||
time.sleep(1)
|
||||
if _check_proxy(port):
|
||||
click.echo(f" Logs: {log_path}")
|
||||
|
|
@ -126,7 +128,7 @@ def _start_proxy(
|
|||
|
||||
proc.kill()
|
||||
log_file.close()
|
||||
raise RuntimeError(f"Proxy failed to start on port {port} within 15 seconds")
|
||||
raise RuntimeError(f"Proxy failed to start on port {port} within 45 seconds")
|
||||
|
||||
|
||||
def _setup_rtk(verbose: bool = False) -> Path | None:
|
||||
|
|
|
|||
|
|
@ -267,14 +267,21 @@ def find_tool_units(messages: list[dict[str, Any]]) -> list[tuple[int, list[int]
|
|||
tool_response_map[tc_id] = i
|
||||
|
||||
# Anthropic format: role="user" with content blocks containing tool_result
|
||||
# Also handles Strands SDK format: {"toolResult": {"toolUseId": "..."}}
|
||||
if msg.get("role") == "user":
|
||||
content = msg.get("content")
|
||||
if isinstance(content, list):
|
||||
for block in content:
|
||||
if isinstance(block, dict) and block.get("type") == "tool_result":
|
||||
tc_id = block.get("tool_use_id")
|
||||
if tc_id:
|
||||
tool_response_map[tc_id] = i
|
||||
if isinstance(block, dict):
|
||||
if block.get("type") == "tool_result":
|
||||
tc_id = block.get("tool_use_id")
|
||||
if tc_id:
|
||||
tool_response_map[tc_id] = i
|
||||
elif "toolResult" in block:
|
||||
# Strands SDK format
|
||||
tc_id = block["toolResult"].get("toolUseId")
|
||||
if tc_id:
|
||||
tool_response_map[tc_id] = i
|
||||
|
||||
# Find assistant messages with tool calls
|
||||
for i, msg in enumerate(messages):
|
||||
|
|
@ -292,13 +299,20 @@ def find_tool_units(messages: list[dict[str, Any]]) -> list[tuple[int, list[int]
|
|||
response_indices.append(tool_response_map[tc_id])
|
||||
|
||||
# Anthropic format: content blocks with type=tool_use
|
||||
# Also handles Strands SDK format: {"toolUse": {"toolUseId": "..."}}
|
||||
content = msg.get("content")
|
||||
if isinstance(content, list):
|
||||
for block in content:
|
||||
if isinstance(block, dict) and block.get("type") == "tool_use":
|
||||
tc_id = block.get("id")
|
||||
if tc_id and tc_id in tool_response_map:
|
||||
response_indices.append(tool_response_map[tc_id])
|
||||
if isinstance(block, dict):
|
||||
if block.get("type") == "tool_use":
|
||||
tc_id = block.get("id")
|
||||
if tc_id and tc_id in tool_response_map:
|
||||
response_indices.append(tool_response_map[tc_id])
|
||||
elif "toolUse" in block:
|
||||
# Strands SDK format
|
||||
tc_id = block["toolUse"].get("toolUseId")
|
||||
if tc_id and tc_id in tool_response_map:
|
||||
response_indices.append(tool_response_map[tc_id])
|
||||
|
||||
if response_indices:
|
||||
# Use set to deduplicate in case same message has both formats
|
||||
|
|
|
|||
|
|
@ -449,9 +449,21 @@ class IntelligentContextManager(Transform):
|
|||
else:
|
||||
insert_idx = len(result_messages)
|
||||
|
||||
# Match the content format of existing messages.
|
||||
# Strands SDK uses list-of-blocks: [{"text": "..."}]
|
||||
# Anthropic/OpenAI use plain strings.
|
||||
# Check what format the conversation uses and match it.
|
||||
_uses_block_format = any(
|
||||
isinstance(m.get("content"), list)
|
||||
for m in result_messages
|
||||
if m.get("role") == "user"
|
||||
)
|
||||
marker_content: str | list[dict[str, str]] = (
|
||||
[{"type": "text", "text": marker}] if _uses_block_format else marker
|
||||
)
|
||||
result_messages.insert(
|
||||
insert_idx,
|
||||
{"role": "user", "content": marker},
|
||||
{"role": "user", "content": marker_content},
|
||||
)
|
||||
|
||||
transforms_applied.append(f"intelligent_cap:{dropped_count}")
|
||||
|
|
|
|||
|
|
@ -175,11 +175,20 @@ class RollingWindow(Transform):
|
|||
else:
|
||||
insert_idx = len(result_messages)
|
||||
|
||||
# Match content format (Strands uses list-of-blocks, Anthropic uses string)
|
||||
_uses_block_format = any(
|
||||
isinstance(m.get("content"), list)
|
||||
for m in result_messages
|
||||
if m.get("role") == "user"
|
||||
)
|
||||
marker_content: str | list[dict[str, str]] = (
|
||||
[{"type": "text", "text": marker}] if _uses_block_format else marker
|
||||
)
|
||||
result_messages.insert(
|
||||
insert_idx,
|
||||
{
|
||||
"role": "user",
|
||||
"content": marker,
|
||||
"content": marker_content,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue