Class: Tools::MetaToolService

Inherits:
Object
  • Object
show all
Extended by:
T::Sig, ToolMeta
Defined in:
app/services/tools/meta_tool_service.rb

Constant Summary

Constants included from ToolMeta

ToolMeta::ParamMetadata

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ToolMeta

clear_registry, extended, registry, tool_description, tool_entrypoint, tool_metadata, tool_name, tool_param

Class Method Details

.ruby_llm_tools(tool_names) ⇒ Object



50
51
52
53
54
55
56
57
58
# File 'app/services/tools/meta_tool_service.rb', line 50

def self.ruby_llm_tools(tool_names)
  ToolMeta.registry.filter_map do |service_class|
    schema = ToolSchema::Builder.build(service_class)
    next unless tool_names.include?(schema[:name])

    tool_class_name = ToolSchema::RubyLlmFactory.tool_class_name(service_class)
    Tools.const_get(tool_class_name) if Tools.const_defined?(tool_class_name)
  end
end

Instance Method Details

#call(action:, tool_name: nil, query: nil, arguments: nil) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'app/services/tools/meta_tool_service.rb', line 30

def call(action:, tool_name: nil, query: nil, arguments: nil)
  case action
  when 'search'
    search_tools(query)
  when 'list'
    { tools: list_tools }
  when 'list_summary'
    { tools: list_summaries }
  when 'get'
    get_tool(tool_name)
  when 'run'
    run_tool(tool_name, arguments || {})
  else
    { error: "Unknown action: #{action}" }
  end
end

#deep_symbolize(value) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'app/services/tools/meta_tool_service.rb', line 137

def deep_symbolize(value)
  case value
  when Hash
    value.each_with_object({}) do |(key, nested), result|
      symbol_key = key.is_a?(String) ? key.to_sym : key
      result[symbol_key] = deep_symbolize(nested)
    end
  when Array
    value.map { |element| deep_symbolize(element) }
  else
    value
  end
end

#detailed_payload(schema) ⇒ Object



126
127
128
# File 'app/services/tools/meta_tool_service.rb', line 126

def detailed_payload(schema)
  summary_payload(schema).merge(params: schema[:params], return_type: schema[:return_type])
end

#find_schema(tool_name) ⇒ Object



110
111
112
113
114
# File 'app/services/tools/meta_tool_service.rb', line 110

def find_schema(tool_name)
  return nil if tool_name.nil? || tool_name.empty?

  schemas.find { |schema| schema[:name] == tool_name }
end

#get_tool(tool_name) ⇒ Object



84
85
86
87
88
89
# File 'app/services/tools/meta_tool_service.rb', line 84

def get_tool(tool_name)
  schema = find_schema(tool_name)
  return { error: "Tool not found: #{tool_name}" } unless schema

  { tool: detailed_payload(schema) }
end

#list_summaries ⇒ Object



79
80
81
# File 'app/services/tools/meta_tool_service.rb', line 79

def list_summaries
  schemas.map { |schema| summary_payload(schema) }
end

#list_tools ⇒ Object



74
75
76
# File 'app/services/tools/meta_tool_service.rb', line 74

def list_tools
  schemas.map { |schema| detailed_payload(schema) }
end

#run_tool(tool_name, arguments) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
# File 'app/services/tools/meta_tool_service.rb', line 92

def run_tool(tool_name, arguments)
  schema = find_schema(tool_name)
  return { error: "Tool not found: #{tool_name}" } unless schema

  service_class = schema[:service_class]
  result = service_class.new.public_send(schema[:entrypoint], **deep_symbolize(arguments))

  { tool: summary_payload(schema), result: result }
rescue ArgumentError => e
  { error: e.message }
end

#schemas ⇒ Object



105
106
107
# File 'app/services/tools/meta_tool_service.rb', line 105

def schemas
  ToolMeta.registry.map { |service_class| ToolSchema::Builder.build(service_class) }
end

#search_tools(query) ⇒ Object



62
63
64
65
66
67
68
69
70
71
# File 'app/services/tools/meta_tool_service.rb', line 62

def search_tools(query)
  return { tools: list_tools } if query.nil? || query.empty?

  normalized = query.downcase
  tools = list_tools.select do |tool|
    tool[:name].downcase.include?(normalized) || tool[:description].downcase.include?(normalized)
  end

  { tools: tools }
end

#summary_payload(schema) ⇒ Object



117
118
119
120
121
122
123
# File 'app/services/tools/meta_tool_service.rb', line 117

def summary_payload(schema)
  {
    name: schema[:name],
    description: schema[:description],
    usage: usage_string(schema)
  }
end

#usage_string(schema) ⇒ Object



131
132
133
134
# File 'app/services/tools/meta_tool_service.rb', line 131

def usage_string(schema)
  param_list = schema[:params].map { |param| param[:name].to_s }.join(', ')
  "#{schema[:name]}(#{param_list})"
end