Class: Kward::ToolRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/kward/tools/registry.rb

Overview

Exposes local workspace, search, skill, and interaction tools to the model and dispatches tool calls into the active conversation.

ToolRegistry is the boundary between model-requested function calls and Ruby tool objects. It owns schema exposure and transcript persistence for tool results; individual tools own validation and side effects. Keep frontend policy outside this class by passing dependencies such as workspace and prompt from CLI or RPC setup.

A tool may exist in @tools but not be advertised in schemas. This allows restored transcripts or compatibility callers to dispatch known tools while config and frontend capability checks decide what the model can request next.

Tool schemas are the strict output contract advertised to models and clients. Incoming calls are intentionally more tolerant: extra fields are ignored by individual tools, and legacy-compatible shapes are accepted where already supported. Required fields and invalid required values should still return explicit tool errors.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(workspace: Workspace.new, prompt: nil, web_search: WebSearch.new, code_search: CodeSearch.new, web_search_enabled: nil, skills: nil, ask_user_question_enabled: nil) ⇒ ToolRegistry

Builds tool objects and the schema list for the current frontend/config.

Parameters:

  • workspace (Workspace) (defaults to: Workspace.new)

    filesystem/shell boundary used by local tools

  • prompt (Object, nil) (defaults to: nil)

    interactive prompt bridge; must implement ask_user_question before that tool is advertised

  • web_search (WebSearch) (defaults to: WebSearch.new)

    live web search implementation

  • code_search (CodeSearch) (defaults to: CodeSearch.new)

    public source/package search implementation

  • web_search_enabled (Boolean, nil) (defaults to: nil)

    override for web search exposure

  • skills (Array<ConfigFiles::Skill>, nil) (defaults to: nil)

    override discovered skills

  • ask_user_question_enabled (Boolean, nil) (defaults to: nil)

    override question exposure



52
53
54
55
56
57
58
59
60
61
62
# File 'lib/kward/tools/registry.rb', line 52

def initialize(workspace: Workspace.new, prompt: nil, web_search: WebSearch.new, code_search: CodeSearch.new, web_search_enabled: nil, skills: nil, ask_user_question_enabled: nil)
  @workspace = workspace
  @prompt = prompt
  @web_search = web_search
  @code_search = code_search
  @skills = skills
  @web_search_enabled = web_search_enabled
  @ask_user_question_enabled = ask_user_question_enabled
  @tools = build_tools.freeze
  @schemas = build_schema_tools.map(&:schema).freeze
end

Instance Attribute Details

#schemasArray<Hash> (readonly)

Tool schemas advertised to the model for the current frontend and config.

Returns:

  • (Array<Hash>)

    tool schemas currently advertised to the model



40
41
42
# File 'lib/kward/tools/registry.rb', line 40

def schemas
  @schemas
end

Instance Method Details

#dispatch(tool_call, conversation, cancellation: nil) ⇒ String

Executes a model-requested tool call and appends the result to the conversation transcript.

Unknown tools are recorded as tool results instead of raising. That keeps the conversation valid for the model and lets the assistant recover by choosing an advertised tool on the next turn.

Parameters:

  • tool_call (Hash)

    model tool call payload

  • conversation (Conversation)

    active conversation

Returns:

  • (String)

    tool output content appended to the conversation



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/kward/tools/registry.rb', line 74

def dispatch(tool_call, conversation, cancellation: nil)
  cancellation&.raise_if_cancelled!
  name = ToolCall.name(tool_call)
  args = ToolCall.arguments(tool_call)
  tool = @tools[name]

  content = if tool
              tool.call(args, conversation, cancellation: cancellation)
            else
              "Unknown tool: #{name}"
            end

  conversation.append_tool(
    tool_call_id: tool_call["id"] || tool_call[:id],
    name: name,
    content: content
  )
  conversation.append_tool_execution(tool_call: tool_call, content: content)

  content
end