This PR introduces the concept of a tool registry. The implementation is straightforward: you can define a tool by creating a class with a function name. The function name gets registered in the registry and can be referenced during LLM calls. When the LLM invokes a tool using the registered name, the registry locates and executes the appropriate tool. If the LLM calls an unregistered tool, the registry returns an error indicating that the tool is not defined.
71 lines
1.8 KiB
Ruby
71 lines
1.8 KiB
Ruby
module Captain::ChatHelper
|
|
def request_chat_completion
|
|
Rails.logger.debug { "[CAPTAIN][ChatCompletion] #{@messages}" }
|
|
|
|
response = @client.chat(
|
|
parameters: {
|
|
model: @model,
|
|
messages: @messages,
|
|
tools: @tool_registry&.registered_tools || [],
|
|
response_format: { type: 'json_object' }
|
|
}
|
|
)
|
|
|
|
handle_response(response)
|
|
end
|
|
|
|
def handle_response(response)
|
|
Rails.logger.debug { "[CAPTAIN][ChatCompletion] #{response}" }
|
|
message = response.dig('choices', 0, 'message')
|
|
if message['tool_calls']
|
|
process_tool_calls(message['tool_calls'])
|
|
else
|
|
JSON.parse(message['content'].strip)
|
|
end
|
|
end
|
|
|
|
def process_tool_calls(tool_calls)
|
|
append_tool_calls(tool_calls)
|
|
tool_calls.each do |tool_call|
|
|
process_tool_call(tool_call)
|
|
end
|
|
request_chat_completion
|
|
end
|
|
|
|
def process_tool_call(tool_call)
|
|
arguments = JSON.parse(tool_call['function']['arguments'])
|
|
function_name = tool_call['function']['name']
|
|
tool_call_id = tool_call['id']
|
|
|
|
if @tool_registry.respond_to?(function_name)
|
|
execute_tool(function_name, arguments, tool_call_id)
|
|
else
|
|
process_invalid_tool_call(tool_call_id)
|
|
end
|
|
end
|
|
|
|
def execute_tool(function_name, arguments, tool_call_id)
|
|
result = @tool_registry.send(function_name, arguments)
|
|
append_tool_response(result, tool_call_id)
|
|
end
|
|
|
|
def append_tool_calls(tool_calls)
|
|
@messages << {
|
|
role: 'assistant',
|
|
tool_calls: tool_calls
|
|
}
|
|
end
|
|
|
|
def process_invalid_tool_call(tool_call_id)
|
|
append_tool_response('Tool not available', tool_call_id)
|
|
end
|
|
|
|
def append_tool_response(content, tool_call_id)
|
|
@messages << {
|
|
role: 'tool',
|
|
tool_call_id: tool_call_id,
|
|
content: content
|
|
}
|
|
end
|
|
end
|