@effect-uai/anthropic 0.12.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Anthropic.d.mts.map +1 -1
- package/dist/Anthropic.mjs +6 -0
- package/dist/Anthropic.mjs.map +1 -1
- package/dist/codec.d.mts.map +1 -1
- package/dist/codec.mjs +2 -0
- package/dist/codec.mjs.map +1 -1
- package/package.json +2 -2
- package/src/Anthropic.ts +7 -0
- package/src/codec.ts +4 -0
package/dist/Anthropic.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Anthropic.d.mts","names":[],"sources":["../src/Anthropic.ts"],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"Anthropic.d.mts","names":[],"sources":["../src/Anthropic.ts"],"mappings":";;;;;;;;;;;;KAgDY,mBAAmB,KAAK;;;;;WAKzB,OAAO;;;;;WAKP;;WAEA,gBAAgB;;;;;;WAMhB,WAAW;;;;WAIX;;KAGC;;;;;;WAMD,eACP,SAAS,qBACN,OAAO,OAAO,eAAe,QAAQ;;;;;WAKjC,aAAa,SAAS,qBAAqB,OAAO,OAAO,WAAW,QAAQ;;;;;;;;WAQ5E,OAAO,SAAS,qBAAqB,OAAO,OAAO,MAAM,QAAQ;;;;;;WAMjE,cAAc,GAAG,GACxB,GAAG,OAAO,OAAO,eAAe,GAAG,OAChC,OAAO,OAAO,WAAW,GAAG;;;;;;;;;cAStB,kBAAkB;KAInB;WACD,QAAQ,SAAS;WACjB;;;;;;WAMA;;;;;;;;;;;;;;;WAeA;aAAqC,MAAM;;;;;;;;cA4QzC,cAAe,GAAG,GAAC,GAC3B,OAAO,OAAO,eAAe,GAAG,OAClC,OAAO,OAAO,WAAW,GAAG;;;;cAmBlB,OAAI,KAAS,WAAS,OAAO,OAAO,yBAAyB,WAAW;;;;;;;;cAqBxE,QAAK,KACX,WACJ,MAAM,MAAM,YAAY,sBAAsB,WAAW"}
|
package/dist/Anthropic.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { KnownProviderEvent, applyEvent } from "./streamEvents.mjs";
|
|
|
5
5
|
import { Context, Effect, Layer, Match, Option, Redacted, Result, Schema, Stream, pipe } from "effect";
|
|
6
6
|
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
7
7
|
import * as AiError from "@effect-uai/core/AiError";
|
|
8
|
+
import * as Capabilities from "@effect-uai/core/Capabilities";
|
|
8
9
|
import { LanguageModel, turnFromStream } from "@effect-uai/core/LanguageModel";
|
|
9
10
|
import * as SSE from "@effect-uai/core/SSE";
|
|
10
11
|
import { descriptorsOf, providerToolsOf } from "@effect-uai/core/Tool";
|
|
@@ -162,6 +163,11 @@ const buildNativeStream = (cfg) => {
|
|
|
162
163
|
})),
|
|
163
164
|
onSuccess: (wire) => Effect.succeed(wire)
|
|
164
165
|
});
|
|
166
|
+
yield* Capabilities.warnDroppedBlocks(request.history, "output_image", {
|
|
167
|
+
provider: "anthropic",
|
|
168
|
+
capability: "output_image",
|
|
169
|
+
reason: "Assistant messages carry no image on this wire. Use `Turn.imagesAsInput` to resend it as user content."
|
|
170
|
+
});
|
|
165
171
|
const bodyResult = buildRequestBody({
|
|
166
172
|
model: request.model,
|
|
167
173
|
history: request.history,
|
package/dist/Anthropic.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Anthropic.mjs","names":[],"sources":["../src/Anthropic.ts"],"sourcesContent":["import {\n Context,\n Effect,\n Layer,\n Match,\n Option,\n Redacted,\n Result,\n Schema,\n Stream,\n pipe,\n} from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport {\n type CommonRequest,\n LanguageModel,\n type LanguageModelService,\n turnFromStream,\n} from \"@effect-uai/core/LanguageModel\"\nimport * as SSE from \"@effect-uai/core/SSE\"\nimport { descriptorsOf, providerToolsOf } from \"@effect-uai/core/Tool\"\nimport { type Turn, TurnEvent } from \"@effect-uai/core/Turn\"\nimport { renderProviderTools } from \"./AnthropicTools.js\"\nimport {\n type Accumulator,\n type CacheControl,\n type CacheTtl,\n type ThinkingConfig,\n accumulatorToTurn,\n buildRequestBody,\n emptyAccumulator,\n} from \"./codec.js\"\nimport type { AnthropicModel } from \"./models.js\"\nimport { KnownProviderEvent, ProviderEvent, applyEvent } from \"./streamEvents.js\"\n\nexport {\n codeExecutionTool,\n webSearchTool,\n type WebSearchOptions,\n type UserLocation,\n} from \"./AnthropicTools.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport type AnthropicRequest = Omit<CommonRequest, \"model\"> & {\n /**\n * Narrows `CommonRequest.model` (`string`) to the typed `AnthropicModel`\n * literal union for autocomplete.\n */\n readonly model: AnthropicModel\n /**\n * Top-K nucleus sampling parameter. Anthropic-specific; not exposed on the\n * common surface.\n */\n readonly topK?: number\n /** Stop sequences that abort generation when matched. */\n readonly stopSequences?: ReadonlyArray<string>\n /**\n * Extended thinking configuration. `0` budget is equivalent to disabled.\n * Only the `claude-sonnet-4-x`, `claude-haiku-4-x`, and pre-Opus-4.7\n * model lines support extended thinking.\n */\n readonly thinking?: ThinkingConfig\n /**\n * `metadata.user_id` on the wire. End-user tracking identifier.\n */\n readonly user?: string\n}\n\nexport type AnthropicService = {\n /**\n * Stream the provider's native event vocabulary (post-SSE-decode).\n * Use this when you need full vendor fidelity (e.g. `signature_delta` for\n * encrypted reasoning state). For provider-portable code, use `streamTurn`.\n */\n readonly streamNative: (\n request: AnthropicRequest,\n ) => Stream.Stream<ProviderEvent, AiError.AiError>\n /**\n * Stream canonical `TurnEvent`s. Implemented as\n * `streamNative |> toCanonical`.\n */\n readonly streamTurn: (request: AnthropicRequest) => Stream.Stream<TurnEvent, AiError.AiError>\n /**\n * Drain a single turn and return the assembled `Turn`. Derived from\n * `streamTurn` — Anthropic's non-streaming `/messages` endpoint\n * differs from the streamed one in subtle ways (no incremental\n * thinking deltas), so we route everything through streaming for\n * consistency.\n */\n readonly turn: (request: AnthropicRequest) => Effect.Effect<Turn, AiError.AiError>\n /**\n * Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.\n * Stateful (threads an `Accumulator` for tool-call lookup and\n * accumulator-to-Turn assembly).\n */\n readonly toCanonical: <E, R>(\n s: Stream.Stream<ProviderEvent, E, R>,\n ) => Stream.Stream<TurnEvent, E, R>\n}\n\n/**\n * Provider-typed service tag. Yield this when you want Anthropic-specific\n * options (`topK`, `stopSequences`, `thinking`); yield the generic\n * `LanguageModel` tag for provider-portable code. Both are registered by\n * `layer`.\n */\nexport class Anthropic extends Context.Service<Anthropic, AnthropicService>()(\n \"@betalyra/effect-uai/providers/anthropic/Anthropic\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n /**\n * Default `max_tokens` for requests that don't override via\n * `request.maxOutputTokens`. Anthropic requires this field; we default to\n * 4096 if neither is set.\n */\n readonly defaultMaxTokens?: number\n /**\n * Prompt caching. Off by default, so the library stays unopinionated about\n * a setting that changes billing. `true` is `{}`: cache with the 5-minute\n * default TTL.\n *\n * Anthropic caches the `tools` → `system` → `messages` prefix and moves the\n * breakpoint forward as the conversation grows, so a long system prompt and\n * tool definitions are paid for once rather than on every turn. Read\n * `usage.input_tokens_details.cached_tokens` to see it working.\n *\n * Mutating earlier history (rewriting an old message, compaction) breaks\n * the prefix from that point on. Anthropic falls back to the longest\n * unchanged prefix, so this costs savings but never correctness.\n */\n readonly promptCaching?: boolean | { readonly ttl?: CacheTtl }\n}\n\nconst ANTHROPIC_VERSION = \"2023-06-01\"\nconst STRUCTURED_OUTPUTS_BETA = \"structured-outputs-2025-11-13\"\nconst FALLBACK_MAX_TOKENS = 4096\n\nconst outputConfig = (request: AnthropicRequest): Option.Option<Record<string, unknown>> =>\n pipe(\n Option.fromUndefinedOr(request.structured),\n Option.map((format) => ({\n format: {\n type: \"json_schema\",\n schema: format.schema[\"~standard\"].jsonSchema.input({ target: \"draft-2020-12\" }),\n },\n })),\n )\n\nconst resolvedMaxTokens = (cfg: Config, request: AnthropicRequest): number =>\n request.maxOutputTokens ?? cfg.defaultMaxTokens ?? FALLBACK_MAX_TOKENS\n\n/**\n * One request-level breakpoint when caching is on. Anthropic places it on the\n * last cacheable block itself, so nothing has to be marked per-block and the\n * breakpoint stays correct as history grows.\n */\nconst cacheControlOf = (cfg: Config): Option.Option<CacheControl> =>\n pipe(\n Option.fromUndefinedOr(cfg.promptCaching),\n Option.flatMap((opt) =>\n opt === false\n ? Option.none<CacheControl>()\n : Option.some<CacheControl>({\n type: \"ephemeral\",\n ...(opt !== true && opt.ttl !== undefined && { ttl: opt.ttl }),\n }),\n ),\n )\n\nconst functionToolDescriptors = (\n request: AnthropicRequest,\n): ReadonlyArray<Record<string, unknown>> =>\n descriptorsOf(request.tools).map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: t.inputSchema,\n }))\n\n/**\n * Merge the custom function-tool declarations with the native hosted-tool\n * entries into the single `tools` array Anthropic expects. `providerWire` is\n * pre-rendered (and validated) by the caller.\n */\nconst toolsWire = (\n request: AnthropicRequest,\n providerWire: ReadonlyArray<Record<string, unknown>>,\n): Option.Option<ReadonlyArray<Record<string, unknown>>> => {\n const tools = [...functionToolDescriptors(request), ...providerWire]\n return tools.length > 0 ? Option.some(tools) : Option.none()\n}\n\nconst toolChoiceWire = (request: AnthropicRequest): Option.Option<Record<string, unknown>> =>\n pipe(\n Option.fromUndefinedOr(request.toolChoice),\n Option.map((choice) =>\n choice === \"auto\"\n ? { type: \"auto\" }\n : choice === \"required\"\n ? { type: \"any\" }\n : choice === \"none\"\n ? { type: \"none\" }\n : { type: \"tool\", name: choice.name },\n ),\n )\n\n// ---------------------------------------------------------------------------\n// SSE event → ProviderEvent\n// ---------------------------------------------------------------------------\n\nconst decodeKnown = Schema.decodeUnknownEffect(KnownProviderEvent)\nconst parseJsonUnknown = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))\n\nconst makeUnknown = (raw: unknown): ProviderEvent => ({ type: \"_unknown\", raw })\n\n/**\n * Parse one SSE event's `data` payload into a typed `ProviderEvent`. Never\n * fails: JSON-parse and schema-decode failures both produce a synthesized\n * `_unknown` event so consumers of `streamNative` never silently miss a\n * wire event we didn't model. When the payload IS valid JSON but doesn't\n * match the known-event schema, `_unknown.raw` carries the parsed value;\n * when JSON parsing fails outright, it carries the raw string.\n */\nconst sseEventToProviderEvent = (ev: SSE.Event): Effect.Effect<ProviderEvent> =>\n parseJsonUnknown(ev.data).pipe(\n Effect.flatMap((parsed) =>\n decodeKnown(parsed).pipe(Effect.orElseSucceed(() => makeUnknown(parsed))),\n ),\n Effect.orElseSucceed(() => makeUnknown(ev.data)),\n )\n\n// ---------------------------------------------------------------------------\n// Per-event derivation of TurnEvents. Drives off the new accumulator and the\n// raw event, since some deltas (`tool_call_args_delta`) need the call_id\n// which lives on the accumulator's per-index block.\n// ---------------------------------------------------------------------------\n\nconst deltasFromEvent = (next: Accumulator, event: ProviderEvent): ReadonlyArray<TurnEvent> =>\n Match.value(event).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n content_block_start: (e) =>\n e.content_block.type === \"tool_use\"\n ? [\n TurnEvent.ToolCallStart({\n call_id: e.content_block.id,\n name: e.content_block.name,\n }),\n ]\n : [],\n content_block_delta: (e) =>\n Match.value(e.delta).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n text_delta: (d) => [TurnEvent.TextDelta({ text: d.text })],\n thinking_delta: (d) => [TurnEvent.ReasoningDelta({ text: d.thinking, kind: \"trace\" })],\n input_json_delta: (d) => {\n const block = next.blocks[e.index]\n if (block === undefined) return []\n const callId = Option.getOrElse(block.id, () => \"\")\n return callId.length === 0\n ? []\n : [TurnEvent.ToolCallArgsDelta({ call_id: callId, delta: d.partial_json })]\n },\n // Encrypted reasoning state - flows through `streamNative` but has\n // no canonical representation.\n signature_delta: () => [],\n }),\n ),\n message_start: (e) =>\n e.message.usage === undefined ? [] : [TurnEvent.UsageUpdate({ usage: next.usage })],\n message_delta: (e) =>\n e.usage === undefined ? [] : [TurnEvent.UsageUpdate({ usage: next.usage })],\n message_stop: () => [TurnEvent.TurnComplete({ turn: accumulatorToTurn(next) })],\n content_block_stop: () => [],\n ping: () => [],\n error: () => [],\n _unknown: () => [],\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Service implementation\n// ---------------------------------------------------------------------------\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"anthropic\"\n const raw = body\n if (status === 429) return new AiError.RateLimited({ provider, raw })\n if (status === 408 || status === 504) return new AiError.Timeout({ provider, raw })\n if (status === 401) return new AiError.AuthFailed({ provider, subtype: \"auth\", raw })\n if (status === 403) return new AiError.AuthFailed({ provider, subtype: \"permission\", raw })\n if (status === 402) return new AiError.AuthFailed({ provider, subtype: \"billing\", raw })\n if (status === 413) return new AiError.ContextLengthExceeded({ provider, raw })\n if (status === 529) return new AiError.Unavailable({ provider, status, raw })\n if (status >= 500) return new AiError.Unavailable({ provider, status, raw })\n return new AiError.InvalidRequest({ provider, raw })\n}\n\nconst buildNativeStream = (cfg: Config) => {\n const baseUrl = cfg.baseUrl ?? \"https://api.anthropic.com\"\n const url = `${baseUrl}/v1/messages`\n return (\n request: AnthropicRequest,\n ): Stream.Stream<ProviderEvent, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const structured = outputConfig(request)\n const providerWire = yield* Result.match(\n renderProviderTools(providerToolsOf(request.tools)),\n {\n onFailure: (e) =>\n Effect.fail(\n new AiError.Unsupported({\n provider: \"anthropic\",\n capability: e.capability,\n reason: e.reason,\n }),\n ),\n onSuccess: (wire) => Effect.succeed(wire),\n },\n )\n const bodyResult = buildRequestBody({\n model: request.model,\n history: request.history,\n maxTokens: resolvedMaxTokens(cfg, request),\n temperature: Option.fromUndefinedOr(request.temperature),\n topP: Option.fromUndefinedOr(request.topP),\n topK: Option.fromUndefinedOr(request.topK),\n stopSequences: Option.fromUndefinedOr(request.stopSequences),\n thinking: Option.fromUndefinedOr(request.thinking),\n tools: toolsWire(request, providerWire),\n toolChoice: toolChoiceWire(request),\n userId: Option.fromUndefinedOr(request.user),\n outputConfig: structured,\n cacheControl: cacheControlOf(cfg),\n })\n\n const body = yield* Result.match(bodyResult, {\n onFailure: (cause) =>\n Effect.fail(\n new AiError.InvalidRequest({\n provider: \"anthropic\",\n param: \"input.function_call.arguments\",\n raw: cause,\n }),\n ),\n onSuccess: (b) => Effect.succeed(b),\n })\n\n const client = yield* HttpClient.HttpClient\n const baseRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.setHeader(\"x-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.setHeader(\"anthropic-version\", ANTHROPIC_VERSION),\n HttpClientRequest.bodyJsonUnsafe(body),\n HttpClientRequest.accept(\"text/event-stream\"),\n )\n const httpRequest = Option.isSome(structured)\n ? baseRequest.pipe(HttpClientRequest.setHeader(\"anthropic-beta\", STRUCTURED_OUTPUTS_BETA))\n : baseRequest\n const response = yield* client\n .execute(httpRequest)\n .pipe(\n Effect.mapError(\n (cause): AiError.AiError =>\n new AiError.Unavailable({ provider: \"anthropic\", raw: cause }),\n ),\n )\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n\n return response.stream.pipe(\n Stream.mapError(\n (cause): AiError.AiError =>\n new AiError.Unavailable({ provider: \"anthropic\", raw: cause }),\n ),\n SSE.fromBytes,\n Stream.mapEffect(sseEventToProviderEvent),\n Stream.flatMap((event) =>\n event.type === \"error\"\n ? Stream.fail(new AiError.Unavailable({ provider: \"anthropic\", raw: event }))\n : Stream.succeed(event),\n ),\n )\n }),\n )\n}\n\n/**\n * Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.\n * Threads a fresh `Accumulator` per stream so tool-call lookup and\n * `accumulatorToTurn` assembly work correctly across the run.\n */\nexport const toCanonical = <E, R>(\n s: Stream.Stream<ProviderEvent, E, R>,\n): Stream.Stream<TurnEvent, E, R> =>\n s.pipe(\n Stream.mapAccum(\n () => emptyAccumulator,\n (acc, event) => {\n const next = applyEvent(acc, event)\n const deltas = deltasFromEvent(next, event)\n return [next, deltas] as const\n },\n ),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build an `AnthropicService` value. For Layer-based setup, prefer `layer`.\n */\nexport const make = (cfg: Config): Effect.Effect<AnthropicService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => {\n const streamNative: AnthropicService[\"streamNative\"] = (request) =>\n buildNativeStream(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client))\n const streamTurn: AnthropicService[\"streamTurn\"] = (request) =>\n toCanonical(streamNative(request))\n return {\n streamNative,\n streamTurn,\n turn: turnFromStream(streamTurn),\n toCanonical,\n }\n })\n\n/**\n * Layer that registers both the provider-specific `Anthropic` tag and the\n * generic `LanguageModel` tag, sharing one underlying implementation.\n *\n * The generic tag accepts `CommonRequest`; the typed tag accepts the full\n * `AnthropicRequest` surface.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<Anthropic | LanguageModel, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(Anthropic, make(cfg))\n const generic = Layer.effect(\n LanguageModel,\n Effect.map(make(cfg), (s): LanguageModelService => ({\n streamTurn: (request) => s.streamTurn(request as AnthropicRequest),\n turn: (request) => s.turn(request as AnthropicRequest),\n })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Anthropic.mjs","names":[],"sources":["../src/Anthropic.ts"],"sourcesContent":["import {\n Context,\n Effect,\n Layer,\n Match,\n Option,\n Redacted,\n Result,\n Schema,\n Stream,\n pipe,\n} from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport * as Capabilities from \"@effect-uai/core/Capabilities\"\nimport {\n type CommonRequest,\n LanguageModel,\n type LanguageModelService,\n turnFromStream,\n} from \"@effect-uai/core/LanguageModel\"\nimport * as SSE from \"@effect-uai/core/SSE\"\nimport { descriptorsOf, providerToolsOf } from \"@effect-uai/core/Tool\"\nimport { type Turn, TurnEvent } from \"@effect-uai/core/Turn\"\nimport { renderProviderTools } from \"./AnthropicTools.js\"\nimport {\n type Accumulator,\n type CacheControl,\n type CacheTtl,\n type ThinkingConfig,\n accumulatorToTurn,\n buildRequestBody,\n emptyAccumulator,\n} from \"./codec.js\"\nimport type { AnthropicModel } from \"./models.js\"\nimport { KnownProviderEvent, ProviderEvent, applyEvent } from \"./streamEvents.js\"\n\nexport {\n codeExecutionTool,\n webSearchTool,\n type WebSearchOptions,\n type UserLocation,\n} from \"./AnthropicTools.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport type AnthropicRequest = Omit<CommonRequest, \"model\"> & {\n /**\n * Narrows `CommonRequest.model` (`string`) to the typed `AnthropicModel`\n * literal union for autocomplete.\n */\n readonly model: AnthropicModel\n /**\n * Top-K nucleus sampling parameter. Anthropic-specific; not exposed on the\n * common surface.\n */\n readonly topK?: number\n /** Stop sequences that abort generation when matched. */\n readonly stopSequences?: ReadonlyArray<string>\n /**\n * Extended thinking configuration. `0` budget is equivalent to disabled.\n * Only the `claude-sonnet-4-x`, `claude-haiku-4-x`, and pre-Opus-4.7\n * model lines support extended thinking.\n */\n readonly thinking?: ThinkingConfig\n /**\n * `metadata.user_id` on the wire. End-user tracking identifier.\n */\n readonly user?: string\n}\n\nexport type AnthropicService = {\n /**\n * Stream the provider's native event vocabulary (post-SSE-decode).\n * Use this when you need full vendor fidelity (e.g. `signature_delta` for\n * encrypted reasoning state). For provider-portable code, use `streamTurn`.\n */\n readonly streamNative: (\n request: AnthropicRequest,\n ) => Stream.Stream<ProviderEvent, AiError.AiError>\n /**\n * Stream canonical `TurnEvent`s. Implemented as\n * `streamNative |> toCanonical`.\n */\n readonly streamTurn: (request: AnthropicRequest) => Stream.Stream<TurnEvent, AiError.AiError>\n /**\n * Drain a single turn and return the assembled `Turn`. Derived from\n * `streamTurn` — Anthropic's non-streaming `/messages` endpoint\n * differs from the streamed one in subtle ways (no incremental\n * thinking deltas), so we route everything through streaming for\n * consistency.\n */\n readonly turn: (request: AnthropicRequest) => Effect.Effect<Turn, AiError.AiError>\n /**\n * Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.\n * Stateful (threads an `Accumulator` for tool-call lookup and\n * accumulator-to-Turn assembly).\n */\n readonly toCanonical: <E, R>(\n s: Stream.Stream<ProviderEvent, E, R>,\n ) => Stream.Stream<TurnEvent, E, R>\n}\n\n/**\n * Provider-typed service tag. Yield this when you want Anthropic-specific\n * options (`topK`, `stopSequences`, `thinking`); yield the generic\n * `LanguageModel` tag for provider-portable code. Both are registered by\n * `layer`.\n */\nexport class Anthropic extends Context.Service<Anthropic, AnthropicService>()(\n \"@betalyra/effect-uai/providers/anthropic/Anthropic\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n /**\n * Default `max_tokens` for requests that don't override via\n * `request.maxOutputTokens`. Anthropic requires this field; we default to\n * 4096 if neither is set.\n */\n readonly defaultMaxTokens?: number\n /**\n * Prompt caching. Off by default, so the library stays unopinionated about\n * a setting that changes billing. `true` is `{}`: cache with the 5-minute\n * default TTL.\n *\n * Anthropic caches the `tools` → `system` → `messages` prefix and moves the\n * breakpoint forward as the conversation grows, so a long system prompt and\n * tool definitions are paid for once rather than on every turn. Read\n * `usage.input_tokens_details.cached_tokens` to see it working.\n *\n * Mutating earlier history (rewriting an old message, compaction) breaks\n * the prefix from that point on. Anthropic falls back to the longest\n * unchanged prefix, so this costs savings but never correctness.\n */\n readonly promptCaching?: boolean | { readonly ttl?: CacheTtl }\n}\n\nconst ANTHROPIC_VERSION = \"2023-06-01\"\nconst STRUCTURED_OUTPUTS_BETA = \"structured-outputs-2025-11-13\"\nconst FALLBACK_MAX_TOKENS = 4096\n\nconst outputConfig = (request: AnthropicRequest): Option.Option<Record<string, unknown>> =>\n pipe(\n Option.fromUndefinedOr(request.structured),\n Option.map((format) => ({\n format: {\n type: \"json_schema\",\n schema: format.schema[\"~standard\"].jsonSchema.input({ target: \"draft-2020-12\" }),\n },\n })),\n )\n\nconst resolvedMaxTokens = (cfg: Config, request: AnthropicRequest): number =>\n request.maxOutputTokens ?? cfg.defaultMaxTokens ?? FALLBACK_MAX_TOKENS\n\n/**\n * One request-level breakpoint when caching is on. Anthropic places it on the\n * last cacheable block itself, so nothing has to be marked per-block and the\n * breakpoint stays correct as history grows.\n */\nconst cacheControlOf = (cfg: Config): Option.Option<CacheControl> =>\n pipe(\n Option.fromUndefinedOr(cfg.promptCaching),\n Option.flatMap((opt) =>\n opt === false\n ? Option.none<CacheControl>()\n : Option.some<CacheControl>({\n type: \"ephemeral\",\n ...(opt !== true && opt.ttl !== undefined && { ttl: opt.ttl }),\n }),\n ),\n )\n\nconst functionToolDescriptors = (\n request: AnthropicRequest,\n): ReadonlyArray<Record<string, unknown>> =>\n descriptorsOf(request.tools).map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: t.inputSchema,\n }))\n\n/**\n * Merge the custom function-tool declarations with the native hosted-tool\n * entries into the single `tools` array Anthropic expects. `providerWire` is\n * pre-rendered (and validated) by the caller.\n */\nconst toolsWire = (\n request: AnthropicRequest,\n providerWire: ReadonlyArray<Record<string, unknown>>,\n): Option.Option<ReadonlyArray<Record<string, unknown>>> => {\n const tools = [...functionToolDescriptors(request), ...providerWire]\n return tools.length > 0 ? Option.some(tools) : Option.none()\n}\n\nconst toolChoiceWire = (request: AnthropicRequest): Option.Option<Record<string, unknown>> =>\n pipe(\n Option.fromUndefinedOr(request.toolChoice),\n Option.map((choice) =>\n choice === \"auto\"\n ? { type: \"auto\" }\n : choice === \"required\"\n ? { type: \"any\" }\n : choice === \"none\"\n ? { type: \"none\" }\n : { type: \"tool\", name: choice.name },\n ),\n )\n\n// ---------------------------------------------------------------------------\n// SSE event → ProviderEvent\n// ---------------------------------------------------------------------------\n\nconst decodeKnown = Schema.decodeUnknownEffect(KnownProviderEvent)\nconst parseJsonUnknown = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))\n\nconst makeUnknown = (raw: unknown): ProviderEvent => ({ type: \"_unknown\", raw })\n\n/**\n * Parse one SSE event's `data` payload into a typed `ProviderEvent`. Never\n * fails: JSON-parse and schema-decode failures both produce a synthesized\n * `_unknown` event so consumers of `streamNative` never silently miss a\n * wire event we didn't model. When the payload IS valid JSON but doesn't\n * match the known-event schema, `_unknown.raw` carries the parsed value;\n * when JSON parsing fails outright, it carries the raw string.\n */\nconst sseEventToProviderEvent = (ev: SSE.Event): Effect.Effect<ProviderEvent> =>\n parseJsonUnknown(ev.data).pipe(\n Effect.flatMap((parsed) =>\n decodeKnown(parsed).pipe(Effect.orElseSucceed(() => makeUnknown(parsed))),\n ),\n Effect.orElseSucceed(() => makeUnknown(ev.data)),\n )\n\n// ---------------------------------------------------------------------------\n// Per-event derivation of TurnEvents. Drives off the new accumulator and the\n// raw event, since some deltas (`tool_call_args_delta`) need the call_id\n// which lives on the accumulator's per-index block.\n// ---------------------------------------------------------------------------\n\nconst deltasFromEvent = (next: Accumulator, event: ProviderEvent): ReadonlyArray<TurnEvent> =>\n Match.value(event).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n content_block_start: (e) =>\n e.content_block.type === \"tool_use\"\n ? [\n TurnEvent.ToolCallStart({\n call_id: e.content_block.id,\n name: e.content_block.name,\n }),\n ]\n : [],\n content_block_delta: (e) =>\n Match.value(e.delta).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n text_delta: (d) => [TurnEvent.TextDelta({ text: d.text })],\n thinking_delta: (d) => [TurnEvent.ReasoningDelta({ text: d.thinking, kind: \"trace\" })],\n input_json_delta: (d) => {\n const block = next.blocks[e.index]\n if (block === undefined) return []\n const callId = Option.getOrElse(block.id, () => \"\")\n return callId.length === 0\n ? []\n : [TurnEvent.ToolCallArgsDelta({ call_id: callId, delta: d.partial_json })]\n },\n // Encrypted reasoning state - flows through `streamNative` but has\n // no canonical representation.\n signature_delta: () => [],\n }),\n ),\n message_start: (e) =>\n e.message.usage === undefined ? [] : [TurnEvent.UsageUpdate({ usage: next.usage })],\n message_delta: (e) =>\n e.usage === undefined ? [] : [TurnEvent.UsageUpdate({ usage: next.usage })],\n message_stop: () => [TurnEvent.TurnComplete({ turn: accumulatorToTurn(next) })],\n content_block_stop: () => [],\n ping: () => [],\n error: () => [],\n _unknown: () => [],\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Service implementation\n// ---------------------------------------------------------------------------\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"anthropic\"\n const raw = body\n if (status === 429) return new AiError.RateLimited({ provider, raw })\n if (status === 408 || status === 504) return new AiError.Timeout({ provider, raw })\n if (status === 401) return new AiError.AuthFailed({ provider, subtype: \"auth\", raw })\n if (status === 403) return new AiError.AuthFailed({ provider, subtype: \"permission\", raw })\n if (status === 402) return new AiError.AuthFailed({ provider, subtype: \"billing\", raw })\n if (status === 413) return new AiError.ContextLengthExceeded({ provider, raw })\n if (status === 529) return new AiError.Unavailable({ provider, status, raw })\n if (status >= 500) return new AiError.Unavailable({ provider, status, raw })\n return new AiError.InvalidRequest({ provider, raw })\n}\n\nconst buildNativeStream = (cfg: Config) => {\n const baseUrl = cfg.baseUrl ?? \"https://api.anthropic.com\"\n const url = `${baseUrl}/v1/messages`\n return (\n request: AnthropicRequest,\n ): Stream.Stream<ProviderEvent, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const structured = outputConfig(request)\n const providerWire = yield* Result.match(\n renderProviderTools(providerToolsOf(request.tools)),\n {\n onFailure: (e) =>\n Effect.fail(\n new AiError.Unsupported({\n provider: \"anthropic\",\n capability: e.capability,\n reason: e.reason,\n }),\n ),\n onSuccess: (wire) => Effect.succeed(wire),\n },\n )\n yield* Capabilities.warnDroppedBlocks(request.history, \"output_image\", {\n provider: \"anthropic\",\n capability: \"output_image\",\n reason:\n \"Assistant messages carry no image on this wire. Use `Turn.imagesAsInput` to resend it as user content.\",\n })\n const bodyResult = buildRequestBody({\n model: request.model,\n history: request.history,\n maxTokens: resolvedMaxTokens(cfg, request),\n temperature: Option.fromUndefinedOr(request.temperature),\n topP: Option.fromUndefinedOr(request.topP),\n topK: Option.fromUndefinedOr(request.topK),\n stopSequences: Option.fromUndefinedOr(request.stopSequences),\n thinking: Option.fromUndefinedOr(request.thinking),\n tools: toolsWire(request, providerWire),\n toolChoice: toolChoiceWire(request),\n userId: Option.fromUndefinedOr(request.user),\n outputConfig: structured,\n cacheControl: cacheControlOf(cfg),\n })\n\n const body = yield* Result.match(bodyResult, {\n onFailure: (cause) =>\n Effect.fail(\n new AiError.InvalidRequest({\n provider: \"anthropic\",\n param: \"input.function_call.arguments\",\n raw: cause,\n }),\n ),\n onSuccess: (b) => Effect.succeed(b),\n })\n\n const client = yield* HttpClient.HttpClient\n const baseRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.setHeader(\"x-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.setHeader(\"anthropic-version\", ANTHROPIC_VERSION),\n HttpClientRequest.bodyJsonUnsafe(body),\n HttpClientRequest.accept(\"text/event-stream\"),\n )\n const httpRequest = Option.isSome(structured)\n ? baseRequest.pipe(HttpClientRequest.setHeader(\"anthropic-beta\", STRUCTURED_OUTPUTS_BETA))\n : baseRequest\n const response = yield* client\n .execute(httpRequest)\n .pipe(\n Effect.mapError(\n (cause): AiError.AiError =>\n new AiError.Unavailable({ provider: \"anthropic\", raw: cause }),\n ),\n )\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n\n return response.stream.pipe(\n Stream.mapError(\n (cause): AiError.AiError =>\n new AiError.Unavailable({ provider: \"anthropic\", raw: cause }),\n ),\n SSE.fromBytes,\n Stream.mapEffect(sseEventToProviderEvent),\n Stream.flatMap((event) =>\n event.type === \"error\"\n ? Stream.fail(new AiError.Unavailable({ provider: \"anthropic\", raw: event }))\n : Stream.succeed(event),\n ),\n )\n }),\n )\n}\n\n/**\n * Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.\n * Threads a fresh `Accumulator` per stream so tool-call lookup and\n * `accumulatorToTurn` assembly work correctly across the run.\n */\nexport const toCanonical = <E, R>(\n s: Stream.Stream<ProviderEvent, E, R>,\n): Stream.Stream<TurnEvent, E, R> =>\n s.pipe(\n Stream.mapAccum(\n () => emptyAccumulator,\n (acc, event) => {\n const next = applyEvent(acc, event)\n const deltas = deltasFromEvent(next, event)\n return [next, deltas] as const\n },\n ),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build an `AnthropicService` value. For Layer-based setup, prefer `layer`.\n */\nexport const make = (cfg: Config): Effect.Effect<AnthropicService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => {\n const streamNative: AnthropicService[\"streamNative\"] = (request) =>\n buildNativeStream(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client))\n const streamTurn: AnthropicService[\"streamTurn\"] = (request) =>\n toCanonical(streamNative(request))\n return {\n streamNative,\n streamTurn,\n turn: turnFromStream(streamTurn),\n toCanonical,\n }\n })\n\n/**\n * Layer that registers both the provider-specific `Anthropic` tag and the\n * generic `LanguageModel` tag, sharing one underlying implementation.\n *\n * The generic tag accepts `CommonRequest`; the typed tag accepts the full\n * `AnthropicRequest` surface.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<Anthropic | LanguageModel, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(Anthropic, make(cfg))\n const generic = Layer.effect(\n LanguageModel,\n Effect.map(make(cfg), (s): LanguageModelService => ({\n streamTurn: (request) => s.streamTurn(request as AnthropicRequest),\n turn: (request) => s.turn(request as AnthropicRequest),\n })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
package/dist/codec.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;;cAiCa,kBAAgB,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;KAMjB,0BAA0B,iBAAiB;cAEjD,WAAS,OAAA;;;;;;KAMH,mBAAmB,UAAU;KAMpC;WACM;WACA;;KAGN;WACM;WACA;WACA;;KAGN;WACM;WACA;WACA;WACA;;KAGN;WACM;WACA;WACA;;KAGN;WACM;WACA;;KAGN;WACM;WACA;aACM;aAAsB;;aACtB;aAAyB;aAA6B;;;KAGlE,0BAA0B,qBAAqB,2BAA2B;KAE1E,+BACD,qBACA,wBACA,yBACA;KAEC;WACM;WACA,SAAS,cAAc;;KAG7B;WACM;WACA,SAAS,cAAc;;KAG7B,iBAAiB,qBAAqB;
|
|
1
|
+
{"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;;cAiCa,kBAAgB,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;KAMjB,0BAA0B,iBAAiB;cAEjD,WAAS,OAAA;;;;;;KAMH,mBAAmB,UAAU;KAMpC;WACM;WACA;;KAGN;WACM;WACA;WACA;;KAGN;WACM;WACA;WACA;WACA;;KAGN;WACM;WACA;WACA;;KAGN;WACM;WACA;;KAGN;WACM;WACA;aACM;aAAsB;;aACtB;aAAyB;aAA6B;;;KAGlE,0BAA0B,qBAAqB,2BAA2B;KAE1E,+BACD,qBACA,wBACA,yBACA;KAEC;WACM;WACA,SAAS,cAAc;;KAG7B;WACM;WACA,SAAS,cAAc;;KAG7B,iBAAiB,qBAAqB;KAwN/B;WACD;WACA;;;KAIC;;;;;;;KAQA;WACD;WACA,MAAM;;KAGL;WACD;WACA,UAAU,cAAc;WACxB;WACA;WACA;WACA;WACA;WACA,iBAAiB;WACjB,WAAW;WACX,QAAQ,cAAc;WACtB,cAAc;WACd;aAAsB;;WACtB,gBAAgB;WAChB,gBAAgB;WAChB;;cAGE,mBAAgB;WAClB;WACA,SAAS,cAAc,MAAM;WAC7B;WACA,aAAa,OAAO;WACpB,MAAM,OAAO;WACb,MAAM,OAAO;WACb,eAAe,OAAO,OAAO;WAC7B,UAAU,OAAO,OAAO;WACxB,OAAO,OAAO,OAAO,cAAc;WACnC,YAAY,OAAO,OAAO;WAC1B,QAAQ,OAAO;WACf,cAAc,OAAO,OAAO;WAC5B,cAAc,OAAO,OAAO;MACnC,OAAO,OAAO,aAAa;KA4D1B;WACM,MAAM;WACN;WACA;WACA;WACA;WACA,IAAI,OAAO;WACX,MAAM,OAAO;WACb,cAAc,OAAO;;KAcpB;WACD,QAAQ,SAAS,eAAe;WAChC,YAAY,OAAO;WACnB,OAAO,MAAM;;cAGX,kBAAkB;cAiBlB,aAAU,KAAS,aAAW,eAAe,OAAS,qBAAmB;cAoBzE,kBAAe,KAAS,aAAW,eAAe,iBAAiB;cAGnE,uBAAoB,KAC1B,aAAW,eACH,oBAEZ;cAEU,sBAAmB,KACzB,aAAW,eACH,qBAEZ;cAEU,uBAAoB,KAC1B,aAAW,eACH,sBAEZ;cAEU,gBAAa,KAAS,aAAW,mBAAmB;cAWpD,aAAU,KAAS,aAAW,MAAQ,cAAY;cA+GlD,oBAAiB,KAAS,gBAAc"}
|
package/dist/codec.mjs
CHANGED
|
@@ -50,6 +50,7 @@ const blockText = Match.type().pipe(Match.discriminatorsExhaustive("type")({
|
|
|
50
50
|
input_text: (b) => b.text,
|
|
51
51
|
input_image: () => "",
|
|
52
52
|
output_text: (b) => b.text,
|
|
53
|
+
output_image: () => "",
|
|
53
54
|
refusal: (b) => b.text
|
|
54
55
|
}));
|
|
55
56
|
const messageText = (message) => message.content.map(blockText).join("");
|
|
@@ -75,6 +76,7 @@ const userContentBlock = (block) => Match.value(block).pipe(Match.discriminators
|
|
|
75
76
|
source: imageSourceToWire(b.source)
|
|
76
77
|
}),
|
|
77
78
|
output_text: () => Result.failVoid,
|
|
79
|
+
output_image: () => Result.failVoid,
|
|
78
80
|
refusal: () => Result.failVoid
|
|
79
81
|
}));
|
|
80
82
|
const parseJson = (s) => Result.try({
|
package/dist/codec.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.mjs","names":["Arr"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Encoding, Match, Option, Order, Result, Schema, pipe } from \"effect\"\nimport * as Items from \"@effect-uai/core/Items\"\nimport { JsonParseError } from \"@effect-uai/core/JSONL\"\nimport type { Turn } from \"@effect-uai/core/Turn\"\n\n// ---------------------------------------------------------------------------\n// Wire schemas - subset of Anthropic Messages API we consume.\n// Reference: https://platform.claude.com/docs/en/api/messages\n// ---------------------------------------------------------------------------\n\nconst WireTextBlock = Schema.Struct({\n type: Schema.Literal(\"text\"),\n text: Schema.String,\n})\n\nconst WireToolUseBlock = Schema.Struct({\n type: Schema.Literal(\"tool_use\"),\n id: Schema.String,\n name: Schema.String,\n input: Schema.Unknown,\n})\n\nconst WireThinkingBlock = Schema.Struct({\n type: Schema.Literal(\"thinking\"),\n thinking: Schema.String,\n signature: Schema.optional(Schema.String),\n})\n\nconst WireRedactedThinkingBlock = Schema.Struct({\n type: Schema.Literal(\"redacted_thinking\"),\n data: Schema.String,\n})\n\nexport const WireContentBlock = Schema.Union([\n WireTextBlock,\n WireToolUseBlock,\n WireThinkingBlock,\n WireRedactedThinkingBlock,\n])\nexport type WireContentBlock = typeof WireContentBlock.Type\n\nconst WireUsage = Schema.Struct({\n input_tokens: Schema.optional(Schema.Number),\n output_tokens: Schema.optional(Schema.Number),\n cache_creation_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n cache_read_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n})\nexport type WireUsage = typeof WireUsage.Type\n\n// ---------------------------------------------------------------------------\n// History → request body\n// ---------------------------------------------------------------------------\n\ntype RequestTextContent = {\n readonly type: \"text\"\n readonly text: string\n}\n\ntype RequestToolResultContent = {\n readonly type: \"tool_result\"\n readonly tool_use_id: string\n readonly content: string\n}\n\ntype RequestToolUseContent = {\n readonly type: \"tool_use\"\n readonly id: string\n readonly name: string\n readonly input: unknown\n}\n\ntype RequestThinkingContent = {\n readonly type: \"thinking\"\n readonly thinking: string\n readonly signature?: string\n}\n\ntype RequestRedactedThinkingContent = {\n readonly type: \"redacted_thinking\"\n readonly data: string\n}\n\ntype RequestImageContent = {\n readonly type: \"image\"\n readonly source:\n | { readonly type: \"url\"; readonly url: string }\n | { readonly type: \"base64\"; readonly media_type: string; readonly data: string }\n}\n\ntype RequestUserContentBlock = RequestTextContent | RequestToolResultContent | RequestImageContent\n\ntype RequestAssistantContentBlock =\n | RequestTextContent\n | RequestToolUseContent\n | RequestThinkingContent\n | RequestRedactedThinkingContent\n\ntype RequestUserMessage = {\n readonly role: \"user\"\n readonly content: ReadonlyArray<RequestUserContentBlock>\n}\n\ntype RequestAssistantMessage = {\n readonly role: \"assistant\"\n readonly content: ReadonlyArray<RequestAssistantContentBlock>\n}\n\ntype RequestMessage = RequestUserMessage | RequestAssistantMessage\n\nconst blockText = Match.type<Items.ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => b.text,\n input_image: () => \"\",\n output_text: (b) => b.text,\n refusal: (b) => b.text,\n }),\n)\n\nconst messageText = (message: Items.Message): string => message.content.map(blockText).join(\"\")\n\nconst imageSourceToWire = Match.type<Items.InputImage[\"source\"]>().pipe(\n Match.tag(\"url\", (s): RequestImageContent[\"source\"] => ({ type: \"url\", url: s.url })),\n Match.tag(\"base64\", (s): RequestImageContent[\"source\"] => ({\n type: \"base64\",\n media_type: s.mimeType,\n data: s.base64,\n })),\n Match.tag(\"bytes\", (s): RequestImageContent[\"source\"] => ({\n type: \"base64\",\n media_type: s.mimeType,\n data: Encoding.encodeBase64(s.bytes),\n })),\n Match.exhaustive,\n)\n\nconst userContentBlock = (\n block: Items.ContentBlock,\n): Result.Result<RequestUserContentBlock, void> =>\n Match.value(block).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) =>\n b.text.length === 0\n ? Result.failVoid\n : Result.succeed({ type: \"text\" as const, text: b.text }),\n input_image: (b) =>\n Result.succeed({ type: \"image\" as const, source: imageSourceToWire(b.source) }),\n // Assistant content; never appears on a user message in practice. Skip.\n output_text: () => Result.failVoid,\n refusal: () => Result.failVoid,\n }),\n )\n\nconst parseJson = (s: string): Result.Result<unknown, JsonParseError> =>\n Result.try({\n try: () => JSON.parse(s) as unknown,\n catch: (cause) => new JsonParseError({ line: s, cause }),\n })\n\ntype RoleBucket = \"user\" | \"assistant\" | \"system\"\n\nconst roleBucket = (item: Items.HistoryItem): RoleBucket =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m) => m.role,\n function_call: () => \"assistant\" as const,\n function_call_output: () => \"user\" as const,\n reasoning: () => \"assistant\" as const,\n }),\n )\n\nconst itemToUserBlocks = (item: Items.HistoryItem): ReadonlyArray<RequestUserContentBlock> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m): ReadonlyArray<RequestUserContentBlock> =>\n m.role === \"user\" ? pipe(m.content, Arr.filterMap(userContentBlock)) : [],\n function_call: (): ReadonlyArray<RequestUserContentBlock> => [],\n function_call_output: (o): ReadonlyArray<RequestUserContentBlock> => [\n { type: \"tool_result\", tool_use_id: o.call_id, content: o.output },\n ],\n reasoning: (): ReadonlyArray<RequestUserContentBlock> => [],\n }),\n )\n\nconst itemToAssistantBlocks = (\n item: Items.HistoryItem,\n): Result.Result<ReadonlyArray<RequestAssistantContentBlock>, JsonParseError> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m): Result.Result<ReadonlyArray<RequestAssistantContentBlock>, JsonParseError> => {\n const text = messageText(m)\n return Result.succeed(\n m.role === \"assistant\" && text.length > 0 ? [{ type: \"text\", text }] : [],\n )\n },\n function_call: (f) =>\n pipe(\n parseJson(f.arguments),\n Result.map((input): ReadonlyArray<RequestAssistantContentBlock> => [\n { type: \"tool_use\", id: f.call_id, name: f.name, input },\n ]),\n ),\n function_call_output: (): Result.Result<\n ReadonlyArray<RequestAssistantContentBlock>,\n JsonParseError\n > => Result.succeed([]),\n reasoning: (r) => {\n const blocks: ReadonlyArray<RequestAssistantContentBlock> =\n r.summary !== undefined\n ? [\n {\n type: \"thinking\",\n thinking: r.summary,\n ...(r.signature !== undefined && { signature: r.signature }),\n },\n ]\n : r.signature !== undefined\n ? [{ type: \"redacted_thinking\", data: r.signature }]\n : []\n return Result.succeed(blocks)\n },\n }),\n )\n\ntype GroupAcc = {\n readonly messages: ReadonlyArray<RequestMessage>\n readonly currentRole: Option.Option<\"user\" | \"assistant\">\n readonly userBuf: ReadonlyArray<RequestUserContentBlock>\n readonly assistantBuf: ReadonlyArray<RequestAssistantContentBlock>\n}\n\nconst flushAcc = (acc: GroupAcc): ReadonlyArray<RequestMessage> =>\n Option.match(acc.currentRole, {\n onNone: () => acc.messages,\n onSome: (role) =>\n role === \"user\" && acc.userBuf.length > 0\n ? [...acc.messages, { role: \"user\", content: acc.userBuf }]\n : role === \"assistant\" && acc.assistantBuf.length > 0\n ? [...acc.messages, { role: \"assistant\", content: acc.assistantBuf }]\n : acc.messages,\n })\n\nconst appendUser = (acc: GroupAcc, blocks: ReadonlyArray<RequestUserContentBlock>): GroupAcc =>\n blocks.length === 0\n ? acc\n : Option.isSome(acc.currentRole) && acc.currentRole.value === \"user\"\n ? { ...acc, userBuf: [...acc.userBuf, ...blocks] }\n : {\n messages: flushAcc(acc),\n currentRole: Option.some(\"user\"),\n userBuf: blocks,\n assistantBuf: [],\n }\n\nconst appendAssistant = (\n acc: GroupAcc,\n blocks: ReadonlyArray<RequestAssistantContentBlock>,\n): GroupAcc =>\n blocks.length === 0\n ? acc\n : Option.isSome(acc.currentRole) && acc.currentRole.value === \"assistant\"\n ? { ...acc, assistantBuf: [...acc.assistantBuf, ...blocks] }\n : {\n messages: flushAcc(acc),\n currentRole: Option.some(\"assistant\"),\n userBuf: [],\n assistantBuf: blocks,\n }\n\nconst groupStep = (\n acc: GroupAcc,\n item: Items.HistoryItem,\n): Result.Result<GroupAcc, JsonParseError> => {\n const bucket = roleBucket(item)\n if (bucket === \"system\") return Result.succeed(acc)\n if (bucket === \"user\") {\n return Result.succeed(appendUser(acc, itemToUserBlocks(item)))\n }\n return pipe(\n itemToAssistantBlocks(item),\n Result.map((blocks) => appendAssistant(acc, blocks)),\n )\n}\n\n/**\n * Group consecutive same-role items into Anthropic-shaped messages.\n * Anthropic requires strict user/assistant alternation; consecutive items\n * from the same role are folded into one message's `content`. Fails if any\n * `function_call.arguments` is not valid JSON, since Anthropic's wire shape\n * requires an object input.\n */\nconst groupedMessages = (\n history: ReadonlyArray<Items.HistoryItem>,\n): Result.Result<ReadonlyArray<RequestMessage>, JsonParseError> => {\n const initial: Result.Result<GroupAcc, JsonParseError> = Result.succeed({\n messages: [],\n currentRole: Option.none(),\n userBuf: [],\n assistantBuf: [],\n })\n return pipe(\n Arr.reduce(history, initial, (acc, item) => Result.flatMap(acc, (a) => groupStep(a, item))),\n Result.map(flushAcc),\n )\n}\n\nconst isSystemMessage = (item: Items.HistoryItem): item is Items.Message =>\n item.type === \"message\" && item.role === \"system\"\n\nconst systemFromHistory = (history: ReadonlyArray<Items.HistoryItem>): Option.Option<string> => {\n const texts = pipe(\n history,\n Arr.filterMap((item) =>\n isSystemMessage(item) ? Result.succeed(messageText(item)) : Result.failVoid,\n ),\n Arr.filter((s) => s.length > 0),\n )\n return texts.length === 0 ? Option.none() : Option.some(texts.join(\"\\n\"))\n}\n\nexport type ThinkingConfig = {\n readonly type: \"enabled\"\n readonly budget_tokens: number\n}\n\n/** Cache lifetime. `1h` writes cost 2x base input; `5m` costs 1.25x. */\nexport type CacheTtl = \"5m\" | \"1h\"\n\n/**\n * Request-level cache breakpoint. Anthropic applies it to the last cacheable\n * block and moves it forward as the conversation grows, so the cached prefix\n * (`tools` → `system` → `messages`) follows the conversation without the\n * caller re-placing anything.\n */\nexport type CacheControl = {\n readonly type: \"ephemeral\"\n readonly ttl?: CacheTtl\n}\n\nexport type RequestBody = {\n readonly model: string\n readonly messages: ReadonlyArray<RequestMessage>\n readonly max_tokens: number\n readonly system?: string\n readonly temperature?: number\n readonly top_p?: number\n readonly top_k?: number\n readonly stop_sequences?: ReadonlyArray<string>\n readonly thinking?: ThinkingConfig\n readonly tools?: ReadonlyArray<Record<string, unknown>>\n readonly tool_choice?: Record<string, unknown>\n readonly metadata?: { readonly user_id: string }\n readonly output_config?: Record<string, unknown>\n readonly cache_control?: CacheControl\n readonly stream: true\n}\n\nexport const buildRequestBody = (params: {\n readonly model: string\n readonly history: ReadonlyArray<Items.HistoryItem>\n readonly maxTokens: number\n readonly temperature: Option.Option<number>\n readonly topP: Option.Option<number>\n readonly topK: Option.Option<number>\n readonly stopSequences: Option.Option<ReadonlyArray<string>>\n readonly thinking: Option.Option<ThinkingConfig>\n readonly tools: Option.Option<ReadonlyArray<Record<string, unknown>>>\n readonly toolChoice: Option.Option<Record<string, unknown>>\n readonly userId: Option.Option<string>\n readonly outputConfig: Option.Option<Record<string, unknown>>\n readonly cacheControl: Option.Option<CacheControl>\n}): Result.Result<RequestBody, JsonParseError> =>\n pipe(\n groupedMessages(params.history),\n Result.map((messages): RequestBody => ({\n model: params.model,\n messages,\n max_tokens: params.maxTokens,\n ...Option.match(systemFromHistory(params.history), {\n onNone: () => ({}),\n onSome: (system) => ({ system }),\n }),\n ...Option.match(params.temperature, {\n onNone: () => ({}),\n onSome: (temperature) => ({ temperature }),\n }),\n ...Option.match(params.topP, {\n onNone: () => ({}),\n onSome: (top_p) => ({ top_p }),\n }),\n ...Option.match(params.topK, {\n onNone: () => ({}),\n onSome: (top_k) => ({ top_k }),\n }),\n ...Option.match(params.stopSequences, {\n onNone: () => ({}),\n onSome: (stop_sequences) => ({ stop_sequences }),\n }),\n ...Option.match(params.thinking, {\n onNone: () => ({}),\n onSome: (thinking) => ({ thinking }),\n }),\n ...Option.match(params.tools, {\n onNone: () => ({}),\n onSome: (tools) => ({ tools }),\n }),\n ...Option.match(params.toolChoice, {\n onNone: () => ({}),\n onSome: (tool_choice) => ({ tool_choice }),\n }),\n ...Option.match(params.userId, {\n onNone: () => ({}),\n onSome: (user_id) => ({ metadata: { user_id } }),\n }),\n ...Option.match(params.outputConfig, {\n onNone: () => ({}),\n onSome: (output_config) => ({ output_config }),\n }),\n ...Option.match(params.cacheControl, {\n onNone: () => ({}),\n onSome: (cache_control) => ({ cache_control }),\n }),\n stream: true,\n })),\n )\n\n// ---------------------------------------------------------------------------\n// Stream-level state - assemble content blocks index-by-index, then emit\n// our `Items.HistoryItem[]` when `message_stop` lands.\n// ---------------------------------------------------------------------------\n\ntype BlockBuffer = {\n readonly type: WireContentBlock[\"type\"]\n readonly text: string\n readonly inputJson: string\n readonly thinking: string\n readonly signature: string\n readonly id: Option.Option<string>\n readonly name: Option.Option<string>\n readonly redactedData: Option.Option<string>\n}\n\nconst emptyBlock = (type: WireContentBlock[\"type\"]): BlockBuffer => ({\n type,\n text: \"\",\n inputJson: \"\",\n thinking: \"\",\n signature: \"\",\n id: Option.none(),\n name: Option.none(),\n redactedData: Option.none(),\n})\n\nexport type Accumulator = {\n readonly blocks: Readonly<Record<number, BlockBuffer>>\n readonly stopReason: Option.Option<string>\n readonly usage: Items.Usage\n}\n\nexport const emptyAccumulator: Accumulator = {\n blocks: {},\n stopReason: Option.none(),\n usage: {},\n}\n\nconst replaceBlock = (acc: Accumulator, index: number, block: BlockBuffer): Accumulator => ({\n ...acc,\n blocks: { ...acc.blocks, [index]: block },\n})\n\nconst updateBlock = (\n acc: Accumulator,\n index: number,\n patch: (block: BlockBuffer) => BlockBuffer,\n): Accumulator => replaceBlock(acc, index, patch(acc.blocks[index] ?? emptyBlock(\"text\")))\n\nexport const startBlock = (acc: Accumulator, index: number, block: WireContentBlock): Accumulator =>\n Match.value(block).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n text: () => replaceBlock(acc, index, emptyBlock(\"text\")),\n tool_use: (b) =>\n replaceBlock(acc, index, {\n ...emptyBlock(\"tool_use\"),\n id: Option.some(b.id),\n name: Option.some(b.name),\n inputJson: typeof b.input === \"string\" ? b.input : \"\",\n }),\n thinking: () => replaceBlock(acc, index, emptyBlock(\"thinking\")),\n redacted_thinking: (b) =>\n replaceBlock(acc, index, {\n ...emptyBlock(\"redacted_thinking\"),\n redactedData: Option.some(b.data),\n }),\n }),\n )\n\nexport const appendTextDelta = (acc: Accumulator, index: number, text: string): Accumulator =>\n updateBlock(acc, index, (b) => ({ ...b, text: b.text + text }))\n\nexport const appendInputJsonDelta = (\n acc: Accumulator,\n index: number,\n partial: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, inputJson: b.inputJson + partial }))\n\nexport const appendThinkingDelta = (\n acc: Accumulator,\n index: number,\n thinking: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, thinking: b.thinking + thinking }))\n\nexport const appendSignatureDelta = (\n acc: Accumulator,\n index: number,\n signature: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, signature: b.signature + signature }))\n\nexport const setStopReason = (acc: Accumulator, reason: string): Accumulator => ({\n ...acc,\n stopReason: Option.some(reason),\n})\n\n// Anthropic splits input across three billed buckets: `input_tokens`\n// (post-breakpoint), cache reads, and cache writes. All three are kept;\n// `input_tokens` is not the input total. Each field carries over from the\n// prior value since a streamed turn spreads usage across events (input +\n// cache on message_start, output_tokens on message_delta), so total_tokens is\n// summed from the accumulated buckets, not one event.\nexport const mergeUsage = (acc: Accumulator, wire: WireUsage): Accumulator => {\n const prev = acc.usage\n const input_tokens = wire.input_tokens ?? prev.input_tokens\n const output_tokens = wire.output_tokens ?? prev.output_tokens\n const cached_tokens = wire.cache_read_input_tokens ?? prev.input_tokens_details?.cached_tokens\n const cache_write_tokens =\n wire.cache_creation_input_tokens ?? prev.input_tokens_details?.cache_write_tokens\n\n const buckets = [input_tokens, cached_tokens, cache_write_tokens, output_tokens]\n const total_tokens = buckets.every((v) => v === undefined)\n ? undefined\n : buckets.reduce<number>((sum, v) => sum + (v ?? 0), 0)\n\n const details: Items.InputTokensDetails = {\n ...(cached_tokens !== undefined && { cached_tokens }),\n ...(cache_write_tokens !== undefined && { cache_write_tokens }),\n }\n const usage: Items.Usage = {\n ...(input_tokens !== undefined && { input_tokens }),\n ...(output_tokens !== undefined && { output_tokens }),\n ...(total_tokens !== undefined && { total_tokens }),\n ...(Object.keys(details).length > 0 && { input_tokens_details: details }),\n }\n return { ...acc, usage }\n}\n\nconst stopReasonFromAnthropic = (reason: Option.Option<string>): Turn[\"stop_reason\"] =>\n Option.match(reason, {\n onNone: () => \"stop\" as const,\n onSome: (r) =>\n Match.value(r).pipe(\n Match.when(\"tool_use\", () => \"tool_calls\" as const),\n Match.when(\"max_tokens\", () => \"max_tokens\" as const),\n Match.orElse(() => \"stop\" as const),\n ),\n })\n\nconst blocksByIndex = (acc: Accumulator): ReadonlyArray<BlockBuffer> =>\n pipe(\n Object.keys(acc.blocks),\n Arr.map((k) => Number(k)),\n Arr.sort(Order.Number),\n Arr.map((i) => acc.blocks[i]!),\n )\n\nconst blockToItems = (block: BlockBuffer): ReadonlyArray<Items.HistoryItem> =>\n Match.value(block.type).pipe(\n Match.when(\"text\", (): ReadonlyArray<Items.HistoryItem> =>\n block.text.length === 0\n ? []\n : [\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: block.text }],\n },\n ],\n ),\n Match.when(\"tool_use\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"function_call\",\n call_id: Option.getOrElse(block.id, () => \"\"),\n name: Option.getOrElse(block.name, () => \"\"),\n arguments: block.inputJson,\n },\n ]),\n Match.when(\"thinking\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"reasoning\",\n ...(block.thinking.length > 0 && { summary: block.thinking }),\n ...(block.signature.length > 0 && { signature: block.signature }),\n },\n ]),\n Match.when(\"redacted_thinking\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"reasoning\",\n ...Option.match(block.redactedData, {\n onNone: () => ({}),\n onSome: (signature) => ({ signature }),\n }),\n },\n ]),\n Match.exhaustive,\n )\n\ntype MergeAcc = {\n readonly out: ReadonlyArray<Items.HistoryItem>\n}\n\nconst mergeStep = (acc: MergeAcc, item: Items.HistoryItem): MergeAcc => {\n const last = Arr.last(acc.out)\n if (\n Option.isSome(last) &&\n last.value.type === \"message\" &&\n last.value.role === \"assistant\" &&\n item.type === \"message\" &&\n item.role === \"assistant\"\n ) {\n const merged: Items.Message = {\n ...last.value,\n content: [...last.value.content, ...item.content],\n }\n return { out: [...acc.out.slice(0, -1), merged] }\n }\n return { out: [...acc.out, item] }\n}\n\nconst mergeAdjacentAssistantText = (\n items: ReadonlyArray<Items.HistoryItem>,\n): ReadonlyArray<Items.HistoryItem> => Arr.reduce(items, { out: [] } as MergeAcc, mergeStep).out\n\nexport const accumulatorToTurn = (acc: Accumulator): Turn => ({\n items: pipe(blocksByIndex(acc), Arr.flatMap(blockToItems), mergeAdjacentAssistantText),\n usage: acc.usage,\n stop_reason: stopReasonFromAnthropic(acc.stopReason),\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"codec.mjs","names":["Arr"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Encoding, Match, Option, Order, Result, Schema, pipe } from \"effect\"\nimport * as Items from \"@effect-uai/core/Items\"\nimport { JsonParseError } from \"@effect-uai/core/JSONL\"\nimport type { Turn } from \"@effect-uai/core/Turn\"\n\n// ---------------------------------------------------------------------------\n// Wire schemas - subset of Anthropic Messages API we consume.\n// Reference: https://platform.claude.com/docs/en/api/messages\n// ---------------------------------------------------------------------------\n\nconst WireTextBlock = Schema.Struct({\n type: Schema.Literal(\"text\"),\n text: Schema.String,\n})\n\nconst WireToolUseBlock = Schema.Struct({\n type: Schema.Literal(\"tool_use\"),\n id: Schema.String,\n name: Schema.String,\n input: Schema.Unknown,\n})\n\nconst WireThinkingBlock = Schema.Struct({\n type: Schema.Literal(\"thinking\"),\n thinking: Schema.String,\n signature: Schema.optional(Schema.String),\n})\n\nconst WireRedactedThinkingBlock = Schema.Struct({\n type: Schema.Literal(\"redacted_thinking\"),\n data: Schema.String,\n})\n\nexport const WireContentBlock = Schema.Union([\n WireTextBlock,\n WireToolUseBlock,\n WireThinkingBlock,\n WireRedactedThinkingBlock,\n])\nexport type WireContentBlock = typeof WireContentBlock.Type\n\nconst WireUsage = Schema.Struct({\n input_tokens: Schema.optional(Schema.Number),\n output_tokens: Schema.optional(Schema.Number),\n cache_creation_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n cache_read_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n})\nexport type WireUsage = typeof WireUsage.Type\n\n// ---------------------------------------------------------------------------\n// History → request body\n// ---------------------------------------------------------------------------\n\ntype RequestTextContent = {\n readonly type: \"text\"\n readonly text: string\n}\n\ntype RequestToolResultContent = {\n readonly type: \"tool_result\"\n readonly tool_use_id: string\n readonly content: string\n}\n\ntype RequestToolUseContent = {\n readonly type: \"tool_use\"\n readonly id: string\n readonly name: string\n readonly input: unknown\n}\n\ntype RequestThinkingContent = {\n readonly type: \"thinking\"\n readonly thinking: string\n readonly signature?: string\n}\n\ntype RequestRedactedThinkingContent = {\n readonly type: \"redacted_thinking\"\n readonly data: string\n}\n\ntype RequestImageContent = {\n readonly type: \"image\"\n readonly source:\n | { readonly type: \"url\"; readonly url: string }\n | { readonly type: \"base64\"; readonly media_type: string; readonly data: string }\n}\n\ntype RequestUserContentBlock = RequestTextContent | RequestToolResultContent | RequestImageContent\n\ntype RequestAssistantContentBlock =\n | RequestTextContent\n | RequestToolUseContent\n | RequestThinkingContent\n | RequestRedactedThinkingContent\n\ntype RequestUserMessage = {\n readonly role: \"user\"\n readonly content: ReadonlyArray<RequestUserContentBlock>\n}\n\ntype RequestAssistantMessage = {\n readonly role: \"assistant\"\n readonly content: ReadonlyArray<RequestAssistantContentBlock>\n}\n\ntype RequestMessage = RequestUserMessage | RequestAssistantMessage\n\nconst blockText = Match.type<Items.ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => b.text,\n input_image: () => \"\",\n output_text: (b) => b.text,\n output_image: () => \"\",\n refusal: (b) => b.text,\n }),\n)\n\nconst messageText = (message: Items.Message): string => message.content.map(blockText).join(\"\")\n\nconst imageSourceToWire = Match.type<Items.InputImage[\"source\"]>().pipe(\n Match.tag(\"url\", (s): RequestImageContent[\"source\"] => ({ type: \"url\", url: s.url })),\n Match.tag(\"base64\", (s): RequestImageContent[\"source\"] => ({\n type: \"base64\",\n media_type: s.mimeType,\n data: s.base64,\n })),\n Match.tag(\"bytes\", (s): RequestImageContent[\"source\"] => ({\n type: \"base64\",\n media_type: s.mimeType,\n data: Encoding.encodeBase64(s.bytes),\n })),\n Match.exhaustive,\n)\n\nconst userContentBlock = (\n block: Items.ContentBlock,\n): Result.Result<RequestUserContentBlock, void> =>\n Match.value(block).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) =>\n b.text.length === 0\n ? Result.failVoid\n : Result.succeed({ type: \"text\" as const, text: b.text }),\n input_image: (b) =>\n Result.succeed({ type: \"image\" as const, source: imageSourceToWire(b.source) }),\n // Assistant content; never appears on a user message in practice. Skip.\n output_text: () => Result.failVoid,\n // No assistant-image block here either. Dropped rather than relocated\n // onto a user message, which is the caller's call: `Turn.imagesAsInput`.\n output_image: () => Result.failVoid,\n refusal: () => Result.failVoid,\n }),\n )\n\nconst parseJson = (s: string): Result.Result<unknown, JsonParseError> =>\n Result.try({\n try: () => JSON.parse(s) as unknown,\n catch: (cause) => new JsonParseError({ line: s, cause }),\n })\n\ntype RoleBucket = \"user\" | \"assistant\" | \"system\"\n\nconst roleBucket = (item: Items.HistoryItem): RoleBucket =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m) => m.role,\n function_call: () => \"assistant\" as const,\n function_call_output: () => \"user\" as const,\n reasoning: () => \"assistant\" as const,\n }),\n )\n\nconst itemToUserBlocks = (item: Items.HistoryItem): ReadonlyArray<RequestUserContentBlock> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m): ReadonlyArray<RequestUserContentBlock> =>\n m.role === \"user\" ? pipe(m.content, Arr.filterMap(userContentBlock)) : [],\n function_call: (): ReadonlyArray<RequestUserContentBlock> => [],\n function_call_output: (o): ReadonlyArray<RequestUserContentBlock> => [\n { type: \"tool_result\", tool_use_id: o.call_id, content: o.output },\n ],\n reasoning: (): ReadonlyArray<RequestUserContentBlock> => [],\n }),\n )\n\nconst itemToAssistantBlocks = (\n item: Items.HistoryItem,\n): Result.Result<ReadonlyArray<RequestAssistantContentBlock>, JsonParseError> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m): Result.Result<ReadonlyArray<RequestAssistantContentBlock>, JsonParseError> => {\n const text = messageText(m)\n return Result.succeed(\n m.role === \"assistant\" && text.length > 0 ? [{ type: \"text\", text }] : [],\n )\n },\n function_call: (f) =>\n pipe(\n parseJson(f.arguments),\n Result.map((input): ReadonlyArray<RequestAssistantContentBlock> => [\n { type: \"tool_use\", id: f.call_id, name: f.name, input },\n ]),\n ),\n function_call_output: (): Result.Result<\n ReadonlyArray<RequestAssistantContentBlock>,\n JsonParseError\n > => Result.succeed([]),\n reasoning: (r) => {\n const blocks: ReadonlyArray<RequestAssistantContentBlock> =\n r.summary !== undefined\n ? [\n {\n type: \"thinking\",\n thinking: r.summary,\n ...(r.signature !== undefined && { signature: r.signature }),\n },\n ]\n : r.signature !== undefined\n ? [{ type: \"redacted_thinking\", data: r.signature }]\n : []\n return Result.succeed(blocks)\n },\n }),\n )\n\ntype GroupAcc = {\n readonly messages: ReadonlyArray<RequestMessage>\n readonly currentRole: Option.Option<\"user\" | \"assistant\">\n readonly userBuf: ReadonlyArray<RequestUserContentBlock>\n readonly assistantBuf: ReadonlyArray<RequestAssistantContentBlock>\n}\n\nconst flushAcc = (acc: GroupAcc): ReadonlyArray<RequestMessage> =>\n Option.match(acc.currentRole, {\n onNone: () => acc.messages,\n onSome: (role) =>\n role === \"user\" && acc.userBuf.length > 0\n ? [...acc.messages, { role: \"user\", content: acc.userBuf }]\n : role === \"assistant\" && acc.assistantBuf.length > 0\n ? [...acc.messages, { role: \"assistant\", content: acc.assistantBuf }]\n : acc.messages,\n })\n\nconst appendUser = (acc: GroupAcc, blocks: ReadonlyArray<RequestUserContentBlock>): GroupAcc =>\n blocks.length === 0\n ? acc\n : Option.isSome(acc.currentRole) && acc.currentRole.value === \"user\"\n ? { ...acc, userBuf: [...acc.userBuf, ...blocks] }\n : {\n messages: flushAcc(acc),\n currentRole: Option.some(\"user\"),\n userBuf: blocks,\n assistantBuf: [],\n }\n\nconst appendAssistant = (\n acc: GroupAcc,\n blocks: ReadonlyArray<RequestAssistantContentBlock>,\n): GroupAcc =>\n blocks.length === 0\n ? acc\n : Option.isSome(acc.currentRole) && acc.currentRole.value === \"assistant\"\n ? { ...acc, assistantBuf: [...acc.assistantBuf, ...blocks] }\n : {\n messages: flushAcc(acc),\n currentRole: Option.some(\"assistant\"),\n userBuf: [],\n assistantBuf: blocks,\n }\n\nconst groupStep = (\n acc: GroupAcc,\n item: Items.HistoryItem,\n): Result.Result<GroupAcc, JsonParseError> => {\n const bucket = roleBucket(item)\n if (bucket === \"system\") return Result.succeed(acc)\n if (bucket === \"user\") {\n return Result.succeed(appendUser(acc, itemToUserBlocks(item)))\n }\n return pipe(\n itemToAssistantBlocks(item),\n Result.map((blocks) => appendAssistant(acc, blocks)),\n )\n}\n\n/**\n * Group consecutive same-role items into Anthropic-shaped messages.\n * Anthropic requires strict user/assistant alternation; consecutive items\n * from the same role are folded into one message's `content`. Fails if any\n * `function_call.arguments` is not valid JSON, since Anthropic's wire shape\n * requires an object input.\n */\nconst groupedMessages = (\n history: ReadonlyArray<Items.HistoryItem>,\n): Result.Result<ReadonlyArray<RequestMessage>, JsonParseError> => {\n const initial: Result.Result<GroupAcc, JsonParseError> = Result.succeed({\n messages: [],\n currentRole: Option.none(),\n userBuf: [],\n assistantBuf: [],\n })\n return pipe(\n Arr.reduce(history, initial, (acc, item) => Result.flatMap(acc, (a) => groupStep(a, item))),\n Result.map(flushAcc),\n )\n}\n\nconst isSystemMessage = (item: Items.HistoryItem): item is Items.Message =>\n item.type === \"message\" && item.role === \"system\"\n\nconst systemFromHistory = (history: ReadonlyArray<Items.HistoryItem>): Option.Option<string> => {\n const texts = pipe(\n history,\n Arr.filterMap((item) =>\n isSystemMessage(item) ? Result.succeed(messageText(item)) : Result.failVoid,\n ),\n Arr.filter((s) => s.length > 0),\n )\n return texts.length === 0 ? Option.none() : Option.some(texts.join(\"\\n\"))\n}\n\nexport type ThinkingConfig = {\n readonly type: \"enabled\"\n readonly budget_tokens: number\n}\n\n/** Cache lifetime. `1h` writes cost 2x base input; `5m` costs 1.25x. */\nexport type CacheTtl = \"5m\" | \"1h\"\n\n/**\n * Request-level cache breakpoint. Anthropic applies it to the last cacheable\n * block and moves it forward as the conversation grows, so the cached prefix\n * (`tools` → `system` → `messages`) follows the conversation without the\n * caller re-placing anything.\n */\nexport type CacheControl = {\n readonly type: \"ephemeral\"\n readonly ttl?: CacheTtl\n}\n\nexport type RequestBody = {\n readonly model: string\n readonly messages: ReadonlyArray<RequestMessage>\n readonly max_tokens: number\n readonly system?: string\n readonly temperature?: number\n readonly top_p?: number\n readonly top_k?: number\n readonly stop_sequences?: ReadonlyArray<string>\n readonly thinking?: ThinkingConfig\n readonly tools?: ReadonlyArray<Record<string, unknown>>\n readonly tool_choice?: Record<string, unknown>\n readonly metadata?: { readonly user_id: string }\n readonly output_config?: Record<string, unknown>\n readonly cache_control?: CacheControl\n readonly stream: true\n}\n\nexport const buildRequestBody = (params: {\n readonly model: string\n readonly history: ReadonlyArray<Items.HistoryItem>\n readonly maxTokens: number\n readonly temperature: Option.Option<number>\n readonly topP: Option.Option<number>\n readonly topK: Option.Option<number>\n readonly stopSequences: Option.Option<ReadonlyArray<string>>\n readonly thinking: Option.Option<ThinkingConfig>\n readonly tools: Option.Option<ReadonlyArray<Record<string, unknown>>>\n readonly toolChoice: Option.Option<Record<string, unknown>>\n readonly userId: Option.Option<string>\n readonly outputConfig: Option.Option<Record<string, unknown>>\n readonly cacheControl: Option.Option<CacheControl>\n}): Result.Result<RequestBody, JsonParseError> =>\n pipe(\n groupedMessages(params.history),\n Result.map((messages): RequestBody => ({\n model: params.model,\n messages,\n max_tokens: params.maxTokens,\n ...Option.match(systemFromHistory(params.history), {\n onNone: () => ({}),\n onSome: (system) => ({ system }),\n }),\n ...Option.match(params.temperature, {\n onNone: () => ({}),\n onSome: (temperature) => ({ temperature }),\n }),\n ...Option.match(params.topP, {\n onNone: () => ({}),\n onSome: (top_p) => ({ top_p }),\n }),\n ...Option.match(params.topK, {\n onNone: () => ({}),\n onSome: (top_k) => ({ top_k }),\n }),\n ...Option.match(params.stopSequences, {\n onNone: () => ({}),\n onSome: (stop_sequences) => ({ stop_sequences }),\n }),\n ...Option.match(params.thinking, {\n onNone: () => ({}),\n onSome: (thinking) => ({ thinking }),\n }),\n ...Option.match(params.tools, {\n onNone: () => ({}),\n onSome: (tools) => ({ tools }),\n }),\n ...Option.match(params.toolChoice, {\n onNone: () => ({}),\n onSome: (tool_choice) => ({ tool_choice }),\n }),\n ...Option.match(params.userId, {\n onNone: () => ({}),\n onSome: (user_id) => ({ metadata: { user_id } }),\n }),\n ...Option.match(params.outputConfig, {\n onNone: () => ({}),\n onSome: (output_config) => ({ output_config }),\n }),\n ...Option.match(params.cacheControl, {\n onNone: () => ({}),\n onSome: (cache_control) => ({ cache_control }),\n }),\n stream: true,\n })),\n )\n\n// ---------------------------------------------------------------------------\n// Stream-level state - assemble content blocks index-by-index, then emit\n// our `Items.HistoryItem[]` when `message_stop` lands.\n// ---------------------------------------------------------------------------\n\ntype BlockBuffer = {\n readonly type: WireContentBlock[\"type\"]\n readonly text: string\n readonly inputJson: string\n readonly thinking: string\n readonly signature: string\n readonly id: Option.Option<string>\n readonly name: Option.Option<string>\n readonly redactedData: Option.Option<string>\n}\n\nconst emptyBlock = (type: WireContentBlock[\"type\"]): BlockBuffer => ({\n type,\n text: \"\",\n inputJson: \"\",\n thinking: \"\",\n signature: \"\",\n id: Option.none(),\n name: Option.none(),\n redactedData: Option.none(),\n})\n\nexport type Accumulator = {\n readonly blocks: Readonly<Record<number, BlockBuffer>>\n readonly stopReason: Option.Option<string>\n readonly usage: Items.Usage\n}\n\nexport const emptyAccumulator: Accumulator = {\n blocks: {},\n stopReason: Option.none(),\n usage: {},\n}\n\nconst replaceBlock = (acc: Accumulator, index: number, block: BlockBuffer): Accumulator => ({\n ...acc,\n blocks: { ...acc.blocks, [index]: block },\n})\n\nconst updateBlock = (\n acc: Accumulator,\n index: number,\n patch: (block: BlockBuffer) => BlockBuffer,\n): Accumulator => replaceBlock(acc, index, patch(acc.blocks[index] ?? emptyBlock(\"text\")))\n\nexport const startBlock = (acc: Accumulator, index: number, block: WireContentBlock): Accumulator =>\n Match.value(block).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n text: () => replaceBlock(acc, index, emptyBlock(\"text\")),\n tool_use: (b) =>\n replaceBlock(acc, index, {\n ...emptyBlock(\"tool_use\"),\n id: Option.some(b.id),\n name: Option.some(b.name),\n inputJson: typeof b.input === \"string\" ? b.input : \"\",\n }),\n thinking: () => replaceBlock(acc, index, emptyBlock(\"thinking\")),\n redacted_thinking: (b) =>\n replaceBlock(acc, index, {\n ...emptyBlock(\"redacted_thinking\"),\n redactedData: Option.some(b.data),\n }),\n }),\n )\n\nexport const appendTextDelta = (acc: Accumulator, index: number, text: string): Accumulator =>\n updateBlock(acc, index, (b) => ({ ...b, text: b.text + text }))\n\nexport const appendInputJsonDelta = (\n acc: Accumulator,\n index: number,\n partial: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, inputJson: b.inputJson + partial }))\n\nexport const appendThinkingDelta = (\n acc: Accumulator,\n index: number,\n thinking: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, thinking: b.thinking + thinking }))\n\nexport const appendSignatureDelta = (\n acc: Accumulator,\n index: number,\n signature: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, signature: b.signature + signature }))\n\nexport const setStopReason = (acc: Accumulator, reason: string): Accumulator => ({\n ...acc,\n stopReason: Option.some(reason),\n})\n\n// Anthropic splits input across three billed buckets: `input_tokens`\n// (post-breakpoint), cache reads, and cache writes. All three are kept;\n// `input_tokens` is not the input total. Each field carries over from the\n// prior value since a streamed turn spreads usage across events (input +\n// cache on message_start, output_tokens on message_delta), so total_tokens is\n// summed from the accumulated buckets, not one event.\nexport const mergeUsage = (acc: Accumulator, wire: WireUsage): Accumulator => {\n const prev = acc.usage\n const input_tokens = wire.input_tokens ?? prev.input_tokens\n const output_tokens = wire.output_tokens ?? prev.output_tokens\n const cached_tokens = wire.cache_read_input_tokens ?? prev.input_tokens_details?.cached_tokens\n const cache_write_tokens =\n wire.cache_creation_input_tokens ?? prev.input_tokens_details?.cache_write_tokens\n\n const buckets = [input_tokens, cached_tokens, cache_write_tokens, output_tokens]\n const total_tokens = buckets.every((v) => v === undefined)\n ? undefined\n : buckets.reduce<number>((sum, v) => sum + (v ?? 0), 0)\n\n const details: Items.InputTokensDetails = {\n ...(cached_tokens !== undefined && { cached_tokens }),\n ...(cache_write_tokens !== undefined && { cache_write_tokens }),\n }\n const usage: Items.Usage = {\n ...(input_tokens !== undefined && { input_tokens }),\n ...(output_tokens !== undefined && { output_tokens }),\n ...(total_tokens !== undefined && { total_tokens }),\n ...(Object.keys(details).length > 0 && { input_tokens_details: details }),\n }\n return { ...acc, usage }\n}\n\nconst stopReasonFromAnthropic = (reason: Option.Option<string>): Turn[\"stop_reason\"] =>\n Option.match(reason, {\n onNone: () => \"stop\" as const,\n onSome: (r) =>\n Match.value(r).pipe(\n Match.when(\"tool_use\", () => \"tool_calls\" as const),\n Match.when(\"max_tokens\", () => \"max_tokens\" as const),\n Match.orElse(() => \"stop\" as const),\n ),\n })\n\nconst blocksByIndex = (acc: Accumulator): ReadonlyArray<BlockBuffer> =>\n pipe(\n Object.keys(acc.blocks),\n Arr.map((k) => Number(k)),\n Arr.sort(Order.Number),\n Arr.map((i) => acc.blocks[i]!),\n )\n\nconst blockToItems = (block: BlockBuffer): ReadonlyArray<Items.HistoryItem> =>\n Match.value(block.type).pipe(\n Match.when(\"text\", (): ReadonlyArray<Items.HistoryItem> =>\n block.text.length === 0\n ? []\n : [\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: block.text }],\n },\n ],\n ),\n Match.when(\"tool_use\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"function_call\",\n call_id: Option.getOrElse(block.id, () => \"\"),\n name: Option.getOrElse(block.name, () => \"\"),\n arguments: block.inputJson,\n },\n ]),\n Match.when(\"thinking\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"reasoning\",\n ...(block.thinking.length > 0 && { summary: block.thinking }),\n ...(block.signature.length > 0 && { signature: block.signature }),\n },\n ]),\n Match.when(\"redacted_thinking\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"reasoning\",\n ...Option.match(block.redactedData, {\n onNone: () => ({}),\n onSome: (signature) => ({ signature }),\n }),\n },\n ]),\n Match.exhaustive,\n )\n\ntype MergeAcc = {\n readonly out: ReadonlyArray<Items.HistoryItem>\n}\n\nconst mergeStep = (acc: MergeAcc, item: Items.HistoryItem): MergeAcc => {\n const last = Arr.last(acc.out)\n if (\n Option.isSome(last) &&\n last.value.type === \"message\" &&\n last.value.role === \"assistant\" &&\n item.type === \"message\" &&\n item.role === \"assistant\"\n ) {\n const merged: Items.Message = {\n ...last.value,\n content: [...last.value.content, ...item.content],\n }\n return { out: [...acc.out.slice(0, -1), merged] }\n }\n return { out: [...acc.out, item] }\n}\n\nconst mergeAdjacentAssistantText = (\n items: ReadonlyArray<Items.HistoryItem>,\n): ReadonlyArray<Items.HistoryItem> => Arr.reduce(items, { out: [] } as MergeAcc, mergeStep).out\n\nexport const accumulatorToTurn = (acc: Accumulator): Turn => ({\n items: pipe(blocksByIndex(acc), Arr.flatMap(blockToItems), mergeAdjacentAssistantText),\n usage: acc.usage,\n stop_reason: stopReasonFromAnthropic(acc.stopReason),\n})\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-uai/anthropic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Anthropic Messages API provider for @effect-uai/core.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"effect": "4.0.0-rc.111",
|
|
47
47
|
"typescript": "^7.0.2",
|
|
48
|
-
"@effect-uai/core": "0.
|
|
48
|
+
"@effect-uai/core": "0.14.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"@effect-uai/core": ">=0.2.0 <1",
|
package/src/Anthropic.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "effect"
|
|
13
13
|
import { HttpClient, HttpClientRequest } from "effect/unstable/http"
|
|
14
14
|
import * as AiError from "@effect-uai/core/AiError"
|
|
15
|
+
import * as Capabilities from "@effect-uai/core/Capabilities"
|
|
15
16
|
import {
|
|
16
17
|
type CommonRequest,
|
|
17
18
|
LanguageModel,
|
|
@@ -324,6 +325,12 @@ const buildNativeStream = (cfg: Config) => {
|
|
|
324
325
|
onSuccess: (wire) => Effect.succeed(wire),
|
|
325
326
|
},
|
|
326
327
|
)
|
|
328
|
+
yield* Capabilities.warnDroppedBlocks(request.history, "output_image", {
|
|
329
|
+
provider: "anthropic",
|
|
330
|
+
capability: "output_image",
|
|
331
|
+
reason:
|
|
332
|
+
"Assistant messages carry no image on this wire. Use `Turn.imagesAsInput` to resend it as user content.",
|
|
333
|
+
})
|
|
327
334
|
const bodyResult = buildRequestBody({
|
|
328
335
|
model: request.model,
|
|
329
336
|
history: request.history,
|
package/src/codec.ts
CHANGED
|
@@ -112,6 +112,7 @@ const blockText = Match.type<Items.ContentBlock>().pipe(
|
|
|
112
112
|
input_text: (b) => b.text,
|
|
113
113
|
input_image: () => "",
|
|
114
114
|
output_text: (b) => b.text,
|
|
115
|
+
output_image: () => "",
|
|
115
116
|
refusal: (b) => b.text,
|
|
116
117
|
}),
|
|
117
118
|
)
|
|
@@ -146,6 +147,9 @@ const userContentBlock = (
|
|
|
146
147
|
Result.succeed({ type: "image" as const, source: imageSourceToWire(b.source) }),
|
|
147
148
|
// Assistant content; never appears on a user message in practice. Skip.
|
|
148
149
|
output_text: () => Result.failVoid,
|
|
150
|
+
// No assistant-image block here either. Dropped rather than relocated
|
|
151
|
+
// onto a user message, which is the caller's call: `Turn.imagesAsInput`.
|
|
152
|
+
output_image: () => Result.failVoid,
|
|
149
153
|
refusal: () => Result.failVoid,
|
|
150
154
|
}),
|
|
151
155
|
)
|