@rudderjs/ai 1.17.2 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -1274
- package/dist/budget-orm/index.d.ts +1 -95
- package/dist/budget-orm/index.d.ts.map +1 -1
- package/dist/budget-orm/index.js +4 -176
- package/dist/budget-orm/index.js.map +1 -1
- package/dist/chat-mentions.d.ts +1 -58
- package/dist/chat-mentions.d.ts.map +1 -1
- package/dist/chat-mentions.js +4 -80
- package/dist/chat-mentions.js.map +1 -1
- package/dist/commands/ai-eval.d.ts +1 -92
- package/dist/commands/ai-eval.d.ts.map +1 -1
- package/dist/commands/ai-eval.js +4 -377
- package/dist/commands/ai-eval.js.map +1 -1
- package/dist/commands/make-agent.d.ts +1 -2
- package/dist/commands/make-agent.d.ts.map +1 -1
- package/dist/commands/make-agent.js +4 -22
- package/dist/commands/make-agent.js.map +1 -1
- package/dist/computer-use/index.d.ts +1 -52
- package/dist/computer-use/index.d.ts.map +1 -1
- package/dist/computer-use/index.js +4 -50
- package/dist/computer-use/index.js.map +1 -1
- package/dist/conversation-orm/index.d.ts +1 -108
- package/dist/conversation-orm/index.d.ts.map +1 -1
- package/dist/conversation-orm/index.js +4 -214
- package/dist/conversation-orm/index.js.map +1 -1
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +4 -65
- package/dist/doctor.js.map +1 -1
- package/dist/eval/index.d.ts +1 -270
- package/dist/eval/index.d.ts.map +1 -1
- package/dist/eval/index.js +4 -509
- package/dist/eval/index.js.map +1 -1
- package/dist/gateway/index.d.ts +1 -10
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js +4 -10
- package/dist/gateway/index.js.map +1 -1
- package/dist/index.d.ts +1 -66
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -78
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -15
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +4 -14
- package/dist/mcp/index.js.map +1 -1
- package/dist/memory-embedding/index.d.ts +1 -120
- package/dist/memory-embedding/index.d.ts.map +1 -1
- package/dist/memory-embedding/index.js +4 -228
- package/dist/memory-embedding/index.js.map +1 -1
- package/dist/memory-orm/index.d.ts +1 -117
- package/dist/memory-orm/index.d.ts.map +1 -1
- package/dist/memory-orm/index.js +4 -186
- package/dist/memory-orm/index.js.map +1 -1
- package/dist/node/index.d.ts +1 -2
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +4 -2
- package/dist/node/index.js.map +1 -1
- package/dist/observers.d.ts +1 -129
- package/dist/observers.d.ts.map +1 -1
- package/dist/observers.js +4 -39
- package/dist/observers.js.map +1 -1
- package/dist/react/index.d.ts +1 -15
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +4 -15
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -1
- package/dist/server/index.js.map +1 -1
- package/package.json +9 -13
- package/boost/guidelines.md +0 -260
- package/boost/skills/ai-agents/SKILL.md +0 -240
- package/boost/skills/ai-tools/SKILL.md +0 -260
- package/dist/agent-run-store.d.ts +0 -161
- package/dist/agent-run-store.d.ts.map +0 -1
- package/dist/agent-run-store.js +0 -98
- package/dist/agent-run-store.js.map +0 -1
- package/dist/agent-sse.d.ts +0 -153
- package/dist/agent-sse.d.ts.map +0 -1
- package/dist/agent-sse.js +0 -282
- package/dist/agent-sse.js.map +0 -1
- package/dist/agent.d.ts +0 -508
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -1538
- package/dist/agent.js.map +0 -1
- package/dist/attachment.d.ts +0 -31
- package/dist/attachment.d.ts.map +0 -1
- package/dist/attachment.js +0 -89
- package/dist/attachment.js.map +0 -1
- package/dist/audio.d.ts +0 -45
- package/dist/audio.d.ts.map +0 -1
- package/dist/audio.js +0 -93
- package/dist/audio.js.map +0 -1
- package/dist/base64.d.ts +0 -7
- package/dist/base64.d.ts.map +0 -1
- package/dist/base64.js +0 -39
- package/dist/base64.js.map +0 -1
- package/dist/budget/pricing.d.ts +0 -124
- package/dist/budget/pricing.d.ts.map +0 -1
- package/dist/budget/pricing.js +0 -175
- package/dist/budget/pricing.js.map +0 -1
- package/dist/budget/storage.d.ts +0 -104
- package/dist/budget/storage.d.ts.map +0 -1
- package/dist/budget/storage.js +0 -0
- package/dist/budget/storage.js.map +0 -1
- package/dist/budget/with-budget.d.ts +0 -119
- package/dist/budget/with-budget.d.ts.map +0 -1
- package/dist/budget/with-budget.js +0 -175
- package/dist/budget/with-budget.js.map +0 -1
- package/dist/cached-embedding.d.ts +0 -14
- package/dist/cached-embedding.d.ts.map +0 -1
- package/dist/cached-embedding.js +0 -44
- package/dist/cached-embedding.js.map +0 -1
- package/dist/computer-use/actions.d.ts +0 -214
- package/dist/computer-use/actions.d.ts.map +0 -1
- package/dist/computer-use/actions.js +0 -48
- package/dist/computer-use/actions.js.map +0 -1
- package/dist/computer-use/errors.d.ts +0 -57
- package/dist/computer-use/errors.d.ts.map +0 -1
- package/dist/computer-use/errors.js +0 -76
- package/dist/computer-use/errors.js.map +0 -1
- package/dist/computer-use/playwright.d.ts +0 -76
- package/dist/computer-use/playwright.d.ts.map +0 -1
- package/dist/computer-use/playwright.js +0 -270
- package/dist/computer-use/playwright.js.map +0 -1
- package/dist/computer-use/tool.d.ts +0 -154
- package/dist/computer-use/tool.d.ts.map +0 -1
- package/dist/computer-use/tool.js +0 -210
- package/dist/computer-use/tool.js.map +0 -1
- package/dist/continuation-validation.d.ts +0 -85
- package/dist/continuation-validation.d.ts.map +0 -1
- package/dist/continuation-validation.js +0 -166
- package/dist/continuation-validation.js.map +0 -1
- package/dist/conversation-persistence.d.ts +0 -46
- package/dist/conversation-persistence.d.ts.map +0 -1
- package/dist/conversation-persistence.js +0 -176
- package/dist/conversation-persistence.js.map +0 -1
- package/dist/conversation.d.ts +0 -11
- package/dist/conversation.d.ts.map +0 -1
- package/dist/conversation.js +0 -55
- package/dist/conversation.js.map +0 -1
- package/dist/eval/fixtures.d.ts +0 -65
- package/dist/eval/fixtures.d.ts.map +0 -1
- package/dist/eval/fixtures.js +0 -110
- package/dist/eval/fixtures.js.map +0 -1
- package/dist/eval/html-reporter.d.ts +0 -25
- package/dist/eval/html-reporter.d.ts.map +0 -1
- package/dist/eval/html-reporter.js +0 -209
- package/dist/eval/html-reporter.js.map +0 -1
- package/dist/eval/json-reporter.d.ts +0 -43
- package/dist/eval/json-reporter.d.ts.map +0 -1
- package/dist/eval/json-reporter.js +0 -40
- package/dist/eval/json-reporter.js.map +0 -1
- package/dist/facade.d.ts +0 -96
- package/dist/facade.d.ts.map +0 -1
- package/dist/facade.js +0 -146
- package/dist/facade.js.map +0 -1
- package/dist/fake.d.ts +0 -201
- package/dist/fake.d.ts.map +0 -1
- package/dist/fake.js +0 -428
- package/dist/fake.js.map +0 -1
- package/dist/file-search.d.ts +0 -168
- package/dist/file-search.d.ts.map +0 -1
- package/dist/file-search.js +0 -158
- package/dist/file-search.js.map +0 -1
- package/dist/files.d.ts +0 -27
- package/dist/files.d.ts.map +0 -1
- package/dist/files.js +0 -44
- package/dist/files.js.map +0 -1
- package/dist/gateway/http-gateway-adapter.d.ts +0 -94
- package/dist/gateway/http-gateway-adapter.d.ts.map +0 -1
- package/dist/gateway/http-gateway-adapter.js +0 -106
- package/dist/gateway/http-gateway-adapter.js.map +0 -1
- package/dist/gateway/sse.d.ts +0 -28
- package/dist/gateway/sse.d.ts.map +0 -1
- package/dist/gateway/sse.js +0 -78
- package/dist/gateway/sse.js.map +0 -1
- package/dist/handoff.d.ts +0 -95
- package/dist/handoff.d.ts.map +0 -1
- package/dist/handoff.js +0 -78
- package/dist/handoff.js.map +0 -1
- package/dist/handoffs-driver.d.ts +0 -58
- package/dist/handoffs-driver.d.ts.map +0 -1
- package/dist/handoffs-driver.js +0 -103
- package/dist/handoffs-driver.js.map +0 -1
- package/dist/image.d.ts +0 -40
- package/dist/image.d.ts.map +0 -1
- package/dist/image.js +0 -109
- package/dist/image.js.map +0 -1
- package/dist/mcp/client-tools.d.ts +0 -39
- package/dist/mcp/client-tools.d.ts.map +0 -1
- package/dist/mcp/client-tools.js +0 -147
- package/dist/mcp/client-tools.js.map +0 -1
- package/dist/mcp/server-from-agent.d.ts +0 -24
- package/dist/mcp/server-from-agent.d.ts.map +0 -1
- package/dist/mcp/server-from-agent.js +0 -113
- package/dist/mcp/server-from-agent.js.map +0 -1
- package/dist/mcp/types.d.ts +0 -64
- package/dist/mcp/types.d.ts.map +0 -1
- package/dist/mcp/types.js +0 -6
- package/dist/mcp/types.js.map +0 -1
- package/dist/memory-extract.d.ts +0 -60
- package/dist/memory-extract.d.ts.map +0 -1
- package/dist/memory-extract.js +0 -163
- package/dist/memory-extract.js.map +0 -1
- package/dist/memory-inject.d.ts +0 -39
- package/dist/memory-inject.d.ts.map +0 -1
- package/dist/memory-inject.js +0 -135
- package/dist/memory-inject.js.map +0 -1
- package/dist/memory.d.ts +0 -55
- package/dist/memory.d.ts.map +0 -1
- package/dist/memory.js +0 -132
- package/dist/memory.js.map +0 -1
- package/dist/middleware.d.ts +0 -18
- package/dist/middleware.d.ts.map +0 -1
- package/dist/middleware.js +0 -72
- package/dist/middleware.js.map +0 -1
- package/dist/node/attachment.d.ts +0 -6
- package/dist/node/attachment.d.ts.map +0 -1
- package/dist/node/attachment.js +0 -35
- package/dist/node/attachment.js.map +0 -1
- package/dist/node/transcription.d.ts +0 -4
- package/dist/node/transcription.d.ts.map +0 -1
- package/dist/node/transcription.js +0 -8
- package/dist/node/transcription.js.map +0 -1
- package/dist/output.d.ts +0 -22
- package/dist/output.d.ts.map +0 -1
- package/dist/output.js +0 -60
- package/dist/output.js.map +0 -1
- package/dist/provider-tools.d.ts +0 -87
- package/dist/provider-tools.d.ts.map +0 -1
- package/dist/provider-tools.js +0 -189
- package/dist/provider-tools.js.map +0 -1
- package/dist/providers/anthropic.d.ts +0 -24
- package/dist/providers/anthropic.d.ts.map +0 -1
- package/dist/providers/anthropic.js +0 -405
- package/dist/providers/anthropic.js.map +0 -1
- package/dist/providers/azure.d.ts +0 -13
- package/dist/providers/azure.d.ts.map +0 -1
- package/dist/providers/azure.js +0 -15
- package/dist/providers/azure.js.map +0 -1
- package/dist/providers/bedrock.d.ts +0 -75
- package/dist/providers/bedrock.d.ts.map +0 -1
- package/dist/providers/bedrock.js +0 -181
- package/dist/providers/bedrock.js.map +0 -1
- package/dist/providers/cohere.d.ts +0 -13
- package/dist/providers/cohere.d.ts.map +0 -1
- package/dist/providers/cohere.js +0 -87
- package/dist/providers/cohere.js.map +0 -1
- package/dist/providers/deepseek.d.ts +0 -12
- package/dist/providers/deepseek.d.ts.map +0 -1
- package/dist/providers/deepseek.js +0 -15
- package/dist/providers/deepseek.js.map +0 -1
- package/dist/providers/elevenlabs.d.ts +0 -98
- package/dist/providers/elevenlabs.d.ts.map +0 -1
- package/dist/providers/elevenlabs.js +0 -229
- package/dist/providers/elevenlabs.js.map +0 -1
- package/dist/providers/google-cache-registry.d.ts +0 -132
- package/dist/providers/google-cache-registry.d.ts.map +0 -1
- package/dist/providers/google-cache-registry.js +0 -209
- package/dist/providers/google-cache-registry.js.map +0 -1
- package/dist/providers/google.d.ts +0 -38
- package/dist/providers/google.d.ts.map +0 -1
- package/dist/providers/google.js +0 -903
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/groq.d.ts +0 -12
- package/dist/providers/groq.d.ts.map +0 -1
- package/dist/providers/groq.js +0 -15
- package/dist/providers/groq.js.map +0 -1
- package/dist/providers/jina.d.ts +0 -13
- package/dist/providers/jina.d.ts.map +0 -1
- package/dist/providers/jina.js +0 -90
- package/dist/providers/jina.js.map +0 -1
- package/dist/providers/mistral.d.ts +0 -13
- package/dist/providers/mistral.d.ts.map +0 -1
- package/dist/providers/mistral.js +0 -46
- package/dist/providers/mistral.js.map +0 -1
- package/dist/providers/ollama.d.ts +0 -11
- package/dist/providers/ollama.d.ts.map +0 -1
- package/dist/providers/ollama.js +0 -15
- package/dist/providers/ollama.js.map +0 -1
- package/dist/providers/openai.d.ts +0 -79
- package/dist/providers/openai.d.ts.map +0 -1
- package/dist/providers/openai.js +0 -792
- package/dist/providers/openai.js.map +0 -1
- package/dist/providers/openrouter.d.ts +0 -43
- package/dist/providers/openrouter.d.ts.map +0 -1
- package/dist/providers/openrouter.js +0 -21
- package/dist/providers/openrouter.js.map +0 -1
- package/dist/providers/voyage.d.ts +0 -91
- package/dist/providers/voyage.d.ts.map +0 -1
- package/dist/providers/voyage.js +0 -166
- package/dist/providers/voyage.js.map +0 -1
- package/dist/providers/xai.d.ts +0 -12
- package/dist/providers/xai.d.ts.map +0 -1
- package/dist/providers/xai.js +0 -15
- package/dist/providers/xai.js.map +0 -1
- package/dist/queue-job.d.ts +0 -100
- package/dist/queue-job.d.ts.map +0 -1
- package/dist/queue-job.js +0 -185
- package/dist/queue-job.js.map +0 -1
- package/dist/react/agent-run.d.ts +0 -111
- package/dist/react/agent-run.d.ts.map +0 -1
- package/dist/react/agent-run.js +0 -107
- package/dist/react/agent-run.js.map +0 -1
- package/dist/react/useAgentRun.d.ts +0 -68
- package/dist/react/useAgentRun.d.ts.map +0 -1
- package/dist/react/useAgentRun.js +0 -125
- package/dist/react/useAgentRun.js.map +0 -1
- package/dist/registry.d.ts +0 -45
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js +0 -131
- package/dist/registry.js.map +0 -1
- package/dist/rerank.d.ts +0 -20
- package/dist/rerank.d.ts.map +0 -1
- package/dist/rerank.js +0 -40
- package/dist/rerank.js.map +0 -1
- package/dist/resume-approval.d.ts +0 -30
- package/dist/resume-approval.d.ts.map +0 -1
- package/dist/resume-approval.js +0 -147
- package/dist/resume-approval.js.map +0 -1
- package/dist/sanitize-conversation.d.ts +0 -43
- package/dist/sanitize-conversation.d.ts.map +0 -1
- package/dist/sanitize-conversation.js +0 -85
- package/dist/sanitize-conversation.js.map +0 -1
- package/dist/scoped-tool.d.ts +0 -98
- package/dist/scoped-tool.d.ts.map +0 -1
- package/dist/scoped-tool.js +0 -174
- package/dist/scoped-tool.js.map +0 -1
- package/dist/server/provider.d.ts +0 -22
- package/dist/server/provider.d.ts.map +0 -1
- package/dist/server/provider.js +0 -194
- package/dist/server/provider.js.map +0 -1
- package/dist/similarity-search.d.ts +0 -163
- package/dist/similarity-search.d.ts.map +0 -1
- package/dist/similarity-search.js +0 -147
- package/dist/similarity-search.js.map +0 -1
- package/dist/sub-agent-run-store.d.ts +0 -157
- package/dist/sub-agent-run-store.d.ts.map +0 -1
- package/dist/sub-agent-run-store.js +0 -87
- package/dist/sub-agent-run-store.js.map +0 -1
- package/dist/tool-execution.d.ts +0 -16
- package/dist/tool-execution.d.ts.map +0 -1
- package/dist/tool-execution.js +0 -498
- package/dist/tool-execution.js.map +0 -1
- package/dist/tool-helpers.d.ts +0 -77
- package/dist/tool-helpers.d.ts.map +0 -1
- package/dist/tool-helpers.js +0 -117
- package/dist/tool-helpers.js.map +0 -1
- package/dist/tool.d.ts +0 -216
- package/dist/tool.d.ts.map +0 -1
- package/dist/tool.js +0 -175
- package/dist/tool.js.map +0 -1
- package/dist/transcription.d.ts +0 -42
- package/dist/transcription.d.ts.map +0 -1
- package/dist/transcription.js +0 -77
- package/dist/transcription.js.map +0 -1
- package/dist/types.d.ts +0 -1020
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/util/hash.d.ts +0 -11
- package/dist/util/hash.d.ts.map +0 -1
- package/dist/util/hash.js +0 -23
- package/dist/util/hash.js.map +0 -1
- package/dist/vector-stores/index.d.ts +0 -96
- package/dist/vector-stores/index.d.ts.map +0 -1
- package/dist/vector-stores/index.js +0 -153
- package/dist/vector-stores/index.js.map +0 -1
- package/dist/vercel-protocol.d.ts +0 -18
- package/dist/vercel-protocol.d.ts.map +0 -1
- package/dist/vercel-protocol.js +0 -75
- package/dist/vercel-protocol.js.map +0 -1
- package/dist/zod-to-json-schema.d.ts +0 -16
- package/dist/zod-to-json-schema.d.ts.map +0 -1
- package/dist/zod-to-json-schema.js +0 -17
- package/dist/zod-to-json-schema.js.map +0 -1
package/dist/providers/google.js
DELETED
|
@@ -1,903 +0,0 @@
|
|
|
1
|
-
import { base64ToUtf8 } from '../base64.js';
|
|
2
|
-
import { buildGoogleCacheKey, splitContentsAtCache, durationToGoogleTtl, _internals as _registryInternals, } from './google-cache-registry.js';
|
|
3
|
-
export class GoogleProvider {
|
|
4
|
-
name = 'google';
|
|
5
|
-
config;
|
|
6
|
-
cacheRegistry;
|
|
7
|
-
constructor(config, cacheRegistry) {
|
|
8
|
-
this.config = config;
|
|
9
|
-
if (cacheRegistry)
|
|
10
|
-
this.cacheRegistry = cacheRegistry;
|
|
11
|
-
}
|
|
12
|
-
create(model) {
|
|
13
|
-
return new GoogleAdapter(this.config, model, this.cacheRegistry);
|
|
14
|
-
}
|
|
15
|
-
createEmbedding(model) {
|
|
16
|
-
return new GoogleEmbeddingAdapter(this.config, model);
|
|
17
|
-
}
|
|
18
|
-
createImage(model) {
|
|
19
|
-
return new GoogleImageAdapter(this.config, model);
|
|
20
|
-
}
|
|
21
|
-
createFiles() {
|
|
22
|
-
return new GoogleFileAdapter(this.config);
|
|
23
|
-
}
|
|
24
|
-
createVectorStores() {
|
|
25
|
-
return new GoogleVectorStoreAdapter(this.config);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
// ─── Adapter ──────────────────────────────────────────────
|
|
29
|
-
export class GoogleAdapter {
|
|
30
|
-
config;
|
|
31
|
-
model;
|
|
32
|
-
cacheRegistry;
|
|
33
|
-
client = null;
|
|
34
|
-
constructor(config, model, cacheRegistry) {
|
|
35
|
-
this.config = config;
|
|
36
|
-
this.model = model;
|
|
37
|
-
this.cacheRegistry = cacheRegistry;
|
|
38
|
-
}
|
|
39
|
-
async getClient() {
|
|
40
|
-
if (this.client)
|
|
41
|
-
return this.client;
|
|
42
|
-
const sdk = await import(/* @vite-ignore */ '@google/genai');
|
|
43
|
-
const GoogleGenAI = sdk.GoogleGenAI ?? sdk.default;
|
|
44
|
-
this.client = new GoogleGenAI({ apiKey: this.config.apiKey });
|
|
45
|
-
return this.client;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Build the request payload, consulting the cache registry if `options.cache`
|
|
49
|
-
* is set. Returns the payload for `generateContent` / `generateContentStream`
|
|
50
|
-
* plus the cache key (so the caller can `forget()` it on a 404 stale-cache
|
|
51
|
-
* retry).
|
|
52
|
-
*/
|
|
53
|
-
async buildPayload(options) {
|
|
54
|
-
const client = await this.getClient();
|
|
55
|
-
const { system, contents } = toGeminiContents(options.messages);
|
|
56
|
-
// `toGeminiTools` returns the already-wrapped top-level array
|
|
57
|
-
// ({functionDeclarations: [...]} + any native blocks like google_search).
|
|
58
|
-
const geminiTools = options.tools?.length ? toGeminiTools(options.tools) : undefined;
|
|
59
|
-
const config = {};
|
|
60
|
-
if (options.maxTokens)
|
|
61
|
-
config['maxOutputTokens'] = options.maxTokens;
|
|
62
|
-
if (options.temperature !== undefined)
|
|
63
|
-
config['temperature'] = options.temperature;
|
|
64
|
-
if (options.topP !== undefined)
|
|
65
|
-
config['topP'] = options.topP;
|
|
66
|
-
if (options.stop)
|
|
67
|
-
config['stopSequences'] = options.stop;
|
|
68
|
-
if (geminiTools && geminiTools.length > 0)
|
|
69
|
-
config['tools'] = geminiTools;
|
|
70
|
-
if (options.toolChoice)
|
|
71
|
-
config['toolConfig'] = toGeminiToolConfig(options.toolChoice);
|
|
72
|
-
// The Gemini SDK reads abortSignal from the config block.
|
|
73
|
-
if (options.signal)
|
|
74
|
-
config['abortSignal'] = options.signal;
|
|
75
|
-
let cacheName = null;
|
|
76
|
-
let cacheKey;
|
|
77
|
-
if (this.cacheRegistry && options.cache) {
|
|
78
|
-
cacheKey = buildGoogleCacheKey(this.model, options.cache, system, contents, geminiTools);
|
|
79
|
-
if (cacheKey) {
|
|
80
|
-
const { cached: cachedSlice } = splitContentsAtCache(contents, options.cache);
|
|
81
|
-
cacheName = await this.cacheRegistry.resolve({
|
|
82
|
-
client,
|
|
83
|
-
model: this.model,
|
|
84
|
-
cacheKey,
|
|
85
|
-
...(options.cache.instructions && system ? { systemInstruction: { parts: [{ text: system }] } } : {}),
|
|
86
|
-
...(cachedSlice.length > 0 ? { contents: cachedSlice } : {}),
|
|
87
|
-
...(options.cache.tools && geminiTools && geminiTools.length > 0 ? { tools: geminiTools } : {}),
|
|
88
|
-
...(options.cache.ttl ? { ttl: durationToGoogleTtl(options.cache.ttl) } : {}),
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (cacheName) {
|
|
93
|
-
// Drop tools / system from the request — they're inherited from the cache resource.
|
|
94
|
-
const { fresh } = splitContentsAtCache(contents, options.cache);
|
|
95
|
-
const configForCachedRequest = { ...config };
|
|
96
|
-
delete configForCachedRequest['tools'];
|
|
97
|
-
configForCachedRequest['cachedContent'] = cacheName;
|
|
98
|
-
return {
|
|
99
|
-
payload: { model: this.model, contents: fresh, config: configForCachedRequest },
|
|
100
|
-
cacheKey,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
return {
|
|
104
|
-
payload: {
|
|
105
|
-
model: this.model,
|
|
106
|
-
contents,
|
|
107
|
-
...(system ? { systemInstruction: { parts: [{ text: system }] } } : {}),
|
|
108
|
-
config,
|
|
109
|
-
},
|
|
110
|
-
cacheKey,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
async generate(options) {
|
|
114
|
-
const { payload, cacheKey } = await this.buildPayload(options);
|
|
115
|
-
const client = await this.getClient();
|
|
116
|
-
try {
|
|
117
|
-
const response = await client.models.generateContent(payload);
|
|
118
|
-
return fromGeminiResponse(response);
|
|
119
|
-
}
|
|
120
|
-
catch (err) {
|
|
121
|
-
if (cacheKey && this.cacheRegistry && _registryInternals.isNotFoundError(err)) {
|
|
122
|
-
// Stale `cachedContent` resource — drop and retry once with a fresh build.
|
|
123
|
-
await this.cacheRegistry.forget(cacheKey);
|
|
124
|
-
const { payload: retryPayload } = await this.buildPayload(options);
|
|
125
|
-
const response = await client.models.generateContent(retryPayload);
|
|
126
|
-
return fromGeminiResponse(response);
|
|
127
|
-
}
|
|
128
|
-
throw err;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
async *stream(options) {
|
|
132
|
-
let payloadAndKey = await this.buildPayload(options);
|
|
133
|
-
const client = await this.getClient();
|
|
134
|
-
let response;
|
|
135
|
-
try {
|
|
136
|
-
response = await client.models.generateContentStream(payloadAndKey.payload);
|
|
137
|
-
}
|
|
138
|
-
catch (err) {
|
|
139
|
-
if (payloadAndKey.cacheKey && this.cacheRegistry && _registryInternals.isNotFoundError(err)) {
|
|
140
|
-
await this.cacheRegistry.forget(payloadAndKey.cacheKey);
|
|
141
|
-
payloadAndKey = await this.buildPayload(options);
|
|
142
|
-
response = await client.models.generateContentStream(payloadAndKey.payload);
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
throw err;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
for await (const chunk of response) {
|
|
149
|
-
const candidate = chunk.candidates?.[0];
|
|
150
|
-
if (!candidate)
|
|
151
|
-
continue;
|
|
152
|
-
for (const part of candidate.content?.parts ?? []) {
|
|
153
|
-
if (part.text) {
|
|
154
|
-
yield { type: 'text-delta', text: part.text };
|
|
155
|
-
}
|
|
156
|
-
if (part.functionCall) {
|
|
157
|
-
yield {
|
|
158
|
-
type: 'tool-call',
|
|
159
|
-
toolCall: {
|
|
160
|
-
id: `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
161
|
-
name: part.functionCall.name,
|
|
162
|
-
arguments: part.functionCall.args ?? {},
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (candidate.finishReason) {
|
|
168
|
-
yield {
|
|
169
|
-
type: 'finish',
|
|
170
|
-
finishReason: candidate.finishReason === 'STOP' ? 'stop' : 'tool_calls',
|
|
171
|
-
usage: chunk.usageMetadata ? {
|
|
172
|
-
promptTokens: chunk.usageMetadata.promptTokenCount ?? 0,
|
|
173
|
-
completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,
|
|
174
|
-
totalTokens: chunk.usageMetadata.totalTokenCount ?? 0,
|
|
175
|
-
} : undefined,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
// ─── Conversion Helpers ──────────────────────────────────
|
|
182
|
-
function contentToString(content) {
|
|
183
|
-
if (typeof content === 'string')
|
|
184
|
-
return content;
|
|
185
|
-
return content.filter(p => p.type === 'text').map(p => p.text).join('');
|
|
186
|
-
}
|
|
187
|
-
function contentToGeminiParts(content) {
|
|
188
|
-
if (typeof content === 'string')
|
|
189
|
-
return [{ text: content }];
|
|
190
|
-
return content.map(p => {
|
|
191
|
-
if (p.type === 'text')
|
|
192
|
-
return { text: p.text };
|
|
193
|
-
if (p.type === 'image')
|
|
194
|
-
return { inlineData: { mimeType: p.mimeType, data: p.data } };
|
|
195
|
-
// document — inline data for PDFs, text for text-based
|
|
196
|
-
if (p.mimeType === 'application/pdf') {
|
|
197
|
-
return { inlineData: { mimeType: p.mimeType, data: p.data } };
|
|
198
|
-
}
|
|
199
|
-
return { text: base64ToUtf8(p.data) };
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
export function toGeminiContents(messages) {
|
|
203
|
-
const systemMsgs = messages.filter(m => m.role === 'system');
|
|
204
|
-
const rest = messages.filter(m => m.role !== 'system');
|
|
205
|
-
const system = systemMsgs.length > 0
|
|
206
|
-
? systemMsgs.map(m => contentToString(m.content)).join('\n\n')
|
|
207
|
-
: undefined;
|
|
208
|
-
// Gemini's `functionResponse.name` must match the originating `functionCall.name`
|
|
209
|
-
// (the function name like "search"), not the synthetic call id the adapter
|
|
210
|
-
// generates per stream. Pre-build a (toolCallId → name) lookup by walking
|
|
211
|
-
// every prior assistant message's `toolCalls`. Without this the receiving
|
|
212
|
-
// model sees `name: "call_1234_abc"` and can't pair the result with the call.
|
|
213
|
-
const toolNameByCallId = new Map();
|
|
214
|
-
for (const m of rest) {
|
|
215
|
-
if (m.role === 'assistant' && m.toolCalls?.length) {
|
|
216
|
-
for (const tc of m.toolCalls)
|
|
217
|
-
toolNameByCallId.set(tc.id, tc.name);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
const contents = rest.map(m => {
|
|
221
|
-
if (m.role === 'assistant' && m.toolCalls?.length) {
|
|
222
|
-
const text = contentToString(m.content);
|
|
223
|
-
return {
|
|
224
|
-
role: 'model',
|
|
225
|
-
parts: [
|
|
226
|
-
...(text ? [{ text }] : []),
|
|
227
|
-
...m.toolCalls.map(tc => ({
|
|
228
|
-
functionCall: { name: tc.name, args: tc.arguments },
|
|
229
|
-
})),
|
|
230
|
-
],
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
if (m.role === 'tool') {
|
|
234
|
-
const callId = m.toolCallId;
|
|
235
|
-
const name = (callId && toolNameByCallId.get(callId)) ?? 'unknown';
|
|
236
|
-
return {
|
|
237
|
-
role: 'user',
|
|
238
|
-
parts: [{
|
|
239
|
-
functionResponse: {
|
|
240
|
-
name,
|
|
241
|
-
response: typeof m.content === 'string' ? { result: m.content } : m.content,
|
|
242
|
-
},
|
|
243
|
-
}],
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
return {
|
|
247
|
-
role: m.role === 'assistant' ? 'model' : 'user',
|
|
248
|
-
parts: contentToGeminiParts(m.content),
|
|
249
|
-
};
|
|
250
|
-
});
|
|
251
|
-
return { system, contents };
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Build Gemini's `tools` array. The Gemini API accepts a mixed array where
|
|
255
|
-
* function declarations live under one wrapper entry and provider-native
|
|
256
|
-
* blocks (e.g. `{ google_search: {} }`) sit as separate top-level entries:
|
|
257
|
-
*
|
|
258
|
-
* tools: [
|
|
259
|
-
* { functionDeclarations: [...] },
|
|
260
|
-
* { google_search: {} },
|
|
261
|
-
* ]
|
|
262
|
-
*
|
|
263
|
-
* Tools tagged with a recognized `providerHint.type` are emitted as their
|
|
264
|
-
* native top-level block; everything else collects into one
|
|
265
|
-
* `functionDeclarations` entry. Tools with unrecognized hints fall through
|
|
266
|
-
* to the function-declaration shape — the input schema's still there, so
|
|
267
|
-
* the worst case is the model treats it as a regular function-call tool.
|
|
268
|
-
*/
|
|
269
|
-
function toGeminiTools(tools) {
|
|
270
|
-
const fnDecls = [];
|
|
271
|
-
const blocks = [];
|
|
272
|
-
for (const t of tools) {
|
|
273
|
-
if (t.providerHint?.type === 'web-search') {
|
|
274
|
-
// Gemini's native search tool. The block's `google_search: {}` form is
|
|
275
|
-
// intentional — Gemini doesn't accept allowed_domains / max_uses on
|
|
276
|
-
// this block, so the WebSearch.domains() / .maxResults() opts are
|
|
277
|
-
// ignored on this provider (documented on WebSearch).
|
|
278
|
-
blocks.push({ google_search: {} });
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
if (t.providerHint?.type === 'file-search') {
|
|
282
|
-
// Gemini's native FileSearch tool (#B8.5). The OpenAI-shaped hint
|
|
283
|
-
// (`vector_store_ids` + typed `filters`) is translated to Gemini's
|
|
284
|
-
// shape (`fileSearchStoreNames` + `metadataFilter` string). `topK`
|
|
285
|
-
// mirrors OpenAI's `max_num_results`.
|
|
286
|
-
const storeNames = t.providerHint['vector_store_ids'] ?? [];
|
|
287
|
-
const fileSearchConfig = {
|
|
288
|
-
fileSearchStoreNames: storeNames,
|
|
289
|
-
};
|
|
290
|
-
const filters = t.providerHint['filters'];
|
|
291
|
-
if (filters !== undefined) {
|
|
292
|
-
fileSearchConfig['metadataFilter'] = filterToGeminiString(filters);
|
|
293
|
-
}
|
|
294
|
-
const maxNumResults = t.providerHint['max_num_results'];
|
|
295
|
-
if (maxNumResults !== undefined) {
|
|
296
|
-
fileSearchConfig['topK'] = maxNumResults;
|
|
297
|
-
}
|
|
298
|
-
blocks.push({ fileSearch: fileSearchConfig });
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
|
-
fnDecls.push({
|
|
302
|
-
name: t.name,
|
|
303
|
-
description: t.description,
|
|
304
|
-
parameters: t.parameters,
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
if (fnDecls.length > 0)
|
|
308
|
-
blocks.unshift({ functionDeclarations: fnDecls });
|
|
309
|
-
return blocks;
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Translate a typed `FileSearchFilter` (OpenAI-shaped) into Gemini's
|
|
313
|
-
* `metadataFilter` string syntax (#B8.5).
|
|
314
|
-
*
|
|
315
|
-
* - `{ type: 'eq', key, value }` → `key = value`
|
|
316
|
-
* - `{ type: 'ne', key, value }` → `key != value`
|
|
317
|
-
* - `{ type: 'gt', key, value }` → `key > value`
|
|
318
|
-
* - `{ type: 'gte', key, value }` → `key >= value`
|
|
319
|
-
* - `{ type: 'lt', key, value }` → `key < value`
|
|
320
|
-
* - `{ type: 'lte', key, value }` → `key <= value`
|
|
321
|
-
* - `{ type: 'and', filters }` → `(f1) AND (f2) AND ...`
|
|
322
|
-
* - `{ type: 'or', filters }` → `(f1) OR (f2) OR ...`
|
|
323
|
-
*
|
|
324
|
-
* String values are wrapped in double quotes with `"` and `\` escaped.
|
|
325
|
-
* Numbers and booleans render bare.
|
|
326
|
-
*
|
|
327
|
-
* Exported for unit testing — see `google-vector-stores.test.ts`.
|
|
328
|
-
*
|
|
329
|
-
* @internal
|
|
330
|
-
*/
|
|
331
|
-
export function filterToGeminiString(filter) {
|
|
332
|
-
switch (filter.type) {
|
|
333
|
-
case 'eq':
|
|
334
|
-
case 'ne':
|
|
335
|
-
case 'gt':
|
|
336
|
-
case 'gte':
|
|
337
|
-
case 'lt':
|
|
338
|
-
case 'lte': {
|
|
339
|
-
const op = GEMINI_FILTER_OP[filter.type];
|
|
340
|
-
return `${filter.key} ${op} ${formatGeminiValue(filter.value)}`;
|
|
341
|
-
}
|
|
342
|
-
case 'and':
|
|
343
|
-
case 'or': {
|
|
344
|
-
if (filter.filters.length === 0) {
|
|
345
|
-
throw new Error(`[Rudder AI] Gemini metadataFilter: ${filter.type.toUpperCase()} requires at least one sub-filter.`);
|
|
346
|
-
}
|
|
347
|
-
const joiner = filter.type === 'and' ? ' AND ' : ' OR ';
|
|
348
|
-
return filter.filters.map(f => `(${filterToGeminiString(f)})`).join(joiner);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const GEMINI_FILTER_OP = {
|
|
353
|
-
eq: '=',
|
|
354
|
-
ne: '!=',
|
|
355
|
-
gt: '>',
|
|
356
|
-
gte: '>=',
|
|
357
|
-
lt: '<',
|
|
358
|
-
lte: '<=',
|
|
359
|
-
};
|
|
360
|
-
function formatGeminiValue(value) {
|
|
361
|
-
if (typeof value === 'string') {
|
|
362
|
-
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
363
|
-
}
|
|
364
|
-
return String(value);
|
|
365
|
-
}
|
|
366
|
-
function toGeminiToolConfig(choice) {
|
|
367
|
-
if (choice === 'auto')
|
|
368
|
-
return { functionCallingConfig: { mode: 'AUTO' } };
|
|
369
|
-
if (choice === 'required')
|
|
370
|
-
return { functionCallingConfig: { mode: 'ANY' } };
|
|
371
|
-
if (choice === 'none')
|
|
372
|
-
return { functionCallingConfig: { mode: 'NONE' } };
|
|
373
|
-
if (typeof choice === 'object' && 'name' in choice) {
|
|
374
|
-
return { functionCallingConfig: { mode: 'ANY', allowedFunctionNames: [choice.name] } };
|
|
375
|
-
}
|
|
376
|
-
return { functionCallingConfig: { mode: 'AUTO' } };
|
|
377
|
-
}
|
|
378
|
-
function fromGeminiResponse(response) {
|
|
379
|
-
const candidate = response.candidates?.[0];
|
|
380
|
-
const toolCalls = [];
|
|
381
|
-
let text = '';
|
|
382
|
-
for (const part of candidate?.content?.parts ?? []) {
|
|
383
|
-
if (part.text)
|
|
384
|
-
text += part.text;
|
|
385
|
-
if (part.functionCall) {
|
|
386
|
-
toolCalls.push({
|
|
387
|
-
id: `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
388
|
-
name: part.functionCall.name,
|
|
389
|
-
arguments: part.functionCall.args ?? {},
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
return {
|
|
394
|
-
message: {
|
|
395
|
-
role: 'assistant',
|
|
396
|
-
content: text,
|
|
397
|
-
...(toolCalls.length > 0 ? { toolCalls } : {}),
|
|
398
|
-
},
|
|
399
|
-
usage: {
|
|
400
|
-
promptTokens: response.usageMetadata?.promptTokenCount ?? 0,
|
|
401
|
-
completionTokens: response.usageMetadata?.candidatesTokenCount ?? 0,
|
|
402
|
-
totalTokens: response.usageMetadata?.totalTokenCount ?? 0,
|
|
403
|
-
},
|
|
404
|
-
finishReason: toolCalls.length > 0 ? 'tool_calls' : 'stop',
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
// ─── Embedding Adapter ──────────────────────────────────
|
|
408
|
-
class GoogleEmbeddingAdapter {
|
|
409
|
-
config;
|
|
410
|
-
model;
|
|
411
|
-
client = null;
|
|
412
|
-
constructor(config, model) {
|
|
413
|
-
this.config = config;
|
|
414
|
-
this.model = model;
|
|
415
|
-
}
|
|
416
|
-
async getClient() {
|
|
417
|
-
if (this.client)
|
|
418
|
-
return this.client;
|
|
419
|
-
const sdk = await import(/* @vite-ignore */ '@google/genai');
|
|
420
|
-
const GoogleGenAI = sdk.GoogleGenAI ?? sdk.default;
|
|
421
|
-
this.client = new GoogleGenAI({ apiKey: this.config.apiKey });
|
|
422
|
-
return this.client;
|
|
423
|
-
}
|
|
424
|
-
async embed(input) {
|
|
425
|
-
const client = await this.getClient();
|
|
426
|
-
const inputs = Array.isArray(input) ? input : [input];
|
|
427
|
-
const results = await Promise.all(inputs.map(text => client.models.embedContent({
|
|
428
|
-
model: this.model,
|
|
429
|
-
content: { parts: [{ text }] },
|
|
430
|
-
})));
|
|
431
|
-
const embeddings = results.map((r) => r.embedding?.values ?? []);
|
|
432
|
-
return {
|
|
433
|
-
embeddings,
|
|
434
|
-
usage: { promptTokens: 0, totalTokens: 0 },
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
// ─── Image Generation Adapter (Imagen) ──────────────────
|
|
439
|
-
const GOOGLE_IMAGE_SIZE_MAP = {
|
|
440
|
-
square: '1024x1024',
|
|
441
|
-
landscape: '1792x1024',
|
|
442
|
-
portrait: '1024x1792',
|
|
443
|
-
};
|
|
444
|
-
class GoogleImageAdapter {
|
|
445
|
-
config;
|
|
446
|
-
model;
|
|
447
|
-
constructor(config, model) {
|
|
448
|
-
this.config = config;
|
|
449
|
-
this.model = model;
|
|
450
|
-
}
|
|
451
|
-
async generate(options) {
|
|
452
|
-
const size = options.size
|
|
453
|
-
? (GOOGLE_IMAGE_SIZE_MAP[options.size] ?? options.size)
|
|
454
|
-
: '1024x1024';
|
|
455
|
-
const [width, height] = size.split('x').map(Number);
|
|
456
|
-
const body = {
|
|
457
|
-
instances: [{ prompt: options.prompt }],
|
|
458
|
-
parameters: {
|
|
459
|
-
sampleCount: options.n ?? 1,
|
|
460
|
-
...(width && height ? { aspectRatio: `${width}:${height}` } : {}),
|
|
461
|
-
},
|
|
462
|
-
};
|
|
463
|
-
const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/${this.model}:predict?key=${this.config.apiKey}`;
|
|
464
|
-
const res = await fetch(endpoint, {
|
|
465
|
-
method: 'POST',
|
|
466
|
-
headers: { 'Content-Type': 'application/json' },
|
|
467
|
-
body: JSON.stringify(body),
|
|
468
|
-
});
|
|
469
|
-
if (!res.ok) {
|
|
470
|
-
throw new Error(`[Rudder AI] Google image generation error: ${res.status} ${await res.text()}`);
|
|
471
|
-
}
|
|
472
|
-
const data = await res.json();
|
|
473
|
-
return {
|
|
474
|
-
images: (data.predictions ?? []).map((p) => ({
|
|
475
|
-
...(p.bytesBase64Encoded ? { base64: p.bytesBase64Encoded } : {}),
|
|
476
|
-
})),
|
|
477
|
-
model: this.model,
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
// ─── Files ──────────────────────────────────────────────
|
|
482
|
-
class GoogleFileAdapter {
|
|
483
|
-
config;
|
|
484
|
-
client = null;
|
|
485
|
-
constructor(config) {
|
|
486
|
-
this.config = config;
|
|
487
|
-
}
|
|
488
|
-
async getClient() {
|
|
489
|
-
if (this.client)
|
|
490
|
-
return this.client;
|
|
491
|
-
const sdk = await import(/* @vite-ignore */ '@google/genai');
|
|
492
|
-
const GoogleGenAI = sdk.GoogleGenAI ?? sdk.default;
|
|
493
|
-
this.client = new GoogleGenAI({ apiKey: this.config.apiKey });
|
|
494
|
-
return this.client;
|
|
495
|
-
}
|
|
496
|
-
async upload(options) {
|
|
497
|
-
const client = await this.getClient();
|
|
498
|
-
const { readFile, stat } = await import(/* @vite-ignore */ 'node:fs/promises');
|
|
499
|
-
const { basename } = await import(/* @vite-ignore */ 'node:path');
|
|
500
|
-
const data = await readFile(options.filePath);
|
|
501
|
-
const stats = await stat(options.filePath);
|
|
502
|
-
const filename = basename(options.filePath);
|
|
503
|
-
const response = await client.files.upload({
|
|
504
|
-
file: new Blob([data]),
|
|
505
|
-
config: { displayName: filename },
|
|
506
|
-
});
|
|
507
|
-
return {
|
|
508
|
-
id: response.name ?? response.uri,
|
|
509
|
-
filename,
|
|
510
|
-
bytes: stats.size,
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
async list() {
|
|
514
|
-
const client = await this.getClient();
|
|
515
|
-
const response = await client.files.list();
|
|
516
|
-
const files = [];
|
|
517
|
-
for (const f of response.files ?? response ?? []) {
|
|
518
|
-
files.push({
|
|
519
|
-
id: f.name ?? f.uri,
|
|
520
|
-
filename: f.displayName ?? f.name ?? '',
|
|
521
|
-
bytes: Number(f.sizeBytes ?? 0),
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
return { files };
|
|
525
|
-
}
|
|
526
|
-
async delete(fileId) {
|
|
527
|
-
const client = await this.getClient();
|
|
528
|
-
await client.files.delete({ name: fileId });
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
// ─── Vector Stores (Gemini FileSearchStores, #B8.5) ──────
|
|
532
|
-
//
|
|
533
|
-
// Gemini's hosted RAG surface is `ai.fileSearchStores.*` — a direct
|
|
534
|
-
// OpenAI-equivalent that handles ingestion, chunking, embedding, and
|
|
535
|
-
// retrieval server-side. NOT available on Vertex AI; the underlying SDK
|
|
536
|
-
// methods throw for Vertex clients.
|
|
537
|
-
//
|
|
538
|
-
// Mapping decisions:
|
|
539
|
-
// - `VectorStoreInfo.id` is the full Gemini resource name
|
|
540
|
-
// (`fileSearchStores/foo-bar`). Apps pass it back verbatim to `get` /
|
|
541
|
-
// `delete` / `addFile`.
|
|
542
|
-
// - `VectorStoreInfo.name` is `displayName`. The OpenAI adapter populates
|
|
543
|
-
// it from the store's name field; we use the user-supplied display name
|
|
544
|
-
// to keep `create('Knowledge Base')` round-trip-able.
|
|
545
|
-
// - `createdAt` is parsed from ISO 8601 to Unix seconds for parity with
|
|
546
|
-
// OpenAI's `created_at`.
|
|
547
|
-
// - `fileCount` sums `activeDocumentsCount + pendingDocumentsCount` (both
|
|
548
|
-
// string-encoded). `failedDocumentsCount` is dropped — it's surfaced
|
|
549
|
-
// per-file via `addFile`'s status when polling.
|
|
550
|
-
// - `bytesUsed` is parsed from `sizeBytes` (string-encoded).
|
|
551
|
-
// - Store-level `metadata` and `expiresAfter` are NOT supported by Gemini.
|
|
552
|
-
// Passing them throws fail-loud so apps don't silently lose data.
|
|
553
|
-
//
|
|
554
|
-
// `addFile` paths:
|
|
555
|
-
// - `{ fileId }` → `importFile` (re-uses an existing Files API file).
|
|
556
|
-
// - `{ filePath | fileBuffer }` → `uploadToFileSearchStore` (single-shot
|
|
557
|
-
// upload). Both paths return long-running operations; default
|
|
558
|
-
// `wait: true` polls `client.operations.get` until `done`.
|
|
559
|
-
// - `attributes` (Record<string, primitive>) → Gemini's `customMetadata`
|
|
560
|
-
// array shape; booleans coerce to `stringValue: 'true' | 'false'`.
|
|
561
|
-
class GoogleVectorStoreAdapter {
|
|
562
|
-
config;
|
|
563
|
-
client = null;
|
|
564
|
-
constructor(config) {
|
|
565
|
-
this.config = config;
|
|
566
|
-
}
|
|
567
|
-
async getClient() {
|
|
568
|
-
if (this.client)
|
|
569
|
-
return this.client;
|
|
570
|
-
const sdk = await import(/* @vite-ignore */ '@google/genai');
|
|
571
|
-
const GoogleGenAI = sdk.GoogleGenAI ?? sdk.default;
|
|
572
|
-
this.client = new GoogleGenAI({ apiKey: this.config.apiKey });
|
|
573
|
-
return this.client;
|
|
574
|
-
}
|
|
575
|
-
async create(opts) {
|
|
576
|
-
if (opts.metadata) {
|
|
577
|
-
throw new Error('[Rudder AI] Gemini FileSearchStores does not support store-level metadata. ' +
|
|
578
|
-
'Attach searchable metadata per-document via addFile({ attributes }).');
|
|
579
|
-
}
|
|
580
|
-
if (opts.expiresAfter) {
|
|
581
|
-
throw new Error('[Rudder AI] Gemini FileSearchStores does not support expiresAfter. ' +
|
|
582
|
-
'Stores persist until explicitly deleted via VectorStores.delete().');
|
|
583
|
-
}
|
|
584
|
-
const client = await this.getClient();
|
|
585
|
-
const response = await client.fileSearchStores.create({
|
|
586
|
-
config: { displayName: opts.name },
|
|
587
|
-
});
|
|
588
|
-
return fromGeminiFileSearchStore(response, opts.name);
|
|
589
|
-
}
|
|
590
|
-
async list(opts) {
|
|
591
|
-
const client = await this.getClient();
|
|
592
|
-
const config = {};
|
|
593
|
-
if (opts?.limit !== undefined)
|
|
594
|
-
config['pageSize'] = opts.limit;
|
|
595
|
-
if (opts?.after !== undefined)
|
|
596
|
-
config['pageToken'] = opts.after;
|
|
597
|
-
// Gemini paginates forward via pageToken only — `before` has no
|
|
598
|
-
// equivalent. Drop it silently (matches OpenAI when `before` is unset).
|
|
599
|
-
const pager = await client.fileSearchStores.list({ config });
|
|
600
|
-
const items = Array.isArray(pager?.page) ? pager.page : [];
|
|
601
|
-
return { stores: items.map(item => fromGeminiFileSearchStore(item)) };
|
|
602
|
-
}
|
|
603
|
-
async get(id) {
|
|
604
|
-
const client = await this.getClient();
|
|
605
|
-
const response = await client.fileSearchStores.get({ name: id });
|
|
606
|
-
return fromGeminiFileSearchStore(response);
|
|
607
|
-
}
|
|
608
|
-
async delete(id) {
|
|
609
|
-
const client = await this.getClient();
|
|
610
|
-
// `force: true` mirrors OpenAI's behavior — deleting a store also
|
|
611
|
-
// drops attached documents. Without `force`, Gemini returns
|
|
612
|
-
// FAILED_PRECONDITION when the store has any documents.
|
|
613
|
-
await client.fileSearchStores.delete({ name: id, config: { force: true } });
|
|
614
|
-
}
|
|
615
|
-
async addFile(storeId, opts) {
|
|
616
|
-
const client = await this.getClient();
|
|
617
|
-
const customMetadata = opts.attributes ? attributesToCustomMetadata(opts.attributes) : undefined;
|
|
618
|
-
// Path 1: re-use an existing Files API file.
|
|
619
|
-
if (opts.fileId) {
|
|
620
|
-
const importConfig = {};
|
|
621
|
-
if (customMetadata)
|
|
622
|
-
importConfig['customMetadata'] = customMetadata;
|
|
623
|
-
if (opts.chunkingStrategy)
|
|
624
|
-
importConfig['chunkingConfig'] = opts.chunkingStrategy;
|
|
625
|
-
const op = await client.fileSearchStores.importFile({
|
|
626
|
-
fileSearchStoreName: storeId,
|
|
627
|
-
fileName: opts.fileId,
|
|
628
|
-
config: importConfig,
|
|
629
|
-
});
|
|
630
|
-
return finishVectorStoreOperation(client, op, storeId, opts);
|
|
631
|
-
}
|
|
632
|
-
// Path 2: upload a local file directly. Either `filePath` or
|
|
633
|
-
// `fileBuffer` is required — Gemini's SDK accepts a path string OR a
|
|
634
|
-
// Blob. For `filePath`, the SDK infers mimeType from the extension;
|
|
635
|
-
// for `fileBuffer`, it reads `blob.type` which is empty on a
|
|
636
|
-
// untyped `new Blob([data])`, so we forward an explicit `mimeType`
|
|
637
|
-
// derived from `filename` to avoid `Can not determine mimeType`.
|
|
638
|
-
if (opts.filePath || opts.fileBuffer) {
|
|
639
|
-
const uploadConfig = {};
|
|
640
|
-
if (customMetadata)
|
|
641
|
-
uploadConfig['customMetadata'] = customMetadata;
|
|
642
|
-
if (opts.chunkingStrategy)
|
|
643
|
-
uploadConfig['chunkingConfig'] = opts.chunkingStrategy;
|
|
644
|
-
if (opts.fileBuffer?.filename)
|
|
645
|
-
uploadConfig['displayName'] = opts.fileBuffer.filename;
|
|
646
|
-
if (opts.fileBuffer?.filename) {
|
|
647
|
-
const mimeType = mimeTypeFromFilename(opts.fileBuffer.filename);
|
|
648
|
-
if (mimeType)
|
|
649
|
-
uploadConfig['mimeType'] = mimeType;
|
|
650
|
-
}
|
|
651
|
-
const file = opts.filePath ?? new Blob([opts.fileBuffer.data]);
|
|
652
|
-
const op = await client.fileSearchStores.uploadToFileSearchStore({
|
|
653
|
-
fileSearchStoreName: storeId,
|
|
654
|
-
file,
|
|
655
|
-
config: uploadConfig,
|
|
656
|
-
});
|
|
657
|
-
return finishVectorStoreOperation(client, op, storeId, opts);
|
|
658
|
-
}
|
|
659
|
-
throw new Error('[Rudder AI] addFile requires fileId, filePath, or fileBuffer. ' +
|
|
660
|
-
'Pass an existing Gemini Files API id via { fileId } (e.g. `files/abc-123`) or ' +
|
|
661
|
-
'a local source via { filePath } / { fileBuffer }.');
|
|
662
|
-
}
|
|
663
|
-
async removeFile(storeId, fileId) {
|
|
664
|
-
const client = await this.getClient();
|
|
665
|
-
// Document resource names are `fileSearchStores/<store>/documents/<doc>`.
|
|
666
|
-
// Apps that pass the full path use it verbatim; apps that pass only
|
|
667
|
-
// the document id get the store prefix joined for them.
|
|
668
|
-
const name = fileId.includes('/documents/') ? fileId : `${storeId}/documents/${fileId}`;
|
|
669
|
-
await client.fileSearchStores.documents.delete({ name });
|
|
670
|
-
}
|
|
671
|
-
async listFiles(storeId, opts) {
|
|
672
|
-
const client = await this.getClient();
|
|
673
|
-
const config = {};
|
|
674
|
-
if (opts?.limit !== undefined)
|
|
675
|
-
config['pageSize'] = opts.limit;
|
|
676
|
-
if (opts?.after !== undefined)
|
|
677
|
-
config['pageToken'] = opts.after;
|
|
678
|
-
const pager = await client.fileSearchStores.documents.list({ parent: storeId, config });
|
|
679
|
-
const items = Array.isArray(pager?.page) ? pager.page : [];
|
|
680
|
-
return { files: items.map(doc => fromGeminiDocument(doc, storeId)) };
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
/**
|
|
684
|
-
* Wait for a long-running file ingestion operation to finish and map the
|
|
685
|
-
* result into `VectorStoreFileInfo`. Honors `wait`/`pollInterval`/
|
|
686
|
-
* `pollTimeout` from `VectorStoreAddOptions` (defaults: wait=true,
|
|
687
|
-
* interval=1000ms, timeout=120_000ms).
|
|
688
|
-
*
|
|
689
|
-
* The terminal state of a Gemini ingestion op is exposed two ways:
|
|
690
|
-
* - `op.error?: { code, message }` when ingestion failed.
|
|
691
|
-
* - `op.response?: { documentName: 'fileSearchStores/.../documents/...' }`
|
|
692
|
-
* when successful.
|
|
693
|
-
*
|
|
694
|
-
* On success we follow up with a single `documents.get` to fetch
|
|
695
|
-
* `state` / `sizeBytes` / `createTime`. On failure we surface the error
|
|
696
|
-
* message via `lastError` and the status flips to `'failed'`.
|
|
697
|
-
*/
|
|
698
|
-
async function finishVectorStoreOperation(client, initialOp, storeId, opts) {
|
|
699
|
-
if (opts.wait === false) {
|
|
700
|
-
return {
|
|
701
|
-
id: initialOp?.name ?? `${storeId}/documents/pending-${Date.now()}`,
|
|
702
|
-
vectorStoreId: storeId,
|
|
703
|
-
status: 'in_progress',
|
|
704
|
-
createdAt: Math.floor(Date.now() / 1000),
|
|
705
|
-
...(opts.attributes ? { attributes: opts.attributes } : {}),
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
const pollInterval = opts.pollInterval ?? 1000;
|
|
709
|
-
const pollTimeout = opts.pollTimeout ?? 120_000;
|
|
710
|
-
const deadline = Date.now() + pollTimeout;
|
|
711
|
-
let current = initialOp;
|
|
712
|
-
while (!current?.done) {
|
|
713
|
-
if (Date.now() > deadline) {
|
|
714
|
-
throw new Error(`[Rudder AI] Gemini FileSearchStore ingestion timed out after ${pollTimeout}ms ` +
|
|
715
|
-
`(store=${storeId}). Increase pollTimeout or set wait: false for fire-and-forget.`);
|
|
716
|
-
}
|
|
717
|
-
await sleep(pollInterval);
|
|
718
|
-
current = await client.operations.get({ operation: current });
|
|
719
|
-
}
|
|
720
|
-
if (current.error) {
|
|
721
|
-
const errMessage = current.error.message ?? 'unknown error';
|
|
722
|
-
return {
|
|
723
|
-
id: current.name ?? `${storeId}/documents/failed-${Date.now()}`,
|
|
724
|
-
vectorStoreId: storeId,
|
|
725
|
-
status: 'failed',
|
|
726
|
-
createdAt: Math.floor(Date.now() / 1000),
|
|
727
|
-
lastError: errMessage,
|
|
728
|
-
...(opts.attributes ? { attributes: opts.attributes } : {}),
|
|
729
|
-
};
|
|
730
|
-
}
|
|
731
|
-
const documentName = current.response?.documentName;
|
|
732
|
-
if (!documentName) {
|
|
733
|
-
// Op done, no error, no documentName — surface as completed without
|
|
734
|
-
// follow-up details rather than failing.
|
|
735
|
-
return {
|
|
736
|
-
id: current.name ?? `${storeId}/documents/unknown-${Date.now()}`,
|
|
737
|
-
vectorStoreId: storeId,
|
|
738
|
-
status: 'completed',
|
|
739
|
-
createdAt: Math.floor(Date.now() / 1000),
|
|
740
|
-
...(opts.attributes ? { attributes: opts.attributes } : {}),
|
|
741
|
-
};
|
|
742
|
-
}
|
|
743
|
-
// Follow up with documents.get to surface real createdAt + sizeBytes.
|
|
744
|
-
// Best-effort: if the get fails (rare race), fall back to the op data.
|
|
745
|
-
try {
|
|
746
|
-
const doc = await client.fileSearchStores.documents.get({ name: documentName });
|
|
747
|
-
const info = fromGeminiDocument(doc, storeId);
|
|
748
|
-
if (opts.attributes && !info.attributes)
|
|
749
|
-
info.attributes = opts.attributes;
|
|
750
|
-
return info;
|
|
751
|
-
}
|
|
752
|
-
catch {
|
|
753
|
-
return {
|
|
754
|
-
id: documentName,
|
|
755
|
-
vectorStoreId: storeId,
|
|
756
|
-
status: 'completed',
|
|
757
|
-
createdAt: Math.floor(Date.now() / 1000),
|
|
758
|
-
...(opts.attributes ? { attributes: opts.attributes } : {}),
|
|
759
|
-
};
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
/**
|
|
763
|
-
* Map a Gemini `FileSearchStore` resource into the framework's
|
|
764
|
-
* `VectorStoreInfo` shape. `displayNameOverride` lets `create()` populate
|
|
765
|
-
* the human-friendly name from the user-supplied value when the API
|
|
766
|
-
* response omits it (some response variants do).
|
|
767
|
-
*
|
|
768
|
-
* @internal
|
|
769
|
-
*/
|
|
770
|
-
export function fromGeminiFileSearchStore(raw, displayNameOverride) {
|
|
771
|
-
const r = raw;
|
|
772
|
-
const id = r.name ?? '';
|
|
773
|
-
const active = Number(r.activeDocumentsCount ?? 0) || 0;
|
|
774
|
-
const pending = Number(r.pendingDocumentsCount ?? 0) || 0;
|
|
775
|
-
const result = {
|
|
776
|
-
id,
|
|
777
|
-
name: r.displayName ?? displayNameOverride ?? id,
|
|
778
|
-
createdAt: r.createTime ? Math.floor(Date.parse(r.createTime) / 1000) : Math.floor(Date.now() / 1000),
|
|
779
|
-
fileCount: active + pending,
|
|
780
|
-
};
|
|
781
|
-
if (r.sizeBytes !== undefined) {
|
|
782
|
-
const bytes = Number(r.sizeBytes);
|
|
783
|
-
if (Number.isFinite(bytes))
|
|
784
|
-
result.bytesUsed = bytes;
|
|
785
|
-
}
|
|
786
|
-
return result;
|
|
787
|
-
}
|
|
788
|
-
/**
|
|
789
|
-
* Map a Gemini `Document` resource into the framework's
|
|
790
|
-
* `VectorStoreFileInfo` shape. `DocumentState` enum values flatten to the
|
|
791
|
-
* shared `'in_progress' | 'completed' | 'failed' | 'cancelled'` union.
|
|
792
|
-
*
|
|
793
|
-
* @internal
|
|
794
|
-
*/
|
|
795
|
-
export function fromGeminiDocument(raw, storeId) {
|
|
796
|
-
const r = raw;
|
|
797
|
-
const status = mapGeminiDocumentState(r.state);
|
|
798
|
-
const result = {
|
|
799
|
-
id: r.name ?? `${storeId}/documents/unknown`,
|
|
800
|
-
vectorStoreId: storeId,
|
|
801
|
-
status,
|
|
802
|
-
createdAt: r.createTime ? Math.floor(Date.parse(r.createTime) / 1000) : Math.floor(Date.now() / 1000),
|
|
803
|
-
};
|
|
804
|
-
if (r.sizeBytes !== undefined) {
|
|
805
|
-
const bytes = Number(r.sizeBytes);
|
|
806
|
-
if (Number.isFinite(bytes))
|
|
807
|
-
result.bytes = bytes;
|
|
808
|
-
}
|
|
809
|
-
if (r.customMetadata && r.customMetadata.length > 0) {
|
|
810
|
-
result.attributes = customMetadataToAttributes(r.customMetadata);
|
|
811
|
-
}
|
|
812
|
-
return result;
|
|
813
|
-
}
|
|
814
|
-
function mapGeminiDocumentState(state) {
|
|
815
|
-
switch (state) {
|
|
816
|
-
case 'STATE_ACTIVE': return 'completed';
|
|
817
|
-
case 'STATE_FAILED': return 'failed';
|
|
818
|
-
case 'STATE_PENDING': return 'in_progress';
|
|
819
|
-
default: return 'in_progress';
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
/**
|
|
823
|
-
* Convert the framework's flat attribute map to Gemini's `CustomMetadata`
|
|
824
|
-
* array shape. Strings → `stringValue`, numbers → `numericValue`,
|
|
825
|
-
* booleans → `stringValue: 'true' | 'false'` (Gemini has no boolean
|
|
826
|
-
* variant — string is the safe lossless choice; filter-builders can
|
|
827
|
-
* still match on `key = "true"`).
|
|
828
|
-
*
|
|
829
|
-
* @internal
|
|
830
|
-
*/
|
|
831
|
-
export function attributesToCustomMetadata(attrs) {
|
|
832
|
-
return Object.entries(attrs).map(([key, value]) => {
|
|
833
|
-
if (typeof value === 'number')
|
|
834
|
-
return { key, numericValue: value };
|
|
835
|
-
if (typeof value === 'boolean')
|
|
836
|
-
return { key, stringValue: value ? 'true' : 'false' };
|
|
837
|
-
return { key, stringValue: value };
|
|
838
|
-
});
|
|
839
|
-
}
|
|
840
|
-
/**
|
|
841
|
-
* Inverse of {@link attributesToCustomMetadata}. Drops `stringListValue`
|
|
842
|
-
* variants (no flat-attribute representation; apps that need lists
|
|
843
|
-
* should read the raw Document via the SDK).
|
|
844
|
-
*
|
|
845
|
-
* @internal
|
|
846
|
-
*/
|
|
847
|
-
export function customMetadataToAttributes(metadata) {
|
|
848
|
-
const out = {};
|
|
849
|
-
for (const entry of metadata) {
|
|
850
|
-
if (!entry.key)
|
|
851
|
-
continue;
|
|
852
|
-
if (entry.numericValue !== undefined)
|
|
853
|
-
out[entry.key] = entry.numericValue;
|
|
854
|
-
else if (entry.stringValue !== undefined) {
|
|
855
|
-
// Round-trip booleans encoded by attributesToCustomMetadata.
|
|
856
|
-
if (entry.stringValue === 'true')
|
|
857
|
-
out[entry.key] = true;
|
|
858
|
-
else if (entry.stringValue === 'false')
|
|
859
|
-
out[entry.key] = false;
|
|
860
|
-
else
|
|
861
|
-
out[entry.key] = entry.stringValue;
|
|
862
|
-
}
|
|
863
|
-
// stringListValue intentionally dropped.
|
|
864
|
-
}
|
|
865
|
-
return out;
|
|
866
|
-
}
|
|
867
|
-
function sleep(ms) {
|
|
868
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
869
|
-
}
|
|
870
|
-
/**
|
|
871
|
-
* Best-effort MIME type from a filename extension. Gemini's
|
|
872
|
-
* `uploadToFileSearchStore` requires a mimeType on Blob uploads (it
|
|
873
|
-
* reads `blob.type`, which is empty on untyped `new Blob([data])`).
|
|
874
|
-
*
|
|
875
|
-
* Coverage matches Gemini's supported FileSearchStore document formats.
|
|
876
|
-
* Unknown extensions return `''` — the caller drops the field so the
|
|
877
|
-
* Gemini SDK's own error fires loudly rather than silently picking a
|
|
878
|
-
* wrong type.
|
|
879
|
-
*
|
|
880
|
-
* @internal
|
|
881
|
-
*/
|
|
882
|
-
export function mimeTypeFromFilename(filename) {
|
|
883
|
-
const ext = filename.toLowerCase().split('.').pop() ?? '';
|
|
884
|
-
switch (ext) {
|
|
885
|
-
case 'txt': return 'text/plain';
|
|
886
|
-
case 'md': return 'text/markdown';
|
|
887
|
-
case 'pdf': return 'application/pdf';
|
|
888
|
-
case 'html':
|
|
889
|
-
case 'htm': return 'text/html';
|
|
890
|
-
case 'json': return 'application/json';
|
|
891
|
-
case 'csv': return 'text/csv';
|
|
892
|
-
case 'tsv': return 'text/tab-separated-values';
|
|
893
|
-
case 'xml': return 'application/xml';
|
|
894
|
-
case 'rtf': return 'application/rtf';
|
|
895
|
-
case 'doc': return 'application/msword';
|
|
896
|
-
case 'docx': return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
|
|
897
|
-
case 'js': return 'text/javascript';
|
|
898
|
-
case 'ts': return 'text/x-typescript';
|
|
899
|
-
case 'py': return 'text/x-python';
|
|
900
|
-
default: return '';
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
//# sourceMappingURL=google.js.map
|