@effect-uai/responses 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/OpenAIEmbedding.d.mts +53 -0
- package/dist/OpenAIEmbedding.d.mts.map +1 -0
- package/dist/OpenAIEmbedding.mjs +157 -0
- package/dist/OpenAIEmbedding.mjs.map +1 -0
- package/dist/Responses.d.mts +96 -0
- package/dist/Responses.d.mts.map +1 -0
- package/dist/Responses.mjs +194 -0
- package/dist/Responses.mjs.map +1 -0
- package/dist/codec.d.mts +133 -0
- package/dist/codec.d.mts.map +1 -0
- package/dist/codec.mjs +218 -0
- package/dist/codec.mjs.map +1 -0
- package/dist/index.d.mts +6 -828
- package/dist/index.mjs +6 -525
- package/dist/models.d.mts +25 -0
- package/dist/models.d.mts.map +1 -0
- package/dist/models.mjs +1 -0
- package/dist/streamEvents.d.mts +607 -0
- package/dist/streamEvents.d.mts.map +1 -0
- package/dist/streamEvents.mjs +170 -0
- package/dist/streamEvents.mjs.map +1 -0
- package/package.json +20 -12
- package/src/OpenAIEmbedding.ts +275 -0
- package/src/Responses.ts +15 -17
- package/src/codec.ts +75 -66
- package/src/index.ts +3 -2
- package/src/models.ts +18 -0
- package/src/streamEvents.ts +48 -52
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { OpenAIEmbeddingModel } from "./models.mjs";
|
|
2
|
+
import { Context, Effect, Layer, Redacted } from "effect";
|
|
3
|
+
import { HttpClient } from "effect/unstable/http";
|
|
4
|
+
import * as AiError from "@effect-uai/core/AiError";
|
|
5
|
+
import { CommonEmbedRequest, EmbedManyResponse, EmbedResponse, EmbeddingModel } from "@effect-uai/core/EmbeddingModel";
|
|
6
|
+
import { EmbedInput } from "@effect-uai/core/Embedding";
|
|
7
|
+
|
|
8
|
+
//#region src/OpenAIEmbedding.d.ts
|
|
9
|
+
declare namespace OpenAIEmbedding_d_exports {
|
|
10
|
+
export { Config, OpenAIEmbedManyRequest, OpenAIEmbedRequest, OpenAIEmbedding, OpenAIEmbeddingService, layer, make };
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* OpenAI's embedding API has no task-type semantics, so `task` is omitted
|
|
14
|
+
* from the request type intentionally - passing it would be a compile
|
|
15
|
+
* error. The generic `EmbeddingModel` registration accepts and silently
|
|
16
|
+
* ignores `task` from `CommonEmbedRequest`.
|
|
17
|
+
*/
|
|
18
|
+
type OpenAIEmbedRequest = Omit<CommonEmbedRequest, "model" | "task"> & {
|
|
19
|
+
/** Narrows `CommonEmbedRequest.model` to the typed OpenAI union. */readonly model: OpenAIEmbeddingModel;
|
|
20
|
+
};
|
|
21
|
+
type OpenAIEmbedManyRequest = Omit<OpenAIEmbedRequest, "input"> & {
|
|
22
|
+
readonly inputs: ReadonlyArray<EmbedInput>;
|
|
23
|
+
};
|
|
24
|
+
type OpenAIEmbeddingService = {
|
|
25
|
+
readonly embed: (request: OpenAIEmbedRequest) => Effect.Effect<EmbedResponse, AiError.AiError>;
|
|
26
|
+
readonly embedMany: (request: OpenAIEmbedManyRequest) => Effect.Effect<EmbedManyResponse, AiError.AiError>;
|
|
27
|
+
};
|
|
28
|
+
declare const OpenAIEmbedding_base: Context.ServiceClass<OpenAIEmbedding, "@betalyra/effect-uai/providers/responses/OpenAIEmbedding", OpenAIEmbeddingService>;
|
|
29
|
+
/**
|
|
30
|
+
* Provider-typed service tag. Yield this for OpenAI-typed embedding
|
|
31
|
+
* requests; yield the generic `EmbeddingModel` tag for provider-portable
|
|
32
|
+
* code. Both are registered by `layer`.
|
|
33
|
+
*/
|
|
34
|
+
declare class OpenAIEmbedding extends OpenAIEmbedding_base {}
|
|
35
|
+
type Config = {
|
|
36
|
+
readonly apiKey: Redacted.Redacted;
|
|
37
|
+
readonly baseUrl?: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Build an `OpenAIEmbeddingService` value. For Layer-based setup, prefer
|
|
41
|
+
* `layer`.
|
|
42
|
+
*/
|
|
43
|
+
declare const make: (cfg: Config) => Effect.Effect<OpenAIEmbeddingService, never, HttpClient.HttpClient>;
|
|
44
|
+
/**
|
|
45
|
+
* Layer that registers both the provider-specific `OpenAIEmbedding` tag
|
|
46
|
+
* and the generic `EmbeddingModel` tag, sharing one underlying
|
|
47
|
+
* implementation. The generic registration trusts the caller; invalid
|
|
48
|
+
* encodings produce an `InvalidRequest` from OpenAI's API.
|
|
49
|
+
*/
|
|
50
|
+
declare const layer: (cfg: Config) => Layer.Layer<OpenAIEmbedding | EmbeddingModel, never, HttpClient.HttpClient>;
|
|
51
|
+
//#endregion
|
|
52
|
+
export { Config, OpenAIEmbedManyRequest, OpenAIEmbedRequest, OpenAIEmbedding, OpenAIEmbeddingService, layer, make, OpenAIEmbedding_d_exports as t };
|
|
53
|
+
//# sourceMappingURL=OpenAIEmbedding.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenAIEmbedding.d.mts","names":[],"sources":["../src/OpenAIEmbedding.ts"],"mappings":";;;;;;;;;;;;;;;;;KAwBY,kBAAA,GAAqB,IAAA,CAAK,kBAAA;+EAE3B,KAAA,EAAO,oBAAA;AAAA;AAAA,KAGN,sBAAA,GAAyB,IAAA,CAAK,kBAAA;EAAA,SAC/B,MAAA,EAAQ,aAAA,CAAc,UAAA;AAAA;AAAA,KAGrB,sBAAA;EAAA,SACD,KAAA,GAAQ,OAAA,EAAS,kBAAA,KAAuB,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,OAAA,CAAQ,OAAA;EAAA,SAC7E,SAAA,GACP,OAAA,EAAS,sBAAA,KACN,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,OAAA,CAAQ,OAAA;AAAA;AAAA,cAC/C,oBAAA;;;;;;cAOY,eAAA,SAAwB,oBAAA;AAAA,KAIzB,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA;AAAA;;;;;cAgME,IAAA,GACX,GAAA,EAAK,MAAA,KACJ,MAAA,CAAO,MAAA,CAAO,sBAAA,SAA+B,UAAA,CAAW,UAAA;;;;;;;cAc9C,KAAA,GACX,GAAA,EAAK,MAAA,KACJ,KAAA,CAAM,KAAA,CAAM,eAAA,GAAkB,cAAA,SAAuB,UAAA,CAAW,UAAA"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
|
+
import { Context, Effect, Layer, Schema } from "effect";
|
|
3
|
+
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
4
|
+
import * as AiError from "@effect-uai/core/AiError";
|
|
5
|
+
import { EmbeddingModel } from "@effect-uai/core/EmbeddingModel";
|
|
6
|
+
//#region src/OpenAIEmbedding.ts
|
|
7
|
+
var OpenAIEmbedding_exports = /* @__PURE__ */ __exportAll({
|
|
8
|
+
OpenAIEmbedding: () => OpenAIEmbedding,
|
|
9
|
+
layer: () => layer,
|
|
10
|
+
make: () => make
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* Provider-typed service tag. Yield this for OpenAI-typed embedding
|
|
14
|
+
* requests; yield the generic `EmbeddingModel` tag for provider-portable
|
|
15
|
+
* code. Both are registered by `layer`.
|
|
16
|
+
*/
|
|
17
|
+
var OpenAIEmbedding = class extends Context.Service()("@betalyra/effect-uai/providers/responses/OpenAIEmbedding") {};
|
|
18
|
+
const imageRejected = (param) => new AiError.InvalidRequest({
|
|
19
|
+
provider: "openai",
|
|
20
|
+
param,
|
|
21
|
+
raw: "OpenAI embeddings are text-only; pass a string or { text }"
|
|
22
|
+
});
|
|
23
|
+
const partToText = (param, part) => "text" in part ? Effect.succeed(part.text) : Effect.fail(imageRejected(param));
|
|
24
|
+
/**
|
|
25
|
+
* OpenAI accepts a single string per input. Pure-text `content[]` is
|
|
26
|
+
* concatenated with newlines (treats them as paragraphs of one document);
|
|
27
|
+
* any image part fails with `InvalidRequest`.
|
|
28
|
+
*/
|
|
29
|
+
const inputToString = (input) => {
|
|
30
|
+
if (typeof input === "string") return Effect.succeed(input);
|
|
31
|
+
if ("text" in input) return Effect.succeed(input.text);
|
|
32
|
+
if ("image" in input) return Effect.fail(imageRejected("input.image"));
|
|
33
|
+
return Effect.forEach(input.content, (p, i) => partToText(`input.content[${i}].image`, p)).pipe(Effect.map((texts) => texts.join("\n")));
|
|
34
|
+
};
|
|
35
|
+
const buildSingleBody = (request) => inputToString(request.input).pipe(Effect.map((input) => ({
|
|
36
|
+
model: request.model,
|
|
37
|
+
input,
|
|
38
|
+
...request.dimensions !== void 0 && { dimensions: request.dimensions }
|
|
39
|
+
})));
|
|
40
|
+
const buildBatchBody = (request) => Effect.forEach(request.inputs, inputToString).pipe(Effect.map((inputs) => ({
|
|
41
|
+
model: request.model,
|
|
42
|
+
input: inputs,
|
|
43
|
+
...request.dimensions !== void 0 && { dimensions: request.dimensions }
|
|
44
|
+
})));
|
|
45
|
+
const WireEmbeddingItem = Schema.Struct({
|
|
46
|
+
embedding: Schema.Array(Schema.Number),
|
|
47
|
+
index: Schema.Number
|
|
48
|
+
});
|
|
49
|
+
const WireUsage = Schema.Struct({
|
|
50
|
+
prompt_tokens: Schema.Number,
|
|
51
|
+
total_tokens: Schema.Number
|
|
52
|
+
});
|
|
53
|
+
const WireResponse = Schema.Struct({
|
|
54
|
+
data: Schema.Array(WireEmbeddingItem),
|
|
55
|
+
model: Schema.String,
|
|
56
|
+
usage: WireUsage
|
|
57
|
+
});
|
|
58
|
+
const valuesToEmbedding = (values) => ({
|
|
59
|
+
_tag: "float32",
|
|
60
|
+
vector: Float32Array.from(values)
|
|
61
|
+
});
|
|
62
|
+
const orderedEmbeddings = (data) => [...data].sort((a, b) => a.index - b.index).map((item) => valuesToEmbedding(item.embedding));
|
|
63
|
+
const httpStatusError = (status, body) => {
|
|
64
|
+
const provider = "openai";
|
|
65
|
+
const raw = body;
|
|
66
|
+
if (status === 429) return new AiError.RateLimited({
|
|
67
|
+
provider,
|
|
68
|
+
raw
|
|
69
|
+
});
|
|
70
|
+
if (status === 408 || status === 504) return new AiError.Timeout({
|
|
71
|
+
provider,
|
|
72
|
+
raw
|
|
73
|
+
});
|
|
74
|
+
if (status === 401) return new AiError.AuthFailed({
|
|
75
|
+
provider,
|
|
76
|
+
subtype: "auth",
|
|
77
|
+
raw
|
|
78
|
+
});
|
|
79
|
+
if (status === 403) return new AiError.AuthFailed({
|
|
80
|
+
provider,
|
|
81
|
+
subtype: "permission",
|
|
82
|
+
raw
|
|
83
|
+
});
|
|
84
|
+
if (status === 402) return new AiError.AuthFailed({
|
|
85
|
+
provider,
|
|
86
|
+
subtype: "billing",
|
|
87
|
+
raw
|
|
88
|
+
});
|
|
89
|
+
if (status === 413) return new AiError.ContextLengthExceeded({
|
|
90
|
+
provider,
|
|
91
|
+
raw
|
|
92
|
+
});
|
|
93
|
+
if (status >= 500) return new AiError.Unavailable({
|
|
94
|
+
provider,
|
|
95
|
+
status,
|
|
96
|
+
raw
|
|
97
|
+
});
|
|
98
|
+
return new AiError.InvalidRequest({
|
|
99
|
+
provider,
|
|
100
|
+
raw
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
const transportFailure = (cause) => new AiError.Unavailable({
|
|
104
|
+
provider: "openai",
|
|
105
|
+
raw: cause
|
|
106
|
+
});
|
|
107
|
+
const baseUrl = (cfg) => cfg.baseUrl ?? "https://api.openai.com/v1";
|
|
108
|
+
const postEmbed = (cfg, body) => Effect.gen(function* () {
|
|
109
|
+
const client = yield* HttpClient.HttpClient;
|
|
110
|
+
const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/embeddings`).pipe(HttpClientRequest.bearerToken(cfg.apiKey), HttpClientRequest.bodyJsonUnsafe(body));
|
|
111
|
+
const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure));
|
|
112
|
+
if (response.status >= 400) {
|
|
113
|
+
const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
|
|
114
|
+
return yield* Effect.fail(httpStatusError(response.status, text));
|
|
115
|
+
}
|
|
116
|
+
const json = yield* response.json.pipe(Effect.mapError(transportFailure));
|
|
117
|
+
return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(Effect.mapError(transportFailure));
|
|
118
|
+
});
|
|
119
|
+
const usageOf = (u) => ({ inputTokens: u.prompt_tokens });
|
|
120
|
+
const embedImpl = (cfg) => (request) => buildSingleBody(request).pipe(Effect.flatMap((body) => postEmbed(cfg, body)), Effect.flatMap((decoded) => {
|
|
121
|
+
const first = decoded.data[0];
|
|
122
|
+
if (first === void 0) return Effect.fail(transportFailure("OpenAI returned empty `data` array"));
|
|
123
|
+
return Effect.succeed({
|
|
124
|
+
embedding: valuesToEmbedding(first.embedding),
|
|
125
|
+
usage: usageOf(decoded.usage)
|
|
126
|
+
});
|
|
127
|
+
}));
|
|
128
|
+
const embedManyImpl = (cfg) => (request) => buildBatchBody(request).pipe(Effect.flatMap((body) => postEmbed(cfg, body)), Effect.map((decoded) => ({
|
|
129
|
+
embeddings: orderedEmbeddings(decoded.data),
|
|
130
|
+
usage: usageOf(decoded.usage)
|
|
131
|
+
})));
|
|
132
|
+
/**
|
|
133
|
+
* Build an `OpenAIEmbeddingService` value. For Layer-based setup, prefer
|
|
134
|
+
* `layer`.
|
|
135
|
+
*/
|
|
136
|
+
const make = (cfg) => Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({
|
|
137
|
+
embed: (request) => embedImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),
|
|
138
|
+
embedMany: (request) => embedManyImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client))
|
|
139
|
+
}));
|
|
140
|
+
/**
|
|
141
|
+
* Layer that registers both the provider-specific `OpenAIEmbedding` tag
|
|
142
|
+
* and the generic `EmbeddingModel` tag, sharing one underlying
|
|
143
|
+
* implementation. The generic registration trusts the caller; invalid
|
|
144
|
+
* encodings produce an `InvalidRequest` from OpenAI's API.
|
|
145
|
+
*/
|
|
146
|
+
const layer = (cfg) => {
|
|
147
|
+
const typed = Layer.effect(OpenAIEmbedding, make(cfg));
|
|
148
|
+
const generic = Layer.effect(EmbeddingModel, Effect.map(make(cfg), (s) => ({
|
|
149
|
+
embed: (req) => s.embed(req),
|
|
150
|
+
embedMany: (req) => s.embedMany(req)
|
|
151
|
+
})));
|
|
152
|
+
return Layer.merge(typed, generic);
|
|
153
|
+
};
|
|
154
|
+
//#endregion
|
|
155
|
+
export { OpenAIEmbedding, layer, make, OpenAIEmbedding_exports as t };
|
|
156
|
+
|
|
157
|
+
//# sourceMappingURL=OpenAIEmbedding.mjs.map
|
|
@@ -0,0 +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 type { Embedding, EmbedContentPart, EmbedInput, Usage } from \"@effect-uai/core/Embedding\"\nimport {\n type CommonEmbedManyRequest,\n type CommonEmbedRequest,\n EmbeddingModel,\n type EmbeddingModelService,\n type EmbedManyResponse,\n type EmbedResponse,\n} from \"@effect-uai/core/EmbeddingModel\"\nimport type { OpenAIEmbeddingModel } from \"./models.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\"> & {\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}\n\n// ---------------------------------------------------------------------------\n// Codec - input flattening to the single string OpenAI expects\n// ---------------------------------------------------------------------------\n\nconst imageRejected = (param: string): AiError.AiError =>\n new AiError.InvalidRequest({\n provider: \"openai\",\n param,\n raw: \"OpenAI embeddings are text-only; pass a string or { text }\",\n })\n\nconst partToText = (\n param: string,\n part: EmbedContentPart,\n): Effect.Effect<string, AiError.AiError> =>\n \"text\" in part ? Effect.succeed(part.text) : Effect.fail(imageRejected(param))\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 `InvalidRequest`.\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(\"input.image\"))\n return Effect.forEach(input.content, (p, i) => partToText(`input.content[${i}].image`, p)).pipe(\n Effect.map((texts) => texts.join(\"\\n\")),\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>): Embedding => ({\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<Embedding> =>\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 baseUrl = (cfg: Config): string => cfg.baseUrl ?? \"https://api.openai.com/v1\"\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(`${baseUrl(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* Effect.fail(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\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(\n (decoded): EmbedManyResponse => ({\n embeddings: orderedEmbeddings(decoded.data),\n usage: usageOf(decoded.usage),\n }),\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.asEffect(), (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(\n make(cfg),\n (s): EmbeddingModelService => ({\n embed: (req: CommonEmbedRequest) => s.embed(req as OpenAIEmbedRequest),\n embedMany: (req: CommonEmbedManyRequest) => s.embedMany(req as OpenAIEmbedManyRequest),\n }),\n ),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { OpenAIModel } from "./models.mjs";
|
|
2
|
+
import { ProviderEvent } from "./streamEvents.mjs";
|
|
3
|
+
import { Context, Effect, Layer, Redacted, Stream } from "effect";
|
|
4
|
+
import { HttpClient } from "effect/unstable/http";
|
|
5
|
+
import * as AiError from "@effect-uai/core/AiError";
|
|
6
|
+
import { CommonRequest, LanguageModel } from "@effect-uai/core/LanguageModel";
|
|
7
|
+
import { TurnEvent } from "@effect-uai/core/Turn";
|
|
8
|
+
|
|
9
|
+
//#region src/Responses.d.ts
|
|
10
|
+
declare namespace Responses_d_exports {
|
|
11
|
+
export { Config, Responses, ResponsesRequest, ResponsesService, layer, make, toCanonical };
|
|
12
|
+
}
|
|
13
|
+
type ResponsesRequest = Omit<CommonRequest, "model"> & {
|
|
14
|
+
/**
|
|
15
|
+
* Narrows `CommonRequest.model` (`string`) to the typed `OpenAIModel`
|
|
16
|
+
* literal union for autocomplete.
|
|
17
|
+
*/
|
|
18
|
+
readonly model: OpenAIModel;
|
|
19
|
+
readonly reasoning?: {
|
|
20
|
+
readonly effort: "low" | "medium" | "high";
|
|
21
|
+
};
|
|
22
|
+
readonly store?: boolean;
|
|
23
|
+
readonly previousResponseId?: string;
|
|
24
|
+
readonly instructions?: string;
|
|
25
|
+
readonly parallelToolCalls?: boolean;
|
|
26
|
+
readonly metadata?: Readonly<Record<string, string>>;
|
|
27
|
+
readonly user?: string;
|
|
28
|
+
readonly safetyIdentifier?: string;
|
|
29
|
+
readonly promptCacheKey?: string;
|
|
30
|
+
readonly truncation?: "auto" | "disabled";
|
|
31
|
+
/**
|
|
32
|
+
* Free-form JSON output: model emits valid JSON without schema
|
|
33
|
+
* constraints. Mutually exclusive with `structured` (schema wins if
|
|
34
|
+
* both are set).
|
|
35
|
+
*/
|
|
36
|
+
readonly responseFormat?: {
|
|
37
|
+
readonly type: "json_object";
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Answer length / level-of-detail hint. GPT-5+ models honour it;
|
|
41
|
+
* earlier models ignore.
|
|
42
|
+
*/
|
|
43
|
+
readonly verbosity?: "low" | "medium" | "high";
|
|
44
|
+
};
|
|
45
|
+
type ResponsesService = {
|
|
46
|
+
/**
|
|
47
|
+
* Stream the provider's native event vocabulary (post-SSE-decode).
|
|
48
|
+
* Use this when you need full vendor fidelity. For provider-portable
|
|
49
|
+
* code, use `streamTurn` instead.
|
|
50
|
+
*/
|
|
51
|
+
readonly streamNative: (request: ResponsesRequest) => Stream.Stream<ProviderEvent, AiError.AiError>;
|
|
52
|
+
/**
|
|
53
|
+
* Stream canonical `TurnEvent`s. Implemented as
|
|
54
|
+
* `streamNative |> toCanonical`.
|
|
55
|
+
*/
|
|
56
|
+
readonly streamTurn: (request: ResponsesRequest) => Stream.Stream<TurnEvent, AiError.AiError>;
|
|
57
|
+
/**
|
|
58
|
+
* Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.
|
|
59
|
+
* Exposed for cases where consumers want to compose with `streamNative`
|
|
60
|
+
* directly (e.g. tap into natives + still get canonical downstream).
|
|
61
|
+
*/
|
|
62
|
+
readonly toCanonical: <E, R>(s: Stream.Stream<ProviderEvent, E, R>) => Stream.Stream<TurnEvent, E, R>;
|
|
63
|
+
};
|
|
64
|
+
declare const Responses_base: Context.ServiceClass<Responses, "@betalyra/effect-uai/providers/responses/Responses", ResponsesService>;
|
|
65
|
+
/**
|
|
66
|
+
* Provider-typed service tag. Yield this when you want Responses-specific
|
|
67
|
+
* options (`reasoning.effort`, `store`, `previousResponseId`) at the call
|
|
68
|
+
* site. Yield the generic `LanguageModel` tag for provider-portable code -
|
|
69
|
+
* both are registered by `layer`.
|
|
70
|
+
*/
|
|
71
|
+
declare class Responses extends Responses_base {}
|
|
72
|
+
type Config = {
|
|
73
|
+
readonly apiKey: Redacted.Redacted;
|
|
74
|
+
readonly baseUrl?: string;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.
|
|
78
|
+
* Threads a fresh `CallIdLookup` per stream so `function_call_arguments.delta`
|
|
79
|
+
* can be tagged with the right `call_id`.
|
|
80
|
+
*/
|
|
81
|
+
declare const toCanonical: <E, R>(s: Stream.Stream<ProviderEvent, E, R>) => Stream.Stream<TurnEvent, E, R>;
|
|
82
|
+
/**
|
|
83
|
+
* Build a `ResponsesService` value. For Layer-based setup, prefer `layer`.
|
|
84
|
+
*/
|
|
85
|
+
declare const make: (cfg: Config) => Effect.Effect<ResponsesService, never, HttpClient.HttpClient>;
|
|
86
|
+
/**
|
|
87
|
+
* Layer that registers both the provider-specific `Responses` tag and the
|
|
88
|
+
* generic `LanguageModel` tag, sharing one underlying implementation.
|
|
89
|
+
*
|
|
90
|
+
* The generic tag accepts `CommonRequest`; the typed tag accepts the full
|
|
91
|
+
* `ResponsesRequest` surface.
|
|
92
|
+
*/
|
|
93
|
+
declare const layer: (cfg: Config) => Layer.Layer<Responses | LanguageModel, never, HttpClient.HttpClient>;
|
|
94
|
+
//#endregion
|
|
95
|
+
export { Config, Responses, ResponsesRequest, ResponsesService, layer, make, Responses_d_exports as t, toCanonical };
|
|
96
|
+
//# sourceMappingURL=Responses.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Responses.d.mts","names":[],"sources":["../src/Responses.ts"],"mappings":";;;;;;;;;;;;KAwBY,gBAAA,GAAmB,IAAA,CAAK,aAAA;;;;;WAKzB,KAAA,EAAO,WAAA;EAAA,SACP,SAAA;IAAA,SAAuB,MAAA;EAAA;EAAA,SACvB,KAAA;EAAA,SACA,kBAAA;EAAA,SACA,YAAA;EAAA,SACA,iBAAA;EAAA,SACA,QAAA,GAAW,QAAA,CAAS,MAAA;EAAA,SACpB,IAAA;EAAA,SACA,gBAAA;EAAA,SACA,cAAA;EAAA,SACA,UAAA;EAJoB;;;;;EAAA,SAUpB,cAAA;IAAA,SAA4B,IAAA;EAAA;EAf5B;;;;EAAA,SAoBA,SAAA;AAAA;AAAA,KAGC,gBAAA;EAlBU;;;;;EAAA,SAwBX,YAAA,GACP,OAAA,EAAS,gBAAA,KACN,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,OAAA,CAAQ,OAAA;EAhBjC;;;;EAAA,SAqBA,UAAA,GAAa,OAAA,EAAS,gBAAA,KAAqB,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,CAAQ,OAAA;EAb3E;;;;;EAAA,SAmBD,WAAA,SACP,CAAA,EAAG,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,CAAA,EAAG,CAAA,MAChC,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,CAAA,EAAG,CAAA;AAAA;AAAA,cAClC,cAAA;;;;;;;cAQY,SAAA,SAAkB,cAAA;AAAA,KAInB,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA;AAAA;;;;;;cA+KE,WAAA,SACX,CAAA,EAAG,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,CAAA,EAAG,CAAA,MAClC,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,CAAA,EAAG,CAAA;;;;cAelB,IAAA,GAAQ,GAAA,EAAK,MAAA,KAAS,MAAA,CAAO,MAAA,CAAO,gBAAA,SAAyB,UAAA,CAAW,UAAA;;;;;;;;cAkBxE,KAAA,GACX,GAAA,EAAK,MAAA,KACJ,KAAA,CAAM,KAAA,CAAM,SAAA,GAAY,aAAA,SAAsB,UAAA,CAAW,UAAA"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
|
+
import { itemsToInput } from "./codec.mjs";
|
|
3
|
+
import { KnownProviderEvent, eventToDeltas, makeCallIdLookup } from "./streamEvents.mjs";
|
|
4
|
+
import { Context, Effect, Layer, Match, Option, Schema, Stream } from "effect";
|
|
5
|
+
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
6
|
+
import * as AiError from "@effect-uai/core/AiError";
|
|
7
|
+
import { LanguageModel } from "@effect-uai/core/LanguageModel";
|
|
8
|
+
import * as SSE from "@effect-uai/core/SSE";
|
|
9
|
+
//#region src/Responses.ts
|
|
10
|
+
var Responses_exports = /* @__PURE__ */ __exportAll({
|
|
11
|
+
Responses: () => Responses,
|
|
12
|
+
layer: () => layer,
|
|
13
|
+
make: () => make,
|
|
14
|
+
toCanonical: () => toCanonical
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Provider-typed service tag. Yield this when you want Responses-specific
|
|
18
|
+
* options (`reasoning.effort`, `store`, `previousResponseId`) at the call
|
|
19
|
+
* site. Yield the generic `LanguageModel` tag for provider-portable code -
|
|
20
|
+
* both are registered by `layer`.
|
|
21
|
+
*/
|
|
22
|
+
var Responses = class extends Context.Service()("@betalyra/effect-uai/providers/responses/Responses") {};
|
|
23
|
+
const jsonSchemaFormat = (format) => ({
|
|
24
|
+
type: "json_schema",
|
|
25
|
+
name: format.name,
|
|
26
|
+
schema: format.schema["~standard"].jsonSchema.input({ target: "draft-2020-12" }),
|
|
27
|
+
...format.description !== void 0 && { description: format.description },
|
|
28
|
+
...format.strict !== void 0 && { strict: format.strict }
|
|
29
|
+
});
|
|
30
|
+
const buildText = (request) => {
|
|
31
|
+
const format = request.structured !== void 0 ? jsonSchemaFormat(request.structured) : request.responseFormat;
|
|
32
|
+
const text = {};
|
|
33
|
+
if (format !== void 0) text.format = format;
|
|
34
|
+
if (request.verbosity !== void 0) text.verbosity = request.verbosity;
|
|
35
|
+
return Object.keys(text).length === 0 ? void 0 : text;
|
|
36
|
+
};
|
|
37
|
+
const buildBody = (request) => {
|
|
38
|
+
const text = buildText(request);
|
|
39
|
+
return {
|
|
40
|
+
model: request.model,
|
|
41
|
+
input: itemsToInput(request.history),
|
|
42
|
+
stream: true,
|
|
43
|
+
...request.tools !== void 0 && request.tools.length > 0 && { tools: request.tools.map((t) => ({
|
|
44
|
+
type: "function",
|
|
45
|
+
name: t.name,
|
|
46
|
+
description: t.description,
|
|
47
|
+
parameters: t.inputSchema,
|
|
48
|
+
...t.strict !== void 0 && { strict: t.strict }
|
|
49
|
+
})) },
|
|
50
|
+
...request.toolChoice !== void 0 && { tool_choice: request.toolChoice },
|
|
51
|
+
...request.temperature !== void 0 && { temperature: request.temperature },
|
|
52
|
+
...request.maxOutputTokens !== void 0 && { max_output_tokens: request.maxOutputTokens },
|
|
53
|
+
...request.reasoning !== void 0 && { reasoning: request.reasoning },
|
|
54
|
+
...request.store !== void 0 && { store: request.store },
|
|
55
|
+
...request.previousResponseId !== void 0 && { previous_response_id: request.previousResponseId },
|
|
56
|
+
...request.instructions !== void 0 && { instructions: request.instructions },
|
|
57
|
+
...request.topP !== void 0 && { top_p: request.topP },
|
|
58
|
+
...request.parallelToolCalls !== void 0 && { parallel_tool_calls: request.parallelToolCalls },
|
|
59
|
+
...request.metadata !== void 0 && { metadata: request.metadata },
|
|
60
|
+
...request.user !== void 0 && { user: request.user },
|
|
61
|
+
...request.safetyIdentifier !== void 0 && { safety_identifier: request.safetyIdentifier },
|
|
62
|
+
...request.promptCacheKey !== void 0 && { prompt_cache_key: request.promptCacheKey },
|
|
63
|
+
...request.truncation !== void 0 && { truncation: request.truncation },
|
|
64
|
+
...text !== void 0 && { text }
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
const decodeKnown = Schema.decodeUnknownEffect(KnownProviderEvent);
|
|
68
|
+
const makeUnknown = (raw) => ({
|
|
69
|
+
type: "_unknown",
|
|
70
|
+
raw
|
|
71
|
+
});
|
|
72
|
+
/**
|
|
73
|
+
* Lift events that carry a terminal failure signal (`error`,
|
|
74
|
+
* `response.failed`) to a typed `AiError`. Other events are not failures
|
|
75
|
+
* and produce `Option.none`.
|
|
76
|
+
*/
|
|
77
|
+
const eventToError = Match.type().pipe(Match.discriminators("type")({
|
|
78
|
+
error: (e) => new AiError.Unavailable({
|
|
79
|
+
provider: "responses",
|
|
80
|
+
raw: e
|
|
81
|
+
}),
|
|
82
|
+
"response.failed": (e) => {
|
|
83
|
+
const code = e.response.error?.code;
|
|
84
|
+
const message = e.response.error?.message;
|
|
85
|
+
return new AiError.GenerationFailed({
|
|
86
|
+
provider: "responses",
|
|
87
|
+
...code !== void 0 && code !== null && { code },
|
|
88
|
+
...message !== void 0 && message !== null && { message },
|
|
89
|
+
raw: e
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}), Match.option);
|
|
93
|
+
const sseEventToProviderEvent = (ev) => Effect.try({
|
|
94
|
+
try: () => JSON.parse(ev.data),
|
|
95
|
+
catch: () => ev.data
|
|
96
|
+
}).pipe(Effect.flatMap((parsed) => decodeKnown(parsed).pipe(Effect.orElseSucceed(() => makeUnknown(parsed)))), Effect.orElseSucceed(() => makeUnknown(ev.data)));
|
|
97
|
+
const httpStatusError = (status, body) => {
|
|
98
|
+
const provider = "responses";
|
|
99
|
+
const raw = body;
|
|
100
|
+
if (status === 429) return new AiError.RateLimited({
|
|
101
|
+
provider,
|
|
102
|
+
raw
|
|
103
|
+
});
|
|
104
|
+
if (status === 408 || status === 504) return new AiError.Timeout({
|
|
105
|
+
provider,
|
|
106
|
+
raw
|
|
107
|
+
});
|
|
108
|
+
if (status === 401) return new AiError.AuthFailed({
|
|
109
|
+
provider,
|
|
110
|
+
subtype: "auth",
|
|
111
|
+
raw
|
|
112
|
+
});
|
|
113
|
+
if (status === 403) return new AiError.AuthFailed({
|
|
114
|
+
provider,
|
|
115
|
+
subtype: "permission",
|
|
116
|
+
raw
|
|
117
|
+
});
|
|
118
|
+
if (status === 402) return new AiError.AuthFailed({
|
|
119
|
+
provider,
|
|
120
|
+
subtype: "billing",
|
|
121
|
+
raw
|
|
122
|
+
});
|
|
123
|
+
if (status === 413) return new AiError.ContextLengthExceeded({
|
|
124
|
+
provider,
|
|
125
|
+
raw
|
|
126
|
+
});
|
|
127
|
+
if (status >= 500) return new AiError.Unavailable({
|
|
128
|
+
provider,
|
|
129
|
+
status,
|
|
130
|
+
raw
|
|
131
|
+
});
|
|
132
|
+
return new AiError.InvalidRequest({
|
|
133
|
+
provider,
|
|
134
|
+
raw
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
const buildNativeStream = (cfg) => {
|
|
138
|
+
const url = `${cfg.baseUrl ?? "https://api.openai.com/v1"}/responses`;
|
|
139
|
+
return (request) => Stream.unwrap(Effect.gen(function* () {
|
|
140
|
+
const client = yield* HttpClient.HttpClient;
|
|
141
|
+
const httpRequest = HttpClientRequest.post(url).pipe(HttpClientRequest.bearerToken(cfg.apiKey), HttpClientRequest.bodyJsonUnsafe(buildBody(request)), HttpClientRequest.accept("text/event-stream"));
|
|
142
|
+
const response = yield* client.execute(httpRequest).pipe(Effect.mapError((cause) => new AiError.Unavailable({
|
|
143
|
+
provider: "responses",
|
|
144
|
+
raw: cause
|
|
145
|
+
})));
|
|
146
|
+
if (response.status >= 400) {
|
|
147
|
+
const body = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
|
|
148
|
+
return Stream.fail(httpStatusError(response.status, body));
|
|
149
|
+
}
|
|
150
|
+
return response.stream.pipe(Stream.mapError((cause) => new AiError.Unavailable({
|
|
151
|
+
provider: "responses",
|
|
152
|
+
raw: cause
|
|
153
|
+
})), SSE.fromBytes, Stream.mapEffect(sseEventToProviderEvent), Stream.flatMap((event) => Option.match(eventToError(event), {
|
|
154
|
+
onNone: () => Stream.succeed(event),
|
|
155
|
+
onSome: Stream.fail
|
|
156
|
+
})));
|
|
157
|
+
}));
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.
|
|
161
|
+
* Threads a fresh `CallIdLookup` per stream so `function_call_arguments.delta`
|
|
162
|
+
* can be tagged with the right `call_id`.
|
|
163
|
+
*/
|
|
164
|
+
const toCanonical = (s) => Stream.unwrap(Effect.sync(() => {
|
|
165
|
+
const lookup = makeCallIdLookup();
|
|
166
|
+
return s.pipe(Stream.flatMap((event) => Stream.fromIterable(eventToDeltas(event, lookup))));
|
|
167
|
+
}));
|
|
168
|
+
/**
|
|
169
|
+
* Build a `ResponsesService` value. For Layer-based setup, prefer `layer`.
|
|
170
|
+
*/
|
|
171
|
+
const make = (cfg) => Effect.map(HttpClient.HttpClient.asEffect(), (client) => {
|
|
172
|
+
const streamNative = (request) => buildNativeStream(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client));
|
|
173
|
+
return {
|
|
174
|
+
streamNative,
|
|
175
|
+
streamTurn: (request) => toCanonical(streamNative(request)),
|
|
176
|
+
toCanonical
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
/**
|
|
180
|
+
* Layer that registers both the provider-specific `Responses` tag and the
|
|
181
|
+
* generic `LanguageModel` tag, sharing one underlying implementation.
|
|
182
|
+
*
|
|
183
|
+
* The generic tag accepts `CommonRequest`; the typed tag accepts the full
|
|
184
|
+
* `ResponsesRequest` surface.
|
|
185
|
+
*/
|
|
186
|
+
const layer = (cfg) => {
|
|
187
|
+
const typed = Layer.effect(Responses, make(cfg));
|
|
188
|
+
const generic = Layer.effect(LanguageModel, Effect.map(make(cfg), (s) => ({ streamTurn: (request) => s.streamTurn(request) })));
|
|
189
|
+
return Layer.merge(typed, generic);
|
|
190
|
+
};
|
|
191
|
+
//#endregion
|
|
192
|
+
export { Responses, layer, make, Responses_exports as t, toCanonical };
|
|
193
|
+
|
|
194
|
+
//# sourceMappingURL=Responses.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Responses.mjs","names":[],"sources":["../src/Responses.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 * as StructuredFormat from \"@effect-uai/core/StructuredFormat\"\nimport {\n type CommonRequest,\n LanguageModel,\n type LanguageModelService,\n} from \"@effect-uai/core/LanguageModel\"\nimport * as SSE from \"@effect-uai/core/SSE\"\nimport type { TurnEvent } from \"@effect-uai/core/Turn\"\nimport { itemsToInput } from \"./codec.js\"\nimport type { OpenAIModel } from \"./models.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 * 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 * 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}\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 = (request: ResponsesRequest): Record<string, unknown> => {\n const text = buildText(request)\n return {\n model: request.model,\n input: itemsToInput(request.history),\n stream: true,\n ...(request.tools !== undefined &&\n request.tools.length > 0 && {\n tools: 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 }),\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 }\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 => new AiError.Unavailable({ provider: \"responses\", raw: e }),\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 sseEventToProviderEvent = (ev: SSE.Event): Effect.Effect<ProviderEvent> =>\n Effect.try({\n try: () => JSON.parse(ev.data) as unknown,\n catch: () => ev.data,\n }).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\nconst 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\nconst buildNativeStream = (cfg: Config) => {\n const url = `${cfg.baseUrl ?? \"https://api.openai.com/v1\"}/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 httpRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.bodyJsonUnsafe(buildBody(request)),\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 if (response.status >= 400) {\n const body = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, body))\n }\n\n return response.stream.pipe(\n Stream.mapError(\n (cause) => new AiError.Unavailable({ provider: \"responses\", raw: cause }),\n ),\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}\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.asEffect(), (client) => {\n const streamNative: ResponsesService[\"streamNative\"] = (request) =>\n buildNativeStream(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client))\n return {\n streamNative,\n streamTurn: (request) => toCanonical(streamNative(request)),\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(\n make(cfg),\n (s): LanguageModelService => ({\n streamTurn: (request) => s.streamTurn(request as ResponsesRequest),\n }),\n ),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|