simplify(llm): default Adapter.fromProtocol auth to Auth.bearer
After the apiKey migration, every adapter explicitly specified `auth`, and three of them (OpenAI Chat, OpenAI Responses, OpenAI-compatible Chat) all wrote `auth: Auth.bearer`. `Auth.bearer` is a no-op when `model.apiKey` is unset, so making it the default is strictly safer than the previous `Auth.passthrough` default — bearer-style adapters drop their explicit `auth` line, and adapters that need a different scheme opt out via `Auth.apiKeyHeader(...)` (Anthropic, Gemini) or a custom `Auth` (Bedrock SigV4 + Bearer). Update doc comments on `fromProtocol.auth`, `Auth` type, and `packages/llm/AGENTS.md` to reflect the new default.
This commit is contained in:
@@ -41,7 +41,7 @@ An adapter is the registered, runnable composition of four orthogonal pieces:
|
||||
|
||||
- **`Protocol`** (`src/protocol.ts`) — semantic API contract. Owns request lowering, target validation, body encoding, and the streaming chunk-to-event state machine. Examples: `OpenAIChat.protocol`, `OpenAIResponses.protocol`, `AnthropicMessages.protocol`, `Gemini.protocol`, `BedrockConverse.protocol`.
|
||||
- **`Endpoint`** (`src/endpoint.ts`) — URL construction. Receives the request and the validated target so it can read `model.id`, `model.baseURL`, `model.native.queryParams`, and any target field that influences the URL (e.g. Bedrock's `modelId` segment). Reach for `Endpoint.baseURL({ default, path })` before hand-rolling a URL.
|
||||
- **`Auth`** (`src/auth.ts`) — per-request transport authentication. Most adapters use `Auth.passthrough`: their auth header is statically baked into `model.headers` by their `model()` constructor. Adapters that need per-request signing (Bedrock SigV4, future Vertex IAM, Azure AAD) implement `Auth` as a function that signs the body and merges signed headers into the result.
|
||||
- **`Auth`** (`src/auth.ts`) — per-request transport authentication. Adapters read `model.apiKey` at request time via `Auth.bearer` (the `Adapter.fromProtocol` default; sets `Authorization: Bearer <apiKey>`) or `Auth.apiKeyHeader(name)` for providers that use a custom header (Anthropic `x-api-key`, Gemini `x-goog-api-key`). Adapters that need per-request signing (Bedrock SigV4, future Vertex IAM, Azure AAD) implement `Auth` as a function that signs the body and merges signed headers into the result.
|
||||
- **`Framing`** (`src/framing.ts`) — bytes → frames. SSE (`Framing.sse`) is shared; Bedrock keeps its AWS event-stream framing as a typed `Framing<object>` value alongside its protocol.
|
||||
|
||||
Compose them via `Adapter.fromProtocol(...)`:
|
||||
@@ -74,7 +74,7 @@ packages/llm/src/
|
||||
|
||||
protocol.ts // Protocol type + Protocol.define
|
||||
endpoint.ts // Endpoint type + Endpoint.baseURL
|
||||
auth.ts // Auth type + Auth.passthrough
|
||||
auth.ts // Auth type + Auth.bearer / Auth.apiKeyHeader / Auth.passthrough
|
||||
framing.ts // Framing type + Framing.sse
|
||||
|
||||
provider/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Effect, Stream } from "effect"
|
||||
import { HttpClientRequest, type HttpClientResponse } from "effect/unstable/http"
|
||||
import type { Auth } from "./auth"
|
||||
import { passthrough as authPassthrough } from "./auth"
|
||||
import { bearer as authBearer } from "./auth"
|
||||
import type { Endpoint } from "./endpoint"
|
||||
import * as LLM from "./llm"
|
||||
import { RequestExecutor } from "./executor"
|
||||
@@ -143,7 +143,13 @@ export interface FromProtocolInput<Draft, Target, Frame, Chunk, State> {
|
||||
readonly protocol: Protocol<Draft, Target, Frame, Chunk, State>
|
||||
/** Where the request is sent. */
|
||||
readonly endpoint: Endpoint<Target>
|
||||
/** Per-request transport authentication. Defaults to `Auth.passthrough`. */
|
||||
/**
|
||||
* Per-request transport authentication. Defaults to `Auth.bearer`, which
|
||||
* sets `Authorization: Bearer <model.apiKey>` when `model.apiKey` is set
|
||||
* and is a no-op otherwise. Override with `Auth.apiKeyHeader(name)` for
|
||||
* providers that use a custom header (Anthropic, Gemini), or supply a
|
||||
* custom `Auth` for per-request signing (Bedrock SigV4).
|
||||
*/
|
||||
readonly auth?: Auth
|
||||
/** Stream framing — bytes -> frames before `protocol.decode`. */
|
||||
readonly framing: Framing<Frame>
|
||||
@@ -177,7 +183,7 @@ export interface FromProtocolInput<Draft, Target, Frame, Chunk, State> {
|
||||
export function fromProtocol<Draft, Target, Frame, Chunk, State>(
|
||||
input: FromProtocolInput<Draft, Target, Frame, Chunk, State>,
|
||||
): AdapterDefinition<Draft, Target> {
|
||||
const auth = input.auth ?? authPassthrough
|
||||
const auth = input.auth ?? authBearer
|
||||
const protocol = input.protocol
|
||||
const buildHeaders = input.headers ?? (() => ({}))
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ import type { LLMError, LLMRequest } from "./schema"
|
||||
* Receives the unsigned HTTP request shape (URL, method, body, headers) and
|
||||
* returns the headers to actually send.
|
||||
*
|
||||
* Most adapters use `Auth.passthrough`: their auth header
|
||||
* (`Authorization: Bearer ...`, `x-api-key`, `x-goog-api-key`) is already
|
||||
* baked into `model.headers` by the provider's `model()` constructor, and
|
||||
* `Auth` has nothing to do per request.
|
||||
* Most adapters use the default `Auth.bearer`, which reads
|
||||
* `request.model.apiKey` and sets `Authorization: Bearer ...`. Providers
|
||||
* that use a different header pick `Auth.apiKeyHeader(name)` (e.g.
|
||||
* Anthropic's `x-api-key`, Gemini's `x-goog-api-key`).
|
||||
*
|
||||
* Adapters that need per-request signing (AWS SigV4, future Vertex IAM,
|
||||
* future Azure AAD) implement `Auth` as a function that hashes the body,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Effect, Schema } from "effect"
|
||||
import { Adapter } from "../adapter"
|
||||
import { Auth } from "../auth"
|
||||
import { Endpoint } from "../endpoint"
|
||||
import { Framing } from "../framing"
|
||||
import { capabilities, model as llmModel, type ModelInput } from "../llm"
|
||||
@@ -356,7 +355,6 @@ export const adapter = Adapter.fromProtocol({
|
||||
id: ADAPTER,
|
||||
protocol,
|
||||
endpoint: Endpoint.baseURL({ default: "https://api.openai.com/v1", path: "/chat/completions" }),
|
||||
auth: Auth.bearer,
|
||||
framing: Framing.sse,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Adapter } from "../adapter"
|
||||
import { Auth } from "../auth"
|
||||
import { Endpoint } from "../endpoint"
|
||||
import { Framing } from "../framing"
|
||||
import { capabilities, model as llmModel, type ModelInput } from "../llm"
|
||||
@@ -36,7 +35,6 @@ export const adapter = Adapter.fromProtocol({
|
||||
path: "/chat/completions",
|
||||
required: "OpenAI-compatible Chat requires a baseURL",
|
||||
}),
|
||||
auth: Auth.bearer,
|
||||
framing: Framing.sse,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Effect, Schema } from "effect"
|
||||
import { Adapter } from "../adapter"
|
||||
import { Auth } from "../auth"
|
||||
import { Endpoint } from "../endpoint"
|
||||
import { Framing } from "../framing"
|
||||
import { capabilities, model as llmModel, type ModelInput } from "../llm"
|
||||
@@ -386,7 +385,6 @@ export const adapter = Adapter.fromProtocol({
|
||||
id: ADAPTER,
|
||||
protocol,
|
||||
endpoint: Endpoint.baseURL({ default: "https://api.openai.com/v1", path: "/responses" }),
|
||||
auth: Auth.bearer,
|
||||
framing: Framing.sse,
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user