# `Nous.Providers.OpenAI`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/providers/openai.ex#L1)

OpenAI-specific provider implementation.

Extends the generic OpenAI-compatible provider with OpenAI-specific features:
- Structured outputs (response_format with json_schema)
- Predicted outputs
- Reasoning models (o1, o3)
- Future: Responses API, Assistants API, etc.

For generic OpenAI-compatible endpoints (Groq, Together, LM Studio, etc.),
use `Nous.Providers.OpenAICompatible` instead.

## Usage

    # Basic chat
    {:ok, response} = Nous.Providers.OpenAI.chat(%{
      model: "gpt-4o",
      messages: [%{"role" => "user", "content" => "Hello"}]
    })

    # With structured output
    {:ok, response} = Nous.Providers.OpenAI.chat(%{
      model: "gpt-4o",
      messages: messages,
      response_format: %{
        type: "json_schema",
        json_schema: %{
          name: "response",
          schema: %{type: "object", properties: %{answer: %{type: "string"}}}
        }
      }
    })

    # Streaming
    {:ok, stream} = Nous.Providers.OpenAI.chat_stream(params)
    Enum.each(stream, fn event -> IO.inspect(event) end)

## Configuration

    # In config.exs
    config :nous, :openai,
      api_key: "sk-...",
      organization: "org-..."  # optional

# `api_key`

```elixir
@spec api_key(keyword()) :: String.t() | nil
```

Get the API key from options, environment, or application config.

Lookup order:
1. `:api_key` option passed directly
2. Environment variable (OPENAI_API_KEY)
3. Application config: `config :nous, openai, api_key: "..."`

# `base_url`

```elixir
@spec base_url(keyword()) :: String.t()
```

Get the base URL from options, application config, or default.

Lookup order:
1. `:base_url` option passed directly
2. Application config: `config :nous, openai, base_url: "..."`
3. Default: https://api.openai.com/v1

# `count_tokens`

```elixir
@spec count_tokens(list()) :: integer()
```

Count tokens in messages (rough estimate).

Override this in your provider for more accurate counting.

# `reasoning_model?`

```elixir
@spec reasoning_model?(String.t()) :: boolean()
```

Check if a model is a reasoning model (o1, o3 series).

Reasoning models have different API requirements:
- No streaming support
- No system messages (use developer messages instead)
- No temperature parameter

# `request`

High-level request with message conversion, telemetry, and error wrapping.

Default implementation that:
1. Converts messages to provider format
2. Builds request params
3. Calls chat/2
4. Parses response
5. Emits telemetry events
6. Wraps errors

# `request_stream`

High-level streaming request with message conversion and telemetry.

---

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