zidane 8.2.1 → 8.2.3
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/acp-cli.js +8 -8
- package/dist/{acp-D7Pr8NGt.js → acp-oYRjlh71.js} +6 -6
- package/dist/{acp-D7Pr8NGt.js.map → acp-oYRjlh71.js.map} +1 -1
- package/dist/acp.d.ts +2 -2
- package/dist/acp.js +1 -1
- package/dist/{agent-DecDtKA_.js → agent-CBQglXtH.js} +9 -379
- package/dist/agent-CBQglXtH.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/{builtin-artifact-kinds-B_OEgf8b.js → builtin-artifact-kinds-BmorPkgz.js} +36 -9
- package/dist/builtin-artifact-kinds-BmorPkgz.js.map +1 -0
- package/dist/chat/pure.d.ts +4 -4
- package/dist/chat/pure.js +6 -6
- package/dist/chat.d.ts +4 -4
- package/dist/chat.js +15 -15
- package/dist/{cli-shared-Dx4vmif6.js → cli-shared-E5m_pI8A.js} +2 -2
- package/dist/{cli-shared-Dx4vmif6.js.map → cli-shared-E5m_pI8A.js.map} +1 -1
- package/dist/{client-core-imHuBWV3.d.ts → client-core-EEEX_u6D.d.ts} +24 -5
- package/dist/{client-core-imHuBWV3.d.ts.map → client-core-EEEX_u6D.d.ts.map} +1 -1
- package/dist/{connection-loop-CdHt3iQi.d.ts → connection-loop-CpgfUmuw.d.ts} +3 -3
- package/dist/{connection-loop-CdHt3iQi.d.ts.map → connection-loop-CpgfUmuw.d.ts.map} +1 -1
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/{credentials-Dti61yoi.js → credentials-B9DfBJP9.js} +11 -7
- package/dist/credentials-B9DfBJP9.js.map +1 -0
- package/dist/daemon-cli.js +2 -2
- package/dist/{dispatcher-vy3J6JMA.js → dispatcher-Bv9-c6I2.js} +28 -14
- package/dist/dispatcher-Bv9-c6I2.js.map +1 -0
- package/dist/{engine-BN6PjWAW.js → engine-XmoPPB6U.js} +5 -5
- package/dist/{engine-BN6PjWAW.js.map → engine-XmoPPB6U.js.map} +1 -1
- package/dist/{eval-BaJtxLCW.js → eval-CfQlBR81.js} +2 -2
- package/dist/{eval-BaJtxLCW.js.map → eval-CfQlBR81.js.map} +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +1 -1
- package/dist/{executor-CT4oUkg6.js → executor-BhthPE1q.js} +3 -3
- package/dist/{executor-CT4oUkg6.js.map → executor-BhthPE1q.js.map} +1 -1
- package/dist/{extensions-BKt7lxxv.js → extensions-BgBwogRW.js} +114 -113
- package/dist/extensions-BgBwogRW.js.map +1 -0
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +2 -2
- package/dist/{fold-rules-BYedvsH7.d.ts → fold-rules-DSypi36k.d.ts} +2 -2
- package/dist/{fold-rules-BYedvsH7.d.ts.map → fold-rules-DSypi36k.d.ts.map} +1 -1
- package/dist/google-O6pRnddf.js +881 -0
- package/dist/google-O6pRnddf.js.map +1 -0
- package/dist/{headless-Ebd1ZEzC.js → headless-R4pbpTkm.js} +3 -3
- package/dist/{headless-Ebd1ZEzC.js.map → headless-R4pbpTkm.js.map} +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/{host-scoped-settings-Cy_zbmB_.js → host-scoped-settings-C-Nmb6vr.js} +2 -2
- package/dist/{host-scoped-settings-Cy_zbmB_.js.map → host-scoped-settings-C-Nmb6vr.js.map} +1 -1
- package/dist/{host-signals-Cx2IAbHh.js → host-signals-BFudPyCT.js} +5 -5
- package/dist/{host-signals-Cx2IAbHh.js.map → host-signals-BFudPyCT.js.map} +1 -1
- package/dist/{index-pc71ADi6.d.ts → index-AqRZ3gEz.d.ts} +71 -4
- package/dist/index-AqRZ3gEz.d.ts.map +1 -0
- package/dist/{index-Bt_G2Z_2.d.ts → index-CDS7YdUC.d.ts} +5 -5
- package/dist/{index-Bt_G2Z_2.d.ts.map → index-CDS7YdUC.d.ts.map} +1 -1
- package/dist/{index-CidMTcxS.d.ts → index-DyQyDLX1.d.ts} +167 -10
- package/dist/index-DyQyDLX1.d.ts.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +15 -15
- package/dist/ipc/remote.d.ts +5 -5
- package/dist/ipc/remote.js +3 -3
- package/dist/ipc.d.ts +5 -5
- package/dist/ipc.js +7 -7
- package/dist/{links-DSZ4uQ5w.js → links-rFaWqS2Y.js} +10 -10
- package/dist/{links-DSZ4uQ5w.js.map → links-rFaWqS2Y.js.map} +1 -1
- package/dist/{mcp-allowlist-BQ5MvhHi.js → mcp-allowlist-CLXweyxk.js} +14 -3
- package/dist/mcp-allowlist-CLXweyxk.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/{oauth-BSqXgads.js → oauth-7_48zR7n.js} +226 -40
- package/dist/oauth-7_48zR7n.js.map +1 -0
- package/dist/{openai-dTmPxM6T.js → openai-Rh6u5NXF.js} +121 -2
- package/dist/openai-Rh6u5NXF.js.map +1 -0
- package/dist/{openrouter-CtZbQDa0.js → openrouter-Dg0yaI-o.js} +91 -2
- package/dist/openrouter-Dg0yaI-o.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/{presets-EzlHyFSi.js → presets-DXeHsd24.js} +3 -3
- package/dist/{presets-EzlHyFSi.js.map → presets-DXeHsd24.js.map} +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/{prompts-D2lPHLOV.d.ts → prompts-DmY13jvF.d.ts} +2 -1
- package/dist/prompts-DmY13jvF.d.ts.map +1 -0
- package/dist/protocol-BIv1Yuvl.js.map +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/google.d.ts +1 -1
- package/dist/providers/google.js +1 -784
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/neuralwatt.d.ts +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +1 -101
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +6 -5
- package/dist/{registry-BBaEgTsX.js → registry-eourRLY2.js} +3 -3
- package/dist/{registry-BBaEgTsX.js.map → registry-eourRLY2.js.map} +1 -1
- package/dist/{remote-hosts-bO7wOt44.js → remote-hosts-DY2J07ut.js} +4 -4
- package/dist/{remote-hosts-bO7wOt44.js.map → remote-hosts-DY2J07ut.js.map} +1 -1
- package/dist/restate.d.ts +1 -1
- package/dist/{secret-shapes-Cda65uDH.js → secret-shapes-BjW4l6U0.js} +17 -3
- package/dist/secret-shapes-BjW4l6U0.js.map +1 -0
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-factory-BazyVdzj.js → session-factory-CMwIXE-7.js} +5 -5
- package/dist/{session-factory-BazyVdzj.js.map → session-factory-CMwIXE-7.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/{settings-keys-zYaer8DY.js → settings-keys-CPdmkTz7.js} +3 -3
- package/dist/{settings-keys-zYaer8DY.js.map → settings-keys-CPdmkTz7.js.map} +1 -1
- package/dist/{shell-path-od9XHRPt.js → shell-path-B8b3EOtK.js} +3 -3
- package/dist/{shell-path-od9XHRPt.js.map → shell-path-B8b3EOtK.js.map} +1 -1
- package/dist/{skills-use-D68a1gyZ.js → skills-use-CssTEaIj.js} +375 -2
- package/dist/skills-use-CssTEaIj.js.map +1 -0
- package/dist/skills.d.ts +1 -1
- package/dist/store-L01W0gGI.js.map +1 -1
- package/dist/{system-freeze-DXvJpveP.js → system-freeze-Um9ZJiPo.js} +24 -9
- package/dist/system-freeze-Um9ZJiPo.js.map +1 -0
- package/dist/{todo-edits-CmqNFvLJ.js → todo-edits-BERpCRPj.js} +3 -3
- package/dist/{todo-edits-CmqNFvLJ.js.map → todo-edits-BERpCRPj.js.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/prompts.d.ts +1 -1
- package/dist/tools/prompts.js +7 -0
- package/dist/tools/prompts.js.map +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/{tools-9Dpm8F-f.js → tools-pZVFIJ_K.js} +273 -2
- package/dist/tools-pZVFIJ_K.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +4 -4
- package/dist/{transcript-anchors-B3TFJqr_.js → transcript-anchors-jHrg-r-H.js} +10 -10
- package/dist/{transcript-anchors-B3TFJqr_.js.map → transcript-anchors-jHrg-r-H.js.map} +1 -1
- package/dist/{transcript-reconcile-BC2DbTmp.js → transcript-reconcile-CPdrNbbR.js} +19 -2
- package/dist/{transcript-reconcile-BC2DbTmp.js.map → transcript-reconcile-CPdrNbbR.js.map} +1 -1
- package/dist/tui.d.ts +7 -7
- package/dist/tui.js +25 -23
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CIFoOebZ.d.ts → turn-operations-DuGtKuc_.d.ts} +10 -3
- package/dist/turn-operations-DuGtKuc_.d.ts.map +1 -0
- package/dist/{types-iFLlZKd0.d.ts → types-C0PJmBaO.d.ts} +2 -2
- package/dist/{types-iFLlZKd0.d.ts.map → types-C0PJmBaO.d.ts.map} +1 -1
- package/dist/types-JzccL5GE.js +24 -0
- package/dist/types-JzccL5GE.js.map +1 -0
- package/dist/types.d.ts +2 -2
- package/dist/{url-BTH-HE9Y.js → url-D8KZ7Edw.js} +2 -2
- package/dist/{url-BTH-HE9Y.js.map → url-D8KZ7Edw.js.map} +1 -1
- package/dist/{version-4T82xRxQ.js → version-bd3DQJkc.js} +2 -2
- package/dist/{version-4T82xRxQ.js.map → version-bd3DQJkc.js.map} +1 -1
- package/dist/wire-DH77Z-rr.js +80 -0
- package/dist/wire-DH77Z-rr.js.map +1 -0
- package/dist/{workspace-exec-stream-By8na6vz.js → workspace-exec-stream-CyfYgqAh.js} +4 -4
- package/dist/{workspace-exec-stream-By8na6vz.js.map → workspace-exec-stream-CyfYgqAh.js.map} +1 -1
- package/dist/{write-file-B4vi3SvQ.js → write-file-QYrcDdl3.js} +3 -3
- package/dist/{write-file-B4vi3SvQ.js.map → write-file-QYrcDdl3.js.map} +1 -1
- package/dist/{ws-CS312Jpx.js → ws-CURQ2igk.js} +5 -5
- package/dist/{ws-CS312Jpx.js.map → ws-CURQ2igk.js.map} +1 -1
- package/dist/{ws-client-wWka-Jku.js → ws-client-BVljYbdb.js} +3 -3
- package/dist/{ws-client-wWka-Jku.js.map → ws-client-BVljYbdb.js.map} +1 -1
- package/dist/xai-CStTwCV2.js +182 -0
- package/dist/xai-CStTwCV2.js.map +1 -0
- package/docs/ARCHITECTURE.md +1 -0
- package/docs/ARTIFACTS.md +1 -1
- package/docs/EXTENSIONS.md +9 -5
- package/package.json +2 -2
- package/dist/agent-DecDtKA_.js.map +0 -1
- package/dist/builtin-artifact-kinds-B_OEgf8b.js.map +0 -1
- package/dist/credentials-Dti61yoi.js.map +0 -1
- package/dist/dispatcher-vy3J6JMA.js.map +0 -1
- package/dist/extensions-BKt7lxxv.js.map +0 -1
- package/dist/index-CidMTcxS.d.ts.map +0 -1
- package/dist/index-pc71ADi6.d.ts.map +0 -1
- package/dist/mcp-allowlist-BQ5MvhHi.js.map +0 -1
- package/dist/oauth-BSqXgads.js.map +0 -1
- package/dist/openai-dTmPxM6T.js.map +0 -1
- package/dist/openrouter-CtZbQDa0.js.map +0 -1
- package/dist/prompts-D2lPHLOV.d.ts.map +0 -1
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/xai.js.map +0 -1
- package/dist/secret-shapes-Cda65uDH.js.map +0 -1
- package/dist/skills-use-D68a1gyZ.js.map +0 -1
- package/dist/system-freeze-DXvJpveP.js.map +0 -1
- package/dist/tools-9Dpm8F-f.js.map +0 -1
- package/dist/turn-operations-CIFoOebZ.d.ts.map +0 -1
package/dist/providers/google.js
CHANGED
|
@@ -1,784 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { i as documentBlockMarker, n as assertResolvedMediaBlock } from "../types-CzmghX7C.js";
|
|
3
|
-
import { A as lookupModel, P as systemNotesAsText, T as fillEstimatedCost, g as createToolNameBoundary, h as userMessage, j as mergeCatalogEntry, m as toolResultsMessage, o as assistantMessage, v as sanitizeToolSpecs, z as unsupportedMediaError } from "../openai-compat-5HkREis_.js";
|
|
4
|
-
import { renderSystemForWire } from "../prompt.js";
|
|
5
|
-
import { c as reasoningWireAction } from "../messages-Wd0QtRUJ.js";
|
|
6
|
-
import { t as GOOGLE_VERTEX_OAUTH_ID } from "../google-oauth-g3JmRoJ-.js";
|
|
7
|
-
import { a as pinnedAccountMissingError, l as resolveOAuthApiKey, o as readAccountOAuthCredential, s as readOAuthCredentials } from "../oauth-DRrs-65k.js";
|
|
8
|
-
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
-
import { join } from "node:path";
|
|
10
|
-
import { tmpdir } from "node:os";
|
|
11
|
-
import { lstat, readdir, rm } from "node:fs/promises";
|
|
12
|
-
import { normalizeContext } from "@earendil-works/pi-ai";
|
|
13
|
-
import { getBuiltinModel } from "@earendil-works/pi-ai/providers/all";
|
|
14
|
-
//#region src/providers/google.ts
|
|
15
|
-
/** Model used when a request names none. Served on free-tier Gemini API keys. */
|
|
16
|
-
const GOOGLE_DEFAULT_MODEL = "gemini-3.8-flash";
|
|
17
|
-
/**
|
|
18
|
-
* Models pi-ai hasn't bundled yet, served on both routes. A pi-ai release
|
|
19
|
-
* that bundles one supersedes its entry (bundled metadata always wins), so
|
|
20
|
-
* delete the entry then.
|
|
21
|
-
*/
|
|
22
|
-
const GOOGLE_EXTRA_MODELS = [];
|
|
23
|
-
const lookupBuiltin = getBuiltinModel;
|
|
24
|
-
function builtinModel(route, modelId) {
|
|
25
|
-
try {
|
|
26
|
-
return lookupBuiltin(route, modelId);
|
|
27
|
-
} catch {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* pi-ai model record for `modelId` on `route`: the bundled entry, else a
|
|
33
|
-
* {@link GOOGLE_EXTRA_MODELS} overlay, else the default model's shape under
|
|
34
|
-
* the requested id. A catalog entry overlays whichever applies.
|
|
35
|
-
*
|
|
36
|
-
* @param route - Backend the request goes to.
|
|
37
|
-
* @param modelId - Requested model id.
|
|
38
|
-
* @param catalog - Per-run catalog override.
|
|
39
|
-
*/
|
|
40
|
-
function resolveGoogleModel(route, modelId, catalog) {
|
|
41
|
-
const entry = lookupModel(route, modelId, catalog);
|
|
42
|
-
const bundled = builtinModel(route, modelId);
|
|
43
|
-
if (bundled) return mergeCatalogEntry(bundled, entry);
|
|
44
|
-
const extra = GOOGLE_EXTRA_MODELS.find((m) => m.id === modelId);
|
|
45
|
-
const template = builtinModel(route, extra?.template ?? "gemini-3.8-flash");
|
|
46
|
-
if (!template) throw new Error(`Google model registry (${route}) is missing ${extra?.template ?? "gemini-3.8-flash"}`);
|
|
47
|
-
if (extra) return mergeCatalogEntry({
|
|
48
|
-
...template,
|
|
49
|
-
id: extra.id,
|
|
50
|
-
name: extra.name,
|
|
51
|
-
contextWindow: extra.contextWindow,
|
|
52
|
-
maxTokens: extra.maxTokens,
|
|
53
|
-
cost: extra.rates()
|
|
54
|
-
}, entry);
|
|
55
|
-
return mergeCatalogEntry({
|
|
56
|
-
...template,
|
|
57
|
-
id: modelId,
|
|
58
|
-
name: modelId
|
|
59
|
-
}, entry);
|
|
60
|
-
}
|
|
61
|
-
function formatTools(tools) {
|
|
62
|
-
return sanitizeToolSpecs(tools, { profile: "openai" }).map((t) => ({
|
|
63
|
-
name: t.name,
|
|
64
|
-
description: t.description ?? "",
|
|
65
|
-
parameters: t.inputSchema
|
|
66
|
-
}));
|
|
67
|
-
}
|
|
68
|
-
function documentMarker(doc) {
|
|
69
|
-
return documentBlockMarker(doc, "document omitted");
|
|
70
|
-
}
|
|
71
|
-
function emptyUsage() {
|
|
72
|
-
return {
|
|
73
|
-
input: 0,
|
|
74
|
-
output: 0,
|
|
75
|
-
cacheRead: 0,
|
|
76
|
-
cacheWrite: 0,
|
|
77
|
-
totalTokens: 0,
|
|
78
|
-
cost: {
|
|
79
|
-
input: 0,
|
|
80
|
-
output: 0,
|
|
81
|
-
cacheRead: 0,
|
|
82
|
-
cacheWrite: 0,
|
|
83
|
-
total: 0
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
function piUserMessage(content, provider) {
|
|
88
|
-
for (const b of content) {
|
|
89
|
-
if (b.type === "audio") throw unsupportedMediaError("audio", provider);
|
|
90
|
-
if (b.type === "video") throw unsupportedMediaError("video", provider);
|
|
91
|
-
}
|
|
92
|
-
const parts = [];
|
|
93
|
-
for (const block of content) if (block.type === "text") parts.push({
|
|
94
|
-
type: "text",
|
|
95
|
-
text: block.text
|
|
96
|
-
});
|
|
97
|
-
else if (block.type === "image") {
|
|
98
|
-
assertResolvedMediaBlock(block, "Google wire messages");
|
|
99
|
-
parts.push({
|
|
100
|
-
type: "image",
|
|
101
|
-
data: block.data,
|
|
102
|
-
mimeType: block.mediaType
|
|
103
|
-
});
|
|
104
|
-
} else if (block.type === "document") {
|
|
105
|
-
assertResolvedMediaBlock(block, "Google wire messages");
|
|
106
|
-
parts.push({
|
|
107
|
-
type: "text",
|
|
108
|
-
text: documentMarker(block)
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
if (parts.length === 0) return null;
|
|
112
|
-
if (parts.length === 1 && parts[0].type === "text") return {
|
|
113
|
-
role: "user",
|
|
114
|
-
content: parts[0].text,
|
|
115
|
-
timestamp: Date.now()
|
|
116
|
-
};
|
|
117
|
-
return {
|
|
118
|
-
role: "user",
|
|
119
|
-
content: parts,
|
|
120
|
-
timestamp: Date.now()
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
function piToolResultMessage(result, toolName, provider) {
|
|
124
|
-
const content = typeof result.output === "string" ? [{
|
|
125
|
-
type: "text",
|
|
126
|
-
text: result.output
|
|
127
|
-
}] : result.output.map((block) => {
|
|
128
|
-
if (block.type === "image") {
|
|
129
|
-
assertResolvedMediaBlock(block, "Google wire messages");
|
|
130
|
-
return {
|
|
131
|
-
type: "image",
|
|
132
|
-
data: block.data,
|
|
133
|
-
mimeType: block.mediaType
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
if (block.type === "audio") throw unsupportedMediaError("audio", provider);
|
|
137
|
-
if (block.type === "video") throw unsupportedMediaError("video", provider);
|
|
138
|
-
if (block.type === "document") {
|
|
139
|
-
assertResolvedMediaBlock(block, "Google wire messages");
|
|
140
|
-
return {
|
|
141
|
-
type: "text",
|
|
142
|
-
text: documentMarker(block)
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
return {
|
|
146
|
-
type: "text",
|
|
147
|
-
text: block.text
|
|
148
|
-
};
|
|
149
|
-
});
|
|
150
|
-
return {
|
|
151
|
-
role: "toolResult",
|
|
152
|
-
toolCallId: result.callId,
|
|
153
|
-
toolName,
|
|
154
|
-
content,
|
|
155
|
-
isError: result.isError ?? false,
|
|
156
|
-
timestamp: Date.now()
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* One assistant turn for pi-ai. A turn holding this route's signatures is
|
|
161
|
-
* stamped with the TARGET model, so pi-ai treats it as its own and replays
|
|
162
|
-
* the signatures (Google accepts them across its models). Any other turn is
|
|
163
|
-
* stamped foreign: pi-ai then sends its reasoning as plain text and its
|
|
164
|
-
* calls unsigned.
|
|
165
|
-
*/
|
|
166
|
-
function piAssistantMessage(content, model, route) {
|
|
167
|
-
const native = content.some((block) => (block.type === "text" || block.type === "tool_call" || block.type === "thinking") && block.signature !== void 0 && block.signatureProducer === route);
|
|
168
|
-
const own = (block) => native && block.signatureProducer === route ? block.signature : void 0;
|
|
169
|
-
const piContent = [];
|
|
170
|
-
for (const block of content) if (block.type === "text") {
|
|
171
|
-
const signature = own(block);
|
|
172
|
-
piContent.push({
|
|
173
|
-
type: "text",
|
|
174
|
-
text: block.text,
|
|
175
|
-
...signature ? { textSignature: signature } : {}
|
|
176
|
-
});
|
|
177
|
-
} else if (block.type === "thinking") {
|
|
178
|
-
const action = reasoningWireAction(block, route);
|
|
179
|
-
if (action === "drop") continue;
|
|
180
|
-
const signature = action === "keep" ? own(block) : void 0;
|
|
181
|
-
piContent.push({
|
|
182
|
-
type: "thinking",
|
|
183
|
-
thinking: block.text,
|
|
184
|
-
...signature ? { thinkingSignature: signature } : {}
|
|
185
|
-
});
|
|
186
|
-
} else if (block.type === "tool_call") {
|
|
187
|
-
const signature = own(block);
|
|
188
|
-
piContent.push({
|
|
189
|
-
type: "toolCall",
|
|
190
|
-
id: block.id,
|
|
191
|
-
name: block.name,
|
|
192
|
-
arguments: block.input,
|
|
193
|
-
...signature ? { thoughtSignature: signature } : {}
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
return {
|
|
197
|
-
role: "assistant",
|
|
198
|
-
content: piContent,
|
|
199
|
-
api: model.api,
|
|
200
|
-
provider: native ? model.provider : "zidane",
|
|
201
|
-
model: native ? model.id : "history",
|
|
202
|
-
usage: emptyUsage(),
|
|
203
|
-
stopReason: "stop",
|
|
204
|
-
timestamp: Date.now()
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Converts session messages to pi-ai messages for a Google backend.
|
|
209
|
-
*
|
|
210
|
-
* Unlike the Responses wire, an assistant turn stays whole: Gemini signs only
|
|
211
|
-
* the FIRST function call of a parallel step, so splitting the calls into
|
|
212
|
-
* separate model turns would leave the others unsigned. Its tool results
|
|
213
|
-
* follow in call order (pi-ai folds them into one user turn), with the
|
|
214
|
-
* synthetic placeholder for any the history lost.
|
|
215
|
-
*
|
|
216
|
-
* @param messages - Session messages, tool names already on the wire.
|
|
217
|
-
* @param model - Target model.
|
|
218
|
-
* @param route - Signature scope of the target (the backend's route name by default).
|
|
219
|
-
* @param provider - Provider named in unsupported-media errors. Defaults to `route`.
|
|
220
|
-
*/
|
|
221
|
-
function toGooglePiMessages(messages, model, route, provider = route) {
|
|
222
|
-
messages = systemNotesAsText(messages);
|
|
223
|
-
const out = [];
|
|
224
|
-
for (let i = 0; i < messages.length; i++) {
|
|
225
|
-
const msg = messages[i];
|
|
226
|
-
if (msg.role === "assistant") {
|
|
227
|
-
out.push(piAssistantMessage(msg.content, model, route));
|
|
228
|
-
const calls = msg.content.filter((b) => b.type === "tool_call");
|
|
229
|
-
if (calls.length === 0) continue;
|
|
230
|
-
const next = messages[i + 1];
|
|
231
|
-
const results = new Map((next?.role === "user" ? next.content : []).filter((b) => b.type === "tool_result").map((r) => [r.callId, r]));
|
|
232
|
-
for (const call of calls) {
|
|
233
|
-
const result = results.get(call.id) ?? {
|
|
234
|
-
type: "tool_result",
|
|
235
|
-
callId: call.id,
|
|
236
|
-
output: "[Tool result missing due to internal error]",
|
|
237
|
-
isError: true
|
|
238
|
-
};
|
|
239
|
-
out.push(piToolResultMessage(result, call.name, provider));
|
|
240
|
-
}
|
|
241
|
-
if (next?.role === "user") {
|
|
242
|
-
const rest = piUserMessage(next.content.filter((b) => b.type !== "tool_result"), provider);
|
|
243
|
-
if (rest) out.push(rest);
|
|
244
|
-
i++;
|
|
245
|
-
}
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
const userMsg = piUserMessage(msg.content.filter((b) => b.type !== "tool_result"), provider);
|
|
249
|
-
if (userMsg) out.push(userMsg);
|
|
250
|
-
}
|
|
251
|
-
return out;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Converts pi-ai's final assistant message to a session message, tagging
|
|
255
|
-
* every thought signature with the route that issued it.
|
|
256
|
-
*
|
|
257
|
-
* @param message - pi-ai assistant message.
|
|
258
|
-
* @param route - Signature scope that produced it.
|
|
259
|
-
*/
|
|
260
|
-
function fromGooglePiAssistantMessage(message, route) {
|
|
261
|
-
const signed = (signature) => signature ? {
|
|
262
|
-
signature,
|
|
263
|
-
signatureProducer: route
|
|
264
|
-
} : {};
|
|
265
|
-
const content = [];
|
|
266
|
-
for (const block of message.content) if (block.type === "text") content.push({
|
|
267
|
-
type: "text",
|
|
268
|
-
text: block.text,
|
|
269
|
-
...signed(block.textSignature)
|
|
270
|
-
});
|
|
271
|
-
else if (block.type === "thinking") content.push({
|
|
272
|
-
type: "thinking",
|
|
273
|
-
text: block.thinking,
|
|
274
|
-
...signed(block.thinkingSignature)
|
|
275
|
-
});
|
|
276
|
-
else if (block.type === "toolCall") content.push({
|
|
277
|
-
type: "tool_call",
|
|
278
|
-
id: block.id,
|
|
279
|
-
name: block.name,
|
|
280
|
-
input: block.arguments,
|
|
281
|
-
...signed(block.thoughtSignature)
|
|
282
|
-
});
|
|
283
|
-
return {
|
|
284
|
-
role: "assistant",
|
|
285
|
-
content
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
function mapStopReason(stopReason, hasToolCalls) {
|
|
289
|
-
switch (stopReason) {
|
|
290
|
-
case "toolUse": return "tool-calls";
|
|
291
|
-
case "length": return "length";
|
|
292
|
-
case "error": return "error";
|
|
293
|
-
case "aborted": return "other";
|
|
294
|
-
default: return hasToolCalls ? "tool-calls" : "stop";
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
/** Forced tool choice, written into Gemini's `functionCallingConfig`. */
|
|
298
|
-
function applyToolChoice(payload, options, toWire) {
|
|
299
|
-
const choice = options.toolChoice;
|
|
300
|
-
if (!choice || choice.type === "auto") return payload;
|
|
301
|
-
const params = payload;
|
|
302
|
-
if (!params.config?.toolConfig) return payload;
|
|
303
|
-
params.config.toolConfig.functionCallingConfig = choice.type === "tool" && choice.name ? {
|
|
304
|
-
mode: "ANY",
|
|
305
|
-
allowedFunctionNames: [toWire(choice.name)]
|
|
306
|
-
} : { mode: "ANY" };
|
|
307
|
-
return params;
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Transient Gemini failures worth a loop-level retry once pi-ai's own retries
|
|
311
|
-
* (408 / 409 / 429 / 5xx before the stream opens) are spent.
|
|
312
|
-
*/
|
|
313
|
-
const TRANSIENT_GOOGLE_MESSAGE_RE = /RESOURCE_EXHAUSTED|UNAVAILABLE|DEADLINE_EXCEEDED|overloaded|high demand|rate.?limit|try again later/i;
|
|
314
|
-
/** Gemini's `RetryInfo` detail in a 429 body: `"retryDelay": "34s"`. */
|
|
315
|
-
const RETRY_DELAY_RE = /"retryDelay"\s*:\s*"(\d+(?:\.\d+)?)s"/;
|
|
316
|
-
/** pi-ai formats HTTP failures as `<status>: <body>`. */
|
|
317
|
-
function leadingStatus(message) {
|
|
318
|
-
const match = /^(\d{3})\b/.exec(message);
|
|
319
|
-
return match ? Number(match[1]) : void 0;
|
|
320
|
-
}
|
|
321
|
-
function parseGoogleErrorBody(text) {
|
|
322
|
-
try {
|
|
323
|
-
const parsed = JSON.parse(text);
|
|
324
|
-
return parsed && typeof parsed.error === "object" && parsed.error ? parsed.error : void 0;
|
|
325
|
-
} catch {
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* The stream error pi-ai reports, as Google wrote it. `@google/genai` wraps a
|
|
331
|
-
* non-JSON response as `{"error":{"message":"<body>"}}`, so Google's own
|
|
332
|
-
* `{"error":{…}}` can arrive JSON-encoded inside it.
|
|
333
|
-
*
|
|
334
|
-
* @param raw - pi-ai's `errorMessage`: `<status>: <body>` or the SDK's message.
|
|
335
|
-
* @returns The innermost message prefixed with its HTTP and RPC status, the
|
|
336
|
-
* status, and the innermost body (it carries details such as `retryDelay`).
|
|
337
|
-
*/
|
|
338
|
-
function unwrapGoogleError(raw) {
|
|
339
|
-
let status = leadingStatus(raw);
|
|
340
|
-
let rpcStatus;
|
|
341
|
-
let text = status === void 0 ? raw : raw.replace(/^\d{3}:\s*/, "");
|
|
342
|
-
let message;
|
|
343
|
-
let body = raw;
|
|
344
|
-
for (let error = parseGoogleErrorBody(text); error; error = parseGoogleErrorBody(text)) {
|
|
345
|
-
body = text;
|
|
346
|
-
if (typeof error.code === "number") status = error.code;
|
|
347
|
-
if (typeof error.status === "string" && /^[A-Z_]+$/.test(error.status)) rpcStatus = error.status;
|
|
348
|
-
if (typeof error.message !== "string") break;
|
|
349
|
-
message = text = error.message.trim();
|
|
350
|
-
}
|
|
351
|
-
if (!message) return {
|
|
352
|
-
message: raw,
|
|
353
|
-
...status !== void 0 ? { status } : {}
|
|
354
|
-
};
|
|
355
|
-
const prefix = [status, rpcStatus].filter((part) => part !== void 0).join(" ");
|
|
356
|
-
return {
|
|
357
|
-
message: prefix ? `${prefix}: ${message}` : message,
|
|
358
|
-
...status !== void 0 ? { status } : {},
|
|
359
|
-
body
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* Classify a Gemini API / Vertex AI error.
|
|
364
|
-
*
|
|
365
|
-
* @param err - Error thrown by pi-ai or the SDK.
|
|
366
|
-
* @param label - Product a limit's message names; routes over other services (Antigravity) pass their own.
|
|
367
|
-
*/
|
|
368
|
-
function classifyGoogleError(err, label = "Gemini") {
|
|
369
|
-
const prelude = classifyErrorPrelude(err);
|
|
370
|
-
if (prelude === "not-object") return null;
|
|
371
|
-
if (prelude === "aborted") return { kind: "aborted" };
|
|
372
|
-
const anyErr = err;
|
|
373
|
-
const message = typeof anyErr.message === "string" ? anyErr.message : "";
|
|
374
|
-
if (matchesContextExceeded(message)) return {
|
|
375
|
-
kind: "context_exceeded",
|
|
376
|
-
providerCode: "context_length_exceeded",
|
|
377
|
-
message
|
|
378
|
-
};
|
|
379
|
-
if (matchesToolPairingError(message)) return {
|
|
380
|
-
kind: "tool_pairing_corruption",
|
|
381
|
-
message
|
|
382
|
-
};
|
|
383
|
-
if (!message) return null;
|
|
384
|
-
const status = typeof anyErr.status === "number" ? anyErr.status : leadingStatus(message);
|
|
385
|
-
const retryable = status !== void 0 && isRetryableHttpStatus(status) || TRANSIENT_GOOGLE_MESSAGE_RE.test(message);
|
|
386
|
-
const reason = providerLimitReason({
|
|
387
|
-
status,
|
|
388
|
-
message
|
|
389
|
-
});
|
|
390
|
-
if (reason) {
|
|
391
|
-
const delay = RETRY_DELAY_RE.exec(typeof anyErr.body === "string" ? anyErr.body : message);
|
|
392
|
-
return classifyProviderLimit({
|
|
393
|
-
reason,
|
|
394
|
-
provider: label,
|
|
395
|
-
...delay ? { retryAfterMs: Number(delay[1]) * 1e3 } : {}
|
|
396
|
-
}, {
|
|
397
|
-
...status !== void 0 ? { providerCode: String(status) } : {},
|
|
398
|
-
retryable
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
return {
|
|
402
|
-
kind: "provider_error",
|
|
403
|
-
...status !== void 0 ? { providerCode: String(status) } : {},
|
|
404
|
-
message,
|
|
405
|
-
...retryable ? { retryable: true } : {}
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
/** Every budget level at `tokens`: pi-ai picks the level, the caller fixed the budget. */
|
|
409
|
-
function budgetsAt(tokens) {
|
|
410
|
-
return {
|
|
411
|
-
minimal: tokens,
|
|
412
|
-
low: tokens,
|
|
413
|
-
medium: tokens,
|
|
414
|
-
high: tokens
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
/** Names a {@link googleCompatible} route can't take: built-in providers and signature producers. */
|
|
418
|
-
const RESERVED_ROUTE_NAMES = /* @__PURE__ */ new Set([
|
|
419
|
-
"google",
|
|
420
|
-
"google-vertex",
|
|
421
|
-
"anthropic",
|
|
422
|
-
"openai",
|
|
423
|
-
"zidane"
|
|
424
|
-
]);
|
|
425
|
-
function createGoogleProvider(route, params) {
|
|
426
|
-
const defaultModel = params?.defaultModel || "gemini-3.8-flash";
|
|
427
|
-
const capabilities = params?.capabilities ?? {
|
|
428
|
-
vision: true,
|
|
429
|
-
imageInToolResult: true,
|
|
430
|
-
audio: false,
|
|
431
|
-
video: false,
|
|
432
|
-
documents: false
|
|
433
|
-
};
|
|
434
|
-
return {
|
|
435
|
-
name: route.name,
|
|
436
|
-
meta: {
|
|
437
|
-
defaultModel,
|
|
438
|
-
isOAuth: route.isOAuth,
|
|
439
|
-
capabilities
|
|
440
|
-
},
|
|
441
|
-
formatTools,
|
|
442
|
-
userMessage,
|
|
443
|
-
assistantMessage,
|
|
444
|
-
toolResultsMessage,
|
|
445
|
-
classifyError: route.classifyError ?? classifyGoogleError,
|
|
446
|
-
async stream(options, callbacks) {
|
|
447
|
-
const auth = await route.auth(callbacks);
|
|
448
|
-
try {
|
|
449
|
-
return await streamTurn(auth, options, callbacks);
|
|
450
|
-
} finally {
|
|
451
|
-
auth.dispose?.();
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
};
|
|
455
|
-
async function streamTurn(auth, options, callbacks) {
|
|
456
|
-
const modelId = options.model || defaultModel;
|
|
457
|
-
const model = route.resolveModel ? route.resolveModel(modelId, options.modelCatalog) : resolveGoogleModel(route.name, modelId, options.modelCatalog);
|
|
458
|
-
const scope = signatureScopeOf(route, model.id);
|
|
459
|
-
const tools = options.tools;
|
|
460
|
-
const nameBoundary = createToolNameBoundary(options.messages, tools.map((tool) => tool.name));
|
|
461
|
-
const context = normalizeContext({
|
|
462
|
-
systemPrompt: renderSystemForWire(options.system),
|
|
463
|
-
messages: toGooglePiMessages(nameBoundary.messages, model, scope, route.name),
|
|
464
|
-
tools: tools.map((tool) => ({
|
|
465
|
-
...tool,
|
|
466
|
-
name: nameBoundary.toWire(tool.name)
|
|
467
|
-
}))
|
|
468
|
-
});
|
|
469
|
-
const api = await route.load();
|
|
470
|
-
const base = {
|
|
471
|
-
...auth.apiKey ? { apiKey: auth.apiKey } : {},
|
|
472
|
-
...auth.env ? { env: auth.env } : {},
|
|
473
|
-
...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
|
|
474
|
-
signal: options.signal,
|
|
475
|
-
...params?.extraHeaders ? { headers: params.extraHeaders } : {},
|
|
476
|
-
onPayload: (payload) => applyToolChoice(payload, options, nameBoundary.toWire)
|
|
477
|
-
};
|
|
478
|
-
let s;
|
|
479
|
-
if (options.thinking === "adaptive" && model.reasoning) {
|
|
480
|
-
const { buildBaseOptions } = await import("@earendil-works/pi-ai/api/simple-options");
|
|
481
|
-
s = api.stream(model, context, {
|
|
482
|
-
...buildBaseOptions(model, context, base),
|
|
483
|
-
thinking: { enabled: true }
|
|
484
|
-
});
|
|
485
|
-
} else {
|
|
486
|
-
const reasoning = options.thinking && options.thinking !== "off" && options.thinking !== "adaptive" ? options.thinking : void 0;
|
|
487
|
-
s = api.streamSimple(model, context, {
|
|
488
|
-
...base,
|
|
489
|
-
...reasoning ? { reasoning } : {},
|
|
490
|
-
...reasoning && options.thinkingBudget !== void 0 ? { thinkingBudgets: budgetsAt(options.thinkingBudget) } : {}
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
let finalMessage;
|
|
494
|
-
let text = "";
|
|
495
|
-
let thinking = "";
|
|
496
|
-
for await (const event of s) if (event.type === "text_delta") {
|
|
497
|
-
text += event.delta;
|
|
498
|
-
callbacks.onText(event.delta);
|
|
499
|
-
} else if (event.type === "thinking_delta") {
|
|
500
|
-
thinking += event.delta;
|
|
501
|
-
callbacks.onThinking?.(event.delta);
|
|
502
|
-
} else if (event.type === "thinking_end") {
|
|
503
|
-
const delta = event.content.startsWith(thinking) ? event.content.slice(thinking.length) : thinking ? "" : event.content;
|
|
504
|
-
if (delta) {
|
|
505
|
-
thinking += delta;
|
|
506
|
-
callbacks.onThinking?.(delta);
|
|
507
|
-
}
|
|
508
|
-
} else if (event.type === "toolcall_start" || event.type === "toolcall_delta") callbacks.onToolCallDelta?.();
|
|
509
|
-
else if (event.type === "done") finalMessage = event.message;
|
|
510
|
-
else if (event.type === "error") {
|
|
511
|
-
const { message, ...details } = unwrapGoogleError(event.error.errorMessage || `Google ${route.name} API error`);
|
|
512
|
-
throw Object.assign(new Error(message), details);
|
|
513
|
-
}
|
|
514
|
-
finalMessage ??= await s.result();
|
|
515
|
-
const toolCalls = finalMessage.content.filter((block) => block.type === "toolCall").map((block) => ({
|
|
516
|
-
id: block.id,
|
|
517
|
-
name: nameBoundary.fromWire(block.name),
|
|
518
|
-
input: block.arguments
|
|
519
|
-
}));
|
|
520
|
-
const assistantTurn = fromGooglePiAssistantMessage(finalMessage, scope);
|
|
521
|
-
assistantTurn.content = assistantTurn.content.map((block) => block.type === "tool_call" ? {
|
|
522
|
-
...block,
|
|
523
|
-
name: nameBoundary.fromWire(block.name)
|
|
524
|
-
} : block);
|
|
525
|
-
const finishReason = mapStopReason(finalMessage.stopReason, toolCalls.length > 0);
|
|
526
|
-
const usage = await fillEstimatedCost({
|
|
527
|
-
input: finalMessage.usage.input,
|
|
528
|
-
output: finalMessage.usage.output,
|
|
529
|
-
cacheRead: finalMessage.usage.cacheRead || void 0,
|
|
530
|
-
...finalMessage.usage.reasoning ? { thinking: finalMessage.usage.reasoning } : {},
|
|
531
|
-
cost: finalMessage.usage.cost.total || void 0,
|
|
532
|
-
finishReason,
|
|
533
|
-
modelId
|
|
534
|
-
}, route.name, void 0, options.modelCatalog);
|
|
535
|
-
return {
|
|
536
|
-
assistantMessage: assistantTurn,
|
|
537
|
-
text: text || finalMessage.content.flatMap((block) => block.type === "text" ? [block.text] : []).join(""),
|
|
538
|
-
toolCalls,
|
|
539
|
-
done: toolCalls.length === 0,
|
|
540
|
-
usage
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* The scope a model's thought signatures carry. A custom route's scope can't
|
|
546
|
-
* impersonate a built-in producer, whose signatures it would then replay.
|
|
547
|
-
*/
|
|
548
|
-
function signatureScopeOf(route, modelId) {
|
|
549
|
-
const scope = route.signatureScope?.(modelId) ?? route.name;
|
|
550
|
-
if (scope !== route.name && RESERVED_ROUTE_NAMES.has(scope)) throw new Error(`${route.name}: signature scope "${scope}" is reserved for a built-in provider`);
|
|
551
|
-
return scope;
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* Creates a provider over another Gemini-wire backend: an extension supplies
|
|
555
|
-
* the transport, auth and model records; message conversion, thought
|
|
556
|
-
* signatures (tagged with the route's scope, never replayed elsewhere), tool
|
|
557
|
-
* names, thinking levels, usage and cost come from the same plumbing as
|
|
558
|
-
* {@link google} and {@link googleVertex}.
|
|
559
|
-
*
|
|
560
|
-
* @param route - The backend: name, transport, auth and models.
|
|
561
|
-
* @param params - Default model, capabilities and extra headers.
|
|
562
|
-
*
|
|
563
|
-
* @example
|
|
564
|
-
* ```ts
|
|
565
|
-
* import { googleCompatible } from 'zidane/providers'
|
|
566
|
-
*
|
|
567
|
-
* const provider = googleCompatible({
|
|
568
|
-
* name: 'my-gateway',
|
|
569
|
-
* isOAuth: false,
|
|
570
|
-
* load: async () => myGeminiTransport,
|
|
571
|
-
* auth: () => ({ apiKey: process.env.MY_GATEWAY_KEY }),
|
|
572
|
-
* resolveModel: id => myModels[id],
|
|
573
|
-
* }, { defaultModel: 'gemini-3.8-flash' })
|
|
574
|
-
* ```
|
|
575
|
-
*/
|
|
576
|
-
function googleCompatible(route, params) {
|
|
577
|
-
if (!route.name || RESERVED_ROUTE_NAMES.has(route.name)) throw new Error(`googleCompatible: route name "${route.name}" is reserved for a built-in provider`);
|
|
578
|
-
return createGoogleProvider(route, params);
|
|
579
|
-
}
|
|
580
|
-
/**
|
|
581
|
-
* Load the {@link GoogleWireKit} from the HOST's pi-ai. An extension's
|
|
582
|
-
* transport gets it through `zidane/providers` (mapped to the host's module)
|
|
583
|
-
* instead of importing pi-ai itself: one pi-ai copy, the exact wire the
|
|
584
|
-
* host's pi-ai speaks, and it resolves inside compiled binaries too. pi-ai's
|
|
585
|
-
* bare specifiers can't be host-mapped the way `zidane` is: the mapping's
|
|
586
|
-
* loader would re-import the specifier it intercepts. Loaded on first call,
|
|
587
|
-
* since `@google/genai` comes with it.
|
|
588
|
-
*/
|
|
589
|
-
async function loadGoogleWireKit() {
|
|
590
|
-
const [shared, core] = await Promise.all([import("@earendil-works/pi-ai/api/google-shared"), import("@earendil-works/pi-ai")]);
|
|
591
|
-
return {
|
|
592
|
-
convertMessages: shared.convertMessages,
|
|
593
|
-
convertTools: shared.convertTools,
|
|
594
|
-
isThinkingPart: shared.isThinkingPart,
|
|
595
|
-
retainThoughtSignature: shared.retainThoughtSignature,
|
|
596
|
-
requiresToolCallId: shared.requiresToolCallId,
|
|
597
|
-
mapStopReasonString: shared.mapStopReasonString,
|
|
598
|
-
resolveGoogleFunctionCallingMode: shared.resolveGoogleFunctionCallingMode,
|
|
599
|
-
supportsGoogleStrictToolSampling: shared.supportsGoogleStrictToolSampling,
|
|
600
|
-
createAssistantMessageEventStream: core.createAssistantMessageEventStream,
|
|
601
|
-
collapseSystemMessages: core.collapseSystemMessages,
|
|
602
|
-
getInitialSystemMessage: core.getInitialSystemMessage,
|
|
603
|
-
getCurrentTools: core.getCurrentTools,
|
|
604
|
-
getSystemMessageText: core.getSystemMessageText,
|
|
605
|
-
calculateCost: core.calculateCost,
|
|
606
|
-
clampThinkingLevel: core.clampThinkingLevel
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
async function loadGeminiApi() {
|
|
610
|
-
return await import("@earendil-works/pi-ai/api/google-generative-ai");
|
|
611
|
-
}
|
|
612
|
-
async function loadVertexApi() {
|
|
613
|
-
return await import("@earendil-works/pi-ai/api/google-vertex");
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Creates the Google Gemini provider on the Gemini API (Google AI Studio).
|
|
617
|
-
* Authenticates with `apiKey`, else `GEMINI_API_KEY`, else `GOOGLE_API_KEY`.
|
|
618
|
-
*
|
|
619
|
-
* @param params - API key, default model, capabilities and extra headers.
|
|
620
|
-
*
|
|
621
|
-
* @example
|
|
622
|
-
* ```ts
|
|
623
|
-
* import { createAgent } from 'zidane'
|
|
624
|
-
* import { google } from 'zidane/providers/google'
|
|
625
|
-
*
|
|
626
|
-
* const agent = createAgent({
|
|
627
|
-
* provider: google({ apiKey: process.env.GEMINI_API_KEY }),
|
|
628
|
-
* })
|
|
629
|
-
* await agent.run({ prompt: 'Say hello.' })
|
|
630
|
-
* ```
|
|
631
|
-
*/
|
|
632
|
-
function google(params) {
|
|
633
|
-
return createGoogleProvider({
|
|
634
|
-
name: "google",
|
|
635
|
-
load: loadGeminiApi,
|
|
636
|
-
isOAuth: false,
|
|
637
|
-
auth: () => {
|
|
638
|
-
const apiKey = params?.apiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
|
|
639
|
-
if (!apiKey) throw new Error("No Gemini API key found. Set GEMINI_API_KEY, or add a key in the Google Gemini provider settings.");
|
|
640
|
-
return { apiKey };
|
|
641
|
-
}
|
|
642
|
-
}, params);
|
|
643
|
-
}
|
|
644
|
-
const VERTEX_DIR_PREFIX = "zidane-vertex-";
|
|
645
|
-
const VERTEX_DIR_RE = /^zidane-vertex-[A-Za-z0-9]{6}$/;
|
|
646
|
-
/** Older than any request: its process crashed or was killed before cleanup. */
|
|
647
|
-
const STALE_VERTEX_DIR_MS = 36e5;
|
|
648
|
-
let nextVertexSweepAt = 0;
|
|
649
|
-
/**
|
|
650
|
-
* Remove this user's credentials dirs that a crashed request left behind;
|
|
651
|
-
* they hold a refresh token. Listing the temp dir can take hundreds of
|
|
652
|
-
* milliseconds, so it runs in the background, at most once an hour.
|
|
653
|
-
*/
|
|
654
|
-
function sweepStaleVertexDirs() {
|
|
655
|
-
const now = Date.now();
|
|
656
|
-
if (now < nextVertexSweepAt) return;
|
|
657
|
-
nextVertexSweepAt = now + STALE_VERTEX_DIR_MS;
|
|
658
|
-
const root = tmpdir();
|
|
659
|
-
const uid = process.getuid?.();
|
|
660
|
-
const sweepOne = async (name) => {
|
|
661
|
-
const dir = join(root, name);
|
|
662
|
-
const stat = await lstat(dir);
|
|
663
|
-
if (stat.isDirectory() && (uid === void 0 || stat.uid === uid) && now - stat.mtimeMs > STALE_VERTEX_DIR_MS) await rm(dir, {
|
|
664
|
-
recursive: true,
|
|
665
|
-
force: true
|
|
666
|
-
});
|
|
667
|
-
};
|
|
668
|
-
readdir(root).then((names) => Promise.allSettled(names.filter((name) => VERTEX_DIR_RE.test(name)).map(sweepOne))).catch(() => {});
|
|
669
|
-
}
|
|
670
|
-
/**
|
|
671
|
-
* Request auth from the stored "Sign in with Google" grant (the pinned
|
|
672
|
-
* account's, else the active one), or `undefined` when none is stored.
|
|
673
|
-
*
|
|
674
|
-
* The access token rotates through zidane's OAuth path (cross-process locks,
|
|
675
|
-
* persistence, refresh hooks, a clear "sign in again" for a revoked grant).
|
|
676
|
-
* Vertex itself authenticates from an `authorized_user` credentials file
|
|
677
|
-
* holding the refresh token: pi-ai's Vertex client takes no bare access
|
|
678
|
-
* token. The file lives in a private temp dir for the length of the request.
|
|
679
|
-
*/
|
|
680
|
-
async function signedInAuth(params, callbacks) {
|
|
681
|
-
const account = params?.account;
|
|
682
|
-
const read = () => account ? readAccountOAuthCredential(GOOGLE_VERTEX_OAUTH_ID, account) : readOAuthCredentials()[GOOGLE_VERTEX_OAUTH_ID];
|
|
683
|
-
if (!read()) {
|
|
684
|
-
if (account) throw new Error(pinnedAccountMissingError("google-vertex"));
|
|
685
|
-
return;
|
|
686
|
-
}
|
|
687
|
-
await resolveOAuthApiKey({
|
|
688
|
-
provider: "google-vertex",
|
|
689
|
-
providerId: GOOGLE_VERTEX_OAUTH_ID,
|
|
690
|
-
...account ? { params: { account } } : {},
|
|
691
|
-
missingError: "No Google sign-in found for Vertex AI. Sign in with Google again.",
|
|
692
|
-
refreshError: (reason) => `Google sign-in refresh failed. Sign in with Google again. ${reason}`
|
|
693
|
-
}, callbacks);
|
|
694
|
-
const grant = read();
|
|
695
|
-
if (!grant?.refresh || !grant.clientId || !grant.clientSecret || !grant.projectId) throw new Error("This Google sign-in is missing its OAuth client or project. Sign in with Google again.");
|
|
696
|
-
const project = params?.project || grant.projectId;
|
|
697
|
-
sweepStaleVertexDirs();
|
|
698
|
-
const dir = mkdtempSync(join(tmpdir(), VERTEX_DIR_PREFIX));
|
|
699
|
-
const file = join(dir, "credentials.json");
|
|
700
|
-
try {
|
|
701
|
-
writeFileSync(file, JSON.stringify({
|
|
702
|
-
type: "authorized_user",
|
|
703
|
-
client_id: grant.clientId,
|
|
704
|
-
client_secret: grant.clientSecret,
|
|
705
|
-
refresh_token: grant.refresh,
|
|
706
|
-
quota_project_id: project
|
|
707
|
-
}), { mode: 384 });
|
|
708
|
-
} catch (err) {
|
|
709
|
-
rmSync(dir, {
|
|
710
|
-
recursive: true,
|
|
711
|
-
force: true
|
|
712
|
-
});
|
|
713
|
-
throw err;
|
|
714
|
-
}
|
|
715
|
-
return {
|
|
716
|
-
env: {
|
|
717
|
-
GOOGLE_CLOUD_PROJECT: project,
|
|
718
|
-
GOOGLE_CLOUD_LOCATION: params?.location || process.env.GOOGLE_CLOUD_LOCATION || "global",
|
|
719
|
-
GOOGLE_APPLICATION_CREDENTIALS: file
|
|
720
|
-
},
|
|
721
|
-
dispose: () => rmSync(dir, {
|
|
722
|
-
recursive: true,
|
|
723
|
-
force: true
|
|
724
|
-
})
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
/**
|
|
728
|
-
* Credentials and settings for a Vertex AI request, read per turn so a
|
|
729
|
-
* credential configured after the provider was built still applies. In
|
|
730
|
-
* order: a pinned account's sign-in, a Vertex API key, the active "Sign in
|
|
731
|
-
* with Google" grant, then Google Cloud credentials for the project.
|
|
732
|
-
*
|
|
733
|
-
* @param params - Explicit settings; environment variables fill the rest.
|
|
734
|
-
* @param callbacks - Stream callbacks, for the OAuth refresh hook.
|
|
735
|
-
*/
|
|
736
|
-
async function resolveVertexAuth(params, callbacks) {
|
|
737
|
-
if (params?.account) return await signedInAuth(params, callbacks);
|
|
738
|
-
const apiKey = params?.apiKey || process.env.GOOGLE_CLOUD_API_KEY;
|
|
739
|
-
if (apiKey) return { apiKey };
|
|
740
|
-
const signedIn = await signedInAuth(params, callbacks);
|
|
741
|
-
if (signedIn) return signedIn;
|
|
742
|
-
const project = params?.project || process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
|
|
743
|
-
if (!project) throw new Error("Vertex AI needs a Google Cloud project. Set GOOGLE_CLOUD_PROJECT, or configure it in the Google Vertex AI provider settings.");
|
|
744
|
-
const location = params?.location || process.env.GOOGLE_CLOUD_LOCATION || "global";
|
|
745
|
-
const credentialsFile = params?.credentialsFile || process.env.GOOGLE_APPLICATION_CREDENTIALS;
|
|
746
|
-
return { env: {
|
|
747
|
-
GOOGLE_CLOUD_PROJECT: project,
|
|
748
|
-
GOOGLE_CLOUD_LOCATION: location,
|
|
749
|
-
...credentialsFile ? { GOOGLE_APPLICATION_CREDENTIALS: credentialsFile } : {}
|
|
750
|
-
} };
|
|
751
|
-
}
|
|
752
|
-
/**
|
|
753
|
-
* Creates the Google Vertex AI provider. Authenticates with a pinned
|
|
754
|
-
* `account`'s Google sign-in, else a Vertex API key (`apiKey` or
|
|
755
|
-
* `GOOGLE_CLOUD_API_KEY`), else the active "Sign in with Google" grant, else
|
|
756
|
-
* Google Cloud credentials for `project` in `location`. See
|
|
757
|
-
* {@link resolveVertexAuth}.
|
|
758
|
-
*
|
|
759
|
-
* @param params - Credentials, project, location, default model, capabilities and extra headers.
|
|
760
|
-
*
|
|
761
|
-
* @example
|
|
762
|
-
* ```ts
|
|
763
|
-
* import { createAgent } from 'zidane'
|
|
764
|
-
* import { googleVertex } from 'zidane/providers/google'
|
|
765
|
-
*
|
|
766
|
-
* // After `gcloud auth application-default login`:
|
|
767
|
-
* const agent = createAgent({
|
|
768
|
-
* provider: googleVertex({ project: 'my-project' }),
|
|
769
|
-
* })
|
|
770
|
-
* await agent.run({ prompt: 'Say hello.' })
|
|
771
|
-
* ```
|
|
772
|
-
*/
|
|
773
|
-
function googleVertex(params) {
|
|
774
|
-
return createGoogleProvider({
|
|
775
|
-
name: "google-vertex",
|
|
776
|
-
load: loadVertexApi,
|
|
777
|
-
isOAuth: !!params?.account || !(params?.apiKey || process.env.GOOGLE_CLOUD_API_KEY),
|
|
778
|
-
auth: (callbacks) => resolveVertexAuth(params, callbacks)
|
|
779
|
-
}, params);
|
|
780
|
-
}
|
|
781
|
-
//#endregion
|
|
782
|
-
export { GOOGLE_DEFAULT_MODEL, GOOGLE_EXTRA_MODELS, classifyGoogleError, fromGooglePiAssistantMessage, google, googleCompatible, googleVertex, loadGoogleWireKit, resolveGoogleModel, resolveVertexAuth, toGooglePiMessages };
|
|
783
|
-
|
|
784
|
-
//# sourceMappingURL=google.js.map
|
|
1
|
+
export { t as GOOGLE_DEFAULT_MODEL, n as GOOGLE_EXTRA_MODELS, r as classifyGoogleError, i as fromGooglePiAssistantMessage, a as google, o as googleCompatible, s as googleVertex, c as loadGoogleWireKit, l as resolveGoogleModel, u as resolveVertexAuth, d as toGooglePiMessages } from "../google-O6pRnddf.js";
|