@effect-uai/responses 0.13.0 → 0.15.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.
@@ -1 +1 @@
1
- {"version":3,"file":"Responses.d.mts","names":[],"sources":["../src/Responses.ts"],"mappings":";;;;;;;;;;;;KAqCY,mBAAmB,KAAK;;;;;WAKzB,OAAO;WACP;aAAuB;;WACvB;WACA;WACA;WACA;WACA,WAAW,SAAS;WACpB;WACA;WACA;WACA;;;;;;;;;;;;;;WAcA,YAAY,SAAS;;;;;;WAMrB;aAA4B;;;;;;WAK5B;;KAGC;;;;;;WAMD,eACP,SAAS,qBACN,OAAO,OAAO,eAAe,QAAQ;;;;;WAKjC,aAAa,SAAS,qBAAqB,OAAO,OAAO,WAAW,QAAQ;;;;;;WAM5E,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;WACA,SAAS;;;;;;WAMT,YAAY,SAAS;;cA0InB,kBAAe,gBAAkB,iBAAiB,QAAQ;;;;;;;;cAoB1D,2BAAwB,UACzB,mBAAmB,uBAC5B,OAAO,OAAO,eAAe,QAAQ;;;;;;cAiE3B,cAAe,GAAG,GAAC,GAC3B,OAAO,OAAO,eAAe,GAAG,OAClC,OAAO,OAAO,WAAW,GAAG;;;;cAelB,OAAI,KAAS,WAAS,OAAO,OAAO,yBAAyB,WAAW;;;;;;;;cAqBxE,QAAK,KACX,WACJ,MAAM,MAAM,YAAY,sBAAsB,WAAW"}
1
+ {"version":3,"file":"Responses.d.mts","names":[],"sources":["../src/Responses.ts"],"mappings":";;;;;;;;;;;;KAsCY,mBAAmB,KAAK;;;;;WAKzB,OAAO;WACP;aAAuB;;WACvB;WACA;WACA;WACA;WACA,WAAW,SAAS;WACpB;WACA;WACA;WACA;;;;;;;;;;;;;;WAcA,YAAY,SAAS;;;;;;WAMrB;aAA4B;;;;;;WAK5B;;KAGC;;;;;;WAMD,eACP,SAAS,qBACN,OAAO,OAAO,eAAe,QAAQ;;;;;WAKjC,aAAa,SAAS,qBAAqB,OAAO,OAAO,WAAW,QAAQ;;;;;;WAM5E,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;WACA,SAAS;;;;;;WAMT,YAAY,SAAS;;cA0InB,kBAAe,gBAAkB,iBAAiB,QAAQ;;;;;;;;cAoB1D,2BAAwB,UACzB,mBAAmB,uBAC5B,OAAO,OAAO,eAAe,QAAQ;;;;;;cAuE3B,cAAe,GAAG,GAAC,GAC3B,OAAO,OAAO,eAAe,GAAG,OAClC,OAAO,OAAO,WAAW,GAAG;;;;cAelB,OAAI,KAAS,WAAS,OAAO,OAAO,yBAAyB,WAAW;;;;;;;;cAqBxE,QAAK,KACX,WACJ,MAAM,MAAM,YAAY,sBAAsB,WAAW"}
@@ -6,6 +6,7 @@ import { KnownProviderEvent, eventToDeltas, makeCallIdLookup } from "./streamEve
6
6
  import { Context, Effect, Layer, Match, Option, Result, Schema, Stream } from "effect";
7
7
  import { HttpClient, HttpClientRequest } from "effect/unstable/http";
8
8
  import * as AiError from "@effect-uai/core/AiError";
9
+ import * as Capabilities from "@effect-uai/core/Capabilities";
9
10
  import { LanguageModel, turnFromStream } from "@effect-uai/core/LanguageModel";
10
11
  import * as SSE from "@effect-uai/core/SSE";
11
12
  import { descriptorsOf, providerToolsOf } from "@effect-uai/core/Tool";
@@ -187,6 +188,11 @@ const buildNativeStream = (cfg) => {
187
188
  })),
188
189
  onSuccess: (wire) => Effect.succeed(wire)
189
190
  });
191
+ yield* Capabilities.warnDroppedBlocks(request.history, "output_image", {
192
+ provider: "responses",
193
+ capability: "output_image",
194
+ reason: "Assistant messages carry no image on this wire; images here come from the `image_generation` tool. Use `Turn.imagesAsInput` to resend one as user content."
195
+ });
190
196
  const httpRequest = HttpClientRequest.post(url).pipe(HttpClientRequest.bearerToken(cfg.apiKey), HttpClientRequest.bodyJsonUnsafe(buildBody(request, providerToolWire, cfg)), HttpClientRequest.accept("text/event-stream"));
191
197
  const response = yield* client.execute(httpRequest).pipe(Effect.mapError((cause) => new AiError.Unavailable({
192
198
  provider: "responses",
@@ -1 +1 @@
1
- {"version":3,"file":"Responses.mjs","names":[],"sources":["../src/Responses.ts"],"sourcesContent":["import { Context, Effect, Layer, Match, Option, Redacted, Result, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest, HttpClientResponse } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport * as StructuredFormat from \"@effect-uai/core/StructuredFormat\"\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 { itemsToInput } from \"./codec.js\"\nimport { renderProviderTools } from \"./ResponsesTools.js\"\n\nexport {\n codeInterpreterTool,\n fileSearchTool,\n webSearchTool,\n type WebSearchOptions,\n type WebSearchFilters,\n type UserLocation,\n} from \"./ResponsesTools.js\"\nimport type { OpenAIModel } from \"./models.js\"\nimport { type OpenAiRegion, resolveHost } from \"./region.js\"\nimport {\n KnownProviderEvent,\n ProviderEvent,\n eventToDeltas,\n makeCallIdLookup,\n} from \"./streamEvents.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport type ResponsesRequest = Omit<CommonRequest, \"model\"> & {\n /**\n * Narrows `CommonRequest.model` (`string`) to the typed `OpenAIModel`\n * literal union for autocomplete.\n */\n readonly model: OpenAIModel\n readonly reasoning?: { readonly effort: \"low\" | \"medium\" | \"high\" }\n readonly store?: boolean\n readonly previousResponseId?: string\n readonly instructions?: string\n readonly parallelToolCalls?: boolean\n readonly metadata?: Readonly<Record<string, string>>\n readonly user?: string\n readonly safetyIdentifier?: string\n readonly promptCacheKey?: string\n readonly truncation?: \"auto\" | \"disabled\"\n /**\n * Vendor-specific body fields merged into the request as-is.\n *\n * The Responses dialect is spoken by gateways as well as by OpenAI, and a\n * gateway carries its own parameters — Requesty's `{ requesty: { auto_cache:\n * true } }`, for example. Modelling each one would tie this provider to a\n * catalogue of gateways it cannot see; passing them through keeps the typed\n * surface OpenAI's and the untyped surface open.\n *\n * Merged over the built body, so a field named here wins over the one this\n * provider would have sent. Per-request keys win over the layer's\n * {@link Config.extraBody}.\n */\n readonly extraBody?: Readonly<Record<string, unknown>>\n /**\n * Free-form JSON output: model emits valid JSON without schema\n * constraints. Mutually exclusive with `structured` (schema wins if\n * both are set).\n */\n readonly responseFormat?: { readonly type: \"json_object\" }\n /**\n * Answer length / level-of-detail hint. GPT-5+ models honour it;\n * earlier models ignore.\n */\n readonly verbosity?: \"low\" | \"medium\" | \"high\"\n}\n\nexport type ResponsesService = {\n /**\n * Stream the provider's native event vocabulary (post-SSE-decode).\n * Use this when you need full vendor fidelity. For provider-portable\n * code, use `streamTurn` instead.\n */\n readonly streamNative: (\n request: ResponsesRequest,\n ) => Stream.Stream<ProviderEvent, AiError.AiError>\n /**\n * Stream canonical `TurnEvent`s. Implemented as\n * `streamNative |> toCanonical`.\n */\n readonly streamTurn: (request: ResponsesRequest) => Stream.Stream<TurnEvent, AiError.AiError>\n /**\n * Drain a single turn and return the assembled `Turn`. Derived from\n * `streamTurn` — Responses doesn't expose a native non-streaming\n * complete endpoint we'd want to take advantage of.\n */\n readonly turn: (request: ResponsesRequest) => Effect.Effect<Turn, AiError.AiError>\n /**\n * Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.\n * Exposed for cases where consumers want to compose with `streamNative`\n * directly (e.g. tap into natives + still get canonical downstream).\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 Responses-specific\n * options (`reasoning.effort`, `store`, `previousResponseId`) at the call\n * site. Yield the generic `LanguageModel` tag for provider-portable code -\n * both are registered by `layer`.\n */\nexport class Responses extends Context.Service<Responses, ResponsesService>()(\n \"@betalyra/effect-uai/providers/responses/Responses\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n readonly region?: OpenAiRegion\n /**\n * Vendor-specific body fields sent on every request from this layer — the\n * place for a gateway's standing configuration, so call sites do not repeat\n * it. Overridden per request by {@link ResponsesRequest.extraBody}.\n */\n readonly extraBody?: Readonly<Record<string, unknown>>\n}\n\n// ---------------------------------------------------------------------------\n// Request body\n// ---------------------------------------------------------------------------\n\nconst jsonSchemaFormat = (\n format: StructuredFormat.StructuredFormat<unknown>,\n): Record<string, unknown> => ({\n type: \"json_schema\",\n name: format.name,\n schema: format.schema[\"~standard\"].jsonSchema.input({ target: \"draft-2020-12\" }),\n ...(format.description !== undefined && { description: format.description }),\n ...(format.strict !== undefined && { strict: format.strict }),\n})\n\nconst buildText = (request: ResponsesRequest): Record<string, unknown> | undefined => {\n const format =\n request.structured !== undefined ? jsonSchemaFormat(request.structured) : request.responseFormat\n const text: Record<string, unknown> = {}\n if (format !== undefined) text.format = format\n if (request.verbosity !== undefined) text.verbosity = request.verbosity\n return Object.keys(text).length === 0 ? undefined : text\n}\n\nconst buildBody = (\n request: ResponsesRequest,\n providerToolWire: ReadonlyArray<Record<string, unknown>>,\n cfg: Config,\n): Record<string, unknown> => {\n const text = buildText(request)\n const functionTools = descriptorsOf(request.tools).map((t) => ({\n type: \"function\",\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n ...(t.strict !== undefined && { strict: t.strict }),\n }))\n const tools = [...functionTools, ...providerToolWire]\n return {\n model: request.model,\n input: itemsToInput(request.history),\n stream: true,\n ...(tools.length > 0 && { tools }),\n ...(request.toolChoice !== undefined && { tool_choice: request.toolChoice }),\n ...(request.temperature !== undefined && { temperature: request.temperature }),\n ...(request.maxOutputTokens !== undefined && {\n max_output_tokens: request.maxOutputTokens,\n }),\n ...(request.reasoning !== undefined && { reasoning: request.reasoning }),\n ...(request.store !== undefined && { store: request.store }),\n ...(request.previousResponseId !== undefined && {\n previous_response_id: request.previousResponseId,\n }),\n ...(request.instructions !== undefined && { instructions: request.instructions }),\n ...(request.topP !== undefined && { top_p: request.topP }),\n ...(request.parallelToolCalls !== undefined && {\n parallel_tool_calls: request.parallelToolCalls,\n }),\n ...(request.metadata !== undefined && { metadata: request.metadata }),\n ...(request.user !== undefined && { user: request.user }),\n ...(request.safetyIdentifier !== undefined && {\n safety_identifier: request.safetyIdentifier,\n }),\n ...(request.promptCacheKey !== undefined && {\n prompt_cache_key: request.promptCacheKey,\n }),\n ...(request.truncation !== undefined && { truncation: request.truncation }),\n ...(text !== undefined && { text }),\n ...cfg.extraBody,\n ...request.extraBody,\n }\n}\n\n// ---------------------------------------------------------------------------\n// SSE event → provider event\n// ---------------------------------------------------------------------------\n\nconst decodeKnown = Schema.decodeUnknownEffect(KnownProviderEvent)\n\nconst makeUnknown = (raw: unknown): ProviderEvent => ({ type: \"_unknown\", raw })\n\n/**\n * Lift events that carry a terminal failure signal (`error`,\n * `response.failed`) to a typed `AiError`. Other events are not failures\n * and produce `Option.none`.\n */\nconst eventToError = Match.type<ProviderEvent>().pipe(\n Match.discriminators(\"type\")({\n error: (e): AiError.AiError => {\n const detail = e.error\n const message = detail?.message ?? e.message\n const code = detail?.code ?? e.code\n const param = detail?.param\n // A top-level SSE `error` is usually a request rejection (bad model,\n // missing access, malformed body): non-retryable, so `InvalidRequest`.\n return detail?.type === \"invalid_request_error\"\n ? new AiError.InvalidRequest({\n provider: \"responses\",\n ...(param != null && { param }),\n raw: e,\n })\n : new AiError.GenerationFailed({\n provider: \"responses\",\n ...(code != null && { code }),\n ...(message != null && { message }),\n raw: e,\n })\n },\n \"response.failed\": (e): AiError.AiError => {\n const code = e.response.error?.code\n const message = e.response.error?.message\n return new AiError.GenerationFailed({\n provider: \"responses\",\n ...(code !== undefined && code !== null && { code }),\n ...(message !== undefined && message !== null && { message }),\n raw: e,\n })\n },\n }),\n Match.option,\n)\n\nconst parseJsonUnknown = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))\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// Service implementation\n// ---------------------------------------------------------------------------\n\nexport const httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"responses\"\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 >= 500) return new AiError.Unavailable({ provider, status, raw })\n return new AiError.InvalidRequest({ provider, raw })\n}\n\n/**\n * Decode an already-executed `/responses` HTTP response into the native\n * `ProviderEvent` stream: fail on a >=400 status, else SSE-decode the body and\n * lift terminal `error` / `response.failed` events to a typed `AiError`. Shared\n * by the streaming turn path (POST) and the deep-research resume stream (GET\n * `?stream=true`).\n */\nexport const providerEventsOfResponse = (\n response: HttpClientResponse.HttpClientResponse,\n): Stream.Stream<ProviderEvent, AiError.AiError> =>\n Stream.unwrap(\n Effect.gen(function* () {\n if (response.status >= 400) {\n const body = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, body))\n }\n return response.stream.pipe(\n Stream.mapError((cause) => new AiError.Unavailable({ provider: \"responses\", raw: cause })),\n SSE.fromBytes,\n Stream.mapEffect(sseEventToProviderEvent),\n Stream.flatMap((event) =>\n Option.match(eventToError(event), {\n onNone: () => Stream.succeed(event),\n onSome: Stream.fail,\n }),\n ),\n )\n }),\n )\n\nconst buildNativeStream = (cfg: Config) => {\n const url = `${resolveHost(cfg)}/responses`\n return (\n request: ResponsesRequest,\n ): Stream.Stream<ProviderEvent, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const providerToolWire = yield* Result.match(\n renderProviderTools(providerToolsOf(request.tools)),\n {\n onFailure: (e) =>\n Effect.fail(\n new AiError.Unsupported({\n provider: \"responses\",\n capability: e.capability,\n reason: e.reason,\n }),\n ),\n onSuccess: (wire) => Effect.succeed(wire),\n },\n )\n const httpRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.bodyJsonUnsafe(buildBody(request, providerToolWire, cfg)),\n HttpClientRequest.accept(\"text/event-stream\"),\n )\n const response = yield* client\n .execute(httpRequest)\n .pipe(\n Effect.mapError(\n (cause) => new AiError.Unavailable({ provider: \"responses\", raw: cause }),\n ),\n )\n return providerEventsOfResponse(response)\n }),\n )\n}\n\n/**\n * Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.\n * Threads a fresh `CallIdLookup` per stream so `function_call_arguments.delta`\n * can be tagged with the right `call_id`.\n */\nexport const toCanonical = <E, R>(\n s: Stream.Stream<ProviderEvent, E, R>,\n): Stream.Stream<TurnEvent, E, R> =>\n Stream.unwrap(\n Effect.sync(() => {\n const lookup = makeCallIdLookup()\n return s.pipe(Stream.flatMap((event) => Stream.fromIterable(eventToDeltas(event, lookup))))\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `ResponsesService` value. For Layer-based setup, prefer `layer`.\n */\nexport const make = (cfg: Config): Effect.Effect<ResponsesService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => {\n const streamNative: ResponsesService[\"streamNative\"] = (request) =>\n buildNativeStream(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client))\n const streamTurn: ResponsesService[\"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 `Responses` 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 * `ResponsesRequest` surface.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<Responses | LanguageModel, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(Responses, make(cfg))\n const generic = Layer.effect(\n LanguageModel,\n Effect.map(make(cfg), (s): LanguageModelService => ({\n streamTurn: (request) => s.streamTurn(request as ResponsesRequest),\n turn: (request) => s.turn(request as ResponsesRequest),\n })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Responses.mjs","names":[],"sources":["../src/Responses.ts"],"sourcesContent":["import { Context, Effect, Layer, Match, Option, Redacted, Result, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest, HttpClientResponse } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport * as Capabilities from \"@effect-uai/core/Capabilities\"\nimport * as StructuredFormat from \"@effect-uai/core/StructuredFormat\"\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 { itemsToInput } from \"./codec.js\"\nimport { renderProviderTools } from \"./ResponsesTools.js\"\n\nexport {\n codeInterpreterTool,\n fileSearchTool,\n webSearchTool,\n type WebSearchOptions,\n type WebSearchFilters,\n type UserLocation,\n} from \"./ResponsesTools.js\"\nimport type { OpenAIModel } from \"./models.js\"\nimport { type OpenAiRegion, resolveHost } from \"./region.js\"\nimport {\n KnownProviderEvent,\n ProviderEvent,\n eventToDeltas,\n makeCallIdLookup,\n} from \"./streamEvents.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport type ResponsesRequest = Omit<CommonRequest, \"model\"> & {\n /**\n * Narrows `CommonRequest.model` (`string`) to the typed `OpenAIModel`\n * literal union for autocomplete.\n */\n readonly model: OpenAIModel\n readonly reasoning?: { readonly effort: \"low\" | \"medium\" | \"high\" }\n readonly store?: boolean\n readonly previousResponseId?: string\n readonly instructions?: string\n readonly parallelToolCalls?: boolean\n readonly metadata?: Readonly<Record<string, string>>\n readonly user?: string\n readonly safetyIdentifier?: string\n readonly promptCacheKey?: string\n readonly truncation?: \"auto\" | \"disabled\"\n /**\n * Vendor-specific body fields merged into the request as-is.\n *\n * The Responses dialect is spoken by gateways as well as by OpenAI, and a\n * gateway carries its own parameters — Requesty's `{ requesty: { auto_cache:\n * true } }`, for example. Modelling each one would tie this provider to a\n * catalogue of gateways it cannot see; passing them through keeps the typed\n * surface OpenAI's and the untyped surface open.\n *\n * Merged over the built body, so a field named here wins over the one this\n * provider would have sent. Per-request keys win over the layer's\n * {@link Config.extraBody}.\n */\n readonly extraBody?: Readonly<Record<string, unknown>>\n /**\n * Free-form JSON output: model emits valid JSON without schema\n * constraints. Mutually exclusive with `structured` (schema wins if\n * both are set).\n */\n readonly responseFormat?: { readonly type: \"json_object\" }\n /**\n * Answer length / level-of-detail hint. GPT-5+ models honour it;\n * earlier models ignore.\n */\n readonly verbosity?: \"low\" | \"medium\" | \"high\"\n}\n\nexport type ResponsesService = {\n /**\n * Stream the provider's native event vocabulary (post-SSE-decode).\n * Use this when you need full vendor fidelity. For provider-portable\n * code, use `streamTurn` instead.\n */\n readonly streamNative: (\n request: ResponsesRequest,\n ) => Stream.Stream<ProviderEvent, AiError.AiError>\n /**\n * Stream canonical `TurnEvent`s. Implemented as\n * `streamNative |> toCanonical`.\n */\n readonly streamTurn: (request: ResponsesRequest) => Stream.Stream<TurnEvent, AiError.AiError>\n /**\n * Drain a single turn and return the assembled `Turn`. Derived from\n * `streamTurn` — Responses doesn't expose a native non-streaming\n * complete endpoint we'd want to take advantage of.\n */\n readonly turn: (request: ResponsesRequest) => Effect.Effect<Turn, AiError.AiError>\n /**\n * Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.\n * Exposed for cases where consumers want to compose with `streamNative`\n * directly (e.g. tap into natives + still get canonical downstream).\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 Responses-specific\n * options (`reasoning.effort`, `store`, `previousResponseId`) at the call\n * site. Yield the generic `LanguageModel` tag for provider-portable code -\n * both are registered by `layer`.\n */\nexport class Responses extends Context.Service<Responses, ResponsesService>()(\n \"@betalyra/effect-uai/providers/responses/Responses\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n readonly region?: OpenAiRegion\n /**\n * Vendor-specific body fields sent on every request from this layer — the\n * place for a gateway's standing configuration, so call sites do not repeat\n * it. Overridden per request by {@link ResponsesRequest.extraBody}.\n */\n readonly extraBody?: Readonly<Record<string, unknown>>\n}\n\n// ---------------------------------------------------------------------------\n// Request body\n// ---------------------------------------------------------------------------\n\nconst jsonSchemaFormat = (\n format: StructuredFormat.StructuredFormat<unknown>,\n): Record<string, unknown> => ({\n type: \"json_schema\",\n name: format.name,\n schema: format.schema[\"~standard\"].jsonSchema.input({ target: \"draft-2020-12\" }),\n ...(format.description !== undefined && { description: format.description }),\n ...(format.strict !== undefined && { strict: format.strict }),\n})\n\nconst buildText = (request: ResponsesRequest): Record<string, unknown> | undefined => {\n const format =\n request.structured !== undefined ? jsonSchemaFormat(request.structured) : request.responseFormat\n const text: Record<string, unknown> = {}\n if (format !== undefined) text.format = format\n if (request.verbosity !== undefined) text.verbosity = request.verbosity\n return Object.keys(text).length === 0 ? undefined : text\n}\n\nconst buildBody = (\n request: ResponsesRequest,\n providerToolWire: ReadonlyArray<Record<string, unknown>>,\n cfg: Config,\n): Record<string, unknown> => {\n const text = buildText(request)\n const functionTools = descriptorsOf(request.tools).map((t) => ({\n type: \"function\",\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n ...(t.strict !== undefined && { strict: t.strict }),\n }))\n const tools = [...functionTools, ...providerToolWire]\n return {\n model: request.model,\n input: itemsToInput(request.history),\n stream: true,\n ...(tools.length > 0 && { tools }),\n ...(request.toolChoice !== undefined && { tool_choice: request.toolChoice }),\n ...(request.temperature !== undefined && { temperature: request.temperature }),\n ...(request.maxOutputTokens !== undefined && {\n max_output_tokens: request.maxOutputTokens,\n }),\n ...(request.reasoning !== undefined && { reasoning: request.reasoning }),\n ...(request.store !== undefined && { store: request.store }),\n ...(request.previousResponseId !== undefined && {\n previous_response_id: request.previousResponseId,\n }),\n ...(request.instructions !== undefined && { instructions: request.instructions }),\n ...(request.topP !== undefined && { top_p: request.topP }),\n ...(request.parallelToolCalls !== undefined && {\n parallel_tool_calls: request.parallelToolCalls,\n }),\n ...(request.metadata !== undefined && { metadata: request.metadata }),\n ...(request.user !== undefined && { user: request.user }),\n ...(request.safetyIdentifier !== undefined && {\n safety_identifier: request.safetyIdentifier,\n }),\n ...(request.promptCacheKey !== undefined && {\n prompt_cache_key: request.promptCacheKey,\n }),\n ...(request.truncation !== undefined && { truncation: request.truncation }),\n ...(text !== undefined && { text }),\n ...cfg.extraBody,\n ...request.extraBody,\n }\n}\n\n// ---------------------------------------------------------------------------\n// SSE event → provider event\n// ---------------------------------------------------------------------------\n\nconst decodeKnown = Schema.decodeUnknownEffect(KnownProviderEvent)\n\nconst makeUnknown = (raw: unknown): ProviderEvent => ({ type: \"_unknown\", raw })\n\n/**\n * Lift events that carry a terminal failure signal (`error`,\n * `response.failed`) to a typed `AiError`. Other events are not failures\n * and produce `Option.none`.\n */\nconst eventToError = Match.type<ProviderEvent>().pipe(\n Match.discriminators(\"type\")({\n error: (e): AiError.AiError => {\n const detail = e.error\n const message = detail?.message ?? e.message\n const code = detail?.code ?? e.code\n const param = detail?.param\n // A top-level SSE `error` is usually a request rejection (bad model,\n // missing access, malformed body): non-retryable, so `InvalidRequest`.\n return detail?.type === \"invalid_request_error\"\n ? new AiError.InvalidRequest({\n provider: \"responses\",\n ...(param != null && { param }),\n raw: e,\n })\n : new AiError.GenerationFailed({\n provider: \"responses\",\n ...(code != null && { code }),\n ...(message != null && { message }),\n raw: e,\n })\n },\n \"response.failed\": (e): AiError.AiError => {\n const code = e.response.error?.code\n const message = e.response.error?.message\n return new AiError.GenerationFailed({\n provider: \"responses\",\n ...(code !== undefined && code !== null && { code }),\n ...(message !== undefined && message !== null && { message }),\n raw: e,\n })\n },\n }),\n Match.option,\n)\n\nconst parseJsonUnknown = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))\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// Service implementation\n// ---------------------------------------------------------------------------\n\nexport const httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"responses\"\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 >= 500) return new AiError.Unavailable({ provider, status, raw })\n return new AiError.InvalidRequest({ provider, raw })\n}\n\n/**\n * Decode an already-executed `/responses` HTTP response into the native\n * `ProviderEvent` stream: fail on a >=400 status, else SSE-decode the body and\n * lift terminal `error` / `response.failed` events to a typed `AiError`. Shared\n * by the streaming turn path (POST) and the deep-research resume stream (GET\n * `?stream=true`).\n */\nexport const providerEventsOfResponse = (\n response: HttpClientResponse.HttpClientResponse,\n): Stream.Stream<ProviderEvent, AiError.AiError> =>\n Stream.unwrap(\n Effect.gen(function* () {\n if (response.status >= 400) {\n const body = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, body))\n }\n return response.stream.pipe(\n Stream.mapError((cause) => new AiError.Unavailable({ provider: \"responses\", raw: cause })),\n SSE.fromBytes,\n Stream.mapEffect(sseEventToProviderEvent),\n Stream.flatMap((event) =>\n Option.match(eventToError(event), {\n onNone: () => Stream.succeed(event),\n onSome: Stream.fail,\n }),\n ),\n )\n }),\n )\n\nconst buildNativeStream = (cfg: Config) => {\n const url = `${resolveHost(cfg)}/responses`\n return (\n request: ResponsesRequest,\n ): Stream.Stream<ProviderEvent, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const providerToolWire = yield* Result.match(\n renderProviderTools(providerToolsOf(request.tools)),\n {\n onFailure: (e) =>\n Effect.fail(\n new AiError.Unsupported({\n provider: \"responses\",\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: \"responses\",\n capability: \"output_image\",\n reason:\n \"Assistant messages carry no image on this wire; images here come from the `image_generation` tool. Use `Turn.imagesAsInput` to resend one as user content.\",\n })\n const httpRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.bodyJsonUnsafe(buildBody(request, providerToolWire, cfg)),\n HttpClientRequest.accept(\"text/event-stream\"),\n )\n const response = yield* client\n .execute(httpRequest)\n .pipe(\n Effect.mapError(\n (cause) => new AiError.Unavailable({ provider: \"responses\", raw: cause }),\n ),\n )\n return providerEventsOfResponse(response)\n }),\n )\n}\n\n/**\n * Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.\n * Threads a fresh `CallIdLookup` per stream so `function_call_arguments.delta`\n * can be tagged with the right `call_id`.\n */\nexport const toCanonical = <E, R>(\n s: Stream.Stream<ProviderEvent, E, R>,\n): Stream.Stream<TurnEvent, E, R> =>\n Stream.unwrap(\n Effect.sync(() => {\n const lookup = makeCallIdLookup()\n return s.pipe(Stream.flatMap((event) => Stream.fromIterable(eventToDeltas(event, lookup))))\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `ResponsesService` value. For Layer-based setup, prefer `layer`.\n */\nexport const make = (cfg: Config): Effect.Effect<ResponsesService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => {\n const streamNative: ResponsesService[\"streamNative\"] = (request) =>\n buildNativeStream(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client))\n const streamTurn: ResponsesService[\"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 `Responses` 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 * `ResponsesRequest` surface.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<Responses | LanguageModel, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(Responses, make(cfg))\n const generic = Layer.effect(\n LanguageModel,\n Effect.map(make(cfg), (s): LanguageModelService => ({\n streamTurn: (request) => s.streamTurn(request as ResponsesRequest),\n turn: (request) => s.turn(request as ResponsesRequest),\n })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;cAqCa,gBAAc,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;cAkEd,gBAAc,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAQf,wBAAwB,eAAe;cA6BtC,uBAAqB,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KActB,+BAA+B,sBAAsB;;cA8FpD,eAAY,OAChB,cAAc,iBACpB,cAAc;cAiBJ,iBAAc,MAAU,mBAAiB,cAAc;cAwEvD,oBAAiB,SAAa,0BAAwB"}
1
+ {"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;cAqCa,gBAAc,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;cAkEd,gBAAc,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAQf,wBAAwB,eAAe;cA6BtC,uBAAqB,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KActB,+BAA+B,sBAAsB;;cAoGpD,eAAY,OAChB,cAAc,iBACpB,cAAc;cAiBJ,iBAAc,MAAU,mBAAiB,cAAc;cAwEvD,oBAAiB,SAAa,0BAAwB"}
package/dist/codec.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { Encoding, Match, Option, Result, Schema, pipe } from "effect";
2
+ import { Array, Encoding, Match, Option, Result, Schema, pipe } from "effect";
3
3
  //#region src/codec.ts
4
4
  var codec_exports = /* @__PURE__ */ __exportAll({
5
5
  WireAnnotation: () => WireAnnotation,
@@ -160,6 +160,7 @@ const contentBlockToInput = Match.type().pipe(Match.discriminatorsExhaustive("ty
160
160
  type: "output_text",
161
161
  text: b.text
162
162
  }),
163
+ output_image: () => void 0,
163
164
  refusal: (b) => ({
164
165
  type: "refusal",
165
166
  refusal: b.text
@@ -169,7 +170,10 @@ const itemToInput = (item) => passthrough(item) ?? Match.value(item).pipe(Match.
169
170
  message: (m) => ({
170
171
  type: "message",
171
172
  role: m.role,
172
- content: m.content.map(contentBlockToInput)
173
+ content: Array.filterMap(m.content, (b) => {
174
+ const encoded = contentBlockToInput(b);
175
+ return encoded === void 0 ? Result.failVoid : Result.succeed(encoded);
176
+ })
173
177
  }),
174
178
  function_call: (f) => ({
175
179
  type: "function_call",
@@ -1 +1 @@
1
- {"version":3,"file":"codec.mjs","names":[],"sources":["../src/codec.ts"],"sourcesContent":["import { Encoding, Match, Option, Result, Schema, pipe } from \"effect\"\nimport type { ContentBlock, InputImage, HistoryItem } from \"@effect-uai/core/Items\"\nimport type { Turn } from \"@effect-uai/core/Turn\"\n\n// ---------------------------------------------------------------------------\n// Wire schemas - minimal subset of the Responses API output we consume.\n// Reference: https://www.openresponses.org/specification\n// ---------------------------------------------------------------------------\n\nconst WireUrlCitation = Schema.Struct({\n type: Schema.Literal(\"url_citation\"),\n url: Schema.String,\n start_index: Schema.Number,\n end_index: Schema.Number,\n title: Schema.String,\n})\n\nconst WireFileCitation = Schema.Struct({\n type: Schema.Literal(\"file_citation\"),\n file_id: Schema.String,\n index: Schema.Number,\n})\n\nconst WireContainerFileCitation = Schema.Struct({\n type: Schema.Literal(\"container_file_citation\"),\n container_id: Schema.String,\n file_id: Schema.String,\n start_index: Schema.Number,\n end_index: Schema.Number,\n})\n\nconst WireFilePath = Schema.Struct({\n type: Schema.Literal(\"file_path\"),\n file_id: Schema.String,\n index: Schema.Number,\n})\n\nexport const WireAnnotation = Schema.Union([\n WireUrlCitation,\n WireFileCitation,\n WireContainerFileCitation,\n WireFilePath,\n])\n\nconst WireOutputTextContent = Schema.Struct({\n type: Schema.Literal(\"output_text\"),\n text: Schema.String,\n annotations: Schema.optional(Schema.NullOr(Schema.Array(WireAnnotation))),\n})\n\nconst WireRefusalContent = Schema.Struct({\n type: Schema.Literal(\"refusal\"),\n refusal: Schema.String,\n})\n\nconst WireMessageContent = Schema.Union([WireOutputTextContent, WireRefusalContent])\n\nconst WireSummaryText = Schema.Struct({\n type: Schema.Literal(\"summary_text\"),\n text: Schema.String,\n})\n\nconst WireMessage = Schema.Struct({\n type: Schema.Literal(\"message\"),\n id: Schema.optional(Schema.String),\n role: Schema.Literal(\"assistant\"),\n content: Schema.Array(WireMessageContent),\n})\n\nconst WireFunctionCall = Schema.Struct({\n type: Schema.Literal(\"function_call\"),\n id: Schema.optional(Schema.String),\n call_id: Schema.String,\n name: Schema.String,\n arguments: Schema.String,\n})\n\nconst WireReasoning = Schema.Struct({\n type: Schema.Literal(\"reasoning\"),\n id: Schema.optional(Schema.String),\n summary: Schema.optional(Schema.Array(WireSummaryText)),\n encrypted_content: Schema.optional(Schema.String),\n})\n\n// Provider-hosted tool call items (web search, code interpreter, file search).\n// The provider executes them, not us — we model them only so a grounded\n// `response.completed` still decodes and assembles a Turn; they are dropped\n// from the turn's items in Phase 1. Surfacing them is a follow-up.\nconst WireWebSearchCall = Schema.Struct({\n type: Schema.Literal(\"web_search_call\"),\n id: Schema.optional(Schema.String),\n})\n\nconst WireCodeInterpreterCall = Schema.Struct({\n type: Schema.Literal(\"code_interpreter_call\"),\n id: Schema.optional(Schema.String),\n})\n\nconst WireFileSearchCall = Schema.Struct({\n type: Schema.Literal(\"file_search_call\"),\n id: Schema.optional(Schema.String),\n})\n\nexport const WireOutputItem = Schema.Union([\n WireMessage,\n WireFunctionCall,\n WireReasoning,\n WireWebSearchCall,\n WireCodeInterpreterCall,\n WireFileSearchCall,\n])\nexport type WireOutputItem = typeof WireOutputItem.Type\n\nconst WireInputTokensDetails = Schema.Struct({\n cached_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n})\n\nconst WireOutputTokensDetails = Schema.Struct({\n reasoning_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n})\n\nconst WireUsage = Schema.Struct({\n input_tokens: Schema.optional(Schema.Number),\n output_tokens: Schema.optional(Schema.Number),\n total_tokens: Schema.optional(Schema.Number),\n input_tokens_details: Schema.optional(Schema.NullOr(WireInputTokensDetails)),\n output_tokens_details: Schema.optional(Schema.NullOr(WireOutputTokensDetails)),\n})\n\nconst WireResponseError = Schema.Struct({\n code: Schema.optional(Schema.NullOr(Schema.String)),\n message: Schema.optional(Schema.NullOr(Schema.String)),\n})\n\n// Many Responses-API fields are emitted as explicit `null` rather than\n// missing - `Schema.optional` alone (T | undefined) doesn't cover that, so\n// we lift each through `NullOr` first. Used by `response.completed`,\n// `response.incomplete`, and `response.failed` events; not every field is\n// populated on every event (e.g. `error` only on failed, `incomplete_details`\n// only on incomplete).\nexport const WireResponseCompleted = Schema.Struct({\n id: Schema.optional(Schema.NullOr(Schema.String)),\n status: Schema.optional(Schema.NullOr(Schema.String)),\n output: Schema.optional(Schema.NullOr(Schema.Array(WireOutputItem))),\n usage: Schema.optional(Schema.NullOr(WireUsage)),\n incomplete_details: Schema.optional(\n Schema.NullOr(\n Schema.Struct({\n reason: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n ),\n ),\n error: Schema.optional(Schema.NullOr(WireResponseError)),\n})\nexport type WireResponseCompleted = typeof WireResponseCompleted.Type\n\n// ---------------------------------------------------------------------------\n// History → request body input\n// ---------------------------------------------------------------------------\n\n/**\n * The item shapes we stash for round-tripping. Narrower than\n * `WireOutputItem`: the provider-hosted call variants are dropped from the\n * turn, so they are never stored. Composed from the internal `Wire*` structs,\n * which keeps the wire model out of the package's public surface.\n */\nconst StoredItem = Schema.Union([WireMessage, WireFunctionCall, WireReasoning])\ntype StoredItem = typeof StoredItem.Type\n\nconst ProviderDataResponses = Schema.Struct({ responses: StoredItem })\nconst decodeResponsesData = Schema.decodeUnknownResult(ProviderDataResponses)\n\n/**\n * If an item carries `providerData.responses` from a prior turn on this API,\n * re-emit it verbatim - preserves `encrypted_content` and item ids, which a\n * fresh encode from our `HistoryItem` shape cannot reconstruct.\n *\n * Namespaced because `providerData` is a shared slot: an item routed through\n * another provider first (dynamic fallback) carries that provider's key, and\n * re-emitting the whole object would send that provider's data as the wire\n * item and drop the real content. Anything that fails to decode is not ours,\n * so we fall back to encoding the item normally.\n */\nconst passthrough = (item: HistoryItem): StoredItem | undefined =>\n pipe(\n decodeResponsesData(item.providerData),\n Result.match({\n onSuccess: (data) => data.responses,\n onFailure: (): StoredItem | undefined => undefined,\n }),\n )\n\n/**\n * OpenAI's `input_image` content block carries a single `image_url` field;\n * inline bytes get encoded as a `data:` URI. Either form (URL or data URI)\n * is fine - the model just dereferences `image_url`.\n */\nconst imageSourceToUrl = Match.type<InputImage[\"source\"]>().pipe(\n Match.tag(\"url\", (s) => s.url),\n Match.tag(\"base64\", (s) => `data:${s.mimeType};base64,${s.base64}`),\n Match.tag(\"bytes\", (s) => `data:${s.mimeType};base64,${Encoding.encodeBase64(s.bytes)}`),\n Match.exhaustive,\n)\n\nconst contentBlockToInput = Match.type<ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => ({ type: \"input_text\", text: b.text }),\n input_image: (b) => ({\n type: \"input_image\",\n image_url: imageSourceToUrl(b.source),\n }),\n output_text: (b) => ({ type: \"output_text\", text: b.text }),\n refusal: (b) => ({ type: \"refusal\", refusal: b.text }),\n }),\n)\n\nconst itemToInput = (item: HistoryItem): Record<string, unknown> =>\n passthrough(item) ??\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m) => ({\n type: \"message\",\n role: m.role,\n content: m.content.map(contentBlockToInput),\n }),\n function_call: (f) => ({\n type: \"function_call\",\n call_id: f.call_id,\n name: f.name,\n arguments: f.arguments,\n }),\n function_call_output: (o) => ({\n type: \"function_call_output\",\n call_id: o.call_id,\n output: o.output,\n }),\n reasoning: (r) => ({\n type: \"reasoning\",\n ...(r.id !== undefined && { id: r.id }),\n ...(r.summary !== undefined && {\n summary: [{ type: \"summary_text\", text: r.summary }],\n }),\n ...(r.signature !== undefined && { encrypted_content: r.signature }),\n }),\n }),\n )\n\n/** Convert our `HistoryItem[]` history into the Responses API `input` array. */\nexport const itemsToInput = (\n items: ReadonlyArray<HistoryItem>,\n): ReadonlyArray<Record<string, unknown>> => items.map(itemToInput)\n\n// ---------------------------------------------------------------------------\n// Wire output items → our Items\n// ---------------------------------------------------------------------------\n\nconst wireMessageContentToBlock = Match.type<typeof WireMessageContent.Type>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n output_text: (c): ContentBlock => ({\n type: \"output_text\",\n text: c.text,\n ...(c.annotations !== undefined && c.annotations !== null && { annotations: c.annotations }),\n }),\n refusal: (c): ContentBlock => ({ type: \"refusal\", text: c.refusal }),\n }),\n)\n\nexport const wireItemToItem = (wire: WireOutputItem): ReadonlyArray<HistoryItem> =>\n Match.value(wire).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m): ReadonlyArray<HistoryItem> => [\n {\n type: \"message\" as const,\n role: m.role,\n content: m.content.map(wireMessageContentToBlock),\n providerData: { responses: m },\n },\n ],\n function_call: (f): ReadonlyArray<HistoryItem> => [\n {\n type: \"function_call\" as const,\n call_id: f.call_id,\n name: f.name,\n arguments: f.arguments,\n providerData: { responses: f },\n },\n ],\n reasoning: (r): ReadonlyArray<HistoryItem> => [\n {\n type: \"reasoning\" as const,\n ...(r.id !== undefined && { id: r.id }),\n ...(r.summary !== undefined && {\n summary: r.summary.map((s) => s.text).join(\"\\n\"),\n }),\n ...(r.encrypted_content !== undefined && { signature: r.encrypted_content }),\n providerData: { responses: r },\n },\n ],\n // Hosted-tool calls are provider-executed; drop from the turn (Phase 1).\n web_search_call: (): ReadonlyArray<HistoryItem> => [],\n code_interpreter_call: (): ReadonlyArray<HistoryItem> => [],\n file_search_call: (): ReadonlyArray<HistoryItem> => [],\n }),\n )\n\n// ---------------------------------------------------------------------------\n// response.completed → Turn\n// ---------------------------------------------------------------------------\n\nconst reasonToStop = Match.type<string>().pipe(\n Match.when(\"max_output_tokens\", () => \"max_tokens\" as const),\n Match.when(\"refusal\", () => \"refusal\" as const),\n Match.when(\"content_filter\", () => \"content_filter\" as const),\n Match.when(\"max_tool_calls\", () => \"max_tool_calls\" as const),\n Match.option,\n)\n\nconst hasRefusalContent = (payload: WireResponseCompleted): boolean =>\n (payload.output ?? []).some(\n (i) => i.type === \"message\" && i.content.some((c) => c.type === \"refusal\"),\n )\n\nconst stopReasonFromCompleted = (payload: WireResponseCompleted): Turn[\"stop_reason\"] =>\n Option.match(reasonToStop(payload.incomplete_details?.reason ?? \"\"), {\n onSome: (s) => s,\n onNone: () =>\n hasRefusalContent(payload)\n ? (\"refusal\" as const)\n : (payload.output ?? []).some((i) => i.type === \"function_call\")\n ? (\"tool_calls\" as const)\n : (\"stop\" as const),\n })\n\nconst cachedTokens = (payload: WireResponseCompleted): number | undefined =>\n payload.usage?.input_tokens_details?.cached_tokens ?? undefined\n\nconst reasoningTokens = (payload: WireResponseCompleted): number | undefined =>\n payload.usage?.output_tokens_details?.reasoning_tokens ?? undefined\n\nexport const turnFromCompleted = (payload: WireResponseCompleted): Turn => {\n const cached = cachedTokens(payload)\n const reasoning = reasoningTokens(payload)\n return {\n items: (payload.output ?? []).flatMap(wireItemToItem),\n usage: {\n ...(payload.usage?.input_tokens !== undefined && {\n input_tokens: payload.usage.input_tokens,\n }),\n ...(payload.usage?.output_tokens !== undefined && {\n output_tokens: payload.usage.output_tokens,\n }),\n ...(payload.usage?.total_tokens !== undefined && {\n total_tokens: payload.usage.total_tokens,\n }),\n ...(cached !== undefined && {\n input_tokens_details: { cached_tokens: cached },\n }),\n ...(reasoning !== undefined && {\n output_tokens_details: { reasoning_tokens: reasoning },\n }),\n },\n stop_reason: stopReasonFromCompleted(payload),\n }\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, Result, Schema, pipe } from \"effect\"\nimport type { ContentBlock, InputImage, HistoryItem } from \"@effect-uai/core/Items\"\nimport type { Turn } from \"@effect-uai/core/Turn\"\n\n// ---------------------------------------------------------------------------\n// Wire schemas - minimal subset of the Responses API output we consume.\n// Reference: https://www.openresponses.org/specification\n// ---------------------------------------------------------------------------\n\nconst WireUrlCitation = Schema.Struct({\n type: Schema.Literal(\"url_citation\"),\n url: Schema.String,\n start_index: Schema.Number,\n end_index: Schema.Number,\n title: Schema.String,\n})\n\nconst WireFileCitation = Schema.Struct({\n type: Schema.Literal(\"file_citation\"),\n file_id: Schema.String,\n index: Schema.Number,\n})\n\nconst WireContainerFileCitation = Schema.Struct({\n type: Schema.Literal(\"container_file_citation\"),\n container_id: Schema.String,\n file_id: Schema.String,\n start_index: Schema.Number,\n end_index: Schema.Number,\n})\n\nconst WireFilePath = Schema.Struct({\n type: Schema.Literal(\"file_path\"),\n file_id: Schema.String,\n index: Schema.Number,\n})\n\nexport const WireAnnotation = Schema.Union([\n WireUrlCitation,\n WireFileCitation,\n WireContainerFileCitation,\n WireFilePath,\n])\n\nconst WireOutputTextContent = Schema.Struct({\n type: Schema.Literal(\"output_text\"),\n text: Schema.String,\n annotations: Schema.optional(Schema.NullOr(Schema.Array(WireAnnotation))),\n})\n\nconst WireRefusalContent = Schema.Struct({\n type: Schema.Literal(\"refusal\"),\n refusal: Schema.String,\n})\n\nconst WireMessageContent = Schema.Union([WireOutputTextContent, WireRefusalContent])\n\nconst WireSummaryText = Schema.Struct({\n type: Schema.Literal(\"summary_text\"),\n text: Schema.String,\n})\n\nconst WireMessage = Schema.Struct({\n type: Schema.Literal(\"message\"),\n id: Schema.optional(Schema.String),\n role: Schema.Literal(\"assistant\"),\n content: Schema.Array(WireMessageContent),\n})\n\nconst WireFunctionCall = Schema.Struct({\n type: Schema.Literal(\"function_call\"),\n id: Schema.optional(Schema.String),\n call_id: Schema.String,\n name: Schema.String,\n arguments: Schema.String,\n})\n\nconst WireReasoning = Schema.Struct({\n type: Schema.Literal(\"reasoning\"),\n id: Schema.optional(Schema.String),\n summary: Schema.optional(Schema.Array(WireSummaryText)),\n encrypted_content: Schema.optional(Schema.String),\n})\n\n// Provider-hosted tool call items (web search, code interpreter, file search).\n// The provider executes them, not us — we model them only so a grounded\n// `response.completed` still decodes and assembles a Turn; they are dropped\n// from the turn's items in Phase 1. Surfacing them is a follow-up.\nconst WireWebSearchCall = Schema.Struct({\n type: Schema.Literal(\"web_search_call\"),\n id: Schema.optional(Schema.String),\n})\n\nconst WireCodeInterpreterCall = Schema.Struct({\n type: Schema.Literal(\"code_interpreter_call\"),\n id: Schema.optional(Schema.String),\n})\n\nconst WireFileSearchCall = Schema.Struct({\n type: Schema.Literal(\"file_search_call\"),\n id: Schema.optional(Schema.String),\n})\n\nexport const WireOutputItem = Schema.Union([\n WireMessage,\n WireFunctionCall,\n WireReasoning,\n WireWebSearchCall,\n WireCodeInterpreterCall,\n WireFileSearchCall,\n])\nexport type WireOutputItem = typeof WireOutputItem.Type\n\nconst WireInputTokensDetails = Schema.Struct({\n cached_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n})\n\nconst WireOutputTokensDetails = Schema.Struct({\n reasoning_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n})\n\nconst WireUsage = Schema.Struct({\n input_tokens: Schema.optional(Schema.Number),\n output_tokens: Schema.optional(Schema.Number),\n total_tokens: Schema.optional(Schema.Number),\n input_tokens_details: Schema.optional(Schema.NullOr(WireInputTokensDetails)),\n output_tokens_details: Schema.optional(Schema.NullOr(WireOutputTokensDetails)),\n})\n\nconst WireResponseError = Schema.Struct({\n code: Schema.optional(Schema.NullOr(Schema.String)),\n message: Schema.optional(Schema.NullOr(Schema.String)),\n})\n\n// Many Responses-API fields are emitted as explicit `null` rather than\n// missing - `Schema.optional` alone (T | undefined) doesn't cover that, so\n// we lift each through `NullOr` first. Used by `response.completed`,\n// `response.incomplete`, and `response.failed` events; not every field is\n// populated on every event (e.g. `error` only on failed, `incomplete_details`\n// only on incomplete).\nexport const WireResponseCompleted = Schema.Struct({\n id: Schema.optional(Schema.NullOr(Schema.String)),\n status: Schema.optional(Schema.NullOr(Schema.String)),\n output: Schema.optional(Schema.NullOr(Schema.Array(WireOutputItem))),\n usage: Schema.optional(Schema.NullOr(WireUsage)),\n incomplete_details: Schema.optional(\n Schema.NullOr(\n Schema.Struct({\n reason: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n ),\n ),\n error: Schema.optional(Schema.NullOr(WireResponseError)),\n})\nexport type WireResponseCompleted = typeof WireResponseCompleted.Type\n\n// ---------------------------------------------------------------------------\n// History → request body input\n// ---------------------------------------------------------------------------\n\n/**\n * The item shapes we stash for round-tripping. Narrower than\n * `WireOutputItem`: the provider-hosted call variants are dropped from the\n * turn, so they are never stored. Composed from the internal `Wire*` structs,\n * which keeps the wire model out of the package's public surface.\n */\nconst StoredItem = Schema.Union([WireMessage, WireFunctionCall, WireReasoning])\ntype StoredItem = typeof StoredItem.Type\n\nconst ProviderDataResponses = Schema.Struct({ responses: StoredItem })\nconst decodeResponsesData = Schema.decodeUnknownResult(ProviderDataResponses)\n\n/**\n * If an item carries `providerData.responses` from a prior turn on this API,\n * re-emit it verbatim - preserves `encrypted_content` and item ids, which a\n * fresh encode from our `HistoryItem` shape cannot reconstruct.\n *\n * Namespaced because `providerData` is a shared slot: an item routed through\n * another provider first (dynamic fallback) carries that provider's key, and\n * re-emitting the whole object would send that provider's data as the wire\n * item and drop the real content. Anything that fails to decode is not ours,\n * so we fall back to encoding the item normally.\n */\nconst passthrough = (item: HistoryItem): StoredItem | undefined =>\n pipe(\n decodeResponsesData(item.providerData),\n Result.match({\n onSuccess: (data) => data.responses,\n onFailure: (): StoredItem | undefined => undefined,\n }),\n )\n\n/**\n * OpenAI's `input_image` content block carries a single `image_url` field;\n * inline bytes get encoded as a `data:` URI. Either form (URL or data URI)\n * is fine - the model just dereferences `image_url`.\n */\nconst imageSourceToUrl = Match.type<InputImage[\"source\"]>().pipe(\n Match.tag(\"url\", (s) => s.url),\n Match.tag(\"base64\", (s) => `data:${s.mimeType};base64,${s.base64}`),\n Match.tag(\"bytes\", (s) => `data:${s.mimeType};base64,${Encoding.encodeBase64(s.bytes)}`),\n Match.exhaustive,\n)\n\nconst contentBlockToInput = Match.type<ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => ({ type: \"input_text\", text: b.text }),\n input_image: (b) => ({\n type: \"input_image\",\n image_url: imageSourceToUrl(b.source),\n }),\n output_text: (b) => ({ type: \"output_text\", text: b.text }),\n // No assistant-image content type here. Dropped rather than relocated\n // onto a user message, which is the caller's call: `Turn.imagesAsInput`.\n output_image: () => undefined,\n refusal: (b) => ({ type: \"refusal\", refusal: b.text }),\n }),\n)\n\nconst itemToInput = (item: HistoryItem): Record<string, unknown> =>\n passthrough(item) ??\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m) => ({\n type: \"message\",\n role: m.role,\n content: Arr.filterMap(m.content, (b) => {\n const encoded = contentBlockToInput(b)\n return encoded === undefined ? Result.failVoid : Result.succeed(encoded)\n }),\n }),\n function_call: (f) => ({\n type: \"function_call\",\n call_id: f.call_id,\n name: f.name,\n arguments: f.arguments,\n }),\n function_call_output: (o) => ({\n type: \"function_call_output\",\n call_id: o.call_id,\n output: o.output,\n }),\n reasoning: (r) => ({\n type: \"reasoning\",\n ...(r.id !== undefined && { id: r.id }),\n ...(r.summary !== undefined && {\n summary: [{ type: \"summary_text\", text: r.summary }],\n }),\n ...(r.signature !== undefined && { encrypted_content: r.signature }),\n }),\n }),\n )\n\n/** Convert our `HistoryItem[]` history into the Responses API `input` array. */\nexport const itemsToInput = (\n items: ReadonlyArray<HistoryItem>,\n): ReadonlyArray<Record<string, unknown>> => items.map(itemToInput)\n\n// ---------------------------------------------------------------------------\n// Wire output items → our Items\n// ---------------------------------------------------------------------------\n\nconst wireMessageContentToBlock = Match.type<typeof WireMessageContent.Type>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n output_text: (c): ContentBlock => ({\n type: \"output_text\",\n text: c.text,\n ...(c.annotations !== undefined && c.annotations !== null && { annotations: c.annotations }),\n }),\n refusal: (c): ContentBlock => ({ type: \"refusal\", text: c.refusal }),\n }),\n)\n\nexport const wireItemToItem = (wire: WireOutputItem): ReadonlyArray<HistoryItem> =>\n Match.value(wire).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m): ReadonlyArray<HistoryItem> => [\n {\n type: \"message\" as const,\n role: m.role,\n content: m.content.map(wireMessageContentToBlock),\n providerData: { responses: m },\n },\n ],\n function_call: (f): ReadonlyArray<HistoryItem> => [\n {\n type: \"function_call\" as const,\n call_id: f.call_id,\n name: f.name,\n arguments: f.arguments,\n providerData: { responses: f },\n },\n ],\n reasoning: (r): ReadonlyArray<HistoryItem> => [\n {\n type: \"reasoning\" as const,\n ...(r.id !== undefined && { id: r.id }),\n ...(r.summary !== undefined && {\n summary: r.summary.map((s) => s.text).join(\"\\n\"),\n }),\n ...(r.encrypted_content !== undefined && { signature: r.encrypted_content }),\n providerData: { responses: r },\n },\n ],\n // Hosted-tool calls are provider-executed; drop from the turn (Phase 1).\n web_search_call: (): ReadonlyArray<HistoryItem> => [],\n code_interpreter_call: (): ReadonlyArray<HistoryItem> => [],\n file_search_call: (): ReadonlyArray<HistoryItem> => [],\n }),\n )\n\n// ---------------------------------------------------------------------------\n// response.completed → Turn\n// ---------------------------------------------------------------------------\n\nconst reasonToStop = Match.type<string>().pipe(\n Match.when(\"max_output_tokens\", () => \"max_tokens\" as const),\n Match.when(\"refusal\", () => \"refusal\" as const),\n Match.when(\"content_filter\", () => \"content_filter\" as const),\n Match.when(\"max_tool_calls\", () => \"max_tool_calls\" as const),\n Match.option,\n)\n\nconst hasRefusalContent = (payload: WireResponseCompleted): boolean =>\n (payload.output ?? []).some(\n (i) => i.type === \"message\" && i.content.some((c) => c.type === \"refusal\"),\n )\n\nconst stopReasonFromCompleted = (payload: WireResponseCompleted): Turn[\"stop_reason\"] =>\n Option.match(reasonToStop(payload.incomplete_details?.reason ?? \"\"), {\n onSome: (s) => s,\n onNone: () =>\n hasRefusalContent(payload)\n ? (\"refusal\" as const)\n : (payload.output ?? []).some((i) => i.type === \"function_call\")\n ? (\"tool_calls\" as const)\n : (\"stop\" as const),\n })\n\nconst cachedTokens = (payload: WireResponseCompleted): number | undefined =>\n payload.usage?.input_tokens_details?.cached_tokens ?? undefined\n\nconst reasoningTokens = (payload: WireResponseCompleted): number | undefined =>\n payload.usage?.output_tokens_details?.reasoning_tokens ?? undefined\n\nexport const turnFromCompleted = (payload: WireResponseCompleted): Turn => {\n const cached = cachedTokens(payload)\n const reasoning = reasoningTokens(payload)\n return {\n items: (payload.output ?? []).flatMap(wireItemToItem),\n usage: {\n ...(payload.usage?.input_tokens !== undefined && {\n input_tokens: payload.usage.input_tokens,\n }),\n ...(payload.usage?.output_tokens !== undefined && {\n output_tokens: payload.usage.output_tokens,\n }),\n ...(payload.usage?.total_tokens !== undefined && {\n total_tokens: payload.usage.total_tokens,\n }),\n ...(cached !== undefined && {\n input_tokens_details: { cached_tokens: cached },\n }),\n ...(reasoning !== undefined && {\n output_tokens_details: { reasoning_tokens: reasoning },\n }),\n },\n stop_reason: stopReasonFromCompleted(payload),\n }\n}\n"],"mappings":";;;;;;;;;;;AASA,MAAM,kBAAkB,OAAO,OAAO;CACpC,MAAM,OAAO,QAAQ,cAAc;CACnC,KAAK,OAAO;CACZ,aAAa,OAAO;CACpB,WAAW,OAAO;CAClB,OAAO,OAAO;AAChB,CAAC;AAED,MAAM,mBAAmB,OAAO,OAAO;CACrC,MAAM,OAAO,QAAQ,eAAe;CACpC,SAAS,OAAO;CAChB,OAAO,OAAO;AAChB,CAAC;AAED,MAAM,4BAA4B,OAAO,OAAO;CAC9C,MAAM,OAAO,QAAQ,yBAAyB;CAC9C,cAAc,OAAO;CACrB,SAAS,OAAO;CAChB,aAAa,OAAO;CACpB,WAAW,OAAO;AACpB,CAAC;AAED,MAAM,eAAe,OAAO,OAAO;CACjC,MAAM,OAAO,QAAQ,WAAW;CAChC,SAAS,OAAO;CAChB,OAAO,OAAO;AAChB,CAAC;AAED,MAAa,iBAAiB,OAAO,MAAM;CACzC;CACA;CACA;CACA;AACF,CAAC;AAED,MAAM,wBAAwB,OAAO,OAAO;CAC1C,MAAM,OAAO,QAAQ,aAAa;CAClC,MAAM,OAAO;CACb,aAAa,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,cAAc,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,qBAAqB,OAAO,OAAO;CACvC,MAAM,OAAO,QAAQ,SAAS;CAC9B,SAAS,OAAO;AAClB,CAAC;AAED,MAAM,qBAAqB,OAAO,MAAM,CAAC,uBAAuB,kBAAkB,CAAC;AAEnF,MAAM,kBAAkB,OAAO,OAAO;CACpC,MAAM,OAAO,QAAQ,cAAc;CACnC,MAAM,OAAO;AACf,CAAC;AAED,MAAM,cAAc,OAAO,OAAO;CAChC,MAAM,OAAO,QAAQ,SAAS;CAC9B,IAAI,OAAO,SAAS,OAAO,MAAM;CACjC,MAAM,OAAO,QAAQ,WAAW;CAChC,SAAS,OAAO,MAAM,kBAAkB;AAC1C,CAAC;AAED,MAAM,mBAAmB,OAAO,OAAO;CACrC,MAAM,OAAO,QAAQ,eAAe;CACpC,IAAI,OAAO,SAAS,OAAO,MAAM;CACjC,SAAS,OAAO;CAChB,MAAM,OAAO;CACb,WAAW,OAAO;AACpB,CAAC;AAED,MAAM,gBAAgB,OAAO,OAAO;CAClC,MAAM,OAAO,QAAQ,WAAW;CAChC,IAAI,OAAO,SAAS,OAAO,MAAM;CACjC,SAAS,OAAO,SAAS,OAAO,MAAM,eAAe,CAAC;CACtD,mBAAmB,OAAO,SAAS,OAAO,MAAM;AAClD,CAAC;AAMD,MAAM,oBAAoB,OAAO,OAAO;CACtC,MAAM,OAAO,QAAQ,iBAAiB;CACtC,IAAI,OAAO,SAAS,OAAO,MAAM;AACnC,CAAC;AAED,MAAM,0BAA0B,OAAO,OAAO;CAC5C,MAAM,OAAO,QAAQ,uBAAuB;CAC5C,IAAI,OAAO,SAAS,OAAO,MAAM;AACnC,CAAC;AAED,MAAM,qBAAqB,OAAO,OAAO;CACvC,MAAM,OAAO,QAAQ,kBAAkB;CACvC,IAAI,OAAO,SAAS,OAAO,MAAM;AACnC,CAAC;AAED,MAAa,iBAAiB,OAAO,MAAM;CACzC;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAGD,MAAM,yBAAyB,OAAO,OAAO,EAC3C,eAAe,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC,EAC7D,CAAC;AAED,MAAM,0BAA0B,OAAO,OAAO,EAC5C,kBAAkB,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC,EAChE,CAAC;AAED,MAAM,YAAY,OAAO,OAAO;CAC9B,cAAc,OAAO,SAAS,OAAO,MAAM;CAC3C,eAAe,OAAO,SAAS,OAAO,MAAM;CAC5C,cAAc,OAAO,SAAS,OAAO,MAAM;CAC3C,sBAAsB,OAAO,SAAS,OAAO,OAAO,sBAAsB,CAAC;CAC3E,uBAAuB,OAAO,SAAS,OAAO,OAAO,uBAAuB,CAAC;AAC/E,CAAC;AAED,MAAM,oBAAoB,OAAO,OAAO;CACtC,MAAM,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CAClD,SAAS,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;AACvD,CAAC;AAQD,MAAa,wBAAwB,OAAO,OAAO;CACjD,IAAI,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CAChD,QAAQ,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CACpD,QAAQ,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,cAAc,CAAC,CAAC;CACnE,OAAO,OAAO,SAAS,OAAO,OAAO,SAAS,CAAC;CAC/C,oBAAoB,OAAO,SACzB,OAAO,OACL,OAAO,OAAO,EACZ,QAAQ,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC,EACtD,CAAC,CACH,CACF;CACA,OAAO,OAAO,SAAS,OAAO,OAAO,iBAAiB,CAAC;AACzD,CAAC;;;;;;;AAaD,MAAM,aAAa,OAAO,MAAM;CAAC;CAAa;CAAkB;AAAa,CAAC;AAG9E,MAAM,wBAAwB,OAAO,OAAO,EAAE,WAAW,WAAW,CAAC;AACrE,MAAM,sBAAsB,OAAO,oBAAoB,qBAAqB;;;;;;;;;;;;AAa5E,MAAM,eAAe,SACnB,KACE,oBAAoB,KAAK,YAAY,GACrC,OAAO,MAAM;CACX,YAAY,SAAS,KAAK;CAC1B,iBAAyC,KAAA;AAC3C,CAAC,CACH;;;;;;AAOF,MAAM,mBAAmB,MAAM,KAA2B,CAAC,CAAC,KAC1D,MAAM,IAAI,QAAQ,MAAM,EAAE,GAAG,GAC7B,MAAM,IAAI,WAAW,MAAM,QAAQ,EAAE,SAAS,UAAU,EAAE,QAAQ,GAClE,MAAM,IAAI,UAAU,MAAM,QAAQ,EAAE,SAAS,UAAU,SAAS,aAAa,EAAE,KAAK,GAAG,GACvF,MAAM,UACR;AAEA,MAAM,sBAAsB,MAAM,KAAmB,CAAC,CAAC,KACrD,MAAM,yBAAyB,MAAM,CAAC,CAAC;CACrC,aAAa,OAAO;EAAE,MAAM;EAAc,MAAM,EAAE;CAAK;CACvD,cAAc,OAAO;EACnB,MAAM;EACN,WAAW,iBAAiB,EAAE,MAAM;CACtC;CACA,cAAc,OAAO;EAAE,MAAM;EAAe,MAAM,EAAE;CAAK;CAGzD,oBAAoB,KAAA;CACpB,UAAU,OAAO;EAAE,MAAM;EAAW,SAAS,EAAE;CAAK;AACtD,CAAC,CACH;AAEA,MAAM,eAAe,SACnB,YAAY,IAAI,KAChB,MAAM,MAAM,IAAI,CAAC,CAAC,KAChB,MAAM,yBAAyB,MAAM,CAAC,CAAC;CACrC,UAAU,OAAO;EACf,MAAM;EACN,MAAM,EAAE;EACR,SAASA,MAAI,UAAU,EAAE,UAAU,MAAM;GACvC,MAAM,UAAU,oBAAoB,CAAC;GACrC,OAAO,YAAY,KAAA,IAAY,OAAO,WAAW,OAAO,QAAQ,OAAO;EACzE,CAAC;CACH;CACA,gBAAgB,OAAO;EACrB,MAAM;EACN,SAAS,EAAE;EACX,MAAM,EAAE;EACR,WAAW,EAAE;CACf;CACA,uBAAuB,OAAO;EAC5B,MAAM;EACN,SAAS,EAAE;EACX,QAAQ,EAAE;CACZ;CACA,YAAY,OAAO;EACjB,MAAM;EACN,GAAI,EAAE,OAAO,KAAA,KAAa,EAAE,IAAI,EAAE,GAAG;EACrC,GAAI,EAAE,YAAY,KAAA,KAAa,EAC7B,SAAS,CAAC;GAAE,MAAM;GAAgB,MAAM,EAAE;EAAQ,CAAC,EACrD;EACA,GAAI,EAAE,cAAc,KAAA,KAAa,EAAE,mBAAmB,EAAE,UAAU;CACpE;AACF,CAAC,CACH;;AAGF,MAAa,gBACX,UAC2C,MAAM,IAAI,WAAW;AAMlE,MAAM,4BAA4B,MAAM,KAAqC,CAAC,CAAC,KAC7E,MAAM,yBAAyB,MAAM,CAAC,CAAC;CACrC,cAAc,OAAqB;EACjC,MAAM;EACN,MAAM,EAAE;EACR,GAAI,EAAE,gBAAgB,KAAA,KAAa,EAAE,gBAAgB,QAAQ,EAAE,aAAa,EAAE,YAAY;CAC5F;CACA,UAAU,OAAqB;EAAE,MAAM;EAAW,MAAM,EAAE;CAAQ;AACpE,CAAC,CACH;AAEA,MAAa,kBAAkB,SAC7B,MAAM,MAAM,IAAI,CAAC,CAAC,KAChB,MAAM,yBAAyB,MAAM,CAAC,CAAC;CACrC,UAAU,MAAkC,CAC1C;EACE,MAAM;EACN,MAAM,EAAE;EACR,SAAS,EAAE,QAAQ,IAAI,yBAAyB;EAChD,cAAc,EAAE,WAAW,EAAE;CAC/B,CACF;CACA,gBAAgB,MAAkC,CAChD;EACE,MAAM;EACN,SAAS,EAAE;EACX,MAAM,EAAE;EACR,WAAW,EAAE;EACb,cAAc,EAAE,WAAW,EAAE;CAC/B,CACF;CACA,YAAY,MAAkC,CAC5C;EACE,MAAM;EACN,GAAI,EAAE,OAAO,KAAA,KAAa,EAAE,IAAI,EAAE,GAAG;EACrC,GAAI,EAAE,YAAY,KAAA,KAAa,EAC7B,SAAS,EAAE,QAAQ,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,EACjD;EACA,GAAI,EAAE,sBAAsB,KAAA,KAAa,EAAE,WAAW,EAAE,kBAAkB;EAC1E,cAAc,EAAE,WAAW,EAAE;CAC/B,CACF;CAEA,uBAAmD,CAAC;CACpD,6BAAyD,CAAC;CAC1D,wBAAoD,CAAC;AACvD,CAAC,CACH;AAMF,MAAM,eAAe,MAAM,KAAa,CAAC,CAAC,KACxC,MAAM,KAAK,2BAA2B,YAAqB,GAC3D,MAAM,KAAK,iBAAiB,SAAkB,GAC9C,MAAM,KAAK,wBAAwB,gBAAyB,GAC5D,MAAM,KAAK,wBAAwB,gBAAyB,GAC5D,MAAM,MACR;AAEA,MAAM,qBAAqB,aACxB,QAAQ,UAAU,CAAC,EAAA,CAAG,MACpB,MAAM,EAAE,SAAS,aAAa,EAAE,QAAQ,MAAM,MAAM,EAAE,SAAS,SAAS,CAC3E;AAEF,MAAM,2BAA2B,YAC/B,OAAO,MAAM,aAAa,QAAQ,oBAAoB,UAAU,EAAE,GAAG;CACnE,SAAS,MAAM;CACf,cACE,kBAAkB,OAAO,IACpB,aACA,QAAQ,UAAU,CAAC,EAAA,CAAG,MAAM,MAAM,EAAE,SAAS,eAAe,IAC1D,eACA;AACX,CAAC;AAEH,MAAM,gBAAgB,YACpB,QAAQ,OAAO,sBAAsB,iBAAiB,KAAA;AAExD,MAAM,mBAAmB,YACvB,QAAQ,OAAO,uBAAuB,oBAAoB,KAAA;AAE5D,MAAa,qBAAqB,YAAyC;CACzE,MAAM,SAAS,aAAa,OAAO;CACnC,MAAM,YAAY,gBAAgB,OAAO;CACzC,OAAO;EACL,QAAQ,QAAQ,UAAU,CAAC,EAAA,CAAG,QAAQ,cAAc;EACpD,OAAO;GACL,GAAI,QAAQ,OAAO,iBAAiB,KAAA,KAAa,EAC/C,cAAc,QAAQ,MAAM,aAC9B;GACA,GAAI,QAAQ,OAAO,kBAAkB,KAAA,KAAa,EAChD,eAAe,QAAQ,MAAM,cAC/B;GACA,GAAI,QAAQ,OAAO,iBAAiB,KAAA,KAAa,EAC/C,cAAc,QAAQ,MAAM,aAC9B;GACA,GAAI,WAAW,KAAA,KAAa,EAC1B,sBAAsB,EAAE,eAAe,OAAO,EAChD;GACA,GAAI,cAAc,KAAA,KAAa,EAC7B,uBAAuB,EAAE,kBAAkB,UAAU,EACvD;EACF;EACA,aAAa,wBAAwB,OAAO;CAC9C;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-uai/responses",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "OpenAI Responses API provider for @effect-uai/core.",
5
5
  "keywords": [
6
6
  "ai",
@@ -57,7 +57,7 @@
57
57
  "devDependencies": {
58
58
  "effect": "4.0.0-rc.111",
59
59
  "typescript": "^7.0.2",
60
- "@effect-uai/core": "0.13.0"
60
+ "@effect-uai/core": "0.15.0"
61
61
  },
62
62
  "peerDependencies": {
63
63
  "@effect-uai/core": ">=0.2.0 <1",
package/src/Responses.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Context, Effect, Layer, Match, Option, Redacted, Result, Schema, Stream } from "effect"
2
2
  import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
3
3
  import * as AiError from "@effect-uai/core/AiError"
4
+ import * as Capabilities from "@effect-uai/core/Capabilities"
4
5
  import * as StructuredFormat from "@effect-uai/core/StructuredFormat"
5
6
  import {
6
7
  type CommonRequest,
@@ -330,6 +331,12 @@ const buildNativeStream = (cfg: Config) => {
330
331
  onSuccess: (wire) => Effect.succeed(wire),
331
332
  },
332
333
  )
334
+ yield* Capabilities.warnDroppedBlocks(request.history, "output_image", {
335
+ provider: "responses",
336
+ capability: "output_image",
337
+ reason:
338
+ "Assistant messages carry no image on this wire; images here come from the `image_generation` tool. Use `Turn.imagesAsInput` to resend one as user content.",
339
+ })
333
340
  const httpRequest = HttpClientRequest.post(url).pipe(
334
341
  HttpClientRequest.bearerToken(cfg.apiKey),
335
342
  HttpClientRequest.bodyJsonUnsafe(buildBody(request, providerToolWire, cfg)),
package/src/codec.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Encoding, Match, Option, Result, Schema, pipe } from "effect"
1
+ import { Array as Arr, Encoding, Match, Option, Result, Schema, pipe } from "effect"
2
2
  import type { ContentBlock, InputImage, HistoryItem } from "@effect-uai/core/Items"
3
3
  import type { Turn } from "@effect-uai/core/Turn"
4
4
 
@@ -210,6 +210,9 @@ const contentBlockToInput = Match.type<ContentBlock>().pipe(
210
210
  image_url: imageSourceToUrl(b.source),
211
211
  }),
212
212
  output_text: (b) => ({ type: "output_text", text: b.text }),
213
+ // No assistant-image content type here. Dropped rather than relocated
214
+ // onto a user message, which is the caller's call: `Turn.imagesAsInput`.
215
+ output_image: () => undefined,
213
216
  refusal: (b) => ({ type: "refusal", refusal: b.text }),
214
217
  }),
215
218
  )
@@ -221,7 +224,10 @@ const itemToInput = (item: HistoryItem): Record<string, unknown> =>
221
224
  message: (m) => ({
222
225
  type: "message",
223
226
  role: m.role,
224
- content: m.content.map(contentBlockToInput),
227
+ content: Arr.filterMap(m.content, (b) => {
228
+ const encoded = contentBlockToInput(b)
229
+ return encoded === undefined ? Result.failVoid : Result.succeed(encoded)
230
+ }),
225
231
  }),
226
232
  function_call: (f) => ({
227
233
  type: "function_call",