# `PhoenixKitAI.Endpoint`
[🔗](https://github.com/BeamLabEU/phoenix_kit_ai/blob/0.19.2/lib/phoenix_kit_ai/endpoint.ex#L1)

AI endpoint schema for PhoenixKit AI system.

An endpoint is a unified configuration that combines provider credentials,
model selection, and generation parameters into a single entity. Each endpoint
represents one complete AI configuration ready for making API requests.

## Schema Fields

### Identity
- `name`: Display name for the endpoint (e.g., "Claude Fast", "GPT-4 Creative")
- `description`: Optional description of the endpoint's purpose

### Provider Configuration
- `provider`: Integration connection key (e.g. `"openrouter"` or
  `"openrouter:my-key"`). Resolved via `PhoenixKit.Integrations`.
- `api_key`: **Deprecated.** Legacy field retained for pre-Integrations
  endpoints — `OpenRouterClient.resolve_api_key/2` reads it as a fallback
  when no `PhoenixKit.Integrations` connection is configured for the
  endpoint's `provider`. The column is `NOT NULL` in core's V34
  migration, so for now you must provide a value (an empty string is
  accepted by the DB but will trigger a per-call `Logger.warning`
  if no Integrations connection is set up either). The recommended
  migration path is documented in `AGENTS.md` "Migrating from legacy
  `endpoint.api_key`" — set up an OpenRouter connection under Settings
  → Integrations and point `provider` at it; the legacy column then
  becomes unused. Planned for removal in a future major version once
  operators have had time to migrate.
- `base_url`: Optional custom base URL for the provider
- `provider_settings`: Provider-specific settings (JSON)
  - For OpenRouter: `http_referer`, `x_title` headers
  - For TTS: `voice` — default voice / voice id used by `PhoenixKitAI.speak/3`
    when the caller passes no explicit voice

### Model Configuration
- `model`: AI model identifier (e.g., "anthropic/claude-3-haiku")

### Generation Parameters
- `temperature`: Sampling temperature (0-2, default: 0.7)
- `max_tokens`: Maximum tokens to generate (nil = model default)
- `top_p`: Nucleus sampling threshold (0-1)
- `top_k`: Top-k sampling parameter
- `frequency_penalty`: Frequency penalty (-2 to 2)
- `presence_penalty`: Presence penalty (-2 to 2)
- `repetition_penalty`: Repetition penalty (0-2)
- `stop`: Stop sequences (array of strings)
- `seed`: Random seed for reproducibility

### Image Generation Parameters
- `image_size`: Image size (e.g., "1024x1024", "1792x1024")
- `image_quality`: Image quality ("standard", "hd")

### Embeddings Parameters
- `dimensions`: Embedding dimensions (model-specific)

### Status
- `enabled`: Whether the endpoint is active
- `sort_order`: Display order for listing
- `last_validated_at`: Last successful API key validation

## Usage Examples

    # Create an endpoint
    {:ok, endpoint} = PhoenixKitAI.create_endpoint(%{
      name: "Claude Fast",
      provider: "openrouter",
      api_key: "sk-or-v1-...",
      model: "anthropic/claude-3-haiku",
      temperature: 0.7
    })

    # Use the endpoint
    {:ok, response} = PhoenixKitAI.ask(endpoint.uuid, "Hello!")

# `t`

```elixir
@type t() :: %PhoenixKitAI.Endpoint{
  __meta__: term(),
  api_key: term(),
  base_url: term(),
  description: term(),
  dimensions: term(),
  enabled: term(),
  frequency_penalty: term(),
  image_quality: term(),
  image_size: term(),
  inserted_at: term(),
  integration_uuid: term(),
  last_validated_at: term(),
  max_tokens: term(),
  model: term(),
  name: term(),
  presence_penalty: term(),
  provider: term(),
  provider_settings: term(),
  reasoning_effort: term(),
  reasoning_enabled: term(),
  reasoning_exclude: term(),
  reasoning_max_tokens: term(),
  repetition_penalty: term(),
  requests: term(),
  seed: term(),
  sort_order: term(),
  stop: term(),
  temperature: term(),
  top_k: term(),
  top_p: term(),
  updated_at: term(),
  uuid: term()
}
```

# `base_provider`

```elixir
@spec base_provider(String.t()) :: String.t()
```

Strips a named connection suffix from a provider string.

The `provider` column may hold the bare key (`"xai"`) or a named
connection string (`"xai:my-key"`, legacy / pre-V107 rows) — this
extracts the base key so callers can compare against it directly
instead of using `String.starts_with?/2`, which would also match
unrelated providers sharing the same prefix.

# `changeset`

Creates a changeset for endpoint creation and updates.

# `default_base_url`

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

Returns the default base URL for a provider, read from the Integrations
registry (`PhoenixKit.Integrations.Providers.base_url/1`).

All `:ai_completions` providers expose an OpenAI-compatible chat
completions endpoint at `<base>/chat/completions`, so the same Completion
HTTP layer works for them. Returns `nil` when the registry has no base URL
for the key (e.g. a legacy integration-uuid provider value) — the operator
can still set `base_url` manually on the endpoint.

# `image_aspect_ratio_options`

Returns xAI image aspect-ratio options for form selects.

Stored in `provider_settings["aspect_ratio"]` and applied by
`PhoenixKitAI.generate_image/3` when the endpoint's provider is xAI.

# `image_gen_model_picker?`

```elixir
@spec image_gen_model_picker?(String.t() | nil) :: boolean()
```

Whether picking "Image generation" in the Endpoint form's model-type
filter could plausibly return models to choose from for `provider`.

Unlike `tts_model_picker?/1`, this reuses the Integrations registry's
`:image_generation` capability directly rather than a hardcoded
provider check — OpenAI, OpenRouter, and xAI all genuinely support
image generation at the same `/images/generations` path
(`PhoenixKitAI.Completion.generate_image/3`), so there's no single
provider to special-case; Mistral/DeepSeek correctly show an empty
list here since they have no image-gen models at all.

# `image_quality_options`

Returns image quality options for form selects.

# `image_resolution_options`

Returns xAI image resolution options for form selects.

Stored in `provider_settings["resolution"]` and applied by
`PhoenixKitAI.generate_image/3` when the endpoint's provider is xAI.

# `image_size_options`

Returns image size options for form selects.

# `kind`

```elixir
@spec kind(t() | String.t() | nil) :: :chat | :tts | :embedding | :image_gen
```

Classifies an endpoint by the kind of model it points at.

Endpoints don't store a model type, so this infers it from the model
id using the same heuristic the model picker uses (see
`PhoenixKitAI.OpenRouterClient` `:tts`/`:image_gen` filters): a `tts`
substring marks text-to-speech, `embed` marks an embedding model,
`dall-e`/`image` marks image generation (`gpt-image-1`,
`grok-imagine-image[-quality]`, `google/gemini-2.5-flash-image`, ...),
and everything else is treated as chat/completion. Used to badge rows
in the admin UI.

# `kind_icon`

```elixir
@spec kind_icon(:chat | :tts | :embedding | :image_gen) :: String.t()
```

Heroicon name representing an endpoint `kind/1`.

# `masked_api_key`

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

Masks the API key for display.

- `nil` or `""` → `"Not set"`.
- Keys shorter than 14 chars → `"•••"` (a 13-char key would otherwise
  leak most of itself with the head+tail shape).
- Longer keys → first 8 + `…` + last 4 (e.g. `"sk-or-v1…mnop"`).
  Recognisable provider prefix retained, identifying suffix retained,
  middle elided. Useful for human-recognition in admin cards while
  still hiding the bulk of the secret.

# `provider_label`

```elixir
@spec provider_label(String.t()) :: String.t()
```

Returns a display label for the provider, read from the Integrations
registry (the provider's `name`).

Unknown providers — e.g. a legacy integration uuid stored in the column —
fall back to the raw string. Brand names stay effectively un-translated:
registry names are gettext strings, but product trademarks like
`"OpenRouter"` / `"Mistral"` have no translations, so gettext returns them
verbatim rather than producing mixed `"OpenRouter Соединение"` strings.

# `provider_options`

```elixir
@spec provider_options() :: [{String.t(), String.t()}]
```

Returns provider options (`{label, key}`) for form selects.

Built from the same capability-discovered list as `valid_providers/0`;
labels come from each provider's registry name.

# `realtime_voice_capable?`

```elixir
@spec realtime_voice_capable?(String.t() | nil) :: boolean()
```

Whether `provider` supports xAI's realtime streaming voice API
(`PhoenixKitAI.Realtime.Session`, WebSocket-based) — gates the
Playground's streaming voice panel to capable endpoints.

# `reasoning_effort_options`

Returns reasoning effort options for form selects.

# `recently_validated?`

Checks if the endpoint has been validated recently (within the last 24 hours).

# `short_model_name`

Extracts the model name without the provider prefix.

# `tts_model_picker?`

```elixir
@spec tts_model_picker?(String.t() | nil) :: boolean()
```

Whether picking "Text-to-Speech" in the Endpoint form's model-type
filter could plausibly return models to choose from for `provider`.

False only for xAI: it has real TTS (`PhoenixKitAI.speak/3` works via
`POST /v1/tts` regardless of what model type the endpoint stores —
see `Completion.text_to_speech/3`) but it isn't model-based at all —
no model id, nothing in `GET /models` — so the picker would always
come back empty. True for every other provider, including ones with
no TTS at all (an empty list there is accurate, not a dead end to
hide).

# `valid_providers`

```elixir
@spec valid_providers() :: [String.t()]
```

Returns the list of valid provider keys.

Discovered from the Integrations registry — every provider declaring the
`:ai_completions` capability (built-in, or contributed by an external
module via `integration_providers/0`). Adding a chat provider to the
registry makes it valid here automatically; nothing is hardcoded.

# `validation_changeset`

Creates a changeset for updating the last_validated_at timestamp.

---

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