# `PhoenixKitAI.Request`
[🔗](https://github.com/BeamLabEU/phoenix_kit_ai/blob/v0.26.1/lib/phoenix_kit_ai/request.ex#L1)

AI request schema for PhoenixKit AI system.

Tracks every AI API request for usage history and statistics.
Used for monitoring costs, performance, and debugging.

## Schema Fields

### Request Identity
- `endpoint_uuid`: Foreign key to the AI endpoint used
- `endpoint_name`: Denormalized endpoint name for historical display
- `prompt_uuid`: Foreign key to the AI prompt used (if request used a prompt template)
- `prompt_name`: Denormalized prompt name for historical display
- `user_uuid`: Foreign key to the user who made the request (nullable if user deleted)
- `slot_index`: Which slot was used (deprecated, for backward compatibility)

### Request Details
- `model`: Model identifier (e.g., "anthropic/claude-3-haiku")
- `request_type`: Type of request (e.g., "text_completion", "chat", "embedding", "tts", "image", "image_edit", "vision")

### Token Usage
- `input_tokens`: Number of tokens in the prompt
- `output_tokens`: Number of tokens in the response
- `total_tokens`: Total tokens used (input + output)

### Performance & Cost
- `cost`: What the call cost in US dollars, a `Decimal` (when known)
- `latency_ms`: Response time in milliseconds
- `status`: Request status - "success", "error", or "timeout"
- `error_message`: Error details if status is not "success"

### Metadata
- `metadata`: Additional context (temperature, max_tokens, etc.)
- `metadata.answered_model`: on chat and embedding rows, the model the
  provider says answered, where it is not the one asked for in `model`
  (`answered_model/1`). Image and vision rows record the answered model
  in `model` itself.
- `metadata.provider_error`: on a failed call, what the provider said —
  `status`, `code`, `message` (trimmed, key-shaped words redacted) and
  `retry_after_s` for an error that is not about money.

## Status Types

- `success` - Request completed successfully
- `error` - Request failed with an error
- `timeout` - Request timed out

## Usage Examples

    # Log a successful request
    {:ok, request} = PhoenixKitAI.create_request(%{
      endpoint_uuid: endpoint.uuid,
      endpoint_name: "Claude Fast",
      user_uuid: user.uuid,
      model: "anthropic/claude-3-haiku",
      request_type: "chat",
      input_tokens: 150,
      output_tokens: 320,
      total_tokens: 470,
      latency_ms: 850,
      status: "success",
      metadata: %{"temperature" => 0.7}
    })

    # Log a failed request
    {:ok, request} = PhoenixKitAI.create_request(%{
      endpoint_uuid: endpoint.uuid,
      endpoint_name: "Claude Fast",
      model: "anthropic/claude-3-haiku",
      status: "error",
      error_message: "Rate limit exceeded"
    })

# `t`

```elixir
@type t() :: %PhoenixKitAI.Request{
  __meta__: term(),
  cost: term(),
  endpoint: term(),
  endpoint_name: term(),
  endpoint_uuid: term(),
  error_message: term(),
  input_tokens: term(),
  inserted_at: term(),
  latency_ms: term(),
  metadata: term(),
  model: term(),
  output_tokens: term(),
  prompt: term(),
  prompt_name: term(),
  prompt_uuid: term(),
  request_type: term(),
  slot_index: term(),
  status: term(),
  total_tokens: term(),
  updated_at: term(),
  user: term(),
  user_uuid: term(),
  uuid: term()
}
```

# `answered_model`

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

The model the provider says answered (`metadata.answered_model`), where it
is not the one asked for; `nil` otherwise.

# `badge_status`

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

The core `status_badge` status that colours a request's status: green for
a success, red for an error, amber for a timeout. The label stays
`status_label/1`.

# `changeset`

Creates a changeset for request creation.

# `format_cost`

```elixir
@spec format_cost(Decimal.t() | 0 | nil) :: String.t()
```

Formats a cost in dollars for display, with the precision the amount needs:
- >= $0.01: 2 decimal places ($0.05, $1.23)
- >= $0.0001: 4 decimal places ($0.0012)
- >= $0.000001: 6 decimal places ($0.000030)
- > $0: at least 10 decimal places, more for an amount that needs them to
  show three significant digits, up to the column's 18 ($0.0000000400,
  $0.00000000000141)

# `format_latency`

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

Formats the latency for display.

# `format_tokens`

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

Formats the token count for display.

# `refused?`

```elixir
@spec refused?(t()) :: boolean()
```

Whether the row is a call the guard refused before any HTTP
(`metadata.refused`). Its status is `"error"`; the Usage tab marks it and
filters on it as `"refused"`.

# `short_model_name`

Extracts the model name without provider prefix.

# `status_color`

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

Returns a CSS class for the status badge.

# `status_label`

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

Returns a human-readable, gettext-translated status label.

Literal-call clauses are required so `mix gettext.extract` picks the
strings up — a `gettext(status)` call over a variable wouldn't make
it into the .pot.

# `valid_request_types`

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

Returns the list of valid request types.

# `valid_statuses`

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

Returns the list of valid status types.

---

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