
# Nous AI
> *"Nous (νοῦς) — the ancient Greek concept of mind, reason, and intellect; the faculty of understanding that grasps truth directly."*
AI agent framework for Elixir with multi-provider LLM support.
[](https://elixir-lang.org)
[](https://github.com/nyo16/nous/blob/master/LICENSE)
[](#features)
Nous AI is an AI agent framework for Elixir with support for any OpenAI-compatible API. Features include tool calling, multi-provider support, local LLM execution, and built-in utilities for dates, strings, web search, and task tracking.
## Installation
Add to your `mix.exs`:
```elixir
def deps do
[
{:nous, "~> 0.5.0"},
{:openai_ex, "~> 0.9.17"}
]
end
```
Then run:
```bash
mix deps.get
```
## Quick Start
```elixir
# Create an agent
agent = Nous.new("lmstudio:qwen/qwen3-30b",
instructions: "Be helpful and concise"
)
# Run it
{:ok, result} = Nous.run(agent, "What is 2+2?")
IO.puts(result.output) # "4"
IO.puts("Tokens: #{result.usage.total_tokens}")
```
## Supported Providers
| Provider | Model String | Status |
|----------|-------------|--------|
| LM Studio | `lmstudio:qwen/qwen3-30b` | Tested & Working |
| OpenAI | `openai:gpt-4` | Supported |
| Anthropic | `anthropic:claude-sonnet-4-5-20250929` | Tested & Working |
| Google Gemini | `gemini:gemini-2.0-flash-exp` | Native API |
| Mistral AI | `mistral:ministral-3-14b-instruct-2512` | Native API + Req |
| Groq | `groq:llama-3.1-70b-versatile` | Supported |
| Ollama | `ollama:llama2` | Supported |
| vLLM | `vllm:model` + `:base_url` | Supported |
| OpenRouter | `openrouter:anthropic/claude-3.5-sonnet` | Supported |
| Together AI | `together:meta-llama/Llama-3-70b-chat-hf` | Supported |
| Custom | `custom:model` + `:base_url` | Supported |
**Local (Zero Cost):** LM Studio, Ollama, vLLM
**Cloud:** OpenAI, Anthropic, Mistral AI, Groq, OpenRouter, Together AI
```elixir
# Switch providers with one line change
agent = Nous.new("lmstudio:qwen/qwen3-30b") # Local
agent = Nous.new("openai:gpt-4") # Cloud
agent = Nous.new("anthropic:claude-sonnet-4-5-20250929") # Claude
agent = Nous.new("mistral:ministral-3-14b-instruct-2512") # Mistral
```
## Agent Types
| Feature | `ReActAgent` | Standard `Agent` |
|---------|--------------|------------------|
| **Planning** | Built-in structured planning | No planning (you control flow) |
| **Todo tracking** | Automatic task breakdown | Manual (via custom tools) |
| **Completion** | Explicit `final_answer` required | Stops naturally |
| **Best for** | Complex multi-step problems | Simple tasks, custom workflows |
| **Token usage** | Higher (planning + tracking) | Lower (direct execution) |
### ReActAgent (Enhanced Planning)
Use for complex, multi-step problems with structured planning. Agent creates plans, tracks todos, and documents findings.
```elixir
agent = Nous.ReActAgent.new("lmstudio:qwen/qwen3-30b",
tools: [&search/2, &calculate/2]
)
{:ok, result} = Nous.ReActAgent.run(agent,
"Who is the oldest F1 driver and when did they win their first championship?"
)
```
See [examples/react_agent_enhanced_demo.exs](examples/react_agent_enhanced_demo.exs) or [by_feature/patterns/](examples/by_feature/README.md#-patterns-agent-reasoning--architecture)
### Standard Agent (Flexible & Simple)
Use for simple Q&A, single-step tasks, or custom workflows where you control behavior.
```elixir
agent = Nous.new("openai:gpt-4",
tools: [&calculate/2]
)
{:ok, result} = Nous.run(agent, "Calculate 15 * 23")
```
## Built-in Tools
### DateTime Tools
Date/time operations with timezone support, date arithmetic, and formatting.
```elixir
alias Nous.Tools.DateTimeTools
tools: [&DateTimeTools.current_date/2, &DateTimeTools.date_difference/2, &DateTimeTools.add_days/2]
```
See [examples/datetime_tools_demo.exs](examples/datetime_tools_demo.exs) or [by_feature/tools/](examples/by_feature/README.md#-tools-function-calling--actions)
### String Tools
Text manipulation: uppercase, replace, split, palindrome detection, number extraction.
```elixir
alias Nous.Tools.StringTools
tools: [&StringTools.to_uppercase/2, &StringTools.replace_text/2, &StringTools.extract_numbers/2]
```
See [examples/string_tools_demo.exs](examples/string_tools_demo.exs) or [by_feature/tools/](examples/by_feature/README.md#-tools-function-calling--actions)
### Todo Tools
Automatic task breakdown and progress tracking for multi-step workflows.
```elixir
alias Nous.Tools.TodoTools
agent = Nous.new("lmstudio:qwen/qwen3-30b",
enable_todos: true,
tools: [&TodoTools.add_todo/2, &TodoTools.complete_todo/2]
)
```
See [examples/todo_tools_demo.exs](examples/todo_tools_demo.exs) or [by_feature/tools/](examples/by_feature/README.md#-tools-function-calling--actions)
### Brave Search (Web Search)
Search the web for current information. Requires `BRAVE_API_KEY` ([get free key](https://brave.com/search/api/)).
```elixir
alias Nous.Tools.BraveSearch
tools: [&BraveSearch.web_search/2, &BraveSearch.news_search/2]
```
See [examples/brave_search_demo.exs](examples/brave_search_demo.exs) or [by_feature/tools/](examples/by_feature/README.md#-tools-function-calling--actions)
## Features
### Tool Calling
Define Elixir functions as tools. AI calls them automatically when needed.
```elixir
def search_database(_ctx, %{"query" => q}), do: DB.search(q)
agent = Nous.new("openai:gpt-4",
tools: [&search_database/2]
)
```
### Tools with Context
Pass dependencies (user, database, API keys) via context:
```elixir
def get_balance(ctx, _args), do: DB.get_balance(ctx.deps.database, ctx.deps.user.id)
{:ok, result} = Nous.run(agent, "What's my balance?",
deps: %{user: %{id: 123}, database: MyApp.DB}
)
```
See [examples/tools_with_context.exs](examples/tools_with_context.exs) or [custom_tools_guide.exs](examples/custom_tools_guide.exs)
### Conversations
```elixir
{:ok, r1} = Nous.run(agent, "Tell me a joke")
{:ok, r2} = Nous.run(agent, "Explain it", message_history: r1.new_messages)
```
### Streaming
```elixir
{:ok, stream} = Nous.run_stream(agent, "Write a poem")
stream |> Stream.each(fn {:text_delta, t} -> IO.write(t) end) |> Stream.run()
```
### Anthropic Extended Features
```elixir
# 1M token context window
agent = Nous.new("anthropic:claude-sonnet-4-5-20250929",
model_settings: %{enable_long_context: true}
)
# Extended thinking mode
agent = Nous.new("anthropic:claude-sonnet-4-5-20250929",
model_settings: %{thinking: %{type: "enabled", budget_tokens: 5000}}
)
```
### LiveView Integration
```elixir
# Spawn linked streaming process from LiveView
spawn_link(fn ->
agent = Nous.new("anthropic:claude-sonnet-4-5-20250929")
{:ok, stream} = Nous.run_stream(agent, message)
stream
|> Stream.each(fn
{:text_delta, text} -> send(parent, {:stream_chunk, text})
{:finish, result} -> send(parent, {:stream_complete, result})
_ -> :ok
end)
|> Stream.run()
end)
```
See [liveview_streaming_example.ex](examples/liveview_streaming_example.ex) for real-time streaming or [LIVEVIEW_INTEGRATION.md](examples/LIVEVIEW_INTEGRATION.md) for patterns
## Logging & Telemetry
Configure log level in `config/config.exs`:
```elixir
config :logger, level: :info # or :debug, :warning, :error
```
Attach telemetry handlers:
```elixir
Nous.Telemetry.attach_default_handler()
```
Events: `[:nous, :agent, :run, :*]`, `[:nous, :model, :request, :*]`, `[:nous, :tool, :execute, :*]`
## Examples
**🚀 [Get Started in 5 Minutes](examples/GETTING_STARTED.md)** - Quick setup guide with local or cloud options.
**📚 [Full Examples Collection](examples/README.md)** - Comprehensive learning path from beginner to production.
### Learning Path Overview
**🟢 Beginner** (5-15 minutes each)
- [basic_hello_world.exs](examples/basic_hello_world.exs) - 30-second minimal example
- [test_lm_studio.exs](examples/test_lm_studio.exs) - Local LM Studio setup
- [tools_simple.exs](examples/tools_simple.exs) - Basic tool calling
- [calculator_demo.exs](examples/calculator_demo.exs) - Multi-tool chaining
**🟡 Intermediate** (15-45 minutes each)
- [streaming_example.exs](examples/streaming_example.exs) - Real-time responses
- [conversation_history_example.exs](examples/conversation_history_example.exs) - Multi-turn chat
- [error_handling_example.exs](examples/error_handling_example.exs) - Graceful failure handling
- [cost_tracking_example.exs](examples/cost_tracking_example.exs) - Monitor token usage
**🔴 Advanced** (45+ minutes each)
- [Trading Desk](examples/trading_desk/README.md) - Production multi-agent system
- [Council](examples/council/README.md) - Multi-LLM deliberation
- [Coderex](examples/coderex/README.md) - AI code editor with SEARCH/REPLACE
**Browse by:**
- [Skill Level](examples/by_level/README.md) - Beginner → Intermediate → Advanced
- [Provider](examples/by_provider/README.md) - Anthropic, OpenAI, Local, etc.
- [Feature](examples/by_feature/README.md) - Tools, Streaming, Patterns, etc.
**Quick Templates:**
- [templates/](examples/templates/README.md) - Copy-paste starter files
## Architecture
```
User Code
↓
Nous.Agent (config)
↓
Nous.AgentRunner (execution loop)
↓
├─→ OpenAICompatible (model adapter)
│ ↓
│ OpenaiEx (HTTP client)
│ ↓
│ LM Studio / OpenAI / Groq / etc.
│
├─→ ToolExecutor (run functions)
├─→ Messages (format conversion)
└─→ Usage (track tokens)
```
## Stats
- **Lines:** ~2,100 | **Modules:** 16 | **Tests:** 18 passing | **Providers:** 7+
## Contributing
Contributions welcome! Areas for improvement:
- More comprehensive tests
- Structured output validation (Ecto integration)
- Performance benchmarks
- More examples
## License
Apache 2.0 License - see [LICENSE](https://github.com/nyo16/nous/blob/master/LICENSE)
## Credits
- Inspired by [Pydantic AI](https://ai.pydantic.dev/)
- Built with [openai_ex](https://github.com/cyberchitta/openai_ex)
- Validation with [ecto](https://github.com/elixir-ecto/ecto)