Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 55 additions & 7 deletions kiro/converters_anthropic.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,11 +341,17 @@ def convert_anthropic_tools(
"""
Converts Anthropic tools to unified format.

Silently skips Anthropic built-in server tools (web_search, code_execution,
bash, text_editor, tool_search_tool_*, etc.) that have no input_schema,
since the Kiro API cannot handle them.

Preserves ``defer_loading`` flag on each tool for downstream expansion.

Args:
tools: List of Anthropic tools
tools: List of Anthropic tools from the request.

Returns:
List of tools in unified format, or None if no tools
List of unified tools, or None if no valid tools found.
"""
if not tools:
return None
Expand All @@ -356,14 +362,22 @@ def convert_anthropic_tools(
if isinstance(tool, dict):
name = tool.get("name", "")
description = tool.get("description")
input_schema = tool.get("input_schema", {})
input_schema = tool.get("input_schema")
defer_loading = tool.get("defer_loading", False)
else:
name = tool.name
description = tool.description
input_schema = tool.input_schema
name = getattr(tool, "name", "") or ""
description = getattr(tool, "description", None)
input_schema = getattr(tool, "input_schema", None)
defer_loading = getattr(tool, "defer_loading", False)

# Skip built-in server tools (no input_schema) — Kiro API can't handle them
if input_schema is None:
logger.debug(f"Skipping server tool '{name}' (no input_schema)")
continue

unified_tools.append(
UnifiedTool(name=name, description=description, input_schema=input_schema)
UnifiedTool(name=name, description=description, input_schema=input_schema,
_defer_loading=defer_loading)
)

return unified_tools if unified_tools else None
Expand Down Expand Up @@ -399,6 +413,40 @@ def anthropic_to_kiro(
# Convert tools to unified format
unified_tools = convert_anthropic_tools(request.tools)

# Handle defer_loading: separate deferred tools, expand referenced ones
if unified_tools:
deferred_by_name = {t.name: t for t in unified_tools if t._defer_loading}
if deferred_by_name:
active_tools = [t for t in unified_tools if not t._defer_loading]

# Scan messages for tool_reference blocks and expand those tools
referenced_names: set[str] = set()
for msg in request.messages:
if not isinstance(msg.content, list):
continue
for block in msg.content:
raw = block if isinstance(block, dict) else block.__dict__ if hasattr(block, '__dict__') else {}
if isinstance(raw, dict):
if raw.get("type") == "tool_reference":
referenced_names.add(raw.get("tool_name", ""))
if raw.get("type") == "tool_result":
inner = raw.get("content")
if isinstance(inner, list):
for item in inner:
if isinstance(item, dict) and item.get("type") == "tool_reference":
referenced_names.add(item.get("tool_name", ""))

for name in referenced_names:
if name in deferred_by_name:
active_tools.append(deferred_by_name[name])

n_expanded = len(referenced_names & set(deferred_by_name))
logger.info(
f"[Tool Search] {len(active_tools)} active, {len(deferred_by_name)} deferred, "
f"{n_expanded} expanded from tool_reference"
)
unified_tools = active_tools if active_tools else None

# System prompt is already separate in Anthropic format!
# It can be a string or list of content blocks (for prompt caching)
system_prompt = extract_system_prompt(request.system)
Expand Down
2 changes: 2 additions & 0 deletions kiro/converters_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ class UnifiedTool:
name: Tool name
description: Tool description
input_schema: JSON Schema for tool parameters
_defer_loading: If True, tool is deferred (not sent to Kiro until referenced)
"""
name: str
description: Optional[str] = None
input_schema: Optional[Dict[str, Any]] = None
_defer_loading: bool = False


@dataclass
Expand Down
33 changes: 30 additions & 3 deletions kiro/models_anthropic.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,45 @@ class ToolUseContentBlock(BaseModel):
input: Dict[str, Any]


class ToolReferenceContentBlock(BaseModel):
"""Tool reference block used by Anthropic's tool search / defer_loading feature."""

type: Literal["tool_reference"] = "tool_reference"
tool_name: str
model_config = {"extra": "allow"}


class ServerToolUseContentBlock(BaseModel):
"""Server-side tool use block (e.g., tool_search invocations handled by Anthropic API)."""

type: Literal["server_tool_use"] = "server_tool_use"
model_config = {"extra": "allow"}


class ToolSearchResultContentBlock(BaseModel):
"""Tool search result block returned by Anthropic's tool search feature."""

type: Literal["tool_search_tool_result"] = "tool_search_tool_result"
model_config = {"extra": "allow"}


class ToolResultContentBlock(BaseModel):
"""
Tool result content block in Anthropic format.

Represents the result of a tool call, sent by the user.
Tool results can contain text, images, or a mix of both.
Tool results can contain text, images, tool references, or a mix.
"""

type: Literal["tool_result"] = "tool_result"
tool_use_id: str
content: Optional[
Union[str, List[Union["TextContentBlock", "ImageContentBlock"]]]
Union[str, List[Union["TextContentBlock", "ImageContentBlock", "ToolReferenceContentBlock"]]]
] = None
is_error: Optional[bool] = None

model_config = {"extra": "allow"}


# ==================================================================================================
# Image Content Block Models
Expand Down Expand Up @@ -146,13 +170,16 @@ class ImageContentBlock(BaseModel):
source: Union[Base64ImageSource, URLImageSource]


# Union type for all content blocks (including images and thinking)
# Union type for all content blocks (including images, thinking, and tool search)
ContentBlock = Union[
TextContentBlock,
ThinkingContentBlock,
ImageContentBlock,
ToolUseContentBlock,
ToolResultContentBlock,
ToolReferenceContentBlock,
ServerToolUseContentBlock,
ToolSearchResultContentBlock,
]


Expand Down