Skip to content

edlontech/sycophant

Repository files navigation

Sycophant

Sycophant

You are absolutely right if you use this lib!

Warning: Sycophant is under active development and the API is not yet stable. Expect breaking changes between versions until 1.0.

Sycophant abstracts the differences between OpenAI, Anthropic, Google Gemini, AWS Bedrock, Azure AI Foundry, and OpenRouter behind a single composable API. Provider-specific wire protocols, authentication, and parameter validation are handled automatically based on the model identifier.

Features

  • Multi-provider -- OpenAI, Anthropic, Google Gemini, AWS Bedrock, Azure, OpenRouter
  • Text generation -- synchronous and streaming responses
  • Structured output -- validated against Zoi or JSON Schema
  • Tool use -- auto-execution loop or manual handling
  • Embeddings -- unified embedding API across providers
  • Multi-turn conversations -- extract context from a response to continue
  • Automatic cost calculation -- token costs from LLMDB pricing data
  • Telemetry -- :telemetry events with optional OpenTelemetry bridge
  • Serialization -- JSON round-trip for all core structs (database persistence)
  • Smart credentials -- per-request, app config, or environment variable fallback

Quick Start

# Generate text
messages = [Sycophant.Message.user("What is the capital of France?")]

{:ok, response} = Sycophant.generate_text("openai:gpt-4o-mini", messages)
response.text
#=> "The capital of France is Paris."
# Continue the conversation
alias Sycophant.Context

ctx = response.context |> Context.add(Sycophant.Message.user("Tell me more"))
{:ok, follow_up} = Sycophant.generate_text("openai:gpt-4o-mini", ctx)
# Structured output with schema validation
schema = Zoi.object(%{name: Zoi.string(), age: Zoi.integer()})
messages = [Sycophant.Message.user("Extract: John is 30 years old")]

{:ok, response} = Sycophant.generate_object("openai:gpt-4o-mini", messages, schema)
response.object
#=> %{name: "John", age: 30}
# Streaming
Sycophant.generate_text("openai:gpt-4o-mini", messages,
  stream: fn chunk -> IO.write(chunk.data) end
)
# Tool use with auto-execution (Zoi schema -- atom keys in function)
weather_tool = %Sycophant.Tool{
  name: "get_weather",
  description: "Gets current weather for a city",
  parameters: Zoi.object(%{city: Zoi.string()}),
  function: fn %{city: city} -> "72F sunny in #{city}" end
}

Sycophant.generate_text("openai:gpt-4o-mini", messages,
  tools: [weather_tool]
)
# Embeddings
request = %Sycophant.EmbeddingRequest{
  inputs: ["Hello world"],
  model: "amazon_bedrock:cohere.embed-english-v3"
}
{:ok, response} = Sycophant.embed(request)

Installation

Add sycophant to your list of dependencies in mix.exs:

def deps do
  [
    {:sycophant, "~> 0.4.1"} # x-release-please-version
  ]
end

Configuration

Credentials are resolved in order: per-request options, application config, then environment variables.

# config/runtime.exs
config :sycophant, :providers,
  openai: [api_key: System.get_env("OPENAI_API_KEY")],
  anthropic: [api_key: System.get_env("ANTHROPIC_API_KEY")],
  google: [api_key: System.get_env("GOOGLE_API_KEY")]

See the Getting Started guide for detailed setup instructions and the full documentation for API reference.

Supported Providers

Provider Model Prefix Auth Wire Protocol
OpenAI openai: Bearer token Chat Completions / Responses
Anthropic anthropic: x-api-key Messages
Google Gemini google: API key Gemini
AWS Bedrock amazon_bedrock: AWS SigV4 Converse
Azure AI Foundry azure: Bearer / API key OpenAI Completions
OpenRouter openrouter: Bearer token OpenAI Completions

Acknowledgements

Sycophant builds on:

  • LLMDB -- the model metadata database that powers model resolution, provider discovery, and pricing data.
  • Req LLM -- a major source of inspiration for Sycophant's API design and multi-provider approach.

License

See LICENSE for details.

About

A simple LLM Client for Elixir

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages