@veryfront/ext-llm-google 0.1.1186 → 0.1.1190
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 +74 -15
- package/esm/_dnt.polyfills.d.ts +12 -0
- package/esm/_dnt.polyfills.d.ts.map +1 -0
- package/esm/_dnt.polyfills.js +15 -0
- package/esm/google-content-parts.d.ts +45 -0
- package/esm/google-content-parts.d.ts.map +1 -0
- package/esm/google-content-parts.js +164 -0
- package/esm/google-grounding-metadata.d.ts +15 -0
- package/esm/google-grounding-metadata.d.ts.map +1 -0
- package/esm/google-grounding-metadata.js +87 -0
- package/esm/google-provider.d.ts +4 -3
- package/esm/google-provider.d.ts.map +1 -1
- package/esm/google-provider.js +407 -77
- package/esm/google-request-builder.d.ts +7 -55
- package/esm/google-request-builder.d.ts.map +1 -1
- package/esm/google-request-builder.js +403 -13
- package/esm/google-stream.d.ts +9 -1
- package/esm/google-stream.d.ts.map +1 -1
- package/esm/google-stream.js +590 -65
- package/esm/google-thought-signatures.d.ts +5 -0
- package/esm/google-thought-signatures.d.ts.map +1 -0
- package/esm/google-thought-signatures.js +296 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +10 -1
- package/package.json +3 -3
package/esm/google-provider.js
CHANGED
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { buildProviderError, createGoogleRequestInit, createWarningCollector, getGoogleEmbeddingUrl, getGoogleGenerateContentUrl, getGoogleStreamGenerateContentUrl, isNumberArray, mergeUsage, parseRetryAfterMs, parseSseChunk, ProviderError, ProviderOverloadedError, ProviderQuotaError, ProviderRateLimitError, ProviderRequestError, readRecord, requestJson, requestStream, stringifyJsonValue, TOOL_INPUT_PENDING_THRESHOLD_MS, unwrapToolInputSchema, } from "veryfront/provider/shared";
|
|
10
10
|
import { buildGoogleGenerateContentRequest, } from "./google-request-builder.js";
|
|
11
|
-
import {
|
|
11
|
+
import { createGoogleToolCallCorrelationRegistry, GOOGLE_CODE_EXECUTION_TOOL_NAME, googleCodeExecutionInput, googleCodeExecutionOutput, readGoogleCodeExecutionResult, readGoogleExecutableCode, readGooglePartDataField, } from "./google-content-parts.js";
|
|
12
|
+
import { readGoogleGroundingMetadata } from "./google-grounding-metadata.js";
|
|
13
|
+
import { createGoogleProviderMetadata, readGoogleThoughtSignature, } from "./google-thought-signatures.js";
|
|
14
|
+
import { extractGoogleCandidateParts, extractGoogleUsage, normalizeGoogleFinishReason, streamGoogleCompatibleParts, } from "./google-stream.js";
|
|
12
15
|
// Re-export error classes so extension tests can import them from this module
|
|
13
16
|
// and from `veryfront/provider/shared` interchangeably.
|
|
14
17
|
export { buildProviderError, isNumberArray, mergeUsage, parseRetryAfterMs, parseSseChunk, ProviderError, ProviderOverloadedError, ProviderQuotaError, ProviderRateLimitError, ProviderRequestError, TOOL_INPUT_PENDING_THRESHOLD_MS, unwrapToolInputSchema, };
|
|
@@ -18,77 +21,364 @@ export { buildProviderError, isNumberArray, mergeUsage, parseRetryAfterMs, parse
|
|
|
18
21
|
// ---------------------------------------------------------------------------
|
|
19
22
|
// Google helper functions
|
|
20
23
|
// ---------------------------------------------------------------------------
|
|
21
|
-
function
|
|
24
|
+
function invalidGoogleResponse(context, issue) {
|
|
25
|
+
return new ProviderRequestError({
|
|
26
|
+
provider: "google",
|
|
27
|
+
status: 200,
|
|
28
|
+
message: `${context.providerLabel} request failed: invalid successful response (${issue})`,
|
|
29
|
+
retryable: false,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function sanitizeRuntimeUsage(usage) {
|
|
33
|
+
const normalized = mergeUsage(undefined, usage);
|
|
34
|
+
return normalized && Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
35
|
+
}
|
|
36
|
+
function readUsageTokenCount(value) {
|
|
37
|
+
return typeof value === "number" &&
|
|
38
|
+
Number.isFinite(value) &&
|
|
39
|
+
Number.isSafeInteger(value) &&
|
|
40
|
+
value >= 0
|
|
41
|
+
? value
|
|
42
|
+
: undefined;
|
|
43
|
+
}
|
|
44
|
+
function extractGoogleEmbedding(payload, context) {
|
|
22
45
|
const record = readRecord(payload);
|
|
46
|
+
if (!record) {
|
|
47
|
+
throw invalidGoogleResponse(context, "embedding response body was not an object");
|
|
48
|
+
}
|
|
23
49
|
const embeddings = record?.embeddings;
|
|
24
|
-
if (
|
|
50
|
+
if (embeddings !== undefined) {
|
|
51
|
+
if (!Array.isArray(embeddings) || embeddings.length !== 1) {
|
|
52
|
+
throw invalidGoogleResponse(context, "embedding response must contain exactly one embedding");
|
|
53
|
+
}
|
|
25
54
|
const firstEmbedding = readRecord(embeddings[0]);
|
|
26
55
|
const values = firstEmbedding?.values;
|
|
27
|
-
if (isNumberArray(values)) {
|
|
56
|
+
if (isNumberArray(values) && values.length > 0) {
|
|
28
57
|
return values;
|
|
29
58
|
}
|
|
59
|
+
throw invalidGoogleResponse(context, "embedding vector missing or invalid");
|
|
30
60
|
}
|
|
31
|
-
const embedding = readRecord(record
|
|
61
|
+
const embedding = readRecord(record.embedding);
|
|
32
62
|
const values = embedding?.values;
|
|
33
|
-
if (isNumberArray(values)) {
|
|
63
|
+
if (isNumberArray(values) && values.length > 0) {
|
|
34
64
|
return values;
|
|
35
65
|
}
|
|
36
|
-
throw
|
|
66
|
+
throw invalidGoogleResponse(context, "embedding vector missing or invalid");
|
|
37
67
|
}
|
|
38
68
|
function extractGoogleUsageTokens(payload) {
|
|
39
69
|
const record = readRecord(payload);
|
|
40
70
|
const usageMetadata = readRecord(record?.usageMetadata);
|
|
41
71
|
const promptTokenCount = usageMetadata?.promptTokenCount;
|
|
42
|
-
return
|
|
72
|
+
return readUsageTokenCount(promptTokenCount);
|
|
73
|
+
}
|
|
74
|
+
function sumGoogleUsageTokens(payloads) {
|
|
75
|
+
let totalTokens = 0;
|
|
76
|
+
for (const payload of payloads) {
|
|
77
|
+
const tokens = extractGoogleUsageTokens(payload);
|
|
78
|
+
if (tokens === undefined) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
totalTokens += tokens;
|
|
82
|
+
if (readUsageTokenCount(totalTokens) === undefined) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return totalTokens;
|
|
43
87
|
}
|
|
44
|
-
function
|
|
45
|
-
const
|
|
88
|
+
function readGoogleGenerateCandidate(payload, context) {
|
|
89
|
+
const record = readRecord(payload);
|
|
90
|
+
if (!record) {
|
|
91
|
+
throw invalidGoogleResponse(context, "generation response body was not an object");
|
|
92
|
+
}
|
|
93
|
+
const candidates = record.candidates;
|
|
94
|
+
if (candidates !== undefined && !Array.isArray(candidates)) {
|
|
95
|
+
throw invalidGoogleResponse(context, "candidates was not an array");
|
|
96
|
+
}
|
|
97
|
+
if (!Array.isArray(candidates) || candidates.length === 0) {
|
|
98
|
+
const promptFeedback = readRecord(record.promptFeedback);
|
|
99
|
+
const promptBlockReason = promptFeedback?.blockReason;
|
|
100
|
+
if (typeof promptBlockReason === "string" && promptBlockReason.length > 0) {
|
|
101
|
+
return { promptBlockReason };
|
|
102
|
+
}
|
|
103
|
+
throw invalidGoogleResponse(context, "candidates array missing or empty");
|
|
104
|
+
}
|
|
105
|
+
if (candidates.length !== 1) {
|
|
106
|
+
throw invalidGoogleResponse(context, "generation response contained multiple candidates");
|
|
107
|
+
}
|
|
108
|
+
const candidate = readRecord(candidates[0]);
|
|
109
|
+
if (!candidate) {
|
|
110
|
+
throw invalidGoogleResponse(context, "first candidate was not an object");
|
|
111
|
+
}
|
|
112
|
+
const finishReason = candidate.finishReason;
|
|
113
|
+
if (finishReason !== undefined && typeof finishReason !== "string") {
|
|
114
|
+
throw invalidGoogleResponse(context, "candidate finish reason was malformed");
|
|
115
|
+
}
|
|
116
|
+
if (candidate.content === undefined) {
|
|
117
|
+
if (typeof finishReason !== "string") {
|
|
118
|
+
throw invalidGoogleResponse(context, "candidate contained no output content");
|
|
119
|
+
}
|
|
120
|
+
return { candidate };
|
|
121
|
+
}
|
|
122
|
+
const content = readRecord(candidate.content);
|
|
123
|
+
if (!content) {
|
|
124
|
+
throw invalidGoogleResponse(context, "candidate content parts missing or malformed");
|
|
125
|
+
}
|
|
126
|
+
if (content.parts === undefined && typeof finishReason === "string") {
|
|
127
|
+
return { candidate };
|
|
128
|
+
}
|
|
129
|
+
if (!Array.isArray(content.parts)) {
|
|
130
|
+
throw invalidGoogleResponse(context, "candidate content parts missing or malformed");
|
|
131
|
+
}
|
|
132
|
+
for (const part of content.parts) {
|
|
133
|
+
if (!readRecord(part)) {
|
|
134
|
+
throw invalidGoogleResponse(context, "candidate content part was not an object");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { candidate };
|
|
138
|
+
}
|
|
139
|
+
function buildGoogleGenerateResult(payload, context) {
|
|
140
|
+
const envelope = readGoogleGenerateCandidate(payload, context);
|
|
141
|
+
const candidate = envelope.candidate;
|
|
142
|
+
const parts = candidate ? extractGoogleCandidateParts(payload) : [];
|
|
46
143
|
const content = [];
|
|
144
|
+
const toolCallRegistry = createGoogleToolCallCorrelationRegistry();
|
|
47
145
|
for (const [index, part] of parts.entries()) {
|
|
48
|
-
|
|
49
|
-
|
|
146
|
+
let thoughtSignature;
|
|
147
|
+
try {
|
|
148
|
+
thoughtSignature = readGoogleThoughtSignature(part);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
throw invalidGoogleResponse(context, "candidate thought signature was malformed");
|
|
152
|
+
}
|
|
153
|
+
if (part.thought !== undefined && typeof part.thought !== "boolean") {
|
|
154
|
+
throw invalidGoogleResponse(context, "candidate thought marker was malformed");
|
|
155
|
+
}
|
|
156
|
+
let dataField;
|
|
157
|
+
try {
|
|
158
|
+
dataField = readGooglePartDataField(part);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
const issue = error instanceof Error &&
|
|
162
|
+
(error.message.includes("unsupported") || error.message.includes("unknown"))
|
|
163
|
+
? "candidate part contained an unsupported data field"
|
|
164
|
+
: "candidate part contained multiple data fields";
|
|
165
|
+
throw invalidGoogleResponse(context, issue);
|
|
166
|
+
}
|
|
167
|
+
if (dataField === "text") {
|
|
168
|
+
if (typeof part.text !== "string") {
|
|
169
|
+
throw invalidGoogleResponse(context, "candidate text part was malformed");
|
|
170
|
+
}
|
|
171
|
+
if (part.thought === true) {
|
|
172
|
+
if (part.text.length > 0 || thoughtSignature !== undefined) {
|
|
173
|
+
content.push({
|
|
174
|
+
type: "reasoning",
|
|
175
|
+
...(part.text.length > 0 ? { text: part.text } : {}),
|
|
176
|
+
...(thoughtSignature !== undefined ? { signature: thoughtSignature } : {}),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (part.text.length > 0) {
|
|
182
|
+
content.push({ type: "text", text: part.text });
|
|
183
|
+
}
|
|
50
184
|
continue;
|
|
51
185
|
}
|
|
52
|
-
|
|
53
|
-
|
|
186
|
+
if (dataField === "functionCall") {
|
|
187
|
+
const functionCall = readRecord(part.functionCall);
|
|
188
|
+
if (!functionCall || typeof functionCall.name !== "string" || functionCall.name.length === 0) {
|
|
189
|
+
throw invalidGoogleResponse(context, "candidate function call was malformed");
|
|
190
|
+
}
|
|
191
|
+
if (functionCall.id !== undefined &&
|
|
192
|
+
(typeof functionCall.id !== "string" || functionCall.id.length === 0)) {
|
|
193
|
+
throw invalidGoogleResponse(context, "candidate function call id was malformed");
|
|
194
|
+
}
|
|
195
|
+
// Gemini omits `args` entirely for zero-parameter tool calls.
|
|
196
|
+
const functionCallArgs = functionCall.args === undefined ? {} : readRecord(functionCall.args);
|
|
197
|
+
if (!functionCallArgs) {
|
|
198
|
+
throw invalidGoogleResponse(context, "candidate function call arguments were not an object");
|
|
199
|
+
}
|
|
200
|
+
let toolCallId;
|
|
201
|
+
try {
|
|
202
|
+
toolCallId = toolCallRegistry.registerFunctionCall(index, typeof functionCall.id === "string" ? functionCall.id : undefined);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
throw invalidGoogleResponse(context, "candidate tool call id was duplicated");
|
|
206
|
+
}
|
|
54
207
|
content.push({
|
|
55
208
|
type: "tool-call",
|
|
56
|
-
toolCallId
|
|
209
|
+
toolCallId,
|
|
57
210
|
toolName: functionCall.name,
|
|
58
|
-
input: stringifyJsonValue(
|
|
211
|
+
input: stringifyJsonValue(functionCallArgs),
|
|
59
212
|
});
|
|
213
|
+
continue;
|
|
60
214
|
}
|
|
215
|
+
if (dataField === "executableCode") {
|
|
216
|
+
let executableCode;
|
|
217
|
+
try {
|
|
218
|
+
executableCode = readGoogleExecutableCode(part.executableCode);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
throw invalidGoogleResponse(context, "candidate executable code was malformed");
|
|
222
|
+
}
|
|
223
|
+
let toolCallId;
|
|
224
|
+
try {
|
|
225
|
+
toolCallId = toolCallRegistry.registerCodeExecution(executableCode.providerId);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
throw invalidGoogleResponse(context, "candidate executable code id was duplicated");
|
|
229
|
+
}
|
|
230
|
+
content.push({
|
|
231
|
+
type: "tool-call",
|
|
232
|
+
toolCallId,
|
|
233
|
+
toolName: GOOGLE_CODE_EXECUTION_TOOL_NAME,
|
|
234
|
+
input: stringifyJsonValue(googleCodeExecutionInput(executableCode)),
|
|
235
|
+
providerExecuted: true,
|
|
236
|
+
});
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
let result;
|
|
240
|
+
try {
|
|
241
|
+
result = readGoogleCodeExecutionResult(part.codeExecutionResult);
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
throw invalidGoogleResponse(context, "candidate code execution result was malformed");
|
|
245
|
+
}
|
|
246
|
+
let toolCallId;
|
|
247
|
+
try {
|
|
248
|
+
toolCallId = toolCallRegistry.resolveCodeExecutionResult(result.providerId);
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
throw invalidGoogleResponse(context, "candidate code execution result did not match executable code");
|
|
252
|
+
}
|
|
253
|
+
content.push({
|
|
254
|
+
type: "tool-result",
|
|
255
|
+
toolCallId,
|
|
256
|
+
toolName: GOOGLE_CODE_EXECUTION_TOOL_NAME,
|
|
257
|
+
result: googleCodeExecutionOutput(result),
|
|
258
|
+
...(result.isError ? { isError: true } : {}),
|
|
259
|
+
providerExecuted: true,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
toolCallRegistry.assertSettled();
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
throw invalidGoogleResponse(context, "candidate executable code had no matching execution result");
|
|
267
|
+
}
|
|
268
|
+
const finishReason = envelope.promptBlockReason
|
|
269
|
+
? { unified: "content-filter", raw: envelope.promptBlockReason }
|
|
270
|
+
: normalizeGoogleFinishReason(candidate?.finishReason);
|
|
271
|
+
const isContentFiltered = typeof finishReason === "object" &&
|
|
272
|
+
finishReason?.unified === "content-filter";
|
|
273
|
+
if (content.length === 0 && !isContentFiltered) {
|
|
274
|
+
throw invalidGoogleResponse(context, "candidate contained no supported output content");
|
|
275
|
+
}
|
|
276
|
+
// Google owns the nested grounding shape and may add fields over time. Keep
|
|
277
|
+
// it opaque, but require the documented object envelope before exposing it.
|
|
278
|
+
let groundingMetadata;
|
|
279
|
+
if (candidate?.groundingMetadata !== undefined) {
|
|
280
|
+
try {
|
|
281
|
+
groundingMetadata = readGoogleGroundingMetadata(candidate.groundingMetadata);
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
throw invalidGoogleResponse(context, "candidate grounding metadata was malformed");
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const usage = sanitizeRuntimeUsage(extractGoogleUsage(payload));
|
|
288
|
+
let providerMetadata;
|
|
289
|
+
try {
|
|
290
|
+
providerMetadata = createGoogleProviderMetadata(parts, groundingMetadata);
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
throw invalidGoogleResponse(context, "provider metadata could not be retained safely");
|
|
61
294
|
}
|
|
62
|
-
// Gemini grounding (google_search / google_search_retrieval) returns
|
|
63
|
-
// a per-candidate groundingMetadata object with web search queries,
|
|
64
|
-
// grounding chunks, and citation indices into the response text.
|
|
65
|
-
// Pass it through opaquely so callers can render footnotes / source
|
|
66
|
-
// chips / "Search results" UI without parsing the wire shape.
|
|
67
|
-
const candidate = extractFirstGoogleCandidate(payload);
|
|
68
|
-
const groundingMetadata = readRecord(candidate?.groundingMetadata);
|
|
69
295
|
return {
|
|
70
296
|
content,
|
|
71
|
-
finishReason
|
|
72
|
-
usage:
|
|
297
|
+
finishReason,
|
|
298
|
+
...(usage ? { usage } : {}),
|
|
73
299
|
...(groundingMetadata ? { groundingMetadata } : {}),
|
|
300
|
+
...(providerMetadata ? { providerMetadata } : {}),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function createGoogleProviderAbortScope(callerSignal) {
|
|
304
|
+
const controller = new AbortController();
|
|
305
|
+
const abortFromCaller = () => controller.abort(callerSignal?.reason);
|
|
306
|
+
if (callerSignal?.aborted) {
|
|
307
|
+
abortFromCaller();
|
|
308
|
+
return { controller, dispose() { } };
|
|
309
|
+
}
|
|
310
|
+
callerSignal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
311
|
+
return {
|
|
312
|
+
controller,
|
|
313
|
+
dispose() {
|
|
314
|
+
callerSignal?.removeEventListener("abort", abortFromCaller);
|
|
315
|
+
},
|
|
74
316
|
};
|
|
75
317
|
}
|
|
318
|
+
function createCancelableGoogleStream(iterable, providerAbortController, disposeAbortScope) {
|
|
319
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
320
|
+
let consumerCanceled = false;
|
|
321
|
+
let disposed = false;
|
|
322
|
+
const dispose = () => {
|
|
323
|
+
if (disposed)
|
|
324
|
+
return;
|
|
325
|
+
disposed = true;
|
|
326
|
+
disposeAbortScope();
|
|
327
|
+
};
|
|
328
|
+
return new ReadableStream({
|
|
329
|
+
async pull(controller) {
|
|
330
|
+
try {
|
|
331
|
+
const next = await iterator.next();
|
|
332
|
+
if (next.done) {
|
|
333
|
+
dispose();
|
|
334
|
+
controller.close();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
controller.enqueue(next.value);
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
dispose();
|
|
341
|
+
if (!consumerCanceled) {
|
|
342
|
+
controller.error(error);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
async cancel(reason) {
|
|
347
|
+
consumerCanceled = true;
|
|
348
|
+
if (!providerAbortController.signal.aborted) {
|
|
349
|
+
providerAbortController.abort(reason);
|
|
350
|
+
}
|
|
351
|
+
try {
|
|
352
|
+
await iterator.return?.();
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
if (!providerAbortController.signal.aborted) {
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
finally {
|
|
360
|
+
dispose();
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
}, { highWaterMark: 0 });
|
|
364
|
+
}
|
|
76
365
|
export function createGoogleModelRuntime(config, modelId) {
|
|
77
366
|
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
367
|
+
const providerLabel = config.name ?? "google";
|
|
368
|
+
const responseContext = { providerLabel };
|
|
78
369
|
return {
|
|
79
|
-
provider:
|
|
370
|
+
provider: providerLabel,
|
|
80
371
|
modelId,
|
|
81
372
|
specificationVersion: "v3",
|
|
82
373
|
supportedUrls: {},
|
|
83
|
-
doGenerate(
|
|
84
|
-
const options = optionsForRuntime;
|
|
374
|
+
doGenerate(options) {
|
|
85
375
|
const url = getGoogleGenerateContentUrl(config.baseURL, modelId);
|
|
86
376
|
const warnings = createWarningCollector();
|
|
87
|
-
const body = buildGoogleGenerateContentRequest(
|
|
377
|
+
const body = buildGoogleGenerateContentRequest(providerLabel, options, warnings);
|
|
88
378
|
return requestJson({
|
|
89
379
|
url,
|
|
90
380
|
fetchImpl,
|
|
91
|
-
providerLabel
|
|
381
|
+
providerLabel,
|
|
92
382
|
providerKind: "google",
|
|
93
383
|
init: createGoogleRequestInit({
|
|
94
384
|
apiKey: config.apiKey,
|
|
@@ -99,74 +389,114 @@ export function createGoogleModelRuntime(config, modelId) {
|
|
|
99
389
|
}).then((payload) => {
|
|
100
390
|
const drained = warnings.drain();
|
|
101
391
|
return {
|
|
102
|
-
...buildGoogleGenerateResult(payload),
|
|
392
|
+
...buildGoogleGenerateResult(payload, responseContext),
|
|
103
393
|
...(drained.length > 0 ? { warnings: drained } : {}),
|
|
104
394
|
};
|
|
105
395
|
});
|
|
106
396
|
},
|
|
107
|
-
doStream(
|
|
108
|
-
const options = optionsForRuntime;
|
|
397
|
+
async doStream(options) {
|
|
109
398
|
const url = getGoogleStreamGenerateContentUrl(config.baseURL, modelId);
|
|
110
399
|
const warnings = createWarningCollector();
|
|
111
|
-
const body = buildGoogleGenerateContentRequest(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
400
|
+
const body = buildGoogleGenerateContentRequest(providerLabel, options, warnings);
|
|
401
|
+
const providerAbortScope = createGoogleProviderAbortScope(options.abortSignal);
|
|
402
|
+
let responseStream;
|
|
403
|
+
try {
|
|
404
|
+
responseStream = await requestStream({
|
|
405
|
+
url,
|
|
406
|
+
fetchImpl,
|
|
407
|
+
providerLabel,
|
|
408
|
+
providerKind: "google",
|
|
409
|
+
init: createGoogleRequestInit({
|
|
410
|
+
apiKey: config.apiKey,
|
|
411
|
+
extraHeaders: options.headers,
|
|
412
|
+
body: JSON.stringify(body),
|
|
413
|
+
signal: providerAbortScope.controller.signal,
|
|
414
|
+
}),
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
providerAbortScope.dispose();
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
const drained = warnings.drain();
|
|
422
|
+
return {
|
|
423
|
+
stream: createCancelableGoogleStream(streamGoogleCompatibleParts(responseStream, responseContext), providerAbortScope.controller, providerAbortScope.dispose),
|
|
424
|
+
...(drained.length > 0 ? { warnings: drained } : {}),
|
|
425
|
+
};
|
|
130
426
|
},
|
|
131
427
|
};
|
|
132
428
|
}
|
|
133
429
|
export function createGoogleEmbeddingRuntime(config, modelId) {
|
|
134
430
|
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
431
|
+
const providerLabel = config.name ?? "google";
|
|
432
|
+
const responseContext = { providerLabel };
|
|
135
433
|
return {
|
|
136
|
-
provider:
|
|
434
|
+
provider: providerLabel,
|
|
137
435
|
modelId,
|
|
138
436
|
supportsParallelCalls: true,
|
|
139
|
-
doEmbed({ values, abortSignal }) {
|
|
437
|
+
async doEmbed({ values, abortSignal }) {
|
|
140
438
|
if (values.length === 0) {
|
|
141
|
-
return
|
|
439
|
+
return {
|
|
142
440
|
embeddings: [],
|
|
143
441
|
warnings: [],
|
|
144
442
|
rawResponse: { embeddings: [] },
|
|
145
|
-
}
|
|
443
|
+
};
|
|
146
444
|
}
|
|
147
445
|
const url = getGoogleEmbeddingUrl(config.baseURL, modelId);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
446
|
+
const providerAbortScope = createGoogleProviderAbortScope(abortSignal);
|
|
447
|
+
try {
|
|
448
|
+
providerAbortScope.controller.signal.throwIfAborted();
|
|
449
|
+
const requests = values.map((value) => requestJson({
|
|
450
|
+
url,
|
|
451
|
+
fetchImpl,
|
|
452
|
+
providerLabel,
|
|
453
|
+
providerKind: "google",
|
|
454
|
+
init: createGoogleRequestInit({
|
|
455
|
+
apiKey: config.apiKey,
|
|
456
|
+
body: JSON.stringify({
|
|
457
|
+
content: {
|
|
458
|
+
parts: [{ text: value }],
|
|
459
|
+
},
|
|
460
|
+
}),
|
|
461
|
+
signal: providerAbortScope.controller.signal,
|
|
159
462
|
}),
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
463
|
+
}).then((payload) => ({
|
|
464
|
+
payload,
|
|
465
|
+
embedding: extractGoogleEmbedding(payload, responseContext),
|
|
466
|
+
})).catch((error) => {
|
|
467
|
+
if (!providerAbortScope.controller.signal.aborted) {
|
|
468
|
+
providerAbortScope.controller.abort(error);
|
|
469
|
+
}
|
|
470
|
+
throw error;
|
|
471
|
+
}));
|
|
472
|
+
let results;
|
|
473
|
+
try {
|
|
474
|
+
results = await Promise.all(requests);
|
|
475
|
+
}
|
|
476
|
+
catch (error) {
|
|
477
|
+
if (abortSignal?.aborted) {
|
|
478
|
+
throw abortSignal.reason;
|
|
479
|
+
}
|
|
480
|
+
throw error;
|
|
481
|
+
}
|
|
482
|
+
const payloads = results.map((result) => result.payload);
|
|
483
|
+
const tokens = sumGoogleUsageTokens(payloads);
|
|
484
|
+
const embeddings = results.map((result) => result.embedding);
|
|
485
|
+
const dimensions = embeddings[0]?.length;
|
|
486
|
+
if (dimensions !== undefined &&
|
|
487
|
+
embeddings.some((embedding) => embedding.length !== dimensions)) {
|
|
488
|
+
throw invalidGoogleResponse(responseContext, "embedding vectors had inconsistent dimensions");
|
|
489
|
+
}
|
|
490
|
+
return {
|
|
491
|
+
embeddings,
|
|
492
|
+
...(tokens !== undefined ? { usage: { tokens } } : {}),
|
|
493
|
+
rawResponse: payloads,
|
|
494
|
+
warnings: [],
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
finally {
|
|
498
|
+
providerAbortScope.dispose();
|
|
499
|
+
}
|
|
170
500
|
},
|
|
171
501
|
};
|
|
172
502
|
}
|
|
@@ -1,62 +1,14 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export
|
|
3
|
-
type: "function";
|
|
4
|
-
name: string;
|
|
5
|
-
description?: string;
|
|
6
|
-
inputSchema: unknown;
|
|
7
|
-
} | {
|
|
8
|
-
type: "provider";
|
|
9
|
-
name: string;
|
|
10
|
-
id: `${string}.${string}`;
|
|
11
|
-
args: Record<string, unknown>;
|
|
12
|
-
};
|
|
13
|
-
type ProviderReasoningEffort = "low" | "medium" | "high" | "max";
|
|
14
|
-
type ProviderReasoningOption = {
|
|
15
|
-
enabled?: boolean;
|
|
16
|
-
effort?: ProviderReasoningEffort;
|
|
17
|
-
budgetTokens?: number;
|
|
18
|
-
};
|
|
19
|
-
export type OpenAICompatibleLanguageOptions = {
|
|
20
|
-
prompt: RuntimePromptMessage[];
|
|
21
|
-
maxOutputTokens?: number;
|
|
22
|
-
temperature?: number;
|
|
23
|
-
topP?: number;
|
|
24
|
-
topK?: number;
|
|
25
|
-
stopSequences?: string[];
|
|
26
|
-
tools?: RuntimeToolDefinition[];
|
|
27
|
-
toolChoice?: unknown;
|
|
28
|
-
seed?: number;
|
|
29
|
-
presencePenalty?: number;
|
|
30
|
-
frequencyPenalty?: number;
|
|
31
|
-
headers?: HeadersInit;
|
|
32
|
-
providerOptions?: Record<string, unknown>;
|
|
33
|
-
includeRawChunks?: boolean;
|
|
34
|
-
abortSignal?: AbortSignal;
|
|
35
|
-
cacheControl?: unknown;
|
|
36
|
-
reasoning?: ProviderReasoningOption;
|
|
37
|
-
userId?: string;
|
|
1
|
+
import type { ModelRuntimeCallOptions, ModelRuntimeToolDefinition } from "veryfront/provider/shared";
|
|
2
|
+
export interface OpenAICompatibleLanguageOptions extends ModelRuntimeCallOptions {
|
|
38
3
|
requestLabels?: Record<string, string>;
|
|
39
|
-
serviceTier?: "auto" | "default" | "flex" | "scale";
|
|
40
|
-
parallelToolCalls?: boolean;
|
|
41
|
-
responseFormat?: {
|
|
42
|
-
type: "text";
|
|
43
|
-
} | {
|
|
44
|
-
type: "json";
|
|
45
|
-
} | {
|
|
46
|
-
type: "json_schema";
|
|
47
|
-
name: string;
|
|
48
|
-
schema: unknown;
|
|
49
|
-
description?: string;
|
|
50
|
-
strict?: boolean;
|
|
51
|
-
};
|
|
52
|
-
anthropicContainer?: unknown;
|
|
53
4
|
googleCachedContent?: string;
|
|
54
|
-
googleSafetySettings?:
|
|
5
|
+
googleSafetySettings?: ReadonlyArray<{
|
|
55
6
|
category: string;
|
|
56
7
|
threshold: string;
|
|
57
8
|
}>;
|
|
58
|
-
|
|
59
|
-
|
|
9
|
+
}
|
|
10
|
+
/** @deprecated Import `ModelRuntimeToolDefinition` from `veryfront/provider/shared` instead. */
|
|
11
|
+
export type RuntimeToolDefinition = ModelRuntimeToolDefinition;
|
|
60
12
|
type WarningCollector = {
|
|
61
13
|
push(warning: {
|
|
62
14
|
type: "unsupported-setting" | "other";
|
|
@@ -73,7 +25,7 @@ type WarningCollector = {
|
|
|
73
25
|
};
|
|
74
26
|
type GoogleCompatibleContent = {
|
|
75
27
|
role: "user" | "model";
|
|
76
|
-
parts:
|
|
28
|
+
parts: readonly Record<string, unknown>[];
|
|
77
29
|
};
|
|
78
30
|
type GoogleCompatibleRequest = {
|
|
79
31
|
contents: GoogleCompatibleContent[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-request-builder.d.ts","sourceRoot":"","sources":["../src/google-request-builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"google-request-builder.d.ts","sourceRoot":"","sources":["../src/google-request-builder.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,uBAAuB,EAEvB,0BAA0B,EAE3B,MAAM,2BAA2B,CAAC;AAYnC,MAAM,WAAW,+BAAgC,SAAQ,uBAAuB;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,aAAa,CAAC;QACnC,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;CACJ;AAED,gGAAgG;AAChG,MAAM,MAAM,qBAAqB,GAAG,0BAA0B,CAAC;AAE/D,KAAK,gBAAgB,GAAG;IACtB,IAAI,CAAC,OAAO,EAAE;QACZ,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC;QACtC,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI,CAAC;IACT,KAAK,IAAI,KAAK,CAAC;QACb,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC;QACtC,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ,CAAC;AAEF,KAAK,uBAAuB,GAAG;IAC7B,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CAC3C,CAAC;AAEF,KAAK,uBAAuB,GAAG;IAC7B,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,iBAAiB,CAAC,EAAE;QAClB,KAAK,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAChC,CAAC;IACF,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACvC,UAAU,CAAC,EAAE;QACX,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChD,CAAC;IACF,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AA0yBF,wBAAgB,iCAAiC,CAC/C,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,+BAA+B,EACxC,QAAQ,EAAE,gBAAgB,GACzB,uBAAuB,CAqDzB"}
|