Class: Raif::ModelTool
- Inherits:
-
Object
- Object
- Raif::ModelTool
- Includes:
- Concerns::JsonSchemaDefinition
- Defined in:
- app/models/raif/model_tool.rb
Direct Known Subclasses
Raif::ModelTools::AgentFinalAnswer, Raif::ModelTools::FetchUrl, Raif::ModelTools::ProviderManaged::Base, Raif::ModelTools::WikipediaSearch
Class Method Summary collapse
-
.description_for_llm(source: nil) ⇒ Object
The description of the tool that will be provided to the model when giving it a list of available tools.
-
.example_model_invocation(source: nil, &block) ⇒ Object
Defines or retrieves the tool's example model invocation.
-
.example_model_invocation_for_source(source) ⇒ Object
Analogous backward-compatible entry point for
example_model_invocation. -
.format_result_for_llm(invocation) ⇒ Object
The content of the tool-call-result message that gets sent back to the model on subsequent turns.
- .invocation_partial_name ⇒ Object
- .invoke_tool(provider_tool_call_id:, tool_arguments:, source:) ⇒ Object
-
.prepare_tool_arguments(arguments, source: nil) ⇒ Hash
Prepares tool arguments before validation and invocation.
-
.prepare_tool_arguments_for_source(arguments, source) ⇒ Object
Analogous backward-compatible entry point for
prepare_tool_arguments. - .process_invocation(invocation) ⇒ Object
- .provider_managed? ⇒ Boolean
- .renderable? ⇒ Boolean
-
.tool_arguments_schema(dynamic: false, source: nil, &block) ⇒ Object
Defines or retrieves the tool's argument schema.
-
.tool_arguments_schema_for_source(source) ⇒ Hash
Backward-compatible entry point for rendering/validating a tool's schema against a caller
source. - .tool_description(&block) ⇒ Object
-
.tool_name ⇒ Object
The name of the tool as it will be provided to the model & used in the model invocation.
-
.triggers_immediate_llm_follow_up?(invocation) ⇒ Boolean
When true, Raif creates a synthetic follow-up entry after this invocation finalizes, prompting the model again immediately so it can react to the tool result in the same conversational turn.
Instance Method Summary collapse
-
#tool_arguments_schema ⇒ Object
Instance method to get the tool arguments schema For instance-dependent schemas, builds the schema with this instance as context For class-level schemas, returns the class-level schema.
Methods included from Concerns::JsonSchemaDefinition
Class Method Details
.description_for_llm(source: nil) ⇒ Object
The description of the tool that will be provided to the model when giving it a list of available tools.
14 15 16 17 18 19 20 21 22 23 |
# File 'app/models/raif/model_tool.rb', line 14 def description_for_llm(source: nil) <<~DESCRIPTION Name: #{tool_name} Description: #{tool_description} Arguments Schema: #{JSON.pretty_generate(tool_arguments_schema_for_source(source))} Example Usage: #{JSON.pretty_generate(example_model_invocation_for_source(source))} DESCRIPTION end |
.example_model_invocation(source: nil, &block) ⇒ Object
Defines or retrieves the tool's example model invocation.
Definition:
- Arity-0 block: the block's return value is the example (evaluated lazily
on first read, then cached).
- Arity-1 block: receives the caller `source` on each read. Not cached,
so the example can reflect per-run context.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'app/models/raif/model_tool.rb', line 51 def example_model_invocation(source: nil, &block) if block_given? @example_model_invocation_block = block @example_model_invocation = nil elsif @example_model_invocation_block.present? if @example_model_invocation_block.arity == 1 @example_model_invocation_block.call(source) else @example_model_invocation ||= @example_model_invocation_block.call end else raise NotImplementedError, "#{name}#example_model_invocation is not implemented" end end |
.example_model_invocation_for_source(source) ⇒ Object
Analogous backward-compatible entry point for example_model_invocation.
125 126 127 128 129 130 131 |
# File 'app/models/raif/model_tool.rb', line 125 def example_model_invocation_for_source(source) if method_accepts_source_kwarg?(:example_model_invocation) example_model_invocation(source: source) else example_model_invocation end end |
.format_result_for_llm(invocation) ⇒ Object
The content of the tool-call-result message that gets sent back to the
model on subsequent turns. Called lazily at message-build time on every
turn — overrides can return live state (e.g. a follow-on record's status)
rather than a static snapshot. The default is the invocation's raw
result jsonb, which is what most tools want.
161 162 163 |
# File 'app/models/raif/model_tool.rb', line 161 def format_result_for_llm(invocation) invocation.result end |
.invocation_partial_name ⇒ Object
70 71 72 |
# File 'app/models/raif/model_tool.rb', line 70 def invocation_partial_name name.gsub("Raif::ModelTools::", "").underscore end |
.invoke_tool(provider_tool_call_id:, tool_arguments:, source:) ⇒ Object
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'app/models/raif/model_tool.rb', line 181 def invoke_tool(provider_tool_call_id:, tool_arguments:, source:) prepared_arguments = prepare_tool_arguments_for_source(tool_arguments, source) tool_invocation = Raif::ModelToolInvocation.new( provider_tool_call_id: provider_tool_call_id, source: source, tool_type: name, tool_arguments: prepared_arguments ) ActiveRecord::Base.transaction do tool_invocation.save! process_invocation(tool_invocation) tool_invocation.completed! end tool_invocation rescue StandardError => e tool_invocation.failed! raise e end |
.prepare_tool_arguments(arguments, source: nil) ⇒ Hash
Prepares tool arguments before validation and invocation. Override in subclasses to add tool-specific argument processing (e.g. type coercion, default injection). The base implementation strips keys not declared in the tool's argument schema, which handles LLMs that hallucinate extra parameters.
213 214 215 |
# File 'app/models/raif/model_tool.rb', line 213 def prepare_tool_arguments(arguments, source: nil) strip_unknown_tool_arguments(arguments, source: source) end |
.prepare_tool_arguments_for_source(arguments, source) ⇒ Object
Analogous backward-compatible entry point for prepare_tool_arguments.
Lets subclass overrides with the pre-source-aware signature
def self.prepare_tool_arguments(arguments) keep working.
136 137 138 139 140 141 142 |
# File 'app/models/raif/model_tool.rb', line 136 def prepare_tool_arguments_for_source(arguments, source) if method_accepts_source_kwarg?(:prepare_tool_arguments) prepare_tool_arguments(arguments, source: source) else prepare_tool_arguments(arguments) end end |
.process_invocation(invocation) ⇒ Object
66 67 68 |
# File 'app/models/raif/model_tool.rb', line 66 def process_invocation(invocation) raise NotImplementedError, "#{name}#process_invocation is not implemented" end |
.provider_managed? ⇒ Boolean
144 145 146 |
# File 'app/models/raif/model_tool.rb', line 144 def provider_managed? false end |
.renderable? ⇒ Boolean
148 149 150 |
# File 'app/models/raif/model_tool.rb', line 148 def renderable? true end |
.tool_arguments_schema(dynamic: false, source: nil, &block) ⇒ Object
Defines or retrieves the tool's argument schema.
When defining:
- arity-0 block with `dynamic: false`: static schema, built once at class load.
- arity-0 block with `dynamic: true`: re-evaluated on every read.
- arity-1 block (any `dynamic:` value): source-aware. Re-evaluated on
every read and receives the caller `source` — typically the agent
invoking the tool. Use this to gate fields on per-run context without
reading global state. The `dynamic:` flag is implied for this form
because a source-dependent schema must re-evaluate on each read.
89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'app/models/raif/model_tool.rb', line 89 def tool_arguments_schema(dynamic: false, source: nil, &block) if block_given? # Arity-1 blocks are inherently source-dependent and must be dynamic. # Auto-promote so callers don't have to think about the interaction. dynamic = true if block.arity == 1 json_schema_definition(:tool_arguments, dynamic: dynamic, &block) elsif schema_defined?(:tool_arguments) schema_for(:tool_arguments, source: source) else raise NotImplementedError, "#{name} must define tool arguments schema via tool_arguments_schema or override #{name}.tool_arguments_schema" end end |
.tool_arguments_schema_for_source(source) ⇒ Hash
Backward-compatible entry point for rendering/validating a tool's schema
against a caller source. Raif's internals go through this helper (LLM
formatters, argument validation, argument stripping) rather than calling
tool_arguments_schema(source: …) directly so that subclasses whose
overrides predate the source: keyword keep working.
If the tool's tool_arguments_schema accepts source: (the base
implementation, or an override that opted in), the helper forwards it.
Otherwise the helper falls back to the no-arg form — the schema simply
doesn't get per-call context, matching pre-source-aware behavior.
116 117 118 119 120 121 122 |
# File 'app/models/raif/model_tool.rb', line 116 def tool_arguments_schema_for_source(source) if method_accepts_source_kwarg?(:tool_arguments_schema) tool_arguments_schema(source: source) else tool_arguments_schema end end |
.tool_description(&block) ⇒ Object
31 32 33 34 35 36 37 38 39 |
# File 'app/models/raif/model_tool.rb', line 31 def tool_description(&block) if block_given? @tool_description = block.call elsif @tool_description.present? @tool_description else raise NotImplementedError, "#{name}#tool_description is not implemented" end end |
.tool_name ⇒ Object
The name of the tool as it will be provided to the model & used in the model invocation. Default for something like Raif::ModelTools::WikipediaSearch would be "wikipedia_search"
27 28 29 |
# File 'app/models/raif/model_tool.rb', line 27 def tool_name name.split("::").last.underscore end |
.triggers_immediate_llm_follow_up?(invocation) ⇒ Boolean
When true, Raif creates a synthetic follow-up entry after this invocation finalizes, prompting the model again immediately so it can react to the tool result in the same conversational turn. Use for tools whose result the model needs to keep reasoning over (search → read → act). Skip for tools whose result is interesting only on a later turn (e.g. queueing a suggestion the user will review later).
Instance-aware so a tool can decide based on what actually happened (e.g. "fire only on partial failure"). Default: false.
177 178 179 |
# File 'app/models/raif/model_tool.rb', line 177 def triggers_immediate_llm_follow_up?(invocation) false end |
Instance Method Details
#tool_arguments_schema ⇒ Object
Instance method to get the tool arguments schema For instance-dependent schemas, builds the schema with this instance as context For class-level schemas, returns the class-level schema
262 263 264 |
# File 'app/models/raif/model_tool.rb', line 262 def tool_arguments_schema schema_for_instance(:tool_arguments) end |