Fix Strands SDK tool pairing, CCR marker format, and startup timeout (#114 #116 #117)

- 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:
chopratejas 2026-04-08 11:28:23 -07:00
parent 9c51f63c48
commit d7122d6bab
4 changed files with 50 additions and 13 deletions

View file

@ -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:

View file

@ -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

View file

@ -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}")

View file

@ -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,
},
)