Changelog
View SourceAll notable changes to this project will be documented in this file.
[0.13.1] - 2026-04-03
Added
Nous.Transcript— Lightweight conversation compaction without LLM calls.compact/2— keep last N messages, summarize older ones into a system messagemaybe_compact/2— auto-compact based on message count (:every), token budget (:token_budget), or percentage threshold (:threshold)compact_async/2andcompact_async/3— background compaction viaNous.TaskSupervisormaybe_compact_async/3— background auto-compact with{:compacted, msgs}/{:unchanged, msgs}callbacksestimate_tokens/1andestimate_messages_tokens/1— word-count-based token estimation
Built-in Coding Tools — 6 tools implementing
Nous.Tool.Behaviourfor coding agents:Nous.Tools.Bash— shell execution via NetRunner with timeout and output limitsNous.Tools.FileRead— file reading with line numbers, offset, and limitNous.Tools.FileWrite— file writing with auto parent directory creationNous.Tools.FileEdit— string replacement with uniqueness check andreplace_allNous.Tools.FileGlob— file pattern matching sorted by modification timeNous.Tools.FileGrep— content search with ripgrep fallback to pure Elixir regex
Nous.Permissions— Tool-level permission policy engine complementing InputGuard:- Three presets:
default_policy/0,permissive_policy/0,strict_policy/0 build_policy/1— custom policies with:deny,:deny_prefixes,:approval_requiredblocked?/2,requires_approval?/2— case-insensitive tool name checkingfilter_tools/2,partition_tools/2— filter tool lists through policies
- Three presets:
Nous.Session.ConfigandNous.Session.Guardrails— session-level turn limits and token budgets:Configstruct withmax_turns,max_budget_tokens,compact_after_turnsGuardrails.check_limits/4— returns:okor{:error, :max_turns_reached | :max_budget_reached}Guardrails.remaining/4,Guardrails.summary/4— budget tracking and reporting
Fixed
- Empty stream silent failure:
run_streamnow emits{:error, :empty_stream}+ warning when a provider returns zero events (e.g. minimax), instead of silently yielding{:complete, %{output: ""}}. Memory.Searchcrash on vector search error:{:ok, results} = store_mod.search_vector(...)pattern match replaced withcase— logs warning and returns empty list on error.- Atom table exhaustion in skill loader:
String.to_atom/1replaced withString.to_existing_atom/1+ rescue fallback with debug logging. - Context deserialization crash on unknown roles:
String.to_existing_atom/1replaced with explicit role whitelist (:system,:user,:assistant,:tool), defaults to:userwith warning. - Unbounded inspect in stream normalizer:
inspect(chunk, limit: :infinity)capped tolimit: 500, printable_limit: 1000. - SQLite embedding decode crash:
JSON.decode!/1wrapped in rescue, returnsnilwith warning on malformed data. - Muninn bare rescue:
rescue _ ->replaced with specific exception types (MatchError,File.Error,ErlangError,RuntimeError).
Documentation
- Memory System Guide (
docs/guides/memory.md) — 630+ line walkthrough covering all 6 store backends, search/scoring, BM25, agent integration, and cross-agent memory sharing. - Context & Dependencies Guide (
docs/guides/context.md) — RunContext, ContextUpdate operations, stateful agent walkthrough, multi-user patterns. - Skills Guide enhanced — added 400+ lines: module-based and file-based skill walkthroughs, skill groups, activation modes, plugin configuration.
- LiveView examples — chat interface (
liveview_chat.exs) and multi-agent dashboard (liveview_multi_agent.exs) reference implementations. - PostgreSQL memory example (
postgresql_full.exs) — end-to-end Store implementation with tsvector + pgvector, BM25 search, hybrid RRF search. - Coding agent example (
19_coding_agent.exs) — permissions, tools, guardrails, and transcript compaction. - Tool permissions example (
tool_permissions.exs) — policy presets, custom deny lists, tool filtering.
[0.13.0] - 2026-03-28
Added
Nous.Workflow— DAG/graph-based workflow engine for orchestrating agents, tools, and control flow as executable directed graphs. Complements Decisions (reasoning tracking) and Teams (persistent agent groups).- Builder API:
Ecto.Multi-style pipes —Workflow.new/1 |> add_node/4 |> connect/3 |> chain/2 |> run/2 - 8 node types:
:agent_step,:tool_step,:transform,:branch,:parallel,:parallel_map,:human_checkpoint,:subworkflow - Hand-rolled graph: dual adjacency maps, Kahn's algorithm for topological sort + cycle detection + parallel execution levels in one O(V+E) pass
- Static parallel: named branches fan-out concurrently via
Task.Supervisor - Dynamic
parallel_map: runtime fan-out over data lists withmax_concurrencythrottling — the scatter-gather pattern - Cycle support: edge-following execution with per-node max-iteration guards for retry/quality-gate loops
- Workflow hooks:
:pre_node,:post_node,:workflow_start,:workflow_end— integrates with existingNous.Hookstruct - Pause/resume: via hook (
{:pause, reason}),:atomicsexternal signal, or:human_checkpointauto-suspend - Error strategies:
:fail_fast,:skip,{:retry, max, delay},{:fallback, node_id}per node - Telemetry:
[:nous, :workflow, :run|:node, :start|:stop|:exception]events - Execution tracing: opt-in per-node timing and status recording (
trace: true) - Checkpointing:
Checkpointstruct +Storebehaviour + ETS backend - Subworkflows: nested workflow invocation with
input_mapper/output_mapperfor data isolation - Runtime graph mutation:
on_node_completecallback,Graph.insert_after/6,Graph.remove_node/2 - Mermaid visualization:
Workflow.to_mermaid/1generates flowchart diagrams with type-specific node shapes - Scratch ETS: optional per-workflow ETS table for large/binary data exchange between steps
- 113 new tests covering all workflow features
- Builder API:
[0.12.17] - 2026-03-28
Removed
- Dead module
Nous.Decisions.Tools: 4 tool functions never used by any plugin or code path. - Dead module
Nous.StreamNormalizer.Mistral: Mistral provider uses the default OpenAI-compatible normalizer. - Dead function
emit_fallback_exhausted/3in Fallback module: Defined but never called. - Dead config
enable_telemetry: Set in config files but never read — telemetry is always on. - Dead config
log_level: Set in dev/test configs but never read by Nous. - Unused test fixtures:
NousTest.Fixtures.LLMResponsesand its generator script (generated Oct 2025, never imported).
Fixed
- Compiler warning in
output_schema.ex: Removed always-truthy conditional aroundto_json_schema/1return value.
Changed
- All JSON encoding/decoding uses built-in
JSONmodule instead ofJason. Jason removed from direct dependencies. - Added
pretty_encode!/1helper to internal JSON module for pretty-printed JSON output (used in LLM prompts and eval reports). - Updated README with Elixir 1.18+ / OTP 27+ requirements.
[0.12.16] - 2026-03-28
Fixed
- Anthropic multimodal messages silently lost image data:
message_to_anthropic/1matched oncontentbeing a list, butMessage.user/2stores content parts inmetadata.content_partsas a string. Multimodal messages were sent as plain text, losing all image data. Now reads from metadata like the OpenAI formatter. - Gemini multimodal messages had the same issue: Same pattern match bug caused all image content to be dropped.
- Anthropic image format incorrect: The
datafield contained the full data URL prefix (data:image/jpeg;base64,...) instead of raw base64;media_typewas hardcoded to"image/jpeg"regardless of actual format; HTTP URLs were incorrectly wrapped as base64 source instead of"type": "url". - Gemini had no image support: All non-text content parts fell through to a
[Image: ...]text representation. Now usesinlineDatafor base64 images andfileDatafor HTTP URLs. - Anthropic duplicate thinking block: Assistant messages with reasoning content emitted the
thinkingblock twice.
Added
ContentPart.parse_data_url/1— extract MIME type and raw base64 data from a data URL string.ContentPart.data_url?/1andContentPart.http_url?/1— URL type predicates.- OpenAI formatter:
:imagecontent type support (converts to data URL) anddetailoption passthrough forimage_urlparts. - Comprehensive vision test pipeline (
test/nous/vision_pipeline_test.exs) with 19 unit tests covering format conversion across all providers and 4 LLM integration tests. - Test fixture images:
test_square.png(100x100 red),test_tiny.webp(minimal WebP).
[0.12.15] - 2026-03-26
Fixed
receive_timeoutsilently dropped inNous.LLM:generate_text/3andstream_text/3with a string model only passed[:base_url, :api_key, :llamacpp_model]toModel.parse, soreceive_timeoutwas silently ignored. Now correctly forwarded.
Removed
- Dead timeout config: Removed unused
default_timeoutandstream_timeoutfromconfig/config.exs. Timeouts are determined by per-provider defaults inModel.default_receive_timeout/1and each provider module's@default_timeout/@streaming_timeoutconstants.
Documentation
- Added "Timeouts" section to README documenting
receive_timeoutoption and default timeouts per provider.
[0.13.0] - 2026-03-21
Added
Hooks system: Granular lifecycle interceptors for tool execution and request/response flow.
- 6 lifecycle events:
pre_tool_use,post_tool_use,pre_request,post_response,session_start,session_end - 3 handler types:
:function(inline),:module(behaviour),:command(shell via NetRunner) - Matcher-based dispatch: string (exact tool name), regex, or predicate function
- Blocking semantics for
pre_tool_useandpre_request— hooks can deny or modify tool calls - Priority-based execution ordering (lower = earlier)
Telemetry events:
[:nous, :hook, :execute, :start | :stop],[:nous, :hook, :denied]Nous.Hook,Nous.Hook.Registry,Nous.Hook.Runner- New option on
Nous.Agent.new/2::hooks - New example:
examples/16_hooks.exs
- 6 lifecycle events:
Skills system: Reusable instruction/capability packages for agents.
- Module-based skills with
use Nous.Skillmacro and behaviour callbacks - File-based skills: markdown files with YAML frontmatter, loaded from directories
- 5 activation modes:
:manual,:auto,{:on_match, fn},{:on_tag, tags},{:on_glob, patterns} - Skill groups:
:coding,:review,:testing,:debug,:git,:docs,:planning - Registry with load/unload, activate/deactivate, group operations, and input matching
Nous.Plugins.Skills— auto-included plugin bridging skills into the agent lifecycle- Directory scanning:
skill_dirs:option andNous.Skill.Registry.register_directory/2 Telemetry events:
[:nous, :skill, :activate | :deactivate | :load | :match]- New options on
Nous.Agent.new/2::skills,:skill_dirs - New example:
examples/17_skills.exs - New guides:
docs/guides/skills.md,docs/guides/hooks.md
- Module-based skills with
21 built-in skills:
- Language-agnostic (10): CodeReview, TestGen, Debug, Refactor, ExplainCode, CommitMessage, DocGen, SecurityScan, Architect, TaskBreakdown
- Elixir-specific (5): PhoenixLiveView, EctoPatterns, OtpPatterns, ElixirTesting, ElixirIdioms
- Python-specific (6): PythonFastAPI, PythonTesting, PythonTyping, PythonDataScience, PythonSecurity, PythonUv
NetRunner dependency (
~> 1.0.4): Zero-zombie-process OS command execution for command hooks with SIGTERM→SIGKILL timeout escalation.76 new tests for hooks and skills systems.
[0.12.11] - 2026-03-19
Added
- Per-run structured output override: Pass
output_type:andstructured_output:as options toNous.Agent.run/3andNous.Agent.run_stream/3to override the agent's defaults per call. The same agent can return raw text or structured data depending on the request. - Multi-schema selection (
{:one_of, [SchemaA, SchemaB]}): New output_type variant where the LLM dynamically chooses which schema to use per response. Each schema becomes a synthetic tool — the LLM's tool choice acts as schema selection. Includes automatic retry and validation against the selected schema.OutputSchema.schema_name/1— public helper to get snake_case name for a schema moduleOutputSchema.tool_name_for_schema/1— build synthetic tool name from schema moduleOutputSchema.find_schema_for_tool_name/2— reverse-map tool name to schema moduleOutputSchema.synthetic_tool_name?/1— predicate for synthetic tool call detectionOutputSchema.extract_response_for_one_of/2— extract text and identify matched schema from tool call- New example: Example 6 (per-run override) and Example 7 (multi-schema) in
examples/14_structured_output.exs - New sections in
docs/guides/structured_output.md
Fixed
- Synthetic tool call handling: Structured output tool calls (
__structured_output__) in:tool_callmode are now correctly filtered from the tool execution loop. Previously, these synthetic calls would produce "Tool not found" errors and cause an unnecessary extra LLM round-trip. Now they terminate the loop immediately and the structured output is extracted directly.
[0.12.10] - 2026-03-19
Added
- Fallback model/provider support: Automatic failover to alternative models when the primary model fails with a
ProviderErrororModelError(rate limit, server error, timeout, auth issue).Nous.Fallback— core fallback logic: eligibility checks, recursive model chain traversal, model string/struct parsing:fallbackoption onNous.Agent.new/2— ordered list of fallback model strings orModelstructs:fallbackoption onNous.generate_text/3andNous.stream_text/3- Tool schemas are automatically re-converted when falling back across providers (e.g., OpenAI → Anthropic)
- Structured output settings are re-injected for the target provider on cross-provider fallback
- Agent model is swapped on successful fallback so remaining iterations use the working model
- Streaming fallback retries stream initialization only, not mid-stream failures
- New telemetry events:
[:nous, :fallback, :activated]and[:nous, :fallback, :exhausted] - Only
ProviderErrorandModelErrortrigger fallback; application-level errors (ValidationError,MaxIterationsExceeded,ExecutionCancelled,ToolError) are returned immediately - 52 new tests across
test/nous/fallback_test.exsandtest/nous/agent_fallback_test.exs
Changed
Nous.Agentstruct gainsfallback: [Model.t()]field (default:[])Nous.LLMnow uses injectable dispatcher (get_dispatcher/0) for testability, consistent withAgentRunner
[0.12.9] - 2026-03-12
Added
- InputGuard plugin: Modular malicious input classifier with pluggable strategy pattern. Detects prompt injection, jailbreak attempts, and other malicious inputs before they reach the LLM.
Nous.Plugins.InputGuard— Main plugin with configurable aggregation (:any/:majority/:all), short-circuit mode, and violation callbacksNous.Plugins.InputGuard.Strategy— Behaviour for custom detection strategiesNous.Plugins.InputGuard.Strategies.Pattern— Built-in regex patterns for instruction override, role reassignment, DAN jailbreaks, prompt extraction, and encoding evasion. Supports:extra_patterns(additive) and:patterns(full override)Nous.Plugins.InputGuard.Strategies.LLMJudge— Secondary LLM classification with fail-open/fail-closed modesNous.Plugins.InputGuard.Strategies.Semantic— Embedding cosine similarity against pre-computed attack vectorsNous.Plugins.InputGuard.Policy— Severity-to-action resolution (:block,:warn,:log,:callback, customfun/2)- Tracks checked message index to prevent re-triggering on tool-call loop iterations
- New example:
examples/15_input_guard.exs
Fixed
- AgentRunner:
before_requestplugin hook now short-circuits the LLM call when a plugin setsneeds_response: false(e.g., InputGuard blocking). Previously the current iteration would still call the LLM before the block took effect on the next iteration.
[0.12.8] - 2026-03-12
Fixed
- Vertex AI v1/v1beta1 bug:
Model.parse("vertex_ai:gemini-2.5-pro-preview-06-05")withGOOGLE_CLOUD_PROJECTset was storing a hardcodedv1URL inmodel.base_url, causing the provider'sv1beta1selection logic to be bypassed. Preview models now correctly usev1beta1at request time.
Added
- Vertex AI input validation: Project ID and region from environment variables are now validated with helpful error messages instead of producing opaque DNS/HTTP errors.
GOOGLE_CLOUD_LOCATIONsupport: Added as a fallback forGOOGLE_CLOUD_REGION, consistent with other Google Cloud libraries and tooling.- Multi-region example script:
examples/providers/vertex_ai_multi_region.exs
[0.12.7] - 2026-03-10
Fixed
- Vertex AI model routing: Fixed
build_request_params/3not including the"model"key in the params map, causingchat/2andchat_stream/2to always fall back to"gemini-2.0-flash"regardless of the requested model. - Vertex AI 404 on preview models: Use
v1beta1API version for preview and experimental models (e.g.,gemini-3.1-pro-preview). Thev1endpoint returns 404 for these models.
Added
Nous.Providers.VertexAI.api_version_for_model/1— returns"v1beta1"for preview/experimental models,"v1"for stable models.Nous.Providers.VertexAI.endpoint/3now accepts an optional model name to select the correct API version.- Debug logging for Vertex AI request URLs.
[0.12.6] - 2026-03-07
Added
- Auto-update memory:
Nous.Plugins.Memorycan now automatically reflect on conversations and update memories after each run — no explicit tool calls needed. Enable withauto_update_memory: trueinmemory_config. Configurable reflection model, frequency, and context limits.- New
after_run/3callback inNous.Pluginbehaviour — runs once after the entire agent run completes. Wired into bothAgentRunner.run/3andrun_with_context/3. Nous.Plugin.run_after_run/4helper for executing the hook across all plugins- New config options:
:auto_update_memory,:auto_update_every,:reflection_model,:reflection_max_tokens,:reflection_max_messages,:reflection_max_memories - New example:
examples/memory/auto_update.exs
- New
[0.12.5] - 2026-03-06
Added
- Vertex AI provider:
Nous.Providers.VertexAIfor accessing Gemini models through Google Cloud Vertex AI. Supports enterprise features (VPC-SC, CMEK, regional endpoints, IAM).- Three auth modes: app config Goth (
config :nous, :vertex_ai, goth: MyApp.Goth), per-model Goth (default_settings: %{goth: MyApp.Goth}), or direct access token (api_key/VERTEX_AI_ACCESS_TOKEN) - Bearer token auth via
api_keyoption,VERTEX_AI_ACCESS_TOKENenv var, or Goth integration - Goth integration (
{:goth, "~> 1.4", optional: true}) for automatic service account token management — reuse existing Goth processes from PubSub, etc. - URL auto-construction from
GOOGLE_CLOUD_PROJECTandGOOGLE_CLOUD_REGIONenv vars Nous.Providers.VertexAI.endpoint/2helper to build endpoint URLs- Reuses existing Gemini message format, response parsing, and stream normalization
- Model string:
"vertex_ai:gemini-2.0-flash"
- Three auth modes: app config Goth (
[0.12.2] - 2026-03-04
Fixed
- Gemini streaming: Fixed streaming responses returning 0 events. The Gemini
streamGenerateContentendpoint returns a JSON array (application/json) by default, not Server-Sent Events. Instead of forcing SSE viaalt=ssequery parameter, added a pluggable stream parser toNous.Providers.HTTP.
Added
Nous.Providers.HTTP.JSONArrayParser— stream buffer parser for JSON array responses. Extracts complete JSON objects from a streaming[{...},{...},...]response by tracking{}nesting depth while respecting string literals and escape sequences.:stream_parseroption onHTTP.stream/4— accepts any module implementingparse_buffer/1with the same{events, remaining_buffer}contract as SSE parsing. Defaults to the existing SSE parser. Enables any provider with a non-SSE streaming format to plug in a custom parser.
[0.12.0] - 2026-02-28
Added
Memory System: Persistent memory for agents with hybrid text + vector search, temporal decay, importance weighting, and flexible scoping.
Nous.Memory.Entry— memory entry struct with type (semantic/episodic/procedural), importance, evergreen flag, and scoping fields (agent_id, session_id, user_id, namespace)Nous.Memory.Store— storage behaviour with 8 callbacks (init, store, fetch, delete, update, search_text, search_vector, list)Nous.Memory.Store.ETS— zero-dep in-memory backend with Jaro-distance text searchNous.Memory.Store.SQLite— SQLite + FTS5 backend (requiresexqlite)Nous.Memory.Store.DuckDB— DuckDB + FTS + vector backend (requiresduckdbex)Nous.Memory.Store.Muninn— Tantivy BM25 text search backend (requiresmuninn)Nous.Memory.Store.Zvec— HNSW vector search backend (requireszvec)Nous.Memory.Store.Hybrid— combines Muninn + Zvec for maximum retrieval qualityNous.Memory.Scoring— pure functions for Reciprocal Rank Fusion, temporal decay, composite scoringNous.Memory.Search— hybrid search orchestrator (text + vector → RRF merge → decay → composite score)Nous.Memory.Embedding— embedding provider behaviour with pluggable implementationsNous.Memory.Embedding.Bumblebee— local on-device embeddings via Bumblebee + EXLA (Qwen 0.6B default)Nous.Memory.Embedding.OpenAI— OpenAI text-embedding-3-small providerNous.Memory.Embedding.Local— generic local endpoint (Ollama, vLLM, LMStudio)Nous.Memory.Tools— agent tools:remember,recall,forgetNous.Plugins.Memory— plugin with auto-injection of relevant memories, configurable search scope and injection strategy- 6 example scripts in
examples/memory/(basic ETS, Bumblebee, SQLite, DuckDB, Hybrid, cross-agent) - 62 new tests across 6 test files
Graceful degradation: No embedding provider = keyword-only search. No optional deps =
Store.ETSwith Jaro matching. The core memory system has zero additional dependencies.
[0.11.3] - 2026-02-26
Fixed
- Anthropic and Gemini streaming: Added missing
Nous.StreamNormalizer.AnthropicandNous.StreamNormalizer.Geminimodules. These were referenced inProvider.default_stream_normalizer/0but never created, causing runtime crashes when streaming with Anthropic or Gemini providers.
Added
Nous.StreamNormalizer.Anthropic— normalizes Anthropic SSE events (content_block_delta,message_delta,content_block_startfor tool use, thinking deltas, error events)Nous.StreamNormalizer.Gemini— normalizes Gemini SSE events (candidatesarray with text parts,functionCall,finishReasonmapping)- 42 tests for both new stream normalizers
[0.11.0] - 2026-02-20
Added
Structured Output Mode: Agents return validated, typed data instead of raw strings. Inspired by instructor_ex.
Nous.OutputSchemacore module: JSON schema generation, provider settings dispatch, parsing and validationuse Nous.OutputSchemamacro with@llm_docattribute for schema-level LLM documentationvalidate_changeset/1optional callback for custom Ecto validation rules- Validation retry loop: failed outputs are sent back to the LLM with error details (
max_retriesoption) - System prompt augmentation with schema instructions
Output Type Variants:
- Ecto schema modules — full JSON schema + changeset validation
- Schemaless Ecto types (
%{name: :string, age: :integer}) — lightweight, no module needed - Raw JSON schema maps (string keys) — passed through as-is
{:regex, pattern}— regex-constrained output (vLLM/SGLang){:grammar, ebnf}— EBNF grammar-constrained output (vLLM){:choice, choices}— choice-constrained output (vLLM/SGLang)
Provider Modes: Controls how structured output is enforced per-provider
:auto(default) — picks best mode for the provider:json_schema—response_formatwith strict JSON schema (OpenAI, vLLM, SGLang, Gemini):tool_call— synthetic tool with tool_choice (Anthropic default):json—response_format: json_object(OpenAI-compatible):md_json— prompt-only enforcement with markdown fence + stop token (all providers)
Provider Passthrough:
response_format,guided_json,guided_regex,guided_grammar,guided_choice,json_schema,regex,generationConfignow passed through inbuild_request_paramsNew Files:
lib/nous/output_schema.ex— core modulelib/nous/output_schema/validator.ex— behaviour definitionlib/nous/output_schema/use_macro.ex—use Nous.OutputSchemamacrodocs/guides/structured_output.md— comprehensive guideexamples/14_structured_output.exs— example script with 5 patternstest/nous/output_schema_test.exs— 42 unit teststest/nous/structured_output_integration_test.exs— 16 integration teststest/eval/agents/structured_output_test.exs— 3 LLM integration tests
Changed
Nous.Agentstruct gainsstructured_outputkeyword list field (mode, max_retries)Nous.Types.output_typeexpanded with schemaless, raw JSON schema, and guided mode tuplesNous.AgentRunnerinjects structured output settings, augments system prompt, handles validation retriesNous.Agents.BasicAgent.extract_output/2routes throughOutputSchema.parse_and_validate/2Nous.Agents.ReActAgent.extract_output/2validatesfinal_answeragainst output_type- Provider
build_request_params/3passes through structured output parameters
[0.10.1] - 2026-02-14
Changed
Sub-Agent plugin unified: Merged
ParallelSubAgentintoNous.Plugins.SubAgent- Single plugin now provides both
delegate_task(single) andspawn_agents(parallel) tools system_prompt/2callback injects orchestration guidance including available templates- Templates accept
%Nous.Agent{}structs (recommended) or config maps (legacy) - Parallel execution via
Task.Supervisor.async_stream_nolink - Configurable concurrency (
parallel_max_concurrency, default: 5) and timeout (parallel_timeout, default: 120s) - Graceful partial failure: crashed/timed-out sub-agents don't block others
- Single plugin now provides both
New Example:
examples/13_sub_agents.exs- Template-based sub-agents using
Nous.Agent.new/2structs - Parallel execution with inline model config
- Direct programmatic invocation bypassing the LLM
- Template-based sub-agents using
[0.10.0] - 2026-02-14
Added
Plugin System: Composable agent extensions via
Nous.Pluginbehaviour- Callbacks:
init/2,tools/2,system_prompt/2,before_request/3,after_response/3 - Add
plugins: [MyPlugin]to any agent for cross-cutting concerns - AgentRunner iterates plugins at each stage of the execution loop
- Callbacks:
Human-in-the-Loop (HITL): Approval workflows for sensitive tool calls
requires_approval: trueonNous.Toolstructapproval_handleronNous.Agent.Contextfor approve/edit/reject decisionsNous.Plugins.HumanInTheLoopfor per-tool configuration via deps
Sub-Agent System: Enable agents to delegate tasks to specialized child agents
Nous.Plugins.SubAgentprovidesdelegate_tasktool- Pre-configured agent templates via
deps[:sub_agent_templates] - Isolated context per sub-agent with shared deps support
Conversation Summarization: Automatic context window management
Nous.Plugins.Summarizationmonitors token usage against configurable threshold- LLM-powered summarization with safe split points (never separates tool_call/tool_result pairs)
- Error-resilient: keeps all messages if summarization fails
State Persistence: Save and restore agent conversation state
Nous.Agent.Context.serialize/1anddeserialize/1for JSON-safe round-tripsNous.Persistencebehaviour withsave/load/delete/listcallbacksNous.Persistence.ETSreference implementation- Auto-save hooks on
Nous.AgentServer
Enhanced Supervision: Production lifecycle management for agents
Nous.AgentRegistryfor session-based process lookup via RegistryNous.AgentDynamicSupervisorfor on-demand agent creation/destruction- Configurable inactivity timeout on
AgentServer(default: 5 minutes) - Added to application supervision tree
Dangling Tool Call Recovery: Resilient session resumption
Nous.Agent.Context.patch_dangling_tool_calls/1injects synthetic results for interrupted tool calls- Called automatically when continuing from an existing context
PubSub Abstraction Layer: Unified
Nous.PubSubmodule for all PubSub usageNous.PubSubwraps Phoenix.PubSub with graceful no-op fallback when unavailable- Application-level configuration via
config :nous, pubsub: MyApp.PubSub - Topic builders:
agent_topic/1,research_topic/1,approval_topic/1 Nous.Agent.Contextgainspubsubandpubsub_topicfields (runtime-only, never serialized)Nous.Agent.Callbacks.execute/3now broadcasts via PubSub as a third channel alongside callbacks andnotify_pidAgentServerrefactored to useNous.PubSub— removes ad-hocsetup_pubsub_functions/0andsubscribe_fn/broadcast_fnfrom state- Research Coordinator broadcasts progress via PubSub when
:session_idis provided - SubAgent plugin propagates parent's PubSub context to child agents
Async HITL Approval via PubSub:
Nous.PubSub.Approvalmodulehandler/1builds an approval handler compatible withNous.Plugins.HumanInTheLoop- Broadcasts
{:approval_required, info}and blocks viareceivefor response respond/4sends approval decisions from external processes (e.g., LiveView)- Configurable timeout with
:rejectas default on expiry - Enables async approval workflows without synchronous I/O
Deep Research Agent: Autonomous multi-step research with citations
Nous.Research.run/2public API with HITL checkpoints between iterations- Five-phase loop: plan → search → synthesize → evaluate → report
Nous.Research.Plannerdecomposes queries into searchable sub-questionsNous.Research.Searcherruns parallel search agents per sub-questionNous.Research.Synthesizerfor deduplication, contradiction detection, gap analysisNous.Research.Reportergenerates markdown reports with inline citations- Progress broadcasting via callbacks,
notify_pid, and PubSub
New Research Tools:
Nous.Tools.WebFetch— URL content extraction with Floki HTML parsingNous.Tools.Summarize— LLM-powered text summarization focused on research queriesNous.Tools.SearchScrape— Parallel fetch + summarize for multiple URLsNous.Tools.TavilySearch— Tavily AI search API integrationNous.Tools.ResearchNotes— Structured finding/gap/contradiction tracking via ContextUpdate
New Dependencies:
floki ~> 0.36(optional, for HTML content extraction)phoenix_pubsub ~> 2.1(test-only, for PubSub integration tests)
Changed
Nous.Agentstruct now acceptsplugins: [module()]optionNous.Toolstruct now acceptsrequires_approval: boolean()optionNous.Agent.Contextnow includesapproval_handler,pubsub, andpubsub_topicfieldsNous.AgentServersupports optional:nameregistration,:persistencebackend, and usesNous.PubSub(removed ad-hocsetup_pubsub_functions/0)Nous.AgentServer:pubsuboption now defaults toNous.PubSub.configured_pubsub()instead ofMyApp.PubSubNous.AgentRunneraccepts:pubsuband:pubsub_topicoptions when building context- Application supervision tree includes AgentRegistry and AgentDynamicSupervisor
[0.9.0] - 2026-01-04
Added
Evaluation Framework: Production-grade testing and benchmarking for AI agents
Nous.Evalmodule for defining and running test suitesNous.Eval.Suitefor test suite management with YAML supportNous.Eval.TestCasefor individual test case definitionsNous.Eval.Runnerfor sequential and parallel test executionNous.Eval.Metricsfor collecting latency, token usage, and cost metricsNous.Eval.Reporterfor console and JSON result reporting- A/B testing support with
Nous.Eval.run_ab/2
Six Built-in Evaluators:
:exact_match- Strict string equality matching:fuzzy_match- Jaro-Winkler similarity with configurable thresholds:contains- Substring and regex pattern matching:tool_usage- Tool call verification with argument validation:schema- Ecto schema validation for structured outputs:llm_judge- LLM-based quality assessment with custom rubrics
Optimization Engine: Automated parameter tuning for agents
Nous.Eval.Optimizerwith three strategies: grid search, random search, Bayesian optimization- Support for float, integer, choice, and boolean parameter types
- Early stopping on threshold achievement
- Detailed trial history and best configuration reporting
New Mix Tasks:
mix nous.eval- Run evaluation suites with filtering, parallelism, and multiple output formatsmix nous.optimize- Parameter optimization with configurable strategies and metrics
New Dependency:
yaml_elixir ~> 2.9for YAML test suite parsing
Documentation
- New comprehensive evaluation framework guide (
docs/guides/evaluation.md) - Five new example scripts in
examples/eval/:01_basic_evaluation.exs- Simple test execution02_yaml_suite.exs- Loading and running YAML suites03_optimization.exs- Parameter optimization workflows04_custom_evaluator.exs- Implementing custom evaluators05_ab_testing.exs- A/B testing configurations
[0.8.1] - 2025-12-31
Fixed
- Fixed
Usagestruct not implementing Access behaviour for telemetry metrics - Fixed
Task.shutdown/2nil return case inAgentServercancellation - Fixed tool call field access for OpenAI-compatible APIs (string vs atom keys)
Added
- Vision/multimodal test suite with image fixtures (
test/nous/vision_test.exs) - ContentPart test suite for image conversion utilities (
test/nous/content_part_test.exs) - Multimodal message examples in conversation demo (
examples/04_conversation.exs)
Changed
- Updated docs to link examples to GitHub source files
- Improved sidebar grouping in hexdocs
[0.8.0] - 2025-12-31
Added
Context Management: New
Nous.Agent.Contextstruct for immutable conversation state, message history, and dependency injection. Supports context continuation between runs:{:ok, result1} = Nous.run(agent, "My name is Alice") {:ok, result2} = Nous.run(agent, "What's my name?", context: result1.context)Agent Behaviour: New
Nous.Agent.Behaviourfor implementing custom agents with lifecycle callbacks (init_context/2,build_messages/2,process_response/3,extract_output/2).Dual Callback System: New
Nous.Agent.Callbackssupporting both map-based callbacks and process messages:# Map callbacks Nous.run(agent, "Hello", callbacks: %{ on_llm_new_delta: fn _event, delta -> IO.write(delta) end }) # Process messages (for LiveView) Nous.run(agent, "Hello", notify_pid: self())Module-Based Tools: New
Nous.Tool.Behaviourfor defining tools as modules withmetadata/0andexecute/2callbacks. UseNous.Tool.from_module/2to create tools from modules.Tool Context Updates: New
Nous.Tool.ContextUpdatestruct allowing tools to modify context state:def my_tool(ctx, args) do {:ok, result, ContextUpdate.new() |> ContextUpdate.set(:key, value)} endTool Testing Helpers: New
Nous.Tool.Testingmodule withmock_tool/2,spy_tool/1, andtest_context/1for testing tool interactions.Tool Validation: New
Nous.Tool.Validatorfor JSON Schema validation of tool arguments.Prompt Templates: New
Nous.PromptTemplatefor EEx-based prompt templates with variable substitution.Built-in Agent Implementations:
Nous.Agents.BasicAgent(default) andNous.Agents.ReActAgent(reasoning with planning tools).Structured Errors: New
Nous.Errorsmodule withMaxIterationsReached,ToolExecutionError, andExecutionCancellederror types.Enhanced Telemetry: New events for iterations (
:iteration), tool timeouts (:tool_timeout), and context updates (:context_update).
Changed
Result Structure:
Nous.run/3now returns%{output: _, context: _, usage: _}instead of just output string.Tool Function Signature: Tools now receive
(ctx, args)instead of(args). The context provides access toctx.depsfor dependency injection.Examples Modernized: Reduced from ~95 files to 21 files. Flattened directory structure from 4 levels to 2 levels. All examples updated to v0.8.0 API.
Removed
Removed deprecated provider modules:
Nous.Providers.Gemini,Nous.Providers.Mistral,Nous.Providers.VLLM,Nous.Providers.SGLang.Removed built-in tools:
Nous.Tools.BraveSearch,Nous.Tools.DateTimeTools,Nous.Tools.StringTools,Nous.Tools.TodoTools. These can be implemented as custom tools.Removed
Nous.RunContext(replaced byNous.Agent.Context).Removed
Nous.PromEx.Plugin(users can implement custom Prometheus metrics using telemetry events).
[0.7.2] - 2025-12-29
Fixed
Stream completion events: The
[DONE]SSE event now properly emits a{:finish, "stop"}event instead of being silently discarded. This ensures stream consumers always receive a completion signal.Documentation links: Fixed broken links in hexdocs documentation. Relative links to
.exsexample files now use absolute GitHub URLs so they work correctly on hexdocs.pm.
[0.7.1] - 2025-12-29
Changed
Make all provider dependencies optional:
openai_ex,anthropix, andgemini_exare now truly optional dependencies. Users only need to install the dependencies for the providers they use.Runtime dependency checks: Provider modules now check for dependency availability at runtime instead of compile-time, allowing the library to compile without any provider-specific dependencies.
OpenAI message format: Messages are now returned as plain maps with string keys (
%{"role" => "user", "content" => "Hi"}) instead ofOpenaiEx.ChatMessagestructs. This removes the compile-time dependency onopenai_exfor message formatting.
Fixed
Fixed "anthropix dependency not available" errors that occurred when using the library in applications without
anthropixinstalled.Fixed compile-time errors that occurred when
openai_exwas not present in the consuming application.
[0.7.0] - 2025-12-27
Initial public release with multi-provider LLM support:
- OpenAI-compatible providers (OpenAI, Groq, OpenRouter, Ollama, LM Studio, vLLM)
- Native Anthropic Claude support with extended thinking
- Google Gemini support
- Mistral AI support
- Tool/function calling
- Streaming support
- ReAct agent implementation