kiln_ai.tools
132class KilnTool(KilnToolInterface): 133 """ 134 Base helper class that provides common functionality for tool implementations. 135 Subclasses only need to implement run() and provide tool configuration. 136 """ 137 138 def __init__( 139 self, 140 tool_id: KilnBuiltInToolId, 141 name: str, 142 description: str, 143 parameters_schema: Dict[str, Any], 144 ): 145 self._id = tool_id 146 self._name = name 147 self._description = description 148 validate_schema_dict(parameters_schema) 149 self._parameters_schema = parameters_schema 150 151 async def id(self) -> KilnBuiltInToolId: 152 return self._id 153 154 async def name(self) -> str: 155 return self._name 156 157 async def description(self) -> str: 158 return self._description 159 160 async def toolcall_definition(self) -> ToolCallDefinition: 161 """Generate OpenAI-compatible tool definition.""" 162 return { 163 "type": "function", 164 "function": { 165 "name": await self.name(), 166 "description": await self.description(), 167 "parameters": self._parameters_schema, 168 }, 169 } 170 171 @abstractmethod 172 async def run( 173 self, context: ToolCallContext | None = None, **kwargs 174 ) -> ToolCallResult: 175 """Subclasses must implement the actual tool logic.""" 176 pass
Base helper class that provides common functionality for tool implementations. Subclasses only need to implement run() and provide tool configuration.
Return a unique identifier for this tool.
160 async def toolcall_definition(self) -> ToolCallDefinition: 161 """Generate OpenAI-compatible tool definition.""" 162 return { 163 "type": "function", 164 "function": { 165 "name": await self.name(), 166 "description": await self.description(), 167 "parameters": self._parameters_schema, 168 }, 169 }
Generate OpenAI-compatible tool definition.
171 @abstractmethod 172 async def run( 173 self, context: ToolCallContext | None = None, **kwargs 174 ) -> ToolCallResult: 175 """Subclasses must implement the actual tool logic.""" 176 pass
Subclasses must implement the actual tool logic.
47class KilnToolInterface(ABC): 48 """ 49 Abstract interface defining the core API that all Kiln tools must implement. 50 This ensures consistency across all tool implementations. 51 """ 52 53 @abstractmethod 54 async def run( 55 self, context: ToolCallContext | None = None, **kwargs 56 ) -> ToolCallResult: 57 """Execute the tool with the given parameters and calling context if provided.""" 58 pass 59 60 @abstractmethod 61 async def toolcall_definition(self) -> ToolCallDefinition: 62 """Return the OpenAI-compatible tool definition for this tool.""" 63 pass 64 65 @abstractmethod 66 async def id(self) -> ToolId: 67 """Return a unique identifier for this tool.""" 68 pass 69 70 @abstractmethod 71 async def name(self) -> str: 72 """Return the tool name (function name) of this tool.""" 73 pass 74 75 @abstractmethod 76 async def description(self) -> str: 77 """Return a description of what this tool does.""" 78 pass
Abstract interface defining the core API that all Kiln tools must implement. This ensures consistency across all tool implementations.
53 @abstractmethod 54 async def run( 55 self, context: ToolCallContext | None = None, **kwargs 56 ) -> ToolCallResult: 57 """Execute the tool with the given parameters and calling context if provided.""" 58 pass
Execute the tool with the given parameters and calling context if provided.
60 @abstractmethod 61 async def toolcall_definition(self) -> ToolCallDefinition: 62 """Return the OpenAI-compatible tool definition for this tool.""" 63 pass
Return the OpenAI-compatible tool definition for this tool.
65 @abstractmethod 66 async def id(self) -> ToolId: 67 """Return a unique identifier for this tool.""" 68 pass
Return a unique identifier for this tool.
81class UnmanagedKilnTool(KilnToolInterface): 82 """ 83 Helper for tools passed via ``AdapterConfig.unmanaged_tools`` (SDK-injected, not from the 84 Kiln tool registry). Use a :class:`~kiln_ai.datamodel.tool_id.ToolId` with prefix 85 ``kiln_unmanaged::<id>`` (see :func:`~kiln_ai.datamodel.tool_id.build_kiln_unmanaged_tool_id`). 86 Subclass and override :meth:`run` for in-adapter execution when ``return_on_tool_call`` is 87 False; default :meth:`run` raises (use ``return_on_tool_call`` and resume with tool results 88 in ``prior_trace``, or provide a subclass that implements :meth:`run`). 89 """ 90 91 def __init__( 92 self, 93 tool_id: ToolId, 94 name: str, 95 description: str, 96 parameters_schema: Dict[str, Any], 97 ): 98 validate_schema_dict(parameters_schema) 99 self._tool_id = tool_id 100 self._name = name 101 self._description = description 102 self._parameters_schema = parameters_schema 103 104 async def id(self) -> ToolId: 105 return self._tool_id 106 107 async def name(self) -> str: 108 return self._name 109 110 async def description(self) -> str: 111 return self._description 112 113 async def toolcall_definition(self) -> ToolCallDefinition: 114 return { 115 "type": "function", 116 "function": { 117 "name": await self.name(), 118 "description": await self.description(), 119 "parameters": self._parameters_schema, 120 }, 121 } 122 123 async def run( 124 self, context: ToolCallContext | None = None, **kwargs 125 ) -> ToolCallResult: 126 raise RuntimeError( 127 "This tool is supplied as an unmanaged KilnTool for API tool definitions only; " 128 "the Kiln adapter does not execute it when return_on_tool_call is True." 129 )
Helper for tools passed via AdapterConfig.unmanaged_tools (SDK-injected, not from the
Kiln tool registry). Use a ~kiln_ai.datamodel.tool_id.ToolId with prefix
kiln_unmanaged::<id> (see ~kiln_ai.datamodel.tool_id.build_kiln_unmanaged_tool_id()).
Subclass and override run() for in-adapter execution when return_on_tool_call is
False; default run() raises (use return_on_tool_call and resume with tool results
in prior_trace, or provide a subclass that implements run()).
91 def __init__( 92 self, 93 tool_id: ToolId, 94 name: str, 95 description: str, 96 parameters_schema: Dict[str, Any], 97 ): 98 validate_schema_dict(parameters_schema) 99 self._tool_id = tool_id 100 self._name = name 101 self._description = description 102 self._parameters_schema = parameters_schema
Return a unique identifier for this tool.
113 async def toolcall_definition(self) -> ToolCallDefinition: 114 return { 115 "type": "function", 116 "function": { 117 "name": await self.name(), 118 "description": await self.description(), 119 "parameters": self._parameters_schema, 120 }, 121 }
Return the OpenAI-compatible tool definition for this tool.
123 async def run( 124 self, context: ToolCallContext | None = None, **kwargs 125 ) -> ToolCallResult: 126 raise RuntimeError( 127 "This tool is supplied as an unmanaged KilnTool for API tool definitions only; " 128 "the Kiln adapter does not execute it when return_on_tool_call is True." 129 )
Execute the tool with the given parameters and calling context if provided.
29def tool_from_id(tool_id: str, task: Task | None = None) -> KilnToolInterface: 30 """ 31 Get a tool from its ID. 32 """ 33 # Check built-in tools 34 if tool_id in [member.value for member in KilnBuiltInToolId]: 35 typed_tool_id = KilnBuiltInToolId(tool_id) 36 match typed_tool_id: 37 case KilnBuiltInToolId.ADD_NUMBERS: 38 return AddTool() 39 case KilnBuiltInToolId.SUBTRACT_NUMBERS: 40 return SubtractTool() 41 case KilnBuiltInToolId.MULTIPLY_NUMBERS: 42 return MultiplyTool() 43 case KilnBuiltInToolId.DIVIDE_NUMBERS: 44 return DivideTool() 45 case KilnBuiltInToolId.CALL_KILN_API: 46 api_base_url = Config.shared().kiln_local_api_base_url() 47 if not api_base_url: 48 raise ValueError( 49 "kiln_local_api_base_url is not configured. The server must set this before starting." 50 ) 51 return KilnApiCallTool(api_base_url=api_base_url) 52 case _: 53 raise_exhaustive_enum_error(typed_tool_id) 54 55 # Check if this looks like an MCP or Kiln Task tool ID that requires a project 56 is_mcp_tool = is_mcp_tool_id(tool_id) 57 is_kiln_task_tool = tool_id.startswith(KILN_TASK_TOOL_ID_PREFIX) 58 59 if is_mcp_tool or is_kiln_task_tool: 60 project = task.parent_project() if task is not None else None 61 if project is None or project.id is None: 62 raise ValueError( 63 f"Unable to resolve tool from id: {tool_id}. Requires a parent project/task." 64 ) 65 66 # Check MCP Server Tools 67 if is_mcp_tool: 68 # Get the tool server ID and tool name from the ID 69 tool_server_id, tool_name = mcp_server_and_tool_name_from_id( 70 tool_id 71 ) # Fixed function name 72 73 server = next( 74 ( 75 server 76 for server in project.external_tool_servers() 77 if server.id == tool_server_id 78 ), 79 None, 80 ) 81 if server is None: 82 raise ValueError( 83 f"External tool server not found: {tool_server_id} in project ID {project.id}" 84 ) 85 86 return MCPServerTool(server, tool_name) 87 88 # Check Kiln Task Tools 89 if is_kiln_task_tool: 90 server_id = kiln_task_server_id_from_tool_id(tool_id) 91 92 server = next( 93 ( 94 server 95 for server in project.external_tool_servers() 96 if server.id == server_id 97 ), 98 None, 99 ) 100 if server is None: 101 raise ValueError( 102 f"Kiln Task External tool server not found: {server_id} in project ID {project.id}" 103 ) 104 105 return KilnTaskTool(project.id, tool_id, server) 106 107 elif tool_id.startswith(RAG_TOOL_ID_PREFIX): 108 project = task.parent_project() if task is not None else None 109 if project is None: 110 raise ValueError( 111 f"Unable to resolve tool from id: {tool_id}. Requires a parent project/task." 112 ) 113 114 rag_config_id = rag_config_id_from_id(tool_id) 115 rag_config = RagConfig.from_id_and_parent_path(rag_config_id, project.path) 116 if rag_config is None: 117 raise ValueError( 118 f"RAG config not found: {rag_config_id} in project {project.id} for tool {tool_id}" 119 ) 120 121 # Lazy import to avoid circular dependency 122 from kiln_ai.tools.rag_tools import RagTool 123 124 return RagTool(tool_id, rag_config) 125 126 elif tool_id.startswith(SKILL_TOOL_ID_PREFIX): 127 raise ValueError( 128 f"Skill tool IDs are resolved by the adapter, not tool_from_id: {tool_id}" 129 ) 130 131 raise ValueError(f"Tool ID {tool_id} not found in tool registry")
Get a tool from its ID.