@effect-uai/responses 0.17.0 → 0.19.0-dev-20261109183959
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/README.md +4 -4
- package/dist/OpenAIDeepResearch.d.mts +1 -1
- package/dist/OpenAIDeepResearch.mjs +2 -2
- package/dist/OpenAIDeepResearch.mjs.map +1 -1
- package/dist/OpenAIEmbedding.d.mts +1 -1
- package/dist/OpenAIEmbedding.mjs +2 -2
- package/dist/OpenAIEmbedding.mjs.map +1 -1
- package/dist/Responses.d.mts +1 -1
- package/dist/Responses.mjs +2 -2
- package/dist/Responses.mjs.map +1 -1
- package/dist/ResponsesTools.mjs +1 -1
- package/dist/codec.d.mts.map +1 -1
- package/dist/codec.mjs +27 -4
- package/dist/codec.mjs.map +1 -1
- package/dist/codec.test.mjs +56 -1
- package/dist/codec.test.mjs.map +1 -1
- package/dist/dist-DHer1cql.mjs +1533 -0
- package/dist/dist-DHer1cql.mjs.map +1 -0
- package/dist/{test.DNmyFkvJ-N3QqMRwZ.mjs → index.C4tKZ0yW-xEu55bU4.mjs} +8276 -7092
- package/dist/index.C4tKZ0yW-xEu55bU4.mjs.map +1 -0
- package/dist/region.test.mjs +1 -1
- package/dist/responsesTools.test.mjs +1 -1
- package/dist/{rolldown-runtime-D7D4PA-g.mjs → rolldown-runtime-EAsCxrDo.mjs} +2 -1
- package/dist/streamEvents.mjs +1 -1
- package/package.json +5 -5
- package/src/OpenAIDeepResearch.ts +1 -1
- package/src/OpenAIEmbedding.ts +1 -1
- package/src/Responses.ts +1 -1
- package/src/codec.test.ts +53 -0
- package/src/codec.ts +39 -4
- package/dist/magic-string.es-CUAnjKYC.mjs +0 -1014
- package/dist/magic-string.es-CUAnjKYC.mjs.map +0 -1
- package/dist/test.DNmyFkvJ-N3QqMRwZ.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -13,18 +13,18 @@ the call site without polluting the cross-provider surface.
|
|
|
13
13
|
pnpm add @effect-uai/responses @effect-uai/core effect
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
ESM-only. Requires `effect
|
|
16
|
+
ESM-only. Requires `effect@>=4.0.1` and `@effect-uai/core` as peers.
|
|
17
17
|
|
|
18
18
|
## Usage
|
|
19
19
|
|
|
20
20
|
```ts
|
|
21
21
|
import { Config, Effect, Layer } from "effect"
|
|
22
|
-
import { FetchHttpClient } from "effect/
|
|
22
|
+
import { FetchHttpClient } from "effect/http"
|
|
23
23
|
import { layer as responsesLayer } from "@effect-uai/responses"
|
|
24
24
|
|
|
25
25
|
const provider = Layer.unwrap(
|
|
26
26
|
Effect.gen(function* () {
|
|
27
|
-
const apiKey = yield* Config.
|
|
27
|
+
const apiKey = yield* Config.Redacted("OPENAI_API_KEY")
|
|
28
28
|
return responsesLayer({ apiKey })
|
|
29
29
|
}),
|
|
30
30
|
)
|
|
@@ -38,7 +38,7 @@ provider-agnostic loop bodies).
|
|
|
38
38
|
|
|
39
39
|
## Docs
|
|
40
40
|
|
|
41
|
-
<https://effect-uai.
|
|
41
|
+
<https://effect-uai.com/providers/responses/>
|
|
42
42
|
|
|
43
43
|
## License
|
|
44
44
|
|
|
@@ -2,7 +2,7 @@ import { WireResponseCompleted } from "./codec.mjs";
|
|
|
2
2
|
import { OpenAIDeepResearchModel } from "./models.mjs";
|
|
3
3
|
import { OpenAiRegion } from "./region.mjs";
|
|
4
4
|
import { Context, Effect, Layer, Redacted } from "effect";
|
|
5
|
-
import { HttpClient } from "effect/
|
|
5
|
+
import { HttpClient } from "effect/http";
|
|
6
6
|
import { DeepResearch, DeepResearchServiceShape } from "@effect-uai/core/DeepResearch";
|
|
7
7
|
import * as Job from "@effect-uai/core/Job";
|
|
8
8
|
import { ResearchRequest, ResearchState } from "@effect-uai/core/Research";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.mjs";
|
|
2
2
|
import { WireResponseCompleted, itemsToInput, turnFromCompleted } from "./codec.mjs";
|
|
3
3
|
import { resolveHost } from "./region.mjs";
|
|
4
4
|
import { httpStatusError, providerEventsOfResponse, toCanonical } from "./Responses.mjs";
|
|
5
5
|
import { Context, Effect, Layer, Match, Option, Schema, Stream } from "effect";
|
|
6
|
-
import { HttpClient, HttpClientRequest } from "effect/
|
|
6
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
7
7
|
import * as AiError from "@effect-uai/core/AiError";
|
|
8
8
|
import { DeepResearch, fromJob } from "@effect-uai/core/DeepResearch";
|
|
9
9
|
import * as Job from "@effect-uai/core/Job";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenAIDeepResearch.mjs","names":[],"sources":["../src/OpenAIDeepResearch.ts"],"sourcesContent":["import { Context, Effect, Layer, Match, Option, Redacted, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport {\n DeepResearch,\n type DeepResearchService,\n type DeepResearchServiceShape,\n fromJob,\n type ResearchJobOps,\n} from \"@effect-uai/core/DeepResearch\"\nimport * as Job from \"@effect-uai/core/Job\"\nimport type { ResearchJobRef, ResearchRequest, ResearchState } from \"@effect-uai/core/Research\"\nimport type { Turn, TurnEvent } from \"@effect-uai/core/Turn\"\nimport { WireResponseCompleted, turnFromCompleted, itemsToInput } from \"./codec.js\"\nimport type { OpenAIDeepResearchModel } from \"./models.js\"\nimport { type OpenAiRegion, resolveHost } from \"./region.js\"\nimport { httpStatusError, providerEventsOfResponse, toCanonical } from \"./Responses.js\"\nimport type { ProviderEvent } from \"./streamEvents.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * OpenAI-typed research request. Narrows `model` to the deep-research ids and\n * adds `maxSearches` (mapped to `max_tool_calls`) and a `reasoning` hint.\n * Provider-portable code uses the generic `DeepResearch` tag instead.\n */\nexport type OpenAIResearchRequest = Omit<ResearchRequest, \"model\"> & {\n readonly model?: OpenAIDeepResearchModel\n /** Cost/latency cap on the agent's tool calls. Maps to `max_tool_calls`. */\n readonly maxSearches?: number\n /** Reasoning-effort hint; `summary: \"auto\"` streams reasoning summaries. */\n readonly reasoning?: {\n readonly effort?: \"low\" | \"medium\" | \"high\"\n readonly summary?: \"auto\" | \"concise\" | \"detailed\"\n }\n}\n\nexport type OpenAIDeepResearchService = DeepResearchServiceShape<OpenAIResearchRequest>\n\n/**\n * Provider-typed tag. Yield this for the OpenAI model / `maxSearches` knobs;\n * yield the generic `DeepResearch` tag for provider-portable code. The\n * background job is detachable and streams live over the resumable Responses\n * SSE, so `streamFrom` is a real event stream (not the synthesized fallback) and\n * a persisted ref survives a restart.\n */\nexport class OpenAIDeepResearch extends Context.Service<\n OpenAIDeepResearch,\n OpenAIDeepResearchService\n>()(\"@betalyra/effect-uai/providers/responses/OpenAIDeepResearch\") {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n readonly region?: OpenAiRegion\n /** Poll cadence / overall timeout for the derived poll loops. */\n readonly job?: Job.JobConfig\n}\n\n// ---------------------------------------------------------------------------\n// Wire codec\n// ---------------------------------------------------------------------------\n\nconst decodeResponse = Schema.decodeUnknownEffect(WireResponseCompleted)\n\n// Any early lifecycle event (`response.created` / `response.queued` / ...)\n// carries the response object; only its id matters here. These events are not\n// in the modeled `ProviderEvent` union, so they arrive as `_unknown`.\nconst WireCreated = Schema.Struct({ response: Schema.Struct({ id: Schema.String }) })\nconst decodeCreated = Schema.decodeUnknownEffect(WireCreated)\n\nconst responseIdOf = (event: ProviderEvent): Effect.Effect<Option.Option<string>> =>\n event.type === \"_unknown\"\n ? decodeCreated(event.raw).pipe(\n Effect.map((wire) => Option.some(wire.response.id)),\n Effect.orElseSucceed(() => Option.none<string>()),\n )\n : Effect.succeedNone\n\n// Map a Responses object's `status` onto the generic `JobState`. `completed`\n// and `incomplete` both carry a usable turn (incomplete = stopped early with a\n// partial answer, same shape); `failed` / `cancelled` are terminal failures.\n// An unknown / absent status keeps the loop polling (bounded by the timeout).\nexport const jobStateOf = (wire: WireResponseCompleted): ResearchState =>\n Match.value(wire.status ?? \"\").pipe(\n Match.when(\"queued\", (): ResearchState => ({ _tag: \"Pending\" })),\n Match.when(\"in_progress\", (): ResearchState => ({ _tag: \"Running\" })),\n Match.whenOr(\"completed\", \"incomplete\", (): ResearchState => ({\n _tag: \"Succeeded\",\n result: turnFromCompleted(wire),\n })),\n Match.whenOr(\"failed\", \"cancelled\", (): ResearchState => ({\n _tag: \"Failed\",\n ...(wire.error?.message != null && { reason: wire.error.message }),\n raw: wire,\n })),\n Match.orElse((): ResearchState => ({ _tag: \"Running\" })),\n )\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"responses\", raw: cause })\n\n// Deep research requires at least one data-source tool; default to web search.\nconst submitBody = (request: OpenAIResearchRequest): Record<string, unknown> => ({\n model: request.model ?? \"o3-deep-research\",\n input: itemsToInput(request.history),\n background: true,\n store: true,\n tools: [{ type: \"web_search_preview\" }],\n ...(request.maxSearches !== undefined && { max_tool_calls: request.maxSearches }),\n ...(request.reasoning !== undefined && { reasoning: request.reasoning }),\n})\n\n// The job is created with `stream: true` on top of `background: true`: OpenAI\n// only exposes a resumable event feed on responses *created* streaming, so this\n// keeps every submitted ref attachable via `streamFrom`. Only the first SSE\n// event is read (for the response id); the connection is then dropped and the\n// background job keeps running server-side.\nconst submitJob = (\n cfg: Config,\n request: OpenAIResearchRequest,\n): Effect.Effect<ResearchJobRef, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(`${resolveHost(cfg)}/responses`).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.accept(\"text/event-stream\"),\n HttpClientRequest.bodyJsonUnsafe({ ...submitBody(request), stream: true }),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n const id = yield* providerEventsOfResponse(response).pipe(\n Stream.mapEffect(responseIdOf),\n Stream.filter(Option.isSome),\n Stream.map((some) => some.value),\n Stream.runHead,\n )\n return yield* Option.match(id, {\n onNone: () =>\n Effect.fail(\n new AiError.GenerationFailed({\n provider: \"responses\",\n message: \"streaming create ended without a response id\",\n raw: submitBody(request),\n }),\n ),\n onSome: (value) => Effect.succeed(Job.jobRef<Turn>(\"responses\", value)),\n })\n })\n\nconst pollJob = (\n cfg: Config,\n ref: ResearchJobRef,\n): Effect.Effect<ResearchState, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.get(`${resolveHost(cfg)}/responses/${ref.id}`).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n const wire = yield* decodeResponse(json).pipe(Effect.mapError(transportFailure))\n return jobStateOf(wire)\n })\n\nconst cancelJob = (\n cfg: Config,\n ref: ResearchJobRef,\n): Effect.Effect<void, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(\n `${resolveHost(cfg)}/responses/${ref.id}/cancel`,\n ).pipe(HttpClientRequest.bearerToken(cfg.apiKey))\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n })\n\n// Attach a live event stream to a detached job via the resumable Responses SSE,\n// reusing the shared decode pipeline and the canonical `TurnEvent` projection.\nconst streamFromRef = (\n cfg: Config,\n ref: ResearchJobRef,\n): Stream.Stream<TurnEvent, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.get(\n `${resolveHost(cfg)}/responses/${ref.id}?stream=true`,\n ).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.accept(\"text/event-stream\"),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n return toCanonical(providerEventsOfResponse(response))\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n// The job ops. OpenAI streams live over the resumable Responses SSE, so it\n// supplies a real `streamFrom` (not the synthesized fallback). Since `submit`\n// creates the job streaming, `fromJob`'s default `researchStream`\n// (submit-then-attach) streams real events too.\nconst jobOps = (\n cfg: Config,\n client: HttpClient.HttpClient,\n): ResearchJobOps<OpenAIResearchRequest> => {\n const withClient = <A>(e: Effect.Effect<A, AiError.AiError, HttpClient.HttpClient>) =>\n Effect.provideService(e, HttpClient.HttpClient, client)\n const withClientStream = <A>(s: Stream.Stream<A, AiError.AiError, HttpClient.HttpClient>) =>\n Stream.provideService(s, HttpClient.HttpClient, client)\n return {\n submit: (request) => withClient(submitJob(cfg, request)),\n poll: (ref) => withClient(pollJob(cfg, ref)),\n cancel: (ref) => withClient(cancelJob(cfg, ref)),\n streamFrom: (ref) => withClientStream(streamFromRef(cfg, ref)),\n }\n}\n\n/** Build an `OpenAIDeepResearchService`. For Layer setup, prefer {@link layer}. */\nexport const make = (\n cfg: Config,\n): Effect.Effect<OpenAIDeepResearchService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => fromJob(jobOps(cfg, client), cfg.job))\n\n/**\n * Layer registering the provider-typed `OpenAIDeepResearch` tag and the generic\n * `DeepResearch` tag. No capability markers: the whole surface (detach, live\n * stream, cancel) is universal.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<OpenAIDeepResearch | DeepResearch, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(OpenAIDeepResearch, make(cfg))\n const generic = Layer.effect(\n DeepResearch,\n Effect.map(HttpClient.HttpClient, (client): DeepResearchService => {\n const ops = jobOps(cfg, client)\n return fromJob<ResearchRequest>(\n { ...ops, submit: (request) => ops.submit(request as OpenAIResearchRequest) },\n cfg.job,\n )\n }),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OpenAIDeepResearch.mjs","names":[],"sources":["../src/OpenAIDeepResearch.ts"],"sourcesContent":["import { Context, Effect, Layer, Match, Option, Redacted, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport {\n DeepResearch,\n type DeepResearchService,\n type DeepResearchServiceShape,\n fromJob,\n type ResearchJobOps,\n} from \"@effect-uai/core/DeepResearch\"\nimport * as Job from \"@effect-uai/core/Job\"\nimport type { ResearchJobRef, ResearchRequest, ResearchState } from \"@effect-uai/core/Research\"\nimport type { Turn, TurnEvent } from \"@effect-uai/core/Turn\"\nimport { WireResponseCompleted, turnFromCompleted, itemsToInput } from \"./codec.js\"\nimport type { OpenAIDeepResearchModel } from \"./models.js\"\nimport { type OpenAiRegion, resolveHost } from \"./region.js\"\nimport { httpStatusError, providerEventsOfResponse, toCanonical } from \"./Responses.js\"\nimport type { ProviderEvent } from \"./streamEvents.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * OpenAI-typed research request. Narrows `model` to the deep-research ids and\n * adds `maxSearches` (mapped to `max_tool_calls`) and a `reasoning` hint.\n * Provider-portable code uses the generic `DeepResearch` tag instead.\n */\nexport type OpenAIResearchRequest = Omit<ResearchRequest, \"model\"> & {\n readonly model?: OpenAIDeepResearchModel\n /** Cost/latency cap on the agent's tool calls. Maps to `max_tool_calls`. */\n readonly maxSearches?: number\n /** Reasoning-effort hint; `summary: \"auto\"` streams reasoning summaries. */\n readonly reasoning?: {\n readonly effort?: \"low\" | \"medium\" | \"high\"\n readonly summary?: \"auto\" | \"concise\" | \"detailed\"\n }\n}\n\nexport type OpenAIDeepResearchService = DeepResearchServiceShape<OpenAIResearchRequest>\n\n/**\n * Provider-typed tag. Yield this for the OpenAI model / `maxSearches` knobs;\n * yield the generic `DeepResearch` tag for provider-portable code. The\n * background job is detachable and streams live over the resumable Responses\n * SSE, so `streamFrom` is a real event stream (not the synthesized fallback) and\n * a persisted ref survives a restart.\n */\nexport class OpenAIDeepResearch extends Context.Service<\n OpenAIDeepResearch,\n OpenAIDeepResearchService\n>()(\"@betalyra/effect-uai/providers/responses/OpenAIDeepResearch\") {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n readonly region?: OpenAiRegion\n /** Poll cadence / overall timeout for the derived poll loops. */\n readonly job?: Job.JobConfig\n}\n\n// ---------------------------------------------------------------------------\n// Wire codec\n// ---------------------------------------------------------------------------\n\nconst decodeResponse = Schema.decodeUnknownEffect(WireResponseCompleted)\n\n// Any early lifecycle event (`response.created` / `response.queued` / ...)\n// carries the response object; only its id matters here. These events are not\n// in the modeled `ProviderEvent` union, so they arrive as `_unknown`.\nconst WireCreated = Schema.Struct({ response: Schema.Struct({ id: Schema.String }) })\nconst decodeCreated = Schema.decodeUnknownEffect(WireCreated)\n\nconst responseIdOf = (event: ProviderEvent): Effect.Effect<Option.Option<string>> =>\n event.type === \"_unknown\"\n ? decodeCreated(event.raw).pipe(\n Effect.map((wire) => Option.some(wire.response.id)),\n Effect.orElseSucceed(() => Option.none<string>()),\n )\n : Effect.succeedNone\n\n// Map a Responses object's `status` onto the generic `JobState`. `completed`\n// and `incomplete` both carry a usable turn (incomplete = stopped early with a\n// partial answer, same shape); `failed` / `cancelled` are terminal failures.\n// An unknown / absent status keeps the loop polling (bounded by the timeout).\nexport const jobStateOf = (wire: WireResponseCompleted): ResearchState =>\n Match.value(wire.status ?? \"\").pipe(\n Match.when(\"queued\", (): ResearchState => ({ _tag: \"Pending\" })),\n Match.when(\"in_progress\", (): ResearchState => ({ _tag: \"Running\" })),\n Match.whenOr(\"completed\", \"incomplete\", (): ResearchState => ({\n _tag: \"Succeeded\",\n result: turnFromCompleted(wire),\n })),\n Match.whenOr(\"failed\", \"cancelled\", (): ResearchState => ({\n _tag: \"Failed\",\n ...(wire.error?.message != null && { reason: wire.error.message }),\n raw: wire,\n })),\n Match.orElse((): ResearchState => ({ _tag: \"Running\" })),\n )\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"responses\", raw: cause })\n\n// Deep research requires at least one data-source tool; default to web search.\nconst submitBody = (request: OpenAIResearchRequest): Record<string, unknown> => ({\n model: request.model ?? \"o3-deep-research\",\n input: itemsToInput(request.history),\n background: true,\n store: true,\n tools: [{ type: \"web_search_preview\" }],\n ...(request.maxSearches !== undefined && { max_tool_calls: request.maxSearches }),\n ...(request.reasoning !== undefined && { reasoning: request.reasoning }),\n})\n\n// The job is created with `stream: true` on top of `background: true`: OpenAI\n// only exposes a resumable event feed on responses *created* streaming, so this\n// keeps every submitted ref attachable via `streamFrom`. Only the first SSE\n// event is read (for the response id); the connection is then dropped and the\n// background job keeps running server-side.\nconst submitJob = (\n cfg: Config,\n request: OpenAIResearchRequest,\n): Effect.Effect<ResearchJobRef, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(`${resolveHost(cfg)}/responses`).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.accept(\"text/event-stream\"),\n HttpClientRequest.bodyJsonUnsafe({ ...submitBody(request), stream: true }),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n const id = yield* providerEventsOfResponse(response).pipe(\n Stream.mapEffect(responseIdOf),\n Stream.filter(Option.isSome),\n Stream.map((some) => some.value),\n Stream.runHead,\n )\n return yield* Option.match(id, {\n onNone: () =>\n Effect.fail(\n new AiError.GenerationFailed({\n provider: \"responses\",\n message: \"streaming create ended without a response id\",\n raw: submitBody(request),\n }),\n ),\n onSome: (value) => Effect.succeed(Job.jobRef<Turn>(\"responses\", value)),\n })\n })\n\nconst pollJob = (\n cfg: Config,\n ref: ResearchJobRef,\n): Effect.Effect<ResearchState, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.get(`${resolveHost(cfg)}/responses/${ref.id}`).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n const wire = yield* decodeResponse(json).pipe(Effect.mapError(transportFailure))\n return jobStateOf(wire)\n })\n\nconst cancelJob = (\n cfg: Config,\n ref: ResearchJobRef,\n): Effect.Effect<void, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(\n `${resolveHost(cfg)}/responses/${ref.id}/cancel`,\n ).pipe(HttpClientRequest.bearerToken(cfg.apiKey))\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n })\n\n// Attach a live event stream to a detached job via the resumable Responses SSE,\n// reusing the shared decode pipeline and the canonical `TurnEvent` projection.\nconst streamFromRef = (\n cfg: Config,\n ref: ResearchJobRef,\n): Stream.Stream<TurnEvent, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.get(\n `${resolveHost(cfg)}/responses/${ref.id}?stream=true`,\n ).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.accept(\"text/event-stream\"),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n return toCanonical(providerEventsOfResponse(response))\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n// The job ops. OpenAI streams live over the resumable Responses SSE, so it\n// supplies a real `streamFrom` (not the synthesized fallback). Since `submit`\n// creates the job streaming, `fromJob`'s default `researchStream`\n// (submit-then-attach) streams real events too.\nconst jobOps = (\n cfg: Config,\n client: HttpClient.HttpClient,\n): ResearchJobOps<OpenAIResearchRequest> => {\n const withClient = <A>(e: Effect.Effect<A, AiError.AiError, HttpClient.HttpClient>) =>\n Effect.provideService(e, HttpClient.HttpClient, client)\n const withClientStream = <A>(s: Stream.Stream<A, AiError.AiError, HttpClient.HttpClient>) =>\n Stream.provideService(s, HttpClient.HttpClient, client)\n return {\n submit: (request) => withClient(submitJob(cfg, request)),\n poll: (ref) => withClient(pollJob(cfg, ref)),\n cancel: (ref) => withClient(cancelJob(cfg, ref)),\n streamFrom: (ref) => withClientStream(streamFromRef(cfg, ref)),\n }\n}\n\n/** Build an `OpenAIDeepResearchService`. For Layer setup, prefer {@link layer}. */\nexport const make = (\n cfg: Config,\n): Effect.Effect<OpenAIDeepResearchService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => fromJob(jobOps(cfg, client), cfg.job))\n\n/**\n * Layer registering the provider-typed `OpenAIDeepResearch` tag and the generic\n * `DeepResearch` tag. No capability markers: the whole surface (detach, live\n * stream, cancel) is universal.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<OpenAIDeepResearch | DeepResearch, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(OpenAIDeepResearch, make(cfg))\n const generic = Layer.effect(\n DeepResearch,\n Effect.map(HttpClient.HttpClient, (client): DeepResearchService => {\n const ops = jobOps(cfg, client)\n return fromJob<ResearchRequest>(\n { ...ops, submit: (request) => ops.submit(request as OpenAIResearchRequest) },\n cfg.job,\n )\n }),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { OpenAIEmbeddingModel } from "./models.mjs";
|
|
2
2
|
import { OpenAiRegion } from "./region.mjs";
|
|
3
3
|
import { Context, Effect, Layer, Redacted } from "effect";
|
|
4
|
-
import { HttpClient } from "effect/
|
|
4
|
+
import { HttpClient } from "effect/http";
|
|
5
5
|
import * as AiError from "@effect-uai/core/AiError";
|
|
6
6
|
import { CommonEmbedRequest, EmbedManyResponse, EmbedResponse, EmbeddingModel } from "@effect-uai/core/EmbeddingModel";
|
|
7
7
|
import { EmbedInput } from "@effect-uai/core/Embedding";
|
package/dist/OpenAIEmbedding.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.mjs";
|
|
2
2
|
import { resolveHost } from "./region.mjs";
|
|
3
3
|
import { Context, Effect, Layer, Schema } from "effect";
|
|
4
|
-
import { HttpClient, HttpClientRequest } from "effect/
|
|
4
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
5
5
|
import * as AiError from "@effect-uai/core/AiError";
|
|
6
6
|
import * as Capabilities from "@effect-uai/core/Capabilities";
|
|
7
7
|
import { EmbeddingModel, assertEncoding } from "@effect-uai/core/EmbeddingModel";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenAIEmbedding.mjs","names":[],"sources":["../src/OpenAIEmbedding.ts"],"sourcesContent":["import { Context, Effect, Layer, Redacted, Schema } 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 type {\n EmbedContentPart,\n EmbedInput,\n Float32Embedding,\n Usage,\n} from \"@effect-uai/core/Embedding\"\nimport {\n assertEncoding,\n type CommonEmbedManyRequest,\n type CommonEmbedRequest,\n type EmbedEncoding,\n EmbeddingModel,\n type EmbeddingModelService,\n type EmbedManyResponse,\n type EmbedResponse,\n} from \"@effect-uai/core/EmbeddingModel\"\nimport type { OpenAIEmbeddingModel } from \"./models.js\"\nimport { type OpenAiRegion, resolveHost } from \"./region.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * OpenAI's embedding API has no task-type semantics, so `task` is omitted\n * from the request type intentionally - passing it would be a compile\n * error. The generic `EmbeddingModel` registration accepts and silently\n * ignores `task` from `CommonEmbedRequest`.\n */\nexport type OpenAIEmbedRequest = Omit<CommonEmbedRequest, \"model\" | \"task\" | \"encoding\"> & {\n /** Narrows `CommonEmbedRequest.model` to the typed OpenAI union. */\n readonly model: OpenAIEmbeddingModel\n}\n\nexport type OpenAIEmbedManyRequest = Omit<OpenAIEmbedRequest, \"input\"> & {\n readonly inputs: ReadonlyArray<EmbedInput>\n}\n\nexport type OpenAIEmbeddingService = {\n readonly embed: (request: OpenAIEmbedRequest) => Effect.Effect<EmbedResponse, AiError.AiError>\n readonly embedMany: (\n request: OpenAIEmbedManyRequest,\n ) => Effect.Effect<EmbedManyResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for OpenAI-typed embedding\n * requests; yield the generic `EmbeddingModel` tag for provider-portable\n * code. Both are registered by `layer`.\n */\nexport class OpenAIEmbedding extends Context.Service<OpenAIEmbedding, OpenAIEmbeddingService>()(\n \"@betalyra/effect-uai/providers/responses/OpenAIEmbedding\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n readonly region?: OpenAiRegion\n}\n\n// ---------------------------------------------------------------------------\n// Codec - input flattening to the single string OpenAI expects\n// ---------------------------------------------------------------------------\n\n// Image input on a text-only embedder is bucket 1: a silently dropped image\n// yields a vector that represents the wrong thing. Reject with `Unsupported`\n// (capability gap), not `InvalidRequest` (which is for wire-shape mismatches).\n// OpenAI's embeddings API remains text-only as of June 2026.\nconst imageRejected: AiError.AiError = new AiError.Unsupported({\n provider: \"openai\",\n capability: \"imageEmbedding\",\n reason: \"OpenAI embeddings are text-only; pass a string or { text }.\",\n})\n\nconst partToText = (part: EmbedContentPart): Effect.Effect<string, AiError.AiError> =>\n \"text\" in part ? Effect.succeed(part.text) : Effect.fail(imageRejected)\n\n/**\n * OpenAI accepts a single string per input. Pure-text `content[]` is\n * concatenated with newlines (treats them as paragraphs of one document);\n * any image part fails with `Unsupported`.\n */\nconst inputToString = (input: EmbedInput): Effect.Effect<string, AiError.AiError> => {\n if (typeof input === \"string\") return Effect.succeed(input)\n if (\"text\" in input) return Effect.succeed(input.text)\n if (\"image\" in input) return Effect.fail(imageRejected)\n return Effect.forEach(input.content, partToText).pipe(Effect.map((texts) => texts.join(\"\\n\")))\n}\n\n// ---------------------------------------------------------------------------\n// Codec - request body\n// ---------------------------------------------------------------------------\n\ntype WireBody = {\n readonly model: string\n readonly input: string | ReadonlyArray<string>\n readonly dimensions?: number\n readonly encoding_format?: \"float\" | \"base64\"\n}\n\nconst buildSingleBody = (request: OpenAIEmbedRequest): Effect.Effect<WireBody, AiError.AiError> =>\n inputToString(request.input).pipe(\n Effect.map((input) => ({\n model: request.model,\n input,\n ...(request.dimensions !== undefined && { dimensions: request.dimensions }),\n })),\n )\n\nconst buildBatchBody = (\n request: OpenAIEmbedManyRequest,\n): Effect.Effect<WireBody, AiError.AiError> =>\n Effect.forEach(request.inputs, inputToString).pipe(\n Effect.map((inputs) => ({\n model: request.model,\n input: inputs,\n ...(request.dimensions !== undefined && { dimensions: request.dimensions }),\n })),\n )\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\nconst WireEmbeddingItem = Schema.Struct({\n embedding: Schema.Array(Schema.Number),\n index: Schema.Number,\n})\n\nconst WireUsage = Schema.Struct({\n prompt_tokens: Schema.Number,\n total_tokens: Schema.Number,\n})\n\nconst WireResponse = Schema.Struct({\n data: Schema.Array(WireEmbeddingItem),\n model: Schema.String,\n usage: WireUsage,\n})\n\nconst valuesToEmbedding = (values: ReadonlyArray<number>): Float32Embedding => ({\n _tag: \"float32\",\n vector: Float32Array.from(values),\n})\n\n// OpenAI returns `data[]` unsorted in theory; sort by `index` so caller\n// gets results in the order they passed inputs.\nconst orderedEmbeddings = (\n data: ReadonlyArray<{ readonly embedding: ReadonlyArray<number>; readonly index: number }>,\n): ReadonlyArray<Float32Embedding> =>\n [...data].sort((a, b) => a.index - b.index).map((item) => valuesToEmbedding(item.embedding))\n\n// ---------------------------------------------------------------------------\n// HTTP errors\n// ---------------------------------------------------------------------------\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"openai\"\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\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"openai\", raw: cause })\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing\n// ---------------------------------------------------------------------------\n\nconst postEmbed = (\n cfg: Config,\n body: WireBody,\n): Effect.Effect<typeof WireResponse.Type, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(`${resolveHost(cfg)}/embeddings`).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(\n Effect.mapError(transportFailure),\n )\n })\n\n// ---------------------------------------------------------------------------\n// Service implementation\n// ---------------------------------------------------------------------------\n\nconst usageOf = (u: typeof WireUsage.Type): Usage => ({ inputTokens: u.prompt_tokens })\n\n// `task` is dropped on the generic path (OpenAI has no task parameter on any\n// model, so this is a per-Layer gap, not per-model). Bucket 2: warn, don't fail.\nconst warnTaskDropped = (task: CommonEmbedRequest[\"task\"]): Effect.Effect<void> =>\n Capabilities.warnDroppedWhen(task, {\n provider: \"openai\",\n capability: \"task\",\n field: \"task\",\n reason: \"OpenAI embeddings have no task-type parameter.\",\n })\n\nconst embedImpl =\n (cfg: Config) =>\n (\n request: OpenAIEmbedRequest,\n ): Effect.Effect<EmbedResponse, AiError.AiError, HttpClient.HttpClient> =>\n buildSingleBody(request).pipe(\n Effect.flatMap((body) => postEmbed(cfg, body)),\n Effect.flatMap((decoded) => {\n const first = decoded.data[0]\n if (first === undefined) {\n return Effect.fail(transportFailure(\"OpenAI returned empty `data` array\"))\n }\n return Effect.succeed<EmbedResponse>({\n embedding: valuesToEmbedding(first.embedding),\n usage: usageOf(decoded.usage),\n })\n }),\n )\n\nconst embedManyImpl =\n (cfg: Config) =>\n (\n request: OpenAIEmbedManyRequest,\n ): Effect.Effect<EmbedManyResponse, AiError.AiError, HttpClient.HttpClient> =>\n buildBatchBody(request).pipe(\n Effect.flatMap((body) => postEmbed(cfg, body)),\n Effect.map((decoded): EmbedManyResponse => ({\n embeddings: orderedEmbeddings(decoded.data),\n usage: usageOf(decoded.usage),\n })),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build an `OpenAIEmbeddingService` value. For Layer-based setup, prefer\n * `layer`.\n */\nexport const make = (\n cfg: Config,\n): Effect.Effect<OpenAIEmbeddingService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => ({\n embed: (request) =>\n embedImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n embedMany: (request) =>\n embedManyImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n }))\n\n/**\n * Layer that registers both the provider-specific `OpenAIEmbedding` tag\n * and the generic `EmbeddingModel` tag, sharing one underlying\n * implementation. The generic registration trusts the caller; invalid\n * encodings produce an `InvalidRequest` from OpenAI's API.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<OpenAIEmbedding | EmbeddingModel, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(OpenAIEmbedding, make(cfg))\n const generic = Layer.effect(\n EmbeddingModel,\n Effect.map(make(cfg), (s): EmbeddingModelService => ({\n // OpenAI emits float32 only and has no task parameter. Reject a\n // non-float32 `encoding` (bucket 1, else a float32 vector comes back\n // mislabeled as the requested type) and warn on a dropped `task`\n // (bucket 2).\n embed: <E extends EmbedEncoding | undefined = undefined>(\n req: Omit<CommonEmbedRequest, \"encoding\"> & { readonly encoding?: E },\n ) =>\n assertEncoding(req.encoding, [\"float32\"], \"openai\").pipe(\n Effect.andThen(warnTaskDropped(req.task)),\n Effect.andThen(s.embed(req as OpenAIEmbedRequest)),\n ) as Effect.Effect<EmbedResponse<E>, AiError.AiError>,\n embedMany: <E extends EmbedEncoding | undefined = undefined>(\n req: Omit<CommonEmbedManyRequest, \"encoding\"> & { readonly encoding?: E },\n ) =>\n assertEncoding(req.encoding, [\"float32\"], \"openai\").pipe(\n Effect.andThen(warnTaskDropped(req.task)),\n Effect.andThen(s.embedMany(req as OpenAIEmbedManyRequest)),\n ) as Effect.Effect<EmbedManyResponse<E>, AiError.AiError>,\n })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OpenAIEmbedding.mjs","names":[],"sources":["../src/OpenAIEmbedding.ts"],"sourcesContent":["import { Context, Effect, Layer, Redacted, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport * as Capabilities from \"@effect-uai/core/Capabilities\"\nimport type {\n EmbedContentPart,\n EmbedInput,\n Float32Embedding,\n Usage,\n} from \"@effect-uai/core/Embedding\"\nimport {\n assertEncoding,\n type CommonEmbedManyRequest,\n type CommonEmbedRequest,\n type EmbedEncoding,\n EmbeddingModel,\n type EmbeddingModelService,\n type EmbedManyResponse,\n type EmbedResponse,\n} from \"@effect-uai/core/EmbeddingModel\"\nimport type { OpenAIEmbeddingModel } from \"./models.js\"\nimport { type OpenAiRegion, resolveHost } from \"./region.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * OpenAI's embedding API has no task-type semantics, so `task` is omitted\n * from the request type intentionally - passing it would be a compile\n * error. The generic `EmbeddingModel` registration accepts and silently\n * ignores `task` from `CommonEmbedRequest`.\n */\nexport type OpenAIEmbedRequest = Omit<CommonEmbedRequest, \"model\" | \"task\" | \"encoding\"> & {\n /** Narrows `CommonEmbedRequest.model` to the typed OpenAI union. */\n readonly model: OpenAIEmbeddingModel\n}\n\nexport type OpenAIEmbedManyRequest = Omit<OpenAIEmbedRequest, \"input\"> & {\n readonly inputs: ReadonlyArray<EmbedInput>\n}\n\nexport type OpenAIEmbeddingService = {\n readonly embed: (request: OpenAIEmbedRequest) => Effect.Effect<EmbedResponse, AiError.AiError>\n readonly embedMany: (\n request: OpenAIEmbedManyRequest,\n ) => Effect.Effect<EmbedManyResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for OpenAI-typed embedding\n * requests; yield the generic `EmbeddingModel` tag for provider-portable\n * code. Both are registered by `layer`.\n */\nexport class OpenAIEmbedding extends Context.Service<OpenAIEmbedding, OpenAIEmbeddingService>()(\n \"@betalyra/effect-uai/providers/responses/OpenAIEmbedding\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n readonly region?: OpenAiRegion\n}\n\n// ---------------------------------------------------------------------------\n// Codec - input flattening to the single string OpenAI expects\n// ---------------------------------------------------------------------------\n\n// Image input on a text-only embedder is bucket 1: a silently dropped image\n// yields a vector that represents the wrong thing. Reject with `Unsupported`\n// (capability gap), not `InvalidRequest` (which is for wire-shape mismatches).\n// OpenAI's embeddings API remains text-only as of June 2026.\nconst imageRejected: AiError.AiError = new AiError.Unsupported({\n provider: \"openai\",\n capability: \"imageEmbedding\",\n reason: \"OpenAI embeddings are text-only; pass a string or { text }.\",\n})\n\nconst partToText = (part: EmbedContentPart): Effect.Effect<string, AiError.AiError> =>\n \"text\" in part ? Effect.succeed(part.text) : Effect.fail(imageRejected)\n\n/**\n * OpenAI accepts a single string per input. Pure-text `content[]` is\n * concatenated with newlines (treats them as paragraphs of one document);\n * any image part fails with `Unsupported`.\n */\nconst inputToString = (input: EmbedInput): Effect.Effect<string, AiError.AiError> => {\n if (typeof input === \"string\") return Effect.succeed(input)\n if (\"text\" in input) return Effect.succeed(input.text)\n if (\"image\" in input) return Effect.fail(imageRejected)\n return Effect.forEach(input.content, partToText).pipe(Effect.map((texts) => texts.join(\"\\n\")))\n}\n\n// ---------------------------------------------------------------------------\n// Codec - request body\n// ---------------------------------------------------------------------------\n\ntype WireBody = {\n readonly model: string\n readonly input: string | ReadonlyArray<string>\n readonly dimensions?: number\n readonly encoding_format?: \"float\" | \"base64\"\n}\n\nconst buildSingleBody = (request: OpenAIEmbedRequest): Effect.Effect<WireBody, AiError.AiError> =>\n inputToString(request.input).pipe(\n Effect.map((input) => ({\n model: request.model,\n input,\n ...(request.dimensions !== undefined && { dimensions: request.dimensions }),\n })),\n )\n\nconst buildBatchBody = (\n request: OpenAIEmbedManyRequest,\n): Effect.Effect<WireBody, AiError.AiError> =>\n Effect.forEach(request.inputs, inputToString).pipe(\n Effect.map((inputs) => ({\n model: request.model,\n input: inputs,\n ...(request.dimensions !== undefined && { dimensions: request.dimensions }),\n })),\n )\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\nconst WireEmbeddingItem = Schema.Struct({\n embedding: Schema.Array(Schema.Number),\n index: Schema.Number,\n})\n\nconst WireUsage = Schema.Struct({\n prompt_tokens: Schema.Number,\n total_tokens: Schema.Number,\n})\n\nconst WireResponse = Schema.Struct({\n data: Schema.Array(WireEmbeddingItem),\n model: Schema.String,\n usage: WireUsage,\n})\n\nconst valuesToEmbedding = (values: ReadonlyArray<number>): Float32Embedding => ({\n _tag: \"float32\",\n vector: Float32Array.from(values),\n})\n\n// OpenAI returns `data[]` unsorted in theory; sort by `index` so caller\n// gets results in the order they passed inputs.\nconst orderedEmbeddings = (\n data: ReadonlyArray<{ readonly embedding: ReadonlyArray<number>; readonly index: number }>,\n): ReadonlyArray<Float32Embedding> =>\n [...data].sort((a, b) => a.index - b.index).map((item) => valuesToEmbedding(item.embedding))\n\n// ---------------------------------------------------------------------------\n// HTTP errors\n// ---------------------------------------------------------------------------\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"openai\"\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\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"openai\", raw: cause })\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing\n// ---------------------------------------------------------------------------\n\nconst postEmbed = (\n cfg: Config,\n body: WireBody,\n): Effect.Effect<typeof WireResponse.Type, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(`${resolveHost(cfg)}/embeddings`).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(\n Effect.mapError(transportFailure),\n )\n })\n\n// ---------------------------------------------------------------------------\n// Service implementation\n// ---------------------------------------------------------------------------\n\nconst usageOf = (u: typeof WireUsage.Type): Usage => ({ inputTokens: u.prompt_tokens })\n\n// `task` is dropped on the generic path (OpenAI has no task parameter on any\n// model, so this is a per-Layer gap, not per-model). Bucket 2: warn, don't fail.\nconst warnTaskDropped = (task: CommonEmbedRequest[\"task\"]): Effect.Effect<void> =>\n Capabilities.warnDroppedWhen(task, {\n provider: \"openai\",\n capability: \"task\",\n field: \"task\",\n reason: \"OpenAI embeddings have no task-type parameter.\",\n })\n\nconst embedImpl =\n (cfg: Config) =>\n (\n request: OpenAIEmbedRequest,\n ): Effect.Effect<EmbedResponse, AiError.AiError, HttpClient.HttpClient> =>\n buildSingleBody(request).pipe(\n Effect.flatMap((body) => postEmbed(cfg, body)),\n Effect.flatMap((decoded) => {\n const first = decoded.data[0]\n if (first === undefined) {\n return Effect.fail(transportFailure(\"OpenAI returned empty `data` array\"))\n }\n return Effect.succeed<EmbedResponse>({\n embedding: valuesToEmbedding(first.embedding),\n usage: usageOf(decoded.usage),\n })\n }),\n )\n\nconst embedManyImpl =\n (cfg: Config) =>\n (\n request: OpenAIEmbedManyRequest,\n ): Effect.Effect<EmbedManyResponse, AiError.AiError, HttpClient.HttpClient> =>\n buildBatchBody(request).pipe(\n Effect.flatMap((body) => postEmbed(cfg, body)),\n Effect.map((decoded): EmbedManyResponse => ({\n embeddings: orderedEmbeddings(decoded.data),\n usage: usageOf(decoded.usage),\n })),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build an `OpenAIEmbeddingService` value. For Layer-based setup, prefer\n * `layer`.\n */\nexport const make = (\n cfg: Config,\n): Effect.Effect<OpenAIEmbeddingService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => ({\n embed: (request) =>\n embedImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n embedMany: (request) =>\n embedManyImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n }))\n\n/**\n * Layer that registers both the provider-specific `OpenAIEmbedding` tag\n * and the generic `EmbeddingModel` tag, sharing one underlying\n * implementation. The generic registration trusts the caller; invalid\n * encodings produce an `InvalidRequest` from OpenAI's API.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<OpenAIEmbedding | EmbeddingModel, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(OpenAIEmbedding, make(cfg))\n const generic = Layer.effect(\n EmbeddingModel,\n Effect.map(make(cfg), (s): EmbeddingModelService => ({\n // OpenAI emits float32 only and has no task parameter. Reject a\n // non-float32 `encoding` (bucket 1, else a float32 vector comes back\n // mislabeled as the requested type) and warn on a dropped `task`\n // (bucket 2).\n embed: <E extends EmbedEncoding | undefined = undefined>(\n req: Omit<CommonEmbedRequest, \"encoding\"> & { readonly encoding?: E },\n ) =>\n assertEncoding(req.encoding, [\"float32\"], \"openai\").pipe(\n Effect.andThen(warnTaskDropped(req.task)),\n Effect.andThen(s.embed(req as OpenAIEmbedRequest)),\n ) as Effect.Effect<EmbedResponse<E>, AiError.AiError>,\n embedMany: <E extends EmbedEncoding | undefined = undefined>(\n req: Omit<CommonEmbedManyRequest, \"encoding\"> & { readonly encoding?: E },\n ) =>\n assertEncoding(req.encoding, [\"float32\"], \"openai\").pipe(\n Effect.andThen(warnTaskDropped(req.task)),\n Effect.andThen(s.embedMany(req as OpenAIEmbedManyRequest)),\n ) as Effect.Effect<EmbedManyResponse<E>, AiError.AiError>,\n })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
package/dist/Responses.d.mts
CHANGED
|
@@ -3,7 +3,7 @@ import { OpenAiRegion } from "./region.mjs";
|
|
|
3
3
|
import { UserLocation, WebSearchFilters, WebSearchOptions, codeInterpreterTool, fileSearchTool, webSearchTool } from "./ResponsesTools.mjs";
|
|
4
4
|
import { ProviderEvent } from "./streamEvents.mjs";
|
|
5
5
|
import { Context, Effect, Layer, Redacted, Stream } from "effect";
|
|
6
|
-
import { HttpClient, HttpClientResponse } from "effect/
|
|
6
|
+
import { HttpClient, HttpClientResponse } from "effect/http";
|
|
7
7
|
import * as AiError from "@effect-uai/core/AiError";
|
|
8
8
|
import { CommonRequest, LanguageModel } from "@effect-uai/core/LanguageModel";
|
|
9
9
|
import { Turn, TurnEvent } from "@effect-uai/core/Turn";
|
package/dist/Responses.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.mjs";
|
|
2
2
|
import { itemsToInput } from "./codec.mjs";
|
|
3
3
|
import { resolveHost } from "./region.mjs";
|
|
4
4
|
import { codeInterpreterTool, fileSearchTool, renderProviderTools, webSearchTool } from "./ResponsesTools.mjs";
|
|
5
5
|
import { KnownProviderEvent, eventToDeltas, makeCallIdLookup } from "./streamEvents.mjs";
|
|
6
6
|
import { Context, Effect, Layer, Match, Option, Result, Schema, Stream } from "effect";
|
|
7
|
-
import { HttpClient, HttpClientRequest } from "effect/
|
|
7
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
8
8
|
import * as AiError from "@effect-uai/core/AiError";
|
|
9
9
|
import * as Capabilities from "@effect-uai/core/Capabilities";
|
|
10
10
|
import { LanguageModel, turnFromStream } from "@effect-uai/core/LanguageModel";
|
package/dist/Responses.mjs.map
CHANGED
|
@@ -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 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
|
+
{"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/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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"}
|
package/dist/ResponsesTools.mjs
CHANGED
package/dist/codec.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;cA6Ca,gBAAc,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;cAkEd,gBAAc,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAQf,wBAAwB,eAAe;cA6BtC,uBAAqB,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KActB,+BAA+B,sBAAsB;;cA+HpD,eAAY,OAChB,cAAc,iBACpB,cAAc;cAiBJ,iBAAc,MAAU,mBAAiB,cAAc;cAwEvD,oBAAiB,SAAa,0BAAwB"}
|
package/dist/codec.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Array,
|
|
1
|
+
import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.mjs";
|
|
2
|
+
import { Array, Match, Option, Result, Schema, pipe } from "effect";
|
|
3
|
+
import { Base64 } from "effect/encoding";
|
|
4
|
+
import * as ToolOutput from "@effect-uai/core/ToolOutput";
|
|
3
5
|
//#region src/codec.ts
|
|
4
6
|
var codec_exports = /* @__PURE__ */ __exportAll({
|
|
5
7
|
WireAnnotation: () => WireAnnotation,
|
|
@@ -146,7 +148,28 @@ const passthrough = (item) => pipe(decodeResponsesData(item.providerData), Resul
|
|
|
146
148
|
* inline bytes get encoded as a `data:` URI. Either form (URL or data URI)
|
|
147
149
|
* is fine - the model just dereferences `image_url`.
|
|
148
150
|
*/
|
|
149
|
-
const imageSourceToUrl = Match.type().pipe(Match.tag("url", (s) => s.url), Match.tag("base64", (s) => `data:${s.mimeType};base64,${s.base64}`), Match.tag("bytes", (s) => `data:${s.mimeType};base64,${
|
|
151
|
+
const imageSourceToUrl = Match.type().pipe(Match.tag("url", (s) => s.url), Match.tag("base64", (s) => `data:${s.mimeType};base64,${s.base64}`), Match.tag("bytes", (s) => `data:${s.mimeType};base64,${Base64.encode(s.bytes)}`), Match.exhaustive);
|
|
152
|
+
const fileSourceToInput = Match.type().pipe(Match.tag("url", (s) => ({ file_url: s.url })), Match.tag("base64", (s) => ({ file_data: `data:${s.mimeType};base64,${s.base64}` })), Match.tag("bytes", (s) => ({ file_data: `data:${s.mimeType};base64,${Base64.encode(s.bytes)}` })), Match.exhaustive);
|
|
153
|
+
const toolOutputPart = Match.type().pipe(Match.discriminatorsExhaustive("type")({
|
|
154
|
+
input_text: (p) => ({
|
|
155
|
+
type: "input_text",
|
|
156
|
+
text: ToolOutput.partText(p)
|
|
157
|
+
}),
|
|
158
|
+
input_json: (p) => ({
|
|
159
|
+
type: "input_text",
|
|
160
|
+
text: ToolOutput.partText(p)
|
|
161
|
+
}),
|
|
162
|
+
input_image: (p) => ({
|
|
163
|
+
type: "input_image",
|
|
164
|
+
image_url: imageSourceToUrl(p.source)
|
|
165
|
+
}),
|
|
166
|
+
input_file: (p) => ({
|
|
167
|
+
type: "input_file",
|
|
168
|
+
...fileSourceToInput(p.source),
|
|
169
|
+
...p.filename !== void 0 && { filename: p.filename }
|
|
170
|
+
})
|
|
171
|
+
}));
|
|
172
|
+
const toolOutput = (parts) => Option.getOrElse(ToolOutput.text(parts), () => parts.map(toolOutputPart));
|
|
150
173
|
const contentBlockToInput = Match.type().pipe(Match.discriminatorsExhaustive("type")({
|
|
151
174
|
input_text: (b) => ({
|
|
152
175
|
type: "input_text",
|
|
@@ -184,7 +207,7 @@ const itemToInput = (item) => passthrough(item) ?? Match.value(item).pipe(Match.
|
|
|
184
207
|
function_call_output: (o) => ({
|
|
185
208
|
type: "function_call_output",
|
|
186
209
|
call_id: o.call_id,
|
|
187
|
-
output: o.output
|
|
210
|
+
output: toolOutput(o.output)
|
|
188
211
|
}),
|
|
189
212
|
reasoning: (r) => ({
|
|
190
213
|
type: "reasoning",
|
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, 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":"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"}
|
|
1
|
+
{"version":3,"file":"codec.mjs","names":["Arr"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Match, Option, Result, Schema, pipe } from \"effect\"\nimport { Base64 } from \"effect/encoding\"\nimport type {\n ContentBlock,\n HistoryItem,\n InputFile,\n InputImage,\n ToolOutputContent,\n} from \"@effect-uai/core/Items\"\nimport * as ToolOutput from \"@effect-uai/core/ToolOutput\"\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,${Base64.encode(s.bytes)}`),\n Match.exhaustive,\n)\n\nconst fileSourceToInput = Match.type<InputFile[\"source\"]>().pipe(\n Match.tag(\"url\", (s) => ({ file_url: s.url })),\n Match.tag(\"base64\", (s) => ({ file_data: `data:${s.mimeType};base64,${s.base64}` })),\n Match.tag(\"bytes\", (s) => ({\n file_data: `data:${s.mimeType};base64,${Base64.encode(s.bytes)}`,\n })),\n Match.exhaustive,\n)\n\nconst toolOutputPart = Match.type<ToolOutputContent>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (p) => ({ type: \"input_text\", text: ToolOutput.partText(p) }),\n input_json: (p) => ({ type: \"input_text\", text: ToolOutput.partText(p) }),\n input_image: (p) => ({ type: \"input_image\", image_url: imageSourceToUrl(p.source) }),\n input_file: (p) => ({\n type: \"input_file\",\n ...fileSourceToInput(p.source),\n ...(p.filename !== undefined && { filename: p.filename }),\n }),\n }),\n)\n\n// Text and JSON only: one string (the widest supported form). With media: native parts.\n// No error flag on this wire: a failure is told by its content.\nconst toolOutput = (parts: ReadonlyArray<ToolOutputContent>): string | ReadonlyArray<object> =>\n Option.getOrElse(ToolOutput.text(parts), () => parts.map(toolOutputPart))\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: toolOutput(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":";;;;;;;;;;;;;AAiBA,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,OAAO,OAAO,EAAE,KAAK,GAAG,GAC/E,MAAM,UACR;AAEA,MAAM,oBAAoB,MAAM,KAA0B,CAAC,CAAC,KAC1D,MAAM,IAAI,QAAQ,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,GAC7C,MAAM,IAAI,WAAW,OAAO,EAAE,WAAW,QAAQ,EAAE,SAAS,UAAU,EAAE,SAAS,EAAE,GACnF,MAAM,IAAI,UAAU,OAAO,EACzB,WAAW,QAAQ,EAAE,SAAS,UAAU,OAAO,OAAO,EAAE,KAAK,IAC/D,EAAE,GACF,MAAM,UACR;AAEA,MAAM,iBAAiB,MAAM,KAAwB,CAAC,CAAC,KACrD,MAAM,yBAAyB,MAAM,CAAC,CAAC;CACrC,aAAa,OAAO;EAAE,MAAM;EAAc,MAAM,WAAW,SAAS,CAAC;CAAE;CACvE,aAAa,OAAO;EAAE,MAAM;EAAc,MAAM,WAAW,SAAS,CAAC;CAAE;CACvE,cAAc,OAAO;EAAE,MAAM;EAAe,WAAW,iBAAiB,EAAE,MAAM;CAAE;CAClF,aAAa,OAAO;EAClB,MAAM;EACN,GAAG,kBAAkB,EAAE,MAAM;EAC7B,GAAI,EAAE,aAAa,KAAA,KAAa,EAAE,UAAU,EAAE,SAAS;CACzD;AACF,CAAC,CACH;AAIA,MAAM,cAAc,UAClB,OAAO,UAAU,WAAW,KAAK,KAAK,SAAS,MAAM,IAAI,cAAc,CAAC;AAE1E,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,WAAW,EAAE,MAAM;CAC7B;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"}
|