Module VersionHex Docs

LangSchema

Converts an abstract schema into JSON schemas required by various AI providers, minimizing code changes when switching providers.

Example

Define one schema, convert to any provider:

schema = %{
  type: :object,
  description: "User",
  properties: [
    name: %{type: :string, description: "Name"},
    age: %{type: :integer, description: "Age"},
    role: %{type: :string, enum: ["admin", "user"], nullable: true}
  ]
}

OpenAI

schema |> LangSchema.function_calling(:openai)
# => %{
#   "type" => "object",
#   "description" => "User",
#   "properties" => %{
#     "name" => %{"type" => "string", "description" => "Name"},
#     "age" => %{"type" => "integer", "description" => "Age"},
#     "role" => %{"type" => ["string", "null"], "enum" => ["admin", "user"]}
#   },
#   "required" => ["name", "age", "role"],
#   "additionalProperties" => false
# }

Google (Gemini)

schema |> LangSchema.function_calling(:google)
# => %{
#   "type" => "object",
#   "description" => "User",
#   "properties" => %{
#     "name" => %{"type" => "string", "description" => "Name"},
#     "age" => %{"type" => "integer", "description" => "Age"},
#     "role" => %{"type" => "string", "enum" => ["admin", "user"], "nullable" => true}
#   }
# }

Same schema, different output — nullable becomes ["string", "null"] for OpenAI and "nullable": true for Google. OpenAI auto-enforces required and additionalProperties: false.

Design Goals

LangSchema aims to define an abstract schema that can be commonly applied across different AI providers, allowing you to switch providers without modifying your original schema. While provider-specific features are not entirely excluded, supporting every custom feature is not the primary goal. Instead, LangSchema prioritizes raising errors when a schema might implicitly behave incorrectly due to differences between providers, making such issues explicit and easier to fix.

Concepts

Converters

Converters are the core components that transform abstract schemas into provider-specific JSON schemas. Each converter implements the LangSchema.Converter behaviour and handles the specific requirements and limitations of different AI providers.

Each converter provides two main functions:

Key features of converters:

Built-in Converters

Writing a Custom Converter

You can create your own converter for a new AI provider by implementing the LangSchema.Converter behaviour.

Here's a simple example based on the OpenAI converter:

defmodule MyApp.LangSchema.Converter.MyProvider do
  use LangSchema.Converter

  @impl LangSchema.Converter
  def wrap(json_schema, opts) do
    name = Keyword.get(opts, :name, "response")

    %{
      "name" => name,
      "schema" => json_schema
    }
  end

  @impl LangSchema.Converter
  def types() do
    # Add type converters
    %{
      integer: MyApp.LangSchema.Type.MyProvider.Integer,
      number: MyApp.LangSchema.Type.MyProvider.Number,
      string: MyApp.LangSchema.Type.MyProvider.String,
      array: MyApp.LangSchema.Type.MyProvider.Array,
      object: MyApp.LangSchema.Type.MyProvider.Object,
      null: LangSchema.Type.Null
    }
  end

  @impl LangSchema.Converter
  def allowed_combinations() do
    # List only the combinations your provider supports, e.g., :any_of, :one_of, :all_of
    [:any_of]
  end

  @impl LangSchema.Converter
  def keywords() do
    %{
      # Add keyword converters
      nullable: MyApp.LangSchema.Keyword.MyProvider.Nullable
    }
  end
end

Adapters

LangSchema provides adapters that convert various input types into abstract schemas or JSON schemas, making it easier to integrate different data structures into the LangSchema ecosystem.

Adapters implement the LangSchema.Adapter behaviour and provide a standardized way to convert inputs like:

into LangSchema-compatible abstract schemas or JSON schemas.

Installation

The package can be installed by adding lang_schema to your list of dependencies in mix.exs:

def deps do
  [
    {:lang_schema, "~> 0.7.0"}
  ]
end

Relationship with Elixir LangChain

Elixir LangChain provides support for structured output using json_schema for chat and parameters_schema for functions. By leveraging LangSchema with LangChain, you can seamlessly switch between AI providers without changing your code, maintaining a unified schema approach across different integrations.