Skip to content

Add Model.to_llm_tool method with multi-provider support #139

Open

Description

@sergiobayona

Summary

Add a convenience method to generate LLM tool/function specs directly from EasyTalk models, with support for multiple providers.

Proposed API

class GetWeather
  include EasyTalk::Model

  define_schema do
    title "get_weather"
    description "Get current weather for a location"
    property :location, String, description: "City and state"
    property :unit, String, enum: ["celsius", "fahrenheit"]
  end
end

# OpenAI format (default)
GetWeather.to_llm_tool
GetWeather.to_llm_tool(format: :openai)

# Anthropic format
GetWeather.to_llm_tool(format: :anthropic)

# Future providers
GetWeather.to_llm_tool(format: :gemini)
GetWeather.to_llm_tool(format: :cohere)

Motivation

Currently, EasyTalk::Tools::FunctionBuilder.new(Model) only generates OpenAI-compatible specs. For Anthropic, users must manually extract and restructure the schema:

# Current (manual, error-prone)
schema = Model.json_schema
tool = {
  name: schema["title"],
  description: schema["description"],
  input_schema: {
    type: "object",
    properties: schema["properties"],
    required: schema["required"]
  }
}

A built-in method would:

  1. Reduce boilerplate
  2. Ensure correct format for each provider
  3. Make EasyTalk more compelling for LLM use cases

Implementation Notes

  • Add to_llm_tool class method to EasyTalk::Model
  • Create provider-specific formatters (OpenAI, Anthropic, etc.)
  • Consider making FunctionBuilder the underlying implementation
  • Provider formats are relatively stable but should be easy to update

Provider Format References

Activity

  1. m13v commented on Mar 22, 2026

    @m13v

    Nice API design. We handle multi-provider tool spec generation in our macOS AI agent (Fazm) and the format differences between providers are subtle but important.

    Key differences to handle in the to_llm_tool method:

    OpenAI format: Tool definitions wrap everything in a function object with name, description, and parameters (JSON Schema). Required fields go in a required array at the schema level.

    Anthropic format: Similar structure but the top-level key is input_schema instead of parameters. Also, Claude is more sensitive to the description field on individual properties - well-described properties significantly improve tool call accuracy.

    Gemini format: Uses function_declarations with yet another schema wrapper. Worth supporting if you want broad coverage.

    One practical suggestion: add a strict option for OpenAI's structured output mode. When strict is true, OpenAI guarantees the output matches the schema exactly (no missing fields, correct types). This changes the schema slightly - all properties must be in required and additionalProperties: false must be set.

    The Ruby DSL approach is clean. For the enum handling, consider adding validation that the enum values match the property type - we've seen subtle bugs where string enums get passed to integer properties.

  2. m13v commented on Mar 22, 2026

    @m13v

    Our multi-provider tool spec handling is open source: https://github.com/m13v/fazm/blob/main/Desktop/Sources/Providers/ChatProvider.swift

    Shows the practical differences between Claude and OpenAI tool definition formats and how we normalize them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: highHigh value features or important fixes

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions