Current section

Files

Jump to
nous lib nous.ex
Raw

lib/nous.ex

defmodule Nous do
@moduledoc """
AI agent framework for Elixir with multi-provider LLM support.
Nous brings the power of AI agents to Elixir with:
- Model-agnostic API (OpenAI, Groq, Ollama, LM Studio, etc.)
- Simple tool definitions
- Structured outputs with Ecto validation
- Streaming support
- Built-in observability
## Quick Start
# Create an agent
agent = Nous.new("openai:gpt-4",
instructions: "Be helpful and concise"
)
# Run it
{:ok, result} = Nous.run(agent, "What is 2+2?")
IO.puts(result.output) # "4"
## With Tools
defmodule MyTools do
@doc "Calculate mathematical expressions"
def calculate(_ctx, expression) do
# Your calculation logic
"Result: \#{expression}"
end
end
agent = Nous.new("groq:llama-3.1-70b-versatile",
instructions: "Help with calculations",
tools: [&MyTools.calculate/2]
)
{:ok, result} = Nous.run(agent, "What is 25 * 4?")
## Local Models
# Use LM Studio (free, local, private)
agent = Nous.new("lmstudio:qwen3-vl-4b-thinking-mlx")
{:ok, result} = Nous.run(agent, "Hello!")
# Use Ollama
agent = Nous.new("ollama:llama2")
{:ok, result} = Nous.run(agent, "Hello!")
## Plugins
Extend agents with composable plugins:
agent = Nous.new("openai:gpt-4",
plugins: [Nous.Plugins.HumanInTheLoop, Nous.Plugins.Summarization],
tools: [&MyTools.send_email/2]
)
## Deep Research
Autonomous multi-step research with citations:
{:ok, report} = Nous.Research.run(
"Best practices for Elixir deployment",
model: "openai:gpt-4o",
search_tool: &Nous.Tools.BraveSearch.web_search/2
)
IO.puts(report.content)
"""
alias Nous.Agent
@doc """
Create a new agent (shorthand for Agent.new/2).
## Examples
# OpenAI
agent = Nous.new("openai:gpt-4")
# Groq (fast inference)
agent = Nous.new("groq:llama-3.1-8b-instant")
# Local LM Studio
agent = Nous.new("lmstudio:qwen3-vl-4b-thinking-mlx")
# With options
agent = Nous.new("openai:gpt-4",
instructions: "Be helpful",
tools: [&MyTools.search/2],
model_settings: %{temperature: 0.7}
)
"""
@spec new(String.t(), keyword()) :: Agent.t()
defdelegate new(model_string, opts \\ []), to: Agent
@doc """
Run an agent with a prompt (shorthand for Agent.run/3).
## Examples
{:ok, result} = Nous.run(agent, "What is the capital of France?")
IO.puts(result.output)
# With dependencies
{:ok, result} = Nous.run(agent, "Search for users",
deps: %{database: MyApp.DB}
)
"""
@spec run(Agent.t(), String.t(), keyword()) :: {:ok, map()} | {:error, term()}
defdelegate run(agent, prompt, opts \\ []), to: Agent
@doc """
Run an agent with streaming (shorthand for Agent.run_stream/3).
## Example
{:ok, stream} = Nous.run_stream(agent, "Tell me a story")
stream
|> Stream.each(fn
{:text_delta, text} -> IO.write(text)
{:complete, _} -> IO.puts("\\nDone!")
end)
|> Stream.run()
"""
@spec run_stream(Agent.t(), String.t(), keyword()) :: {:ok, Enumerable.t()} | {:error, term()}
defdelegate run_stream(agent, prompt, opts \\ []), to: Agent
# Simple LLM API (without agents)
@doc """
Generate text from a model without creating an agent.
This is a simpler API for text generation. Supports tools - if the model
calls a tool, it will be executed and the conversation continues until
the model returns a text response.
## Examples
{:ok, text} = Nous.generate_text("openai:gpt-4", "What is 2+2?")
{:ok, text} = Nous.generate_text("anthropic:claude-haiku-4-5", "Hello",
system: "You are a pirate",
temperature: 0.7,
max_tokens: 500
)
# With tools
weather_tool = Nous.Tool.from_function(&MyTools.get_weather/2,
name: "get_weather",
description: "Get weather for a city",
parameters: %{
"type" => "object",
"properties" => %{
"location" => %{"type" => "string", "description" => "City name"}
},
"required" => ["location"]
}
)
{:ok, text} = Nous.generate_text("openai:gpt-4", "What's the weather in Paris?",
tools: [weather_tool]
)
## Options
* `:system` - System prompt
* `:temperature` - Sampling temperature (0.0 to 2.0)
* `:max_tokens` - Maximum tokens to generate
* `:top_p` - Nucleus sampling parameter
* `:base_url` - Override API base URL
* `:api_key` - Override API key
* `:tools` - List of tool functions or `Nous.Tool` structs
* `:deps` - Dependencies to pass to tool functions
"""
@spec generate_text(String.t(), String.t(), keyword()) :: {:ok, String.t()} | {:error, term()}
defdelegate generate_text(model, prompt, opts \\ []), to: Nous.LLM
@doc """
Generate text from a model, raising on error.
## Example
text = Nous.generate_text!("openai:gpt-4", "What is 2+2?")
IO.puts(text) # "4"
"""
@spec generate_text!(String.t(), String.t(), keyword()) :: String.t()
defdelegate generate_text!(model, prompt, opts \\ []), to: Nous.LLM
@doc """
Stream text from a model.
Returns a stream that yields text chunks as strings.
## Example
{:ok, stream} = Nous.stream_text("openai:gpt-4", "Write a haiku")
stream |> Stream.each(&IO.write/1) |> Stream.run()
## Options
Same as `generate_text/3`.
"""
@spec stream_text(String.t(), String.t(), keyword()) :: {:ok, Enumerable.t()} | {:error, term()}
defdelegate stream_text(model, prompt, opts \\ []), to: Nous.LLM
end