Contributing to Nous

Copy Markdown View Source

Thanks for your interest in contributing. This document covers everything you need to develop, test, and submit changes to Nous.

Prerequisites

  • Elixir 1.18+ (uses the built-in JSON module)
  • OTP 27+

Setup

git clone https://github.com/nyo16/nous.git
cd nous
mix deps.get
mix compile

Running Tests

# Run all tests
mix test

# Run a specific test file
mix test test/nous/decisions_test.exs

# Run tests with verbose output
mix test --trace

Code Quality

# Check formatting
mix format --check-formatted

# Run credo linter
mix credo --strict

# Run dialyzer (first run builds PLT, takes a few minutes)
mix dialyzer

# All checks at once
mix compile --warnings-as-errors && mix format --check-formatted && mix credo --strict && mix test

Configuration

API keys are configured via environment variables:

export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
export GROQ_API_KEY="gsk_..."
# See config/config.exs for all supported providers

For local models (no API key needed):

# LM Studio — start the server, then:
agent = Nous.new("lmstudio:qwen3")

# Ollama — start the server, then:
agent = Nous.new("ollama:llama2")

# LlamaCpp — load a GGUF model directly (requires llama_cpp_ex dep):
:ok = LlamaCppEx.init()
{:ok, llm} = LlamaCppEx.load_model("model.gguf", n_gpu_layers: -1)
agent = Nous.new("llamacpp:local", llamacpp_model: llm)

# For thinking models (Qwen3, DeepSeek, etc.), disable <think> tags:
agent = Nous.new("llamacpp:local",
  llamacpp_model: llm,
  model_settings: %{enable_thinking: false}
)

Running Examples

# Run any example script
mix run examples/01_hello_world.exs

# Run with a specific provider
OPENAI_API_KEY=sk-... mix run examples/02_with_tools.exs

Generating Docs

mix docs
open doc/index.html

Project Structure

lib/nous/
 agent.ex                  # Agent struct and builder
 agent_dynamic_supervisor.ex # DynamicSupervisor for AgentServer processes
 agent_registry.ex         # Registry for agent lookup by session ID
 agent_runner.ex           # Core execution loop
 agent_server.ex           # GenServer wrapper for supervised agents
 application.ex            # OTP application and supervision tree
 decisions.ex              # Decision graph top-level API
 errors.ex                 # Error types
 eval.ex                   # Evaluation framework entry point
 fallback.ex               # Fallback model chain support
 hook.ex                   # Lifecycle interceptor structs
 json.ex                   # Internal JSON helpers
 knowledge_base.ex         # Knowledge base top-level API
 llm.ex                    # Direct model calls without agents
 memory.ex                 # Memory system top-level API
 message.ex                # Conversation message struct
 messages.ex               # Conversation/message-list utilities
 model.ex                  # Provider + model configuration
 model_dispatcher.ex       # Routes requests to provider modules
 output_schema.ex          # Structured output
 permissions.ex            # Tool-level permission policy engine
 persistence.ex            # Context persistence API
 plugin.ex                 # Plugin behaviour
 prompt_template.ex        # Safe prompt templates
 provider.ex               # Provider behaviour
 pubsub.ex                 # PubSub abstraction and topic helpers
 react_agent.ex            # ReAct agent wrapper
 research.ex               # Deep research top-level API
 run_context.ex            # Context passed to tools and dynamic prompts
 skill.ex                  # Skill struct and API
 stream_normalizer.ex      # Stream chunk normalization behaviour
 teams.ex                  # Multi-agent team orchestration API
 telemetry.ex              # Telemetry events
 tool.ex                   # Tool struct
 tool_call.ex              # Tool-call field access helpers
 tool_executor.ex          # Tool execution with retries and timeouts
 tool_schema.ex            # Tool -> provider schema conversion
 transcript.ex             # Conversation history compaction
 types.ex                  # Core type definitions
 usage.ex                  # Token/cost usage tracking
 util.ex                   # Shared internal helpers
 workflow.ex               # Workflow DAG top-level API
 agent/                    # Context, callbacks, agent behaviour
 agent_runner/             # Runner internals (prompt assembly, dispatch,
                             #   iteration loop, streaming, tool execution)
 agents/                   # Built-in behaviours (basic, ReAct, KB)
 decisions/                # Decision graph (nodes, edges, store backends,
                             #   context builder)
 errors/                   # Error base and retry info
 eval/                     # Evaluators, metrics, optimizer, reporters,
                             #   suites, YAML loader
 hook/                     # Hook registry and runner
 http/                     # HTTP + streaming backend behaviours and impls
 knowledge_base/           # LLM-compiled wiki (entries, links, documents,
                             #   tools, workflows, prompts, store backends)
 memory/                   # Persistent memory (search, scoring, scopes,
                             #   tools, store and embedding backends)
 message/                  # Multimodal content parts
 messages/                 # Per-provider message marshalling
 output_schema/            # `use Nous.OutputSchema` macro and validator
 permissions/              # Permission policy struct
 persistence/              # Persistence backends (ETS)
 plugins/                  # Agent plugins (memory, decisions, KB, skills,
                             #   sub-agent, teams, HITL, guards, summarization)
 prom_ex/                  # PromEx plugin for Prometheus metrics
 providers/                # LLM provider adapters
 pubsub/                   # Approval request/response over PubSub
 research/                 # Deep research pipeline
 session/                  # Session config and guardrails
 skill/                    # Skill loader and registry
 skills/                   # Bundled skill modules
 stream_normalizer/        # Per-provider stream chunk normalizers
 teams/                    # Team coordinator, shared state, roles, limits
 tool/                     # Tool system (behaviour, schema DSL, registry,
                             #   validator, context updates, test helpers)
 tools/                    # Built-in tools (bash, files, search, web, todos)
 workflow/                 # Workflow graph, compiler, engine, checkpoints

Submitting changes

# Fork, clone, then:
mix deps.get
mix test                     # Make sure tests pass
mix format                   # Format your code
mix credo --strict           # Check for issues
# Open a PR against master

See CHANGELOG.md for recent changes.

Security

Nous has project-wide security rules that are non-negotiable. Code that breaks these will be rejected on review:

  1. Never String.to_atom/1 on untrusted input. Use String.to_existing_atom/1 with rescue, or pattern-match on a whitelist of literal strings.
  2. Tools requiring approval are rejected without an :approval_handler. Bash, FileWrite, FileEdit need one wired in RunContext or they refuse to run.
  3. File tools enforce a workspace root via PathGuard. Don't bypass it.
  4. HTTP from agents goes through UrlGuard. Don't make raw Req.get/1 calls from a tool to a user-controlled URL.
  5. PromptTemplate rejects <% ... %> blocks — only <%= @var %> substitution is allowed (RCE prevention).
  6. Sub-agent deps don't auto-forward. Declare which deps a sub-agent sees with :sub_agent_shared_deps, [:key1, :key2].

The full text and rationale for each rule lives in AGENTS.md.