@opencode-ai/ai 0.0.0-beta-17492
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 +392 -0
- package/dist/cache-policy.d.ts +2 -0
- package/dist/cache-policy.js +121 -0
- package/dist/image-client.d.ts +19 -0
- package/dist/image-client.js +19 -0
- package/dist/image.d.ts +110 -0
- package/dist/image.js +102 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +12 -0
- package/dist/llm.d.ts +230 -0
- package/dist/llm.js +77 -0
- package/dist/protocols/anthropic-messages.d.ts +633 -0
- package/dist/protocols/anthropic-messages.js +860 -0
- package/dist/protocols/bedrock-converse.d.ts +533 -0
- package/dist/protocols/bedrock-converse.js +584 -0
- package/dist/protocols/bedrock-event-stream.d.ts +9 -0
- package/dist/protocols/bedrock-event-stream.js +73 -0
- package/dist/protocols/gemini.d.ts +300 -0
- package/dist/protocols/gemini.js +512 -0
- package/dist/protocols/google-images.d.ts +30 -0
- package/dist/protocols/google-images.js +191 -0
- package/dist/protocols/index.d.ts +10 -0
- package/dist/protocols/index.js +10 -0
- package/dist/protocols/open-responses-channel.d.ts +26 -0
- package/dist/protocols/open-responses-channel.js +120 -0
- package/dist/protocols/open-responses.d.ts +951 -0
- package/dist/protocols/open-responses.js +873 -0
- package/dist/protocols/openai-chat.d.ts +795 -0
- package/dist/protocols/openai-chat.js +718 -0
- package/dist/protocols/openai-compatible-chat.d.ts +107 -0
- package/dist/protocols/openai-compatible-chat.js +20 -0
- package/dist/protocols/openai-compatible-responses.d.ts +96 -0
- package/dist/protocols/openai-compatible-responses.js +17 -0
- package/dist/protocols/openai-images.d.ts +32 -0
- package/dist/protocols/openai-images.js +188 -0
- package/dist/protocols/openai-responses-channel.d.ts +12 -0
- package/dist/protocols/openai-responses-channel.js +142 -0
- package/dist/protocols/openai-responses.d.ts +596 -0
- package/dist/protocols/openai-responses.js +208 -0
- package/dist/protocols/shared.d.ts +194 -0
- package/dist/protocols/shared.js +238 -0
- package/dist/protocols/utils/bedrock-auth.d.ts +21 -0
- package/dist/protocols/utils/bedrock-auth.js +43 -0
- package/dist/protocols/utils/bedrock-cache.d.ts +15 -0
- package/dist/protocols/utils/bedrock-cache.js +29 -0
- package/dist/protocols/utils/bedrock-media.d.ts +49 -0
- package/dist/protocols/utils/bedrock-media.js +70 -0
- package/dist/protocols/utils/cache.d.ts +6 -0
- package/dist/protocols/utils/cache.js +8 -0
- package/dist/protocols/utils/gemini-tool-schema.d.ts +2 -0
- package/dist/protocols/utils/gemini-tool-schema.js +103 -0
- package/dist/protocols/utils/image-input.d.ts +21 -0
- package/dist/protocols/utils/image-input.js +22 -0
- package/dist/protocols/utils/lifecycle.d.ts +20 -0
- package/dist/protocols/utils/lifecycle.js +68 -0
- package/dist/protocols/utils/open-responses-options.d.ts +21 -0
- package/dist/protocols/utils/open-responses-options.js +41 -0
- package/dist/protocols/utils/openai-image.d.ts +5 -0
- package/dist/protocols/utils/openai-image.js +18 -0
- package/dist/protocols/utils/openai-options.d.ts +14 -0
- package/dist/protocols/utils/openai-options.js +14 -0
- package/dist/protocols/utils/tool-schema.d.ts +8 -0
- package/dist/protocols/utils/tool-schema.js +81 -0
- package/dist/protocols/utils/tool-stream.d.ts +639 -0
- package/dist/protocols/utils/tool-stream.js +141 -0
- package/dist/protocols/xai-images.d.ts +26 -0
- package/dist/protocols/xai-images.js +111 -0
- package/dist/protocols/zai-images.d.ts +22 -0
- package/dist/protocols/zai-images.js +84 -0
- package/dist/protocols.d.ts +1 -0
- package/dist/protocols.js +1 -0
- package/dist/provider-error.d.ts +14 -0
- package/dist/provider-error.js +130 -0
- package/dist/provider-package.d.ts +15 -0
- package/dist/provider-package.js +1 -0
- package/dist/provider.d.ts +23 -0
- package/dist/provider.js +2 -0
- package/dist/providers/amazon-bedrock/mantle/chat.d.ts +2 -0
- package/dist/providers/amazon-bedrock/mantle/chat.js +1 -0
- package/dist/providers/amazon-bedrock/mantle/responses.d.ts +2 -0
- package/dist/providers/amazon-bedrock/mantle/responses.js +1 -0
- package/dist/providers/amazon-bedrock/mantle.d.ts +2 -0
- package/dist/providers/amazon-bedrock/mantle.js +1 -0
- package/dist/providers/amazon-bedrock-mantle.d.ts +247 -0
- package/dist/providers/amazon-bedrock-mantle.js +68 -0
- package/dist/providers/amazon-bedrock.d.ts +172 -0
- package/dist/providers/amazon-bedrock.js +41 -0
- package/dist/providers/anthropic-compatible.d.ts +218 -0
- package/dist/providers/anthropic-compatible.js +45 -0
- package/dist/providers/anthropic.d.ts +216 -0
- package/dist/providers/anthropic.js +40 -0
- package/dist/providers/azure/chat.d.ts +2 -0
- package/dist/providers/azure/chat.js +1 -0
- package/dist/providers/azure/responses.d.ts +2 -0
- package/dist/providers/azure/responses.js +1 -0
- package/dist/providers/azure.d.ts +248 -0
- package/dist/providers/azure.js +90 -0
- package/dist/providers/cloudflare.d.ts +338 -0
- package/dist/providers/cloudflare.js +87 -0
- package/dist/providers/google-vertex/chat.d.ts +2 -0
- package/dist/providers/google-vertex/chat.js +1 -0
- package/dist/providers/google-vertex/gemini.d.ts +2 -0
- package/dist/providers/google-vertex/gemini.js +1 -0
- package/dist/providers/google-vertex/messages.d.ts +2 -0
- package/dist/providers/google-vertex/messages.js +1 -0
- package/dist/providers/google-vertex/responses.d.ts +2 -0
- package/dist/providers/google-vertex/responses.js +1 -0
- package/dist/providers/google-vertex-chat.d.ts +131 -0
- package/dist/providers/google-vertex-chat.js +50 -0
- package/dist/providers/google-vertex-messages.d.ts +215 -0
- package/dist/providers/google-vertex-messages.js +75 -0
- package/dist/providers/google-vertex-responses.d.ts +122 -0
- package/dist/providers/google-vertex-responses.js +51 -0
- package/dist/providers/google-vertex-shared.d.ts +22 -0
- package/dist/providers/google-vertex-shared.js +61 -0
- package/dist/providers/google-vertex.d.ts +115 -0
- package/dist/providers/google-vertex.js +64 -0
- package/dist/providers/google.d.ts +112 -0
- package/dist/providers/google.js +43 -0
- package/dist/providers/index.d.ts +18 -0
- package/dist/providers/index.js +18 -0
- package/dist/providers/open-responses-options.d.ts +16 -0
- package/dist/providers/open-responses-options.js +1 -0
- package/dist/providers/openai/chat.d.ts +2 -0
- package/dist/providers/openai/chat.js +1 -0
- package/dist/providers/openai/responses.d.ts +2 -0
- package/dist/providers/openai/responses.js +1 -0
- package/dist/providers/openai-compatible/responses.d.ts +1 -0
- package/dist/providers/openai-compatible/responses.js +1 -0
- package/dist/providers/openai-compatible-profile.d.ts +43 -0
- package/dist/providers/openai-compatible-profile.js +12 -0
- package/dist/providers/openai-compatible-responses.d.ts +120 -0
- package/dist/providers/openai-compatible-responses.js +33 -0
- package/dist/providers/openai-compatible.d.ts +167 -0
- package/dist/providers/openai-compatible.js +55 -0
- package/dist/providers/openai-options.d.ts +21 -0
- package/dist/providers/openai-options.js +41 -0
- package/dist/providers/openai.d.ts +264 -0
- package/dist/providers/openai.js +83 -0
- package/dist/providers/openrouter.d.ts +537 -0
- package/dist/providers/openrouter.js +117 -0
- package/dist/providers/xai.d.ts +249 -0
- package/dist/providers/xai.js +79 -0
- package/dist/providers/zai.d.ts +24 -0
- package/dist/providers/zai.js +21 -0
- package/dist/providers.d.ts +1 -0
- package/dist/providers.js +1 -0
- package/dist/route/auth-options.d.ts +34 -0
- package/dist/route/auth-options.js +14 -0
- package/dist/route/auth.d.ts +51 -0
- package/dist/route/auth.js +89 -0
- package/dist/route/client.d.ts +348 -0
- package/dist/route/client.js +214 -0
- package/dist/route/endpoint.d.ts +28 -0
- package/dist/route/endpoint.js +21 -0
- package/dist/route/executor.d.ts +23 -0
- package/dist/route/executor.js +227 -0
- package/dist/route/framing.d.ts +23 -0
- package/dist/route/framing.js +4 -0
- package/dist/route/index.d.ts +16 -0
- package/dist/route/index.js +9 -0
- package/dist/route/protocol.d.ts +77 -0
- package/dist/route/protocol.js +17 -0
- package/dist/route/transport/http.d.ts +34 -0
- package/dist/route/transport/http.js +63 -0
- package/dist/route/transport/index.d.ts +39 -0
- package/dist/route/transport/index.js +4 -0
- package/dist/route/transport/websocket-channel.d.ts +56 -0
- package/dist/route/transport/websocket-channel.js +1 -0
- package/dist/route/transport/websocket.d.ts +55 -0
- package/dist/route/transport/websocket.js +326 -0
- package/dist/route.d.ts +1 -0
- package/dist/route.js +1 -0
- package/dist/schema/errors.d.ts +159 -0
- package/dist/schema/errors.js +155 -0
- package/dist/schema/events.d.ts +4587 -0
- package/dist/schema/events.js +492 -0
- package/dist/schema/ids.d.ts +30 -0
- package/dist/schema/ids.js +19 -0
- package/dist/schema/index.d.ts +5 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/messages.d.ts +402 -0
- package/dist/schema/messages.js +244 -0
- package/dist/schema/options.d.ts +159 -0
- package/dist/schema/options.js +202 -0
- package/dist/testing.d.ts +1493 -0
- package/dist/testing.js +88 -0
- package/dist/tool-runtime.d.ts +15 -0
- package/dist/tool-runtime.js +54 -0
- package/dist/tool.d.ts +135 -0
- package/dist/tool.js +66 -0
- package/dist/utils/record.d.ts +2 -0
- package/dist/utils/record.js +2 -0
- package/package.json +47 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Effect, Encoding, Schema } from "effect";
|
|
2
|
+
import { Headers, HttpClientRequest } from "effect/unstable/http";
|
|
3
|
+
import { GeneratedImage, ImageModel, ImageResponse, } from "../image.js";
|
|
4
|
+
import { Auth } from "../route/auth.js";
|
|
5
|
+
import { InvalidProviderOutputReason, AIError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema/index.js";
|
|
6
|
+
import { ProviderShared } from "./shared.js";
|
|
7
|
+
import { ImageInputs } from "./utils/image-input.js";
|
|
8
|
+
const ADAPTER = "google-images";
|
|
9
|
+
export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
|
|
10
|
+
const GoogleUsage = Schema.StructWithRest(Schema.Struct({
|
|
11
|
+
cachedContentTokenCount: Schema.optional(Schema.Number),
|
|
12
|
+
thoughtsTokenCount: Schema.optional(Schema.Number),
|
|
13
|
+
promptTokenCount: Schema.optional(Schema.Number),
|
|
14
|
+
candidatesTokenCount: Schema.optional(Schema.Number),
|
|
15
|
+
totalTokenCount: Schema.optional(Schema.Number),
|
|
16
|
+
promptTokensDetails: Schema.optional(Schema.Unknown),
|
|
17
|
+
candidatesTokensDetails: Schema.optional(Schema.Unknown),
|
|
18
|
+
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
19
|
+
const GoogleImageResponse = Schema.Struct({
|
|
20
|
+
candidates: Schema.optional(Schema.Array(Schema.Struct({
|
|
21
|
+
index: Schema.optional(Schema.Number),
|
|
22
|
+
content: Schema.optional(Schema.Struct({
|
|
23
|
+
parts: Schema.Array(Schema.Struct({
|
|
24
|
+
text: Schema.optional(Schema.String),
|
|
25
|
+
thought: Schema.optional(Schema.Boolean),
|
|
26
|
+
thoughtSignature: Schema.optional(Schema.String),
|
|
27
|
+
inlineData: Schema.optional(Schema.Struct({
|
|
28
|
+
mimeType: Schema.String,
|
|
29
|
+
data: Schema.String,
|
|
30
|
+
})),
|
|
31
|
+
})),
|
|
32
|
+
})),
|
|
33
|
+
finishReason: Schema.optional(Schema.String),
|
|
34
|
+
finishMessage: Schema.optional(Schema.String),
|
|
35
|
+
safetyRatings: Schema.optional(Schema.Unknown),
|
|
36
|
+
citationMetadata: Schema.optional(Schema.Unknown),
|
|
37
|
+
groundingMetadata: Schema.optional(Schema.Unknown),
|
|
38
|
+
}))),
|
|
39
|
+
usageMetadata: Schema.optional(GoogleUsage),
|
|
40
|
+
modelVersion: Schema.optional(Schema.String),
|
|
41
|
+
responseId: Schema.optional(Schema.String),
|
|
42
|
+
promptFeedback: Schema.optional(Schema.Unknown),
|
|
43
|
+
});
|
|
44
|
+
const nativeOptions = (options) => {
|
|
45
|
+
const { aspectRatio, imageSize, seed, thinkingLevel, includeThoughts, ...native } = options ?? {};
|
|
46
|
+
const image = {
|
|
47
|
+
aspectRatio,
|
|
48
|
+
imageSize,
|
|
49
|
+
};
|
|
50
|
+
const thinkingConfig = {
|
|
51
|
+
thinkingLevel,
|
|
52
|
+
includeThoughts,
|
|
53
|
+
};
|
|
54
|
+
return (mergeJsonRecords({
|
|
55
|
+
responseModalities: ["IMAGE"],
|
|
56
|
+
imageConfig: Object.values(image).some((value) => value !== undefined) ? image : undefined,
|
|
57
|
+
seed,
|
|
58
|
+
thinkingConfig: Object.values(thinkingConfig).some((value) => value !== undefined) ? thinkingConfig : undefined,
|
|
59
|
+
}, native) ?? { responseModalities: ["IMAGE"] });
|
|
60
|
+
};
|
|
61
|
+
const invalidOutput = (message, providerMetadata) => new AIError({
|
|
62
|
+
module: ADAPTER,
|
|
63
|
+
method: "generate",
|
|
64
|
+
reason: new InvalidProviderOutputReason({ message, route: ADAPTER, providerMetadata }),
|
|
65
|
+
});
|
|
66
|
+
const applyQuery = (url, query) => {
|
|
67
|
+
if (!query)
|
|
68
|
+
return url;
|
|
69
|
+
const next = new URL(url);
|
|
70
|
+
Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
|
|
71
|
+
return next.toString();
|
|
72
|
+
};
|
|
73
|
+
export const model = (input) => {
|
|
74
|
+
const route = {
|
|
75
|
+
id: ADAPTER,
|
|
76
|
+
generate: Effect.fn("GoogleImages.generate")(function* (request, execute) {
|
|
77
|
+
const imageParts = yield* Effect.forEach(request.images ?? [], googleImagePart);
|
|
78
|
+
const http = mergeHttpOptions(request.model.http, request.http);
|
|
79
|
+
const requestBody = mergeJsonRecords({
|
|
80
|
+
contents: [{ role: "user", parts: [{ text: request.prompt }, ...imageParts] }],
|
|
81
|
+
generationConfig: nativeOptions(request.options),
|
|
82
|
+
}, http?.body);
|
|
83
|
+
const text = ProviderShared.encodeJson(requestBody);
|
|
84
|
+
const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}/models/${request.model.id}:generateContent`, http?.query);
|
|
85
|
+
const headers = yield* Auth.toEffect(input.auth)({
|
|
86
|
+
request,
|
|
87
|
+
method: "POST",
|
|
88
|
+
url,
|
|
89
|
+
body: text,
|
|
90
|
+
headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
|
|
91
|
+
});
|
|
92
|
+
const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
|
|
93
|
+
const payload = yield* response.json.pipe(Effect.mapError(() => invalidOutput("Failed to read the Google Images response")));
|
|
94
|
+
const decoded = yield* Schema.decodeUnknownEffect(GoogleImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("Google Images returned an invalid response")));
|
|
95
|
+
const candidates = decoded.candidates ?? [];
|
|
96
|
+
const candidateMetadata = candidates.map((candidate, candidateIndex) => ({
|
|
97
|
+
index: candidate.index ?? candidateIndex,
|
|
98
|
+
finishReason: candidate.finishReason,
|
|
99
|
+
finishMessage: candidate.finishMessage,
|
|
100
|
+
safetyRatings: candidate.safetyRatings,
|
|
101
|
+
citationMetadata: candidate.citationMetadata,
|
|
102
|
+
groundingMetadata: candidate.groundingMetadata,
|
|
103
|
+
parts: (candidate.content?.parts ?? []).map((part) => part.inlineData === undefined
|
|
104
|
+
? {
|
|
105
|
+
type: "text",
|
|
106
|
+
text: part.text,
|
|
107
|
+
thought: part.thought,
|
|
108
|
+
thoughtSignature: part.thoughtSignature,
|
|
109
|
+
}
|
|
110
|
+
: {
|
|
111
|
+
type: "inlineData",
|
|
112
|
+
mediaType: part.inlineData.mimeType,
|
|
113
|
+
thought: part.thought,
|
|
114
|
+
thoughtSignature: part.thoughtSignature,
|
|
115
|
+
}),
|
|
116
|
+
}));
|
|
117
|
+
const encoded = candidates.flatMap((candidate, candidateIndex) => (candidate.content?.parts ?? []).flatMap((part, partIndex) => part.inlineData === undefined || part.thought === true
|
|
118
|
+
? []
|
|
119
|
+
: [{ candidate, candidateIndex, partIndex, inlineData: part.inlineData }]));
|
|
120
|
+
const images = yield* Effect.forEach(encoded, (item) => Effect.fromResult(Encoding.decodeBase64(item.inlineData.data)).pipe(Effect.mapError(() => invalidOutput(`Google Images candidate ${item.candidateIndex} part ${item.partIndex} contains invalid base64 data`)), Effect.map((data) => new GeneratedImage({
|
|
121
|
+
mediaType: item.inlineData.mimeType,
|
|
122
|
+
data,
|
|
123
|
+
providerMetadata: {
|
|
124
|
+
google: {
|
|
125
|
+
candidateIndex: item.candidate.index ?? item.candidateIndex,
|
|
126
|
+
partIndex: item.partIndex,
|
|
127
|
+
finishReason: item.candidate.finishReason,
|
|
128
|
+
safetyRatings: item.candidate.safetyRatings,
|
|
129
|
+
citationMetadata: item.candidate.citationMetadata,
|
|
130
|
+
groundingMetadata: item.candidate.groundingMetadata,
|
|
131
|
+
thoughtSignature: item.candidate.content?.parts[item.partIndex]?.thoughtSignature,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
}))));
|
|
135
|
+
if (images.length === 0) {
|
|
136
|
+
const finishReasons = candidates.flatMap((candidate) => candidate.finishReason === undefined ? [] : [candidate.finishReason]);
|
|
137
|
+
return yield* invalidOutput(`Google Images returned no final images${finishReasons.length === 0 ? "" : ` (finish reasons: ${finishReasons.join(", ")})`}; inspect reason.providerMetadata.google for prompt feedback and candidate details`, {
|
|
138
|
+
google: {
|
|
139
|
+
promptFeedback: decoded.promptFeedback,
|
|
140
|
+
candidates: candidateMetadata,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
const usage = decoded.usageMetadata;
|
|
145
|
+
const outputTokens = usage?.candidatesTokenCount === undefined
|
|
146
|
+
? undefined
|
|
147
|
+
: usage.candidatesTokenCount + (usage.thoughtsTokenCount ?? 0);
|
|
148
|
+
return new ImageResponse({
|
|
149
|
+
images,
|
|
150
|
+
usage: usage === undefined
|
|
151
|
+
? undefined
|
|
152
|
+
: new Usage({
|
|
153
|
+
inputTokens: usage.promptTokenCount,
|
|
154
|
+
outputTokens,
|
|
155
|
+
nonCachedInputTokens: ProviderShared.subtractTokens(usage.promptTokenCount, usage.cachedContentTokenCount),
|
|
156
|
+
cacheReadInputTokens: usage.cachedContentTokenCount,
|
|
157
|
+
reasoningTokens: usage.thoughtsTokenCount,
|
|
158
|
+
totalTokens: ProviderShared.totalTokens(usage.promptTokenCount, outputTokens, usage.totalTokenCount),
|
|
159
|
+
providerMetadata: { google: usage },
|
|
160
|
+
}),
|
|
161
|
+
providerMetadata: {
|
|
162
|
+
google: {
|
|
163
|
+
modelVersion: decoded.modelVersion,
|
|
164
|
+
responseId: decoded.responseId,
|
|
165
|
+
promptFeedback: decoded.promptFeedback,
|
|
166
|
+
candidates: candidateMetadata,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}),
|
|
171
|
+
};
|
|
172
|
+
return ImageModel.make({ id: input.id, provider: "google", route, http: input.http });
|
|
173
|
+
};
|
|
174
|
+
const googleImagePart = (image) => {
|
|
175
|
+
if (image.type === "bytes")
|
|
176
|
+
return Effect.succeed({ inlineData: { mimeType: image.mediaType, data: Encoding.encodeBase64(image.data) } });
|
|
177
|
+
if (image.type === "file-uri")
|
|
178
|
+
return Effect.succeed({ fileData: { mimeType: image.mediaType, fileUri: image.uri } });
|
|
179
|
+
if (image.type === "url")
|
|
180
|
+
return ImageInputs.decodeDataUrl(image.url, ADAPTER).pipe(Effect.flatMap((decoded) => {
|
|
181
|
+
if (decoded === undefined)
|
|
182
|
+
return Effect.fail(ImageInputs.invalid(ADAPTER, "Google generateContent does not fetch public image URLs; use bytes, a data URL, or a Gemini file URI"));
|
|
183
|
+
return Effect.succeed({
|
|
184
|
+
inlineData: { mimeType: decoded.mediaType, data: Encoding.encodeBase64(decoded.data) },
|
|
185
|
+
});
|
|
186
|
+
}));
|
|
187
|
+
return Effect.fail(ImageInputs.invalid(ADAPTER, "Google generateContent requires Gemini file URIs rather than provider file IDs"));
|
|
188
|
+
};
|
|
189
|
+
export const GoogleImages = {
|
|
190
|
+
model,
|
|
191
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * as AnthropicMessages from "./anthropic-messages.js";
|
|
2
|
+
export * as BedrockConverse from "./bedrock-converse.js";
|
|
3
|
+
export * as Gemini from "./gemini.js";
|
|
4
|
+
export * as OpenAIChat from "./openai-chat.js";
|
|
5
|
+
export * as OpenAIImages from "./openai-images.js";
|
|
6
|
+
export * as OpenAICompatibleChat from "./openai-compatible-chat.js";
|
|
7
|
+
export * as OpenAICompatibleResponses from "./openai-compatible-responses.js";
|
|
8
|
+
export * as OpenAIResponses from "./openai-responses.js";
|
|
9
|
+
export * as OpenResponses from "./open-responses.js";
|
|
10
|
+
export * as OpenResponsesChannel from "./open-responses-channel.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * as AnthropicMessages from "./anthropic-messages.js";
|
|
2
|
+
export * as BedrockConverse from "./bedrock-converse.js";
|
|
3
|
+
export * as Gemini from "./gemini.js";
|
|
4
|
+
export * as OpenAIChat from "./openai-chat.js";
|
|
5
|
+
export * as OpenAIImages from "./openai-images.js";
|
|
6
|
+
export * as OpenAICompatibleChat from "./openai-compatible-chat.js";
|
|
7
|
+
export * as OpenAICompatibleResponses from "./openai-compatible-responses.js";
|
|
8
|
+
export * as OpenAIResponses from "./openai-responses.js";
|
|
9
|
+
export * as OpenResponses from "./open-responses.js";
|
|
10
|
+
export * as OpenResponsesChannel from "./open-responses-channel.js";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Headers } from "effect/unstable/http";
|
|
2
|
+
import { HttpTransport, type Transport, type WebSocketChannelDriver } from "../route/transport/index.js";
|
|
3
|
+
export interface Options {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly rotateAfterMs?: number;
|
|
7
|
+
readonly headers?: (headers: Headers.Headers) => Headers.Headers;
|
|
8
|
+
readonly driver?: (input: {
|
|
9
|
+
readonly request: Readonly<Record<string, unknown>>;
|
|
10
|
+
readonly message: string;
|
|
11
|
+
readonly base: WebSocketChannelDriver;
|
|
12
|
+
}) => WebSocketChannelDriver;
|
|
13
|
+
}
|
|
14
|
+
export interface Prepared {
|
|
15
|
+
readonly http: HttpTransport.HttpPrepared<string>;
|
|
16
|
+
readonly channel?: {
|
|
17
|
+
readonly url: string;
|
|
18
|
+
readonly headers: Headers.Headers;
|
|
19
|
+
readonly rotateAfterMs?: number;
|
|
20
|
+
readonly driver: WebSocketChannelDriver;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare const transport: <Body>(options: Options) => Transport<Body, Prepared, string>;
|
|
24
|
+
export declare const OpenResponsesChannel: {
|
|
25
|
+
readonly transport: <Body>(options: Options) => Transport<Body, Prepared, string>;
|
|
26
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Effect, Schema, Stream } from "effect";
|
|
2
|
+
import { Headers } from "effect/unstable/http";
|
|
3
|
+
import { Framing } from "../route/framing.js";
|
|
4
|
+
import { HttpTransport, WebSocketTransport, } from "../route/transport/index.js";
|
|
5
|
+
import * as ProviderShared from "./shared.js";
|
|
6
|
+
import { OpenResponses } from "./open-responses.js";
|
|
7
|
+
const WebSocketResponseCreate = Schema.StructWithRest(Schema.Struct({ type: Schema.tag("response.create") }), [
|
|
8
|
+
Schema.Record(Schema.String, Schema.Unknown),
|
|
9
|
+
]);
|
|
10
|
+
const decodeMessage = ProviderShared.validateWith(Schema.decodeUnknownEffect(WebSocketResponseCreate));
|
|
11
|
+
const encodeMessage = Schema.encodeSync(Schema.fromJsonString(WebSocketResponseCreate));
|
|
12
|
+
const decodeEvent = Schema.decodeUnknownEffect(OpenResponses.protocol.stream.event);
|
|
13
|
+
const message = (body) => Effect.gen(function* () {
|
|
14
|
+
if (!ProviderShared.isRecord(body))
|
|
15
|
+
return yield* ProviderShared.invalidRequest("Open Responses WebSocket body must be a JSON object");
|
|
16
|
+
const { stream: _stream, stream_options: _streamOptions, background: _background, ...request } = body;
|
|
17
|
+
const decoded = yield* decodeMessage({ ...request, type: "response.create" });
|
|
18
|
+
return { request: decoded, message: encodeMessage(decoded) };
|
|
19
|
+
});
|
|
20
|
+
const driver = (options, body) => {
|
|
21
|
+
let responseID;
|
|
22
|
+
let terminal = false;
|
|
23
|
+
return {
|
|
24
|
+
create: () => Effect.sync(() => {
|
|
25
|
+
responseID = undefined;
|
|
26
|
+
terminal = false;
|
|
27
|
+
return { message: body, mode: "full" };
|
|
28
|
+
}),
|
|
29
|
+
observe: (_create, frame) => Effect.gen(function* () {
|
|
30
|
+
const event = yield* decodeEvent(frame).pipe(Effect.mapError(() => ProviderShared.eventError(options.id, `Invalid ${options.name} WebSocket event`, frame)));
|
|
31
|
+
if (terminal)
|
|
32
|
+
return yield* ProviderShared.eventError(options.id, `${options.name} emitted ${event.type} after a terminal event`, frame);
|
|
33
|
+
if (event.type === "error") {
|
|
34
|
+
terminal = true;
|
|
35
|
+
yield* OpenResponses.decodeKnownErrorEvent(event).pipe(Effect.mapError(() => ProviderShared.eventError(options.id, `${options.name} returned a malformed error event`, frame)));
|
|
36
|
+
return {
|
|
37
|
+
type: "provider-failure",
|
|
38
|
+
error: OpenResponses.providerFailure(options.id, event, `${options.name} stream error`),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (event.type === "response.failed") {
|
|
42
|
+
terminal = true;
|
|
43
|
+
if (responseID && event.response?.id && event.response.id !== responseID)
|
|
44
|
+
return yield* ProviderShared.eventError(options.id, `${options.name} response ID changed during execution`, frame);
|
|
45
|
+
return {
|
|
46
|
+
type: "provider-failure",
|
|
47
|
+
error: OpenResponses.providerFailure(options.id, event, `${options.name} response failed`),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (event.type === "response.created") {
|
|
51
|
+
const created = event.response?.id;
|
|
52
|
+
if (responseID)
|
|
53
|
+
return yield* ProviderShared.eventError(options.id, `${options.name} emitted duplicate response.created`, frame);
|
|
54
|
+
if (!created)
|
|
55
|
+
return yield* ProviderShared.eventError(options.id, `${options.name} response.created is missing response.id`, frame);
|
|
56
|
+
responseID = created;
|
|
57
|
+
return { type: "frame", frame };
|
|
58
|
+
}
|
|
59
|
+
if (!responseID)
|
|
60
|
+
return yield* ProviderShared.eventError(options.id, `${options.name} emitted ${event.type} before response.created`, frame);
|
|
61
|
+
if (event.response?.id && event.response.id !== responseID)
|
|
62
|
+
return yield* ProviderShared.eventError(options.id, `${options.name} response ID changed during execution`, frame);
|
|
63
|
+
if (event.type === "response.completed") {
|
|
64
|
+
terminal = true;
|
|
65
|
+
return { type: "completed", frame };
|
|
66
|
+
}
|
|
67
|
+
if (event.type === "response.incomplete") {
|
|
68
|
+
terminal = true;
|
|
69
|
+
return { type: "incomplete", frame };
|
|
70
|
+
}
|
|
71
|
+
return { type: "frame", frame };
|
|
72
|
+
}),
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
export const transport = (options) => {
|
|
76
|
+
const http = HttpTransport.sseJson.with();
|
|
77
|
+
return {
|
|
78
|
+
id: http.id,
|
|
79
|
+
prepare: (input) => Effect.gen(function* () {
|
|
80
|
+
const parts = yield* HttpTransport.jsonRequestParts(input);
|
|
81
|
+
const headers = Headers.remove(options.headers?.(parts.headers) ?? parts.headers, "content-length");
|
|
82
|
+
const channel = input.webSocket
|
|
83
|
+
? yield* Effect.gen(function* () {
|
|
84
|
+
const create = yield* message(parts.jsonBody);
|
|
85
|
+
const base = driver(options, create.message);
|
|
86
|
+
return {
|
|
87
|
+
url: yield* WebSocketTransport.toWebSocketUrl(parts.url),
|
|
88
|
+
headers,
|
|
89
|
+
rotateAfterMs: options.rotateAfterMs,
|
|
90
|
+
driver: options.driver?.({ request: create.request, message: create.message, base }) ?? base,
|
|
91
|
+
};
|
|
92
|
+
})
|
|
93
|
+
: undefined;
|
|
94
|
+
return {
|
|
95
|
+
http: {
|
|
96
|
+
request: ProviderShared.jsonPost({ url: parts.url, body: parts.bodyText, headers: parts.headers }),
|
|
97
|
+
framing: Framing.sse,
|
|
98
|
+
middleware: input.middleware,
|
|
99
|
+
},
|
|
100
|
+
channel,
|
|
101
|
+
};
|
|
102
|
+
}),
|
|
103
|
+
execute: (prepared, request, runtime, executeOptions) => {
|
|
104
|
+
if (!executeOptions?.webSocket || !prepared.channel)
|
|
105
|
+
return http.execute(prepared.http, request, runtime);
|
|
106
|
+
const exchange = {
|
|
107
|
+
id: request.id ?? "request",
|
|
108
|
+
connect: {
|
|
109
|
+
url: prepared.channel.url,
|
|
110
|
+
headers: prepared.channel.headers,
|
|
111
|
+
rotateAfterMs: prepared.channel.rotateAfterMs,
|
|
112
|
+
},
|
|
113
|
+
fallback: () => Stream.unwrap(http.execute(prepared.http, request, runtime).pipe(Effect.map((execution) => execution.frames))),
|
|
114
|
+
driver: prepared.channel.driver,
|
|
115
|
+
};
|
|
116
|
+
return executeOptions.webSocket.execute(exchange);
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
export const OpenResponsesChannel = { transport };
|