# Prompts and Responses

Every call to a [LanguageModel](https://effect.plants.sh/ai/language-model/) flows through two
provider-neutral data models:

- **`Prompt`** — the conversation you send *into* the model: an ordered list of
  messages (`system`, `user`, `assistant`, `tool`), each made up of typed
  content **parts** (text, files, tool calls, tool results, reasoning, approval).
- **`Response`** — the model output that comes *back*: an ordered list of typed
  response **parts** (text, reasoning, tool calls, tool results, sources,
  metadata, finish info, errors), with both a non-streaming and a streaming
  taxonomy.

Both live in the `effect/unstable/ai` package:

```ts
import { Prompt, Response } from "effect/unstable/ai"
```
**You rarely build these by hand:** `LanguageModel.generateText` accepts a plain string or a message array and
  builds the `Prompt` for you, and it returns a `GenerateTextResponse` with
  convenient accessors (`.text`, `.toolCalls`, `.usage`, ...). You reach for the
  raw `Prompt`/`Response` constructors when persisting conversation history,
  building custom providers, or assembling multimodal input.

## The common case

### What `prompt` accepts (`RawInput`)

The `prompt` option of `generateText` / `streamText` is typed as
`Prompt.RawInput`, which accepts three shapes: a **string**, an **array of
encoded messages**, or an already-built **`Prompt`**.

```ts
import { Prompt } from "effect/unstable/ai"

// 1. A string — becomes a single user message
Prompt.make("Summarize the Effect docs in one sentence.")

// 2. An array of (encoded) messages
Prompt.make([
  { role: "system", content: "You are a terse assistant." },
  { role: "user", content: [{ type: "text", text: "Say hi." }] }
])

// 3. An existing Prompt is passed through unchanged
const existing = Prompt.make("hello")
Prompt.make(existing) // => existing
```

In a `LanguageModel` call you usually pass the `RawInput` directly:

```ts
import { Effect } from "effect"
import { LanguageModel } from "effect/unstable/ai"

const program = Effect.gen(function* () {
  // string RawInput
  const a = yield* LanguageModel.generateText({
    prompt: "What is 2 + 2?"
  })

  // message-array RawInput
  const b = yield* LanguageModel.generateText({
    prompt: [
      { role: "system", content: "You only answer with numbers." },
      { role: "user", content: [{ type: "text", text: "What is 2 + 2?" }] }
    ]
  })

  return [a.text, b.text]
})
```

### Building a multi-message prompt

Use the role-specific constructors when you want typed, explicit messages —
for example when storing and replaying conversation history.

```ts
import { Prompt } from "effect/unstable/ai"

const prompt = Prompt.fromMessages([
  Prompt.systemMessage({
    content: "You are a helpful assistant specialized in mathematics."
  }),
  Prompt.userMessage({
    content: [Prompt.textPart({ text: "What is the derivative of x²?" })]
  }),
  Prompt.assistantMessage({
    content: [Prompt.textPart({ text: "The derivative of x² is 2x." })]
  })
])

prompt.content.length // => 3
```

`setSystem`, `prependSystem`, `appendSystem`, and `concat` let you layer system
instructions and merge prompts without mutating the originals:

```ts
import { Prompt } from "effect/unstable/ai"

const base = Prompt.make("Write a haiku about TypeScript.")

const withSystem = base.pipe(
  Prompt.prependSystem("You are a poet. ")
)
// => system: "You are a poet. " + user: "Write a haiku about TypeScript."

const longer = Prompt.concat(withSystem, "Now translate it to French.")
// => appends a second user message
```

### Reading a non-streaming response

`LanguageModel.generateText` resolves to a `GenerateTextResponse` whose
accessors pull the relevant `Response` parts out of `response.content` for you.

```ts
import { Effect } from "effect"
import { LanguageModel } from "effect/unstable/ai"

const program = Effect.gen(function* () {
  const response = yield* LanguageModel.generateText({
    prompt: "What's the weather in SF?"
  })

  response.text // => the concatenated text of all TextParts
  response.finishReason // => "stop" | "length" | "tool-calls" | ...
  response.usage // => Response.Usage instance
  response.toolCalls // => ReadonlyArray<Response.ToolCallPart<...>>
  response.toolResults // => ReadonlyArray<Response.ToolResultPart<...>>
  response.content // => the raw ordered Array<Response.Part>

  return response.text
})
```

If you need finer control, iterate `response.content` and switch on
`part.type` — every part carries a `type` discriminator:

```ts
import { Effect } from "effect"
import { LanguageModel } from "effect/unstable/ai"

const program = Effect.gen(function* () {
  const response = yield* LanguageModel.generateText({
    prompt: "Call a tool, then summarize."
  })

  for (const part of response.content) {
    switch (part.type) {
      case "text":
        console.log("text:", part.text)
        break
      case "tool-call":
        console.log("calling", part.name, "with", part.params)
        break
      case "tool-result":
        console.log("result for", part.name, "=>", part.result)
        break
      case "finish":
        console.log("done because", part.reason)
        break
    }
  }
})
```

### Consuming a streamed response

`LanguageModel.streamText` produces a `Stream` of `Response.StreamPart`s. Text
arrives as a `text-start` / `text-delta` / `text-end` sequence keyed by a shared
`id`; reasoning and tool parameters follow the same start/delta/end pattern.
Accumulate the deltas, then treat the completed text as final.

```ts
import { Effect, Stream } from "effect"
import { LanguageModel } from "effect/unstable/ai"

const program = Effect.gen(function* () {
  yield* LanguageModel.streamText({
    prompt: "Stream a short story."
  }).pipe(
    Stream.runForEach((part) =>
      Effect.sync(() => {
        switch (part.type) {
          case "text-delta":
            process.stdout.write(part.delta) // incremental chunk
            break
          case "tool-call":
            console.log("\ntool-call:", part.name)
            break
          case "finish":
            console.log("\nfinished:", part.reason)
            break
        }
      })
    )
  )
})
```
**Deltas are incremental:** `text-delta`, `reasoning-delta`, and `tool-params-delta` parts each carry only
  a *fragment*. Concatenate fragments sharing the same `id` (between the matching
  `*-start` and `*-end` parts) before treating the value as complete. A
  `tool-result` part with `preliminary: true` is a progress update — only the
  final result (`preliminary: false`) is authoritative.

---

## `Prompt` reference

A `Prompt` is `{ content: ReadonlyArray<Message> }` plus a brand. Build prompts
with the constructors below rather than constructing the object literal.

### `RawInput`

The union accepted by `make` and `concat`: `string | Iterable<MessageEncoded> |
Prompt`. A string becomes a single user message; an iterable is decoded as
encoded messages.

```ts
import type { Prompt } from "effect/unstable/ai"

const a: Prompt.RawInput = "hi"
const b: Prompt.RawInput = [{ role: "user", content: "hi" }]
```

### `Prompt` (interface + schema)

`Prompt.Prompt` is the model type; the `Prompt.Prompt` const is a
`Schema.Codec<Prompt, PromptEncoded>` for decoding/encoding whole conversations
(e.g. to persist them).

```ts
import { Schema } from "effect"
import { Prompt } from "effect/unstable/ai"

const decode = Schema.decodeUnknownSync(Prompt.Prompt)
const prompt = decode({
  content: [{ role: "user", content: "Hello" }]
})
prompt.content.length // => 1
```

### `PromptEncoded`

The serialized shape: `{ content: ReadonlyArray<MessageEncoded> }`. This is what
`Schema.encode(Prompt.Prompt)` produces and what you store on disk / in a DB.

### `empty`

A `Prompt` with no messages — a useful identity for folds and `concat`.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.empty.content // => []
```

### `make`

The primary constructor. Normalizes any `RawInput` into a `Prompt`.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.make("Hello").content.length // => 1 (a user message)
```

### `fromMessages`

Builds a `Prompt` directly from an array of already-constructed `Message`s (no
decoding step).

```ts
import { Prompt } from "effect/unstable/ai"

const prompt = Prompt.fromMessages([
  Prompt.systemMessage({ content: "Be brief." }),
  Prompt.userMessage({ content: [Prompt.textPart({ text: "Hi" })] })
])
prompt.content.length // => 2
```

### `fromResponseParts`

Folds an array of `Response.AnyPart`s back into a `Prompt` — completed text and
reasoning streams become assistant parts, tool calls/approvals go in an
assistant message, and non-preliminary tool results go in a tool message. This
is how you append a model turn to conversation history.

```ts
import { Prompt, Response } from "effect/unstable/ai"

const prompt = Prompt.fromResponseParts([
  Response.makePart("text", { text: "Hello there!" })
])
prompt.content[0].role // => "assistant"
```

### `concat`

Appends one `RawInput`'s messages after another prompt's messages, preserving
order. Dual-signature (data-first and data-last/pipeable).

```ts
import { Prompt } from "effect/unstable/ai"

const sys = Prompt.make([{ role: "system", content: "You are helpful." }])
Prompt.concat(sys, "Hello!").content.length // => 2
```

### `setSystem`

Returns a new prompt whose system message is replaced by the given text, dropping
any previous system messages.

```ts
import { Prompt } from "effect/unstable/ai"

const p = Prompt.make([{ role: "system", content: "Old." }, { role: "user", content: "Hi" }])
Prompt.setSystem(p, "New.").content[0] // => system message with content "New."
```

### `prependSystem`

Prepends content to the existing system message (or creates one), leaving a
leading system message.

```ts
import { Prompt } from "effect/unstable/ai"

const p = Prompt.make([{ role: "system", content: "an expert." }])
Prompt.prependSystem(p, "You are ").content[0]
// => system content: "You are an expert."
```

### `appendSystem`

Like `prependSystem`, but appends content to the existing system message instead.

```ts
import { Prompt } from "effect/unstable/ai"

const p = Prompt.make([{ role: "system", content: "You are an expert." }])
Prompt.appendSystem(p, " Be concise.").content[0]
// => system content: "You are an expert. Be concise."
```

### `isPrompt`

Type guard for a `Prompt` value.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.isPrompt(Prompt.empty) // => true
Prompt.isPrompt("hello") // => false
```

### `isMessage` / `isPart`

Type guards for a prompt `Message` and a prompt content `Part` respectively.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.isMessage(Prompt.systemMessage({ content: "hi" })) // => true
Prompt.isPart(Prompt.textPart({ text: "hi" })) // => true
```

### `makeMessage`

Generic message constructor — pass a role literal and the role's params. The
role-specific helpers below wrap this.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.makeMessage("user", {
  content: [Prompt.makePart("text", { text: "Hi" })]
}).role // => "user"
```

### `makePart`

Generic part constructor — pass a part `type` literal and its params. The
part-specific helpers below wrap this.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.makePart("text", { text: "Hi" }).type // => "text"
```

## Prompt messages

`Prompt.Message` is the union `SystemMessage | UserMessage | AssistantMessage |
ToolMessage`. Each role has an interface, a `Schema` const of the same name, and
a lowercase constructor.

### `SystemMessage` / `systemMessage`

System instructions/context. `content` is plain text.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.systemMessage({ content: "You are a helpful assistant." }).role
// => "system"
```

### `UserMessage` / `userMessage`

User input. `content` is an array of `TextPart | FilePart` (the schema also
decodes a bare string into a single text part via `ContentFromString`).

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.userMessage({
  content: [
    Prompt.textPart({ text: "What is in this image?" }),
    Prompt.filePart({ mediaType: "image/jpeg", data: "data:image/jpeg;base64,..." })
  ]
}).role // => "user"
```

### `AssistantMessage` / `assistantMessage`

Model responses for history. `content` may contain text, file, reasoning,
tool-call, tool-result, and tool-approval-request parts.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.assistantMessage({
  content: [
    Prompt.textPart({ text: "Let me check the weather." }),
    Prompt.toolCallPart({
      id: "call_1",
      name: "get_weather",
      params: { city: "SF" },
      providerExecuted: false
    })
  ]
}).role // => "assistant"
```

### `ToolMessage` / `toolMessage`

Tool-side content: `ToolResultPart`s and `ToolApprovalResponsePart`s answering an
assistant's requests.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.toolMessage({
  content: [
    Prompt.toolResultPart({
      id: "call_1",
      name: "get_weather",
      isFailure: false,
      result: { temperature: 72 }
    })
  ]
}).role // => "tool"
```

### `Message` / `ContentFromString`

`Prompt.Message` is also a `Schema.Codec` over the message union, used internally
by the `Prompt` codec. `Prompt.ContentFromString` is the transform that lets a
string stand in for `[{ type: "text", text }]` when decoding user/assistant
content.

```ts
import { Schema } from "effect"
import { Prompt } from "effect/unstable/ai"

const m = Schema.decodeUnknownSync(Prompt.Message)({
  role: "user",
  content: "Hello" // decoded into a single TextPart
})
m.role // => "user"
```

## Prompt parts

`Prompt.Part` is the union of all content parts. Each has an interface, a `Schema`
const of the same name, and a lowercase constructor. All parts carry an
`options` field of type `ProviderOptions` (defaults to `{}`).

### `TextPart` / `textPart`

Plain text content. The most common part.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.textPart({ text: "Hello, world!" }).type // => "text"
```

### `ReasoningPart` / `reasoningPart`

Provider-supplied reasoning summary attached to an assistant message. Not raw
hidden chain-of-thought.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.reasoningPart({ text: "Compared options by price." }).type
// => "reasoning"
```

### `FilePart` / `filePart`

A file attachment. `data` may be a base64/data-URL string, a `Uint8Array`, or a
`URL`.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.filePart({
  mediaType: "application/pdf",
  fileName: "report.pdf",
  data: new Uint8Array([1, 2, 3])
}).mediaType // => "application/pdf"
```

### `ToolCallPart` / `toolCallPart`

A request to invoke a tool. `providerExecuted` distinguishes provider-run from
framework-run tools.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.toolCallPart({
  id: "call_123",
  name: "get_weather",
  params: { city: "San Francisco" },
  providerExecuted: false
}).name // => "get_weather"
```

### `ToolResultPart` / `toolResultPart`

The result of a tool call to feed back into the conversation. `isFailure` flags
whether the handler errored.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.toolResultPart({
  id: "call_123",
  name: "get_weather",
  isFailure: false,
  result: { temperature: 22, condition: "sunny" }
}).isFailure // => false
```

### `ToolApprovalRequestPart` / `toolApprovalRequestPart`

Stored in an assistant message when a tool needs user approval before running.
Pairs an `approvalId` with the `toolCallId` awaiting approval.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.toolApprovalRequestPart({
  approvalId: "approval_123",
  toolCallId: "call_456"
}).approvalId // => "approval_123"
```

### `ToolApprovalResponsePart` / `toolApprovalResponsePart`

The user's answer to an approval request, placed in a tool message. Carries
`approved` and an optional `reason`.

```ts
import { Prompt } from "effect/unstable/ai"

Prompt.toolApprovalResponsePart({
  approvalId: "approval_123",
  approved: false,
  reason: "Operation not allowed"
}).approved // => false
```

### `ProviderOptions`

`Prompt.ProviderOptions` is a `Schema.Record<String, Json | null>` for
provider-specific options attached to any message or part via its `options`
field. Augment per-part `*Options` interfaces to type these for a provider.

```ts
import type { Prompt } from "effect/unstable/ai"

const opts: Prompt.ProviderOptions = {
  anthropic: { cacheControl: { type: "ephemeral" } }
}
```

---

## `Response` reference

A response is an ordered sequence of typed parts. Non-streaming responses use
*complete* parts (`TextPart`, `ToolCallPart`, ...); streaming responses use
*event* parts (`TextStartPart`, `TextDeltaPart`, ...). Every response part
carries a `metadata` field of type `ProviderMetadata` (defaults to `{}`) — note
this is **`metadata`**, distinct from a prompt part's **`options`**.

<Aside type="note" title="Two Prompt vs Response namespaces">
  `Prompt` and `Response` both export `TextPart`, `ReasoningPart`, `ToolCallPart`,
  etc. They are *different types*: prompt parts model conversation input
  (`options`, base64-or-`Uint8Array`-or-`URL` file data), response parts model
  model output (`metadata`, `Uint8Array` file data, richer tool-result typing).
  Keep the Response.makePart("text", { text: "Hello" }).type // => "text"
```

### `isPart`

Type guard for any response part.

```ts
Response.isPart(Response.makePart("text", { text: "hi" })) // => true
```

## Result parts (non-streaming)

These appear in `generateText` output (`response.content`).

### `TextPart`

Plain text content. `response.text` concatenates the `text` of all `TextPart`s.

```ts
Response.makePart("text", { text: "The answer is 42." }).text
// => "The answer is 42."
```

### `ReasoningPart`

Provider-exposed reasoning summary. Read via `response.reasoningText`.

```ts
Response.makePart("reasoning", { text: "Step 1: analyze the question..." }).type
// => "reasoning"
```

### `ToolCallPart` / `toolCallPart`

A tool invocation requested by the model. Generic over `Name` and `Params` so the
parameters stay typed to the tool. `Response.ToolCallPart(name, paramsSchema)`
builds a schema for a specific tool.

```ts
const part = Response.toolCallPart({
  id: "call_123",
  name: "get_weather",
  params: { city: "San Francisco" },
  providerExecuted: false
})
part.params // => { city: "San Francisco" }
```

### `ToolResultPart` / `toolResultPart`

The decoded result of a tool call. It is a union of `ToolResultSuccess` and
`ToolResultFailure` (discriminated by `isFailure`), both carrying the decoded
`result`, the `encodedResult`, `providerExecuted`, and `preliminary` flags.
`Response.ToolResultPart(name, success, failure)` builds a tool-specific schema.

```ts
// success variant
const ok = Response.toolResultPart({
  id: "call_123",
  name: "get_weather",
  isFailure: false,
  result: { temperature: 22 },
  encodedResult: { temperature: 22 },
  providerExecuted: false,
  preliminary: false
})
ok.isFailure // => false

// failure variant
const bad = Response.toolResultPart({
  id: "call_124",
  name: "get_weather",
  isFailure: true,
  result: { message: "city not found" },
  encodedResult: { message: "city not found" },
  providerExecuted: false,
  preliminary: false
})
bad.isFailure // => true
```

### `ToolApprovalRequestPart` / `toolApprovalRequestPart`

Emitted instead of running a tool when the tool's `needsApproval` is set. Pairs
an `approvalId` with the `toolCallId`.

```ts
Response.toolApprovalRequestPart({
  approvalId: "approval_123",
  toolCallId: "call_456"
}).toolCallId // => "call_456"
```

### `FilePart`

A binary file returned by the model. `data` is a `Uint8Array` when decoded (a
base64 string when encoded).

```ts
Response.makePart("file", {
  mediaType: "image/png",
  data: new Uint8Array([1, 2, 3])
}).mediaType // => "image/png"
```

### `DocumentSourcePart` / `UrlSourcePart`

Citations for sources used to generate the response. Both have `type: "source"`
discriminated by `sourceType` (`"document"` vs `"url"`).

```ts
const url = Response.makePart("source", {
  sourceType: "url",
  id: "src_1",
  url: new URL("https://effect.website"),
  title: "Effect"
})
url.sourceType // => "url"

const doc = Response.makePart("source", {
  sourceType: "document",
  id: "src_2",
  mediaType: "application/pdf",
  title: "Spec"
})
doc.sourceType // => "document"
```

### `ResponseMetadataPart`

Per-response metadata: provider `id`, `modelId`, `timestamp`, and the HTTP
`request` details. All fields may be `undefined`.

```ts
Response.makePart("response-metadata", {
  id: "resp_123",
  modelId: "gpt-4",
  timestamp: DateTime.nowUnsafe(),
  request: undefined
}).modelId // => "gpt-4"
```

### `FinishPart`

The terminal part of every response: the `reason`, token `usage`, and optional
HTTP `response` details. `response.finishReason` and `response.usage` read from
this part.

```ts
Response.makePart("finish", {
  reason: "stop",
  usage: new Response.Usage({
    inputTokens: { uncached: undefined, total: 50, cacheRead: undefined, cacheWrite: undefined },
    outputTokens: { total: 25, text: undefined, reasoning: undefined }
  }),
  response: undefined
}).reason // => "stop"
```

### `ErrorPart`

Signals an error occurred while generating. `error` is `unknown` — narrow it
before reading `Error` fields.

```ts
Response.makePart("error", { error: new Error("boom") }).type // => "error"
```

## Stream parts

In addition to the result parts above (which can also appear mid-stream), a
streamed response emits start/delta/end events. Accumulate deltas keyed by `id`.

### `TextStartPart` / `TextDeltaPart` / `TextEndPart`

A text chunk lifecycle: `text-start` opens an `id`, each `text-delta` adds a
`delta` fragment, `text-end` closes it.

```ts
Response.makePart("text-start", { id: "t1" }).type // => "text-start"
Response.makePart("text-delta", { id: "t1", delta: "Hel" }).delta // => "Hel"
Response.makePart("text-end", { id: "t1" }).type // => "text-end"
```

### `ReasoningStartPart` / `ReasoningDeltaPart` / `ReasoningEndPart`

The same start/delta/end lifecycle for streamed reasoning text.

```ts
Response.makePart("reasoning-start", { id: "r1" }).type // => "reasoning-start"
Response.makePart("reasoning-delta", { id: "r1", delta: "Think" }).delta // => "Think"
Response.makePart("reasoning-end", { id: "r1" }).type // => "reasoning-end"
```

### `ToolParamsStartPart` / `ToolParamsDeltaPart` / `ToolParamsEndPart`

Streamed tool-call parameters. `tool-params-start` includes the tool `name` and
`providerExecuted`; deltas are JSON fragments; `tool-params-end` closes the
chunk, after which a complete `tool-call` part follows.

```ts
Response.makePart("tool-params-start", {
  id: "tp1",
  name: "get_weather",
  providerExecuted: false
}).name // => "get_weather"
Response.makePart("tool-params-delta", { id: "tp1", delta: '{"city":' }).delta
// => '{"city":'
Response.makePart("tool-params-end", { id: "tp1" }).type // => "tool-params-end"
```

## Unions and schema builders

The `Response` module distinguishes three families. The type-level unions
describe the shapes; the like-named functions build a tool-aware `Schema.Codec`
from a [`Toolkit`](https://effect.plants.sh/ai/tools/) so tool-call params and tool-result payloads stay
aligned with each tool definition.

### `AnyPart` / `Part` / `StreamPart` (types)

- `AnyPart` / `AnyPartEncoded` — every possible response part (tool parts use
  `any`).
- `Part<Tools>` / `PartEncoded` — non-streaming result parts, tool-typed.
- `StreamPart<Tools>` / `StreamPartEncoded` — streaming event parts, tool-typed.
- `AllParts<Tools>` / `AllPartsEncoded` — the full set (result + stream),
  tool-typed.

```ts
// StreamPart is what Stream.runForEach receives from streamText
declare const part: Response.StreamPart<{}>
part.type // => one of the streaming part type literals
```

### `AllParts` / `Part` / `StreamPart` (schema builders)

Each takes a `Toolkit` and returns a `Schema.Codec` over the corresponding union,
with `ToolCallPart`/`ToolResultPart` schemas generated per tool.

```ts
const toolkit = Toolkit.make(
  Tool.make("GetWeather", {
    parameters: Schema.Struct({ city: Schema.String }),
    success: Schema.Struct({ temperature: Schema.Number })
  })
)

const partSchema = Response.Part(toolkit) // non-streaming, tool-aware
const streamSchema = Response.StreamPart(toolkit) // streaming, tool-aware
const allSchema = Response.AllParts(toolkit) // both
```

## Usage, finish reason, and HTTP details

### `Usage`

A `Schema.Class` of token-usage statistics, split into `inputTokens`
(`uncached`, `total`, `cacheRead`, `cacheWrite`) and `outputTokens` (`total`,
`text`, `reasoning`). Every field is `number | undefined`.

```ts
const usage = new Response.Usage({
  inputTokens: { uncached: 40, total: 50, cacheRead: 10, cacheWrite: 0 },
  outputTokens: { total: 25, text: 20, reasoning: 5 }
})
usage.outputTokens.total // => 25
```

### `FinishReason`

A `Schema.Literals` of why generation stopped:
`"stop"`, `"length"`, `"content-filter"`, `"tool-calls"`, `"error"`, `"pause"`,
`"other"`, `"unknown"`.

```ts
const reason: Response.FinishReason = "tool-calls"
```

### `ProviderMetadata`

`Schema.Record<String, Json | null>` for provider-specific metadata attached to
any response part via its `metadata` field. Provider extra usage info typically
lands here on the `FinishPart`.

```ts
const meta: Response.ProviderMetadata = {
  openai: { systemFingerprint: "fp_123" }
}
```

### `HttpRequestDetails` / `HttpResponseDetails`

Schemas describing the HTTP exchange with the provider. `HttpRequestDetails`
(on `ResponseMetadataPart.request`) captures `method`, `url`, `urlParams`,
`hash`, and `headers`; `HttpResponseDetails` (on `FinishPart.response`) captures
`status` and `headers`. Header values may be `Redacted`.

```ts
const details: typeof Response.HttpResponseDetails.Type = {
  status: 200,
  headers: { "X-Request-Id": "req_abc123" }
}
```

## Utility types and guards

### `ConstructorParams`

Given a response part type, the params object accepted by its constructor — the
part minus the brand, `type`, `sourceType`, and `metadata` (which is optional).

```ts
type Params = Response.ConstructorParams<Response.TextPart>
// => { readonly text: string; readonly metadata?: ... }
```

### `ToolCallParts` / `ToolResultParts`

Type-level helpers that map a `Record<string, Tool.Any>` to the union of its
`ToolCallPart`s / `ToolResultPart`s. Used internally to build `Part`,
`StreamPart`, and `AllParts`.

```ts
declare const tools: Record<string, Tool.Any>
type Calls = Response.ToolCallParts<typeof tools>
type Results = Response.ToolResultParts<typeof tools>
```

## See also

- [LanguageModel](https://effect.plants.sh/ai/language-model/) — produces a `Prompt` from `RawInput` and
  returns responses with `.text`, `.toolCalls`, `.usage`, and friends.
- [Tools and Toolkits](https://effect.plants.sh/ai/tools/) — define the tools whose schemas drive the
  tool-aware `Response.Part` / `StreamPart` / `AllParts` builders.