Packages
nous
0.14.3
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/tool_schema.ex
defmodule Nous.ToolSchema do
@moduledoc """
Convert tools to different schema formats for various providers.
Different LLM providers expect different tool schema formats:
- **OpenAI format**: String keys, function wrapper
- **Anthropic format**: Atom keys, input_schema field
- **Custom providers**: May require format-specific adaptations
## Examples
# Convert to OpenAI format (used by OpenAI, Groq, OpenRouter, local providers)
openai_schema = ToolSchema.to_openai(tool)
# Returns: %{"type" => "function", "function" => %{"name" => "...", ...}}
# Convert to Anthropic format (used by Claude)
anthropic_schema = ToolSchema.to_anthropic(tool)
# Returns: %{name: "...", description: "...", input_schema: %{type: :object, ...}}
The schema conversion preserves all tool metadata while adapting to provider requirements.
"""
alias Nous.Tool
@doc """
Convert tool to OpenAI function calling schema (string keys).
"""
@spec to_openai(Tool.t()) :: map()
def to_openai(tool) do
Tool.to_openai_schema(tool)
end
@doc """
Convert tool to Anthropic tool schema (atom keys).
Anthropic uses a different format with atom keys:
%{
name: "tool_name",
description: "Tool description",
input_schema: %{
type: "object",
properties: %{...},
required: [...]
}
}
"""
@spec to_anthropic(Tool.t()) :: map()
def to_anthropic(tool) do
# Convert from OpenAI format to Anthropic format
openai_schema = Tool.to_openai_schema(tool)
func = openai_schema["function"]
%{
name: func["name"],
description: func["description"] || "No description provided",
input_schema: convert_to_atom_keys(func["parameters"] || %{})
}
end
# Convert string keys to atom keys recursively for Anthropic
# Only converts well-known schema keys to atoms for safety
defp convert_to_atom_keys(map) when is_map(map) do
Map.new(map, fn {k, v} ->
key = safe_string_to_atom(k)
value = convert_to_atom_keys(v)
{key, value}
end)
end
defp convert_to_atom_keys(list) when is_list(list) do
Enum.map(list, &convert_to_atom_keys/1)
end
defp convert_to_atom_keys(value), do: value
# Safely convert string to atom - only converts known schema keys
defp safe_string_to_atom(string) when is_binary(string) do
# Whitelist of known JSON schema keys that are safe to convert to atoms
case string do
"type" -> :type
"properties" -> :properties
"required" -> :required
"items" -> :items
"description" -> :description
"enum" -> :enum
"default" -> :default
"minimum" -> :minimum
"maximum" -> :maximum
"minLength" -> :minLength
"maxLength" -> :maxLength
"pattern" -> :pattern
"format" -> :format
"additionalProperties" -> :additionalProperties
# Keep unknown keys as strings to prevent atom exhaustion
_ -> string
end
end
defp safe_string_to_atom(other), do: other
end