Schema & Structured Output

JSON Schema DSL for the ask-rb ecosystem. Used by tool parameters AND structured LLM output. Zero external dependencies.

gem "ask-schema"

Quick Start

require "ask-schema"

schema = Ask::Schema.define do
  string :name, desc: "The person's name"
  integer :age, desc: "Their age"
  enum :role, %w[admin user guest], desc: "Access level"
end

schema.to_json_schema
# => {
#   type: "object",
#   properties: {
#     name: { type: "string", description: "The person's name" },
#     age: { type: "integer", description: "Their age" },
#     role: { type: "string", enum: ["admin", "user", "guest"] }
#   },
#   required: ["name", "age", "role"]
# }

Basic Types

Ask::Schema.define do
  string  :name                          # string
  integer :count                         # integer
  number  :price                         # number (float)
  boolean :active                        # boolean
  enum    :status, %w[pending active]    # string with enum
  array   :tags, type: :string           # array of strings
end

Nested Schemas

Ask::Schema.define do
  string :title

  object :author do
    string :name
    string :email
  end

  array :comments, type: :object do
    string :text
    string :author
  end
end

Optional Fields

By default, all fields are required. Make a field optional:

Ask::Schema.define do
  string :name
  string :nickname, required: false
  integer :age, required: false
end

Using with Tools

The schema DSL powers tool parameter definitions:

class SearchTool < Ask::Tool
  description "Search the knowledge base"

  param :query, type: :string, desc: "Search query", required: true
  param :limit, type: :integer, desc: "Max results", required: false

  def execute(query:, limit: 10)
    # ...
  end
end

Each param declaration generates a JSON Schema entry in params_schema.

Using with Structured Output

Pass a schema to get structured JSON back from the LLM:

schema = Ask::Schema.define do
  string :name
  integer :age
  array  :hobbies, type: :string
end

response = provider.chat(
  [{ role: "user", content: "Tell me about John, 28, who likes hiking and photography" }],
  model: "gpt-4o",
  schema: schema
)

JSON.parse(response.content)
# => { "name" => "John", "age" => 28, "hobbies" => ["hiking", "photography"] }

Conditional Schemas

Use oneOf for conditional fields:

Ask::Schema.define do
  string :type, enum: ["user", "admin"]

  # Branch based on type
  object :details do
    oneOf do
      schema do
        string :department  # for admins
      end
      schema do
        string :subscription_tier  # for users
      end
    end
  end
end

Implementation

  • Zero dependencies — pure Ruby, no JSON Schema gems required
  • Immutable definitions — schemas are frozen after construction
  • Thread-safe — stateless definition blocks
  • Standalone — works without any other ask-rb gem

Next Steps


This site uses Just the Docs, a documentation theme for Jekyll.