# `Nous.Types`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/types.ex#L1)

Core type definitions for Nous AI.

This module defines all the types used throughout the library.
No functions, just type specifications for documentation and Dialyzer.

Every type here is public, so your own `@spec`s can refer to them by name
instead of restating a shape that Nous may widen later.

## Examples

`t:output_type/0` is what you pass as `:output_type` when building an agent.
All eight variants are values you write literally:

    # raw text (the default)
    Nous.Agent.new("openai:gpt-4o-mini", output_type: :string)

    # schemaless Ecto types
    Nous.Agent.new("openai:gpt-4o-mini", output_type: %{city: :string, population: :integer})

    # an Ecto schema module
    Nous.Agent.new("openai:gpt-4o-mini", output_type: MyApp.WeatherReport)

    # constrained decoding (vLLM/SGLang)
    Nous.Agent.new("vllm:qwen3", output_type: {:choice, ["positive", "negative"]})

`t:content/0` describes the parts of the legacy tuple message format. You
build one with `{:user_prompt, [content()]}` and convert it with
`Nous.Message.from_legacy/1`:

    Nous.Message.from_legacy(
      {:user_prompt,
       [
         {:text, "What is in this picture?"},
         {:image_url, "https://example.com/cat.png"}
       ]}
    )

New code should build the message directly instead — same result, no
conversion step:

    alias Nous.Message
    alias Nous.Message.ContentPart

    Message.user([
      ContentPart.text("What is in this picture?"),
      ContentPart.image_url("https://example.com/cat.png")
    ])

`t:stream_event/0` is the tuple set emitted by `Nous.AgentRunner.run_stream/3`.
Matching on it exhaustively is the point of the type:

    Enum.reduce(stream, "", fn
      {:text_delta, text}, acc ->
        IO.write(text)
        acc <> text

      {:error, reason}, _acc ->
        raise "stream failed: #{inspect(reason)}"

      event, acc
      when elem(event, 0) in [:thinking_delta, :tool_call_delta, :usage, :finish, :complete] ->
        acc
    end)

When you write your own helpers, reference the types rather than copying them:

    @spec summarise([Nous.Types.message()]) :: String.t()
    def summarise(messages), do: Enum.map_join(messages, "\n", &render/1)

# `content`

```elixir
@type content() ::
  String.t()
  | {:text, String.t()}
  | {:image_url, String.t()}
  | {:audio_url, String.t()}
  | {:document_url, String.t()}
```

Message content - can be text or multi-modal.

## Examples

    "Just text"
    {:text, "Formatted text"}
    {:image_url, "https://example.com/image.png"}

# `message`

```elixir
@type message() :: model_request() | model_response()
```

Any message type

# `model`

```elixir
@type model() :: String.t()
```

Model identifier - provider:model string

# `model_request`

```elixir
@type model_request() :: %{parts: [request_part()], timestamp: DateTime.t()}
```

Model request message.

A message we send to the model.

# `model_response`

```elixir
@type model_response() :: %{
  parts: [response_part()],
  usage: Nous.Usage.t(),
  model_name: String.t(),
  timestamp: DateTime.t()
}
```

Model response message.

A message we receive from the model, including usage information.

# `output_type`

```elixir
@type output_type() ::
  :string
  | module()
  | %{required(atom()) =&gt; atom()}
  | map()
  | {:regex, String.t()}
  | {:grammar, String.t()}
  | {:choice, [String.t()]}
  | {:one_of, [module()]}
```

Output type specification.

Controls how agent output is parsed and validated:
- `:string` — raw text (default)
- `module()` — Ecto schema module → JSON schema + changeset validation
- `%{atom() => atom()}` — schemaless Ecto types (e.g. `%{name: :string, age: :integer}`)
- `%{String.t() => map()}` — raw JSON schema map (string keys, passed through as-is)
- `{:regex, String.t()}` — regex-constrained output (vLLM/SGLang)
- `{:grammar, String.t()}` — EBNF grammar-constrained output (vLLM)
- `{:choice, [String.t()]}` — choice-constrained output (vLLM/SGLang)
- `{:one_of, [module()]}` — multi-schema selection: LLM chooses which schema to use

# `request_part`

```elixir
@type request_part() :: system_prompt_part() | user_prompt_part() | tool_return_part()
```

Message parts that can appear in requests to the model

# `response_part`

```elixir
@type response_part() :: text_part() | tool_call_part() | thinking_part()
```

Message parts that can appear in responses from the model

# `stream_event`

```elixir
@type stream_event() ::
  {:text_delta, String.t()}
  | {:thinking_delta, String.t()}
  | {:tool_call_delta, any()}
  | {:usage, map()}
  | {:finish, String.t()}
  | {:complete, map()}
  | {:error, term()}
```

Stream event types emitted by `run_stream/3` and the `stream: true` path of `run/3`.

On successful streams, events typically arrive in this order:
- `{:text_delta, text}` — incremental text content
- `{:thinking_delta, text}` — incremental reasoning/thinking content
- `{:tool_call_delta, calls}` — tool call information (list for OpenAI, map/string for others)
- `{:usage, usage}` — token usage, emitted as a final chunk for OpenAI-compat
  providers when `stream_options.include_usage` is enabled, or alongside
  Anthropic `message_delta` / Gemini `usageMetadata` chunks
- `{:finish, reason}` — stream finished, reason is a string like `"stop"` or `"length"`
- `{:complete, result}` — final aggregated result with `%{output: text, finish_reason: reason}`

`{:error, reason}` indicates a stream error (HTTP error, timeout, etc.) and may be
emitted at any point in the stream. When an error occurs, `{:finish, _}` and
`{:complete, _}` may not be emitted.

# `system_prompt_part`

```elixir
@type system_prompt_part() :: {:system_prompt, String.t()}
```

System prompt message part

# `text_part`

```elixir
@type text_part() :: {:text, String.t()}
```

Text response part from model

# `thinking_part`

```elixir
@type thinking_part() :: {:thinking, String.t()}
```

Thinking/reasoning part from model

# `tool_call`

```elixir
@type tool_call() :: %{id: String.t(), name: String.t(), arguments: map()}
```

Tool call information from the model.

The model requests to call a tool with these parameters.

# `tool_call_part`

```elixir
@type tool_call_part() :: {:tool_call, tool_call()}
```

Tool call part from model

# `tool_return`

```elixir
@type tool_return() :: %{call_id: String.t(), result: any()}
```

Tool return information sent back to the model.

The result of executing a tool call.

# `tool_return_part`

```elixir
@type tool_return_part() :: {:tool_return, tool_return()}
```

Tool return message part

# `user_prompt_part`

```elixir
@type user_prompt_part() :: {:user_prompt, String.t() | [content()]}
```

User prompt message part

---

*Consult [api-reference.md](api-reference.md) for complete listing*
