-
-
Notifications
You must be signed in to change notification settings - Fork 227
Description
Scope check
- This is core LLM communication (not application logic)
- This benefits most users (not just my use case)
- This can't be solved in application code with current RubyLLM
- I read the Contributing Guide
Due diligence
- I searched existing issues
- I checked the documentation
What problem does this solve?
Currently, defining tools with RubyLLM requires using a DSL. The DSL for defining parameters really only supports a handful of schema definitions: the name of the property, its type, and whether or not it's required.
I had the need to make a parameter include a default
value, and while I could just add to the DSL, it would be more flexible to simply support JSON schemas, just like chatting with output schemas. Supporting JSON schema would resolve the issue of not providing default
and allow for the full range of JSON schema properties (enums, min/max values, etc).
Furthermore, while initially investigating this, it seems like several providers each use their own function to format tooling.
- Anthropic tools support JSON schema, and the provider uses
Tool.function_for
function to format these - Gemini supports a subset of JSON schema, and the provider uses its own
function_decleration_for
function to format these - OpenAI tools support JSON schema, and it—you guessed it—uses its own function
tool_for
to format these
Although each provider needs its own formatting, the logic between them often overlaps, making an additive change like this require updates in at least three places.
Proposed solution
First, I would consolidate the various tool assemblages into one module as much as possible.
For actually supporting the schema, I would introduce a function called with_schema
, that takes a block whose return should be the schema you expect the tool to have. Something like:
class Weather < RubyLLM::Tool
with_schema do
{
type: 'function',
name: 'get_weather',
description: 'Retrieves current weather for the given location.',
parameters: {
type: 'object',
properties: {
location: {
type: 'string',
description: 'City and country e.g. Bogotá, Colombia'
},
units: {
type: 'string',
enum: %w[
celsius
fahrenheit
],
description: 'Units the temperature will be returned in.'
}
},
required: %w[
location
units
],
additionalProperties: false
},
strict: true
}
end
def execute(latitude:, longitude:)
# ...
end
end
I don't mind doing this consolidation work and opening a PR! But I wouldn't take such an undertaking without prior approval. ✌️
Why this belongs in RubyLLM
Because calling tools with JSON schemas is a core feature of these APIs.