@rudderjs/ai 1.17.3 → 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/openai.js
DELETED
|
@@ -1,792 +0,0 @@
|
|
|
1
|
-
import { cyrb53Hex } from '../util/hash.js';
|
|
2
|
-
import { base64ToUtf8 } from '../base64.js';
|
|
3
|
-
export class OpenAIProvider {
|
|
4
|
-
name = 'openai';
|
|
5
|
-
config;
|
|
6
|
-
constructor(config) {
|
|
7
|
-
this.config = config;
|
|
8
|
-
}
|
|
9
|
-
create(model) {
|
|
10
|
-
return new OpenAIAdapter(this.config, model);
|
|
11
|
-
}
|
|
12
|
-
createEmbedding(model) {
|
|
13
|
-
return new OpenAIEmbeddingAdapter(this.config, model);
|
|
14
|
-
}
|
|
15
|
-
createImage(model) {
|
|
16
|
-
return new OpenAIImageAdapter(this.config, model);
|
|
17
|
-
}
|
|
18
|
-
createTts(model) {
|
|
19
|
-
return new OpenAITtsAdapter(this.config, model);
|
|
20
|
-
}
|
|
21
|
-
createStt(model) {
|
|
22
|
-
return new OpenAISttAdapter(this.config, model);
|
|
23
|
-
}
|
|
24
|
-
createFiles() {
|
|
25
|
-
return new OpenAIFileAdapter(this.config);
|
|
26
|
-
}
|
|
27
|
-
createVectorStores() {
|
|
28
|
-
return new OpenAIVectorStoreAdapter(this.config);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
// ─── Adapter (also reused by Ollama) ─────────────────────
|
|
32
|
-
export class OpenAIAdapter {
|
|
33
|
-
config;
|
|
34
|
-
model;
|
|
35
|
-
client = null;
|
|
36
|
-
constructor(config, model) {
|
|
37
|
-
this.config = config;
|
|
38
|
-
this.model = model;
|
|
39
|
-
}
|
|
40
|
-
async getClient() {
|
|
41
|
-
if (this.client)
|
|
42
|
-
return this.client;
|
|
43
|
-
const sdk = await import(/* @vite-ignore */ 'openai');
|
|
44
|
-
const OpenAI = sdk.default ?? sdk.OpenAI;
|
|
45
|
-
this.client = new OpenAI({
|
|
46
|
-
apiKey: this.config.apiKey,
|
|
47
|
-
...(this.config.baseUrl ? { baseURL: this.config.baseUrl } : {}),
|
|
48
|
-
...(this.config.organization ? { organization: this.config.organization } : {}),
|
|
49
|
-
...(this.config.defaultHeaders ? { defaultHeaders: this.config.defaultHeaders } : {}),
|
|
50
|
-
});
|
|
51
|
-
return this.client;
|
|
52
|
-
}
|
|
53
|
-
async generate(options) {
|
|
54
|
-
const client = await this.getClient();
|
|
55
|
-
const messages = toOpenAIMessages(options.messages);
|
|
56
|
-
const tools = options.tools?.length ? toOpenAITools(options.tools) : undefined;
|
|
57
|
-
const params = {
|
|
58
|
-
model: this.model,
|
|
59
|
-
messages,
|
|
60
|
-
};
|
|
61
|
-
if (options.maxTokens)
|
|
62
|
-
params['max_tokens'] = options.maxTokens;
|
|
63
|
-
if (options.temperature !== undefined)
|
|
64
|
-
params['temperature'] = options.temperature;
|
|
65
|
-
if (options.topP !== undefined)
|
|
66
|
-
params['top_p'] = options.topP;
|
|
67
|
-
if (options.stop)
|
|
68
|
-
params['stop'] = options.stop;
|
|
69
|
-
if (tools)
|
|
70
|
-
params['tools'] = tools;
|
|
71
|
-
if (options.toolChoice)
|
|
72
|
-
params['tool_choice'] = toOpenAIToolChoice(options.toolChoice);
|
|
73
|
-
const cacheKey = buildPromptCacheKey(messages, tools, options.cache);
|
|
74
|
-
if (cacheKey)
|
|
75
|
-
params['prompt_cache_key'] = cacheKey;
|
|
76
|
-
const response = await client.chat.completions.create(params, options.signal ? { signal: options.signal } : undefined);
|
|
77
|
-
return fromOpenAIResponse(response);
|
|
78
|
-
}
|
|
79
|
-
async *stream(options) {
|
|
80
|
-
const client = await this.getClient();
|
|
81
|
-
const messages = toOpenAIMessages(options.messages);
|
|
82
|
-
const tools = options.tools?.length ? toOpenAITools(options.tools) : undefined;
|
|
83
|
-
const params = {
|
|
84
|
-
model: this.model,
|
|
85
|
-
messages,
|
|
86
|
-
stream: true,
|
|
87
|
-
};
|
|
88
|
-
if (options.maxTokens)
|
|
89
|
-
params['max_tokens'] = options.maxTokens;
|
|
90
|
-
if (options.temperature !== undefined)
|
|
91
|
-
params['temperature'] = options.temperature;
|
|
92
|
-
if (options.topP !== undefined)
|
|
93
|
-
params['top_p'] = options.topP;
|
|
94
|
-
if (options.stop)
|
|
95
|
-
params['stop'] = options.stop;
|
|
96
|
-
if (tools)
|
|
97
|
-
params['tools'] = tools;
|
|
98
|
-
if (options.toolChoice)
|
|
99
|
-
params['tool_choice'] = toOpenAIToolChoice(options.toolChoice);
|
|
100
|
-
const cacheKey = buildPromptCacheKey(messages, tools, options.cache);
|
|
101
|
-
if (cacheKey)
|
|
102
|
-
params['prompt_cache_key'] = cacheKey;
|
|
103
|
-
const stream = await client.chat.completions.create(params, options.signal ? { signal: options.signal } : undefined);
|
|
104
|
-
for await (const chunk of stream) {
|
|
105
|
-
const choice = chunk.choices?.[0];
|
|
106
|
-
if (!choice)
|
|
107
|
-
continue;
|
|
108
|
-
const delta = choice.delta;
|
|
109
|
-
if (delta?.content) {
|
|
110
|
-
yield { type: 'text-delta', text: delta.content };
|
|
111
|
-
}
|
|
112
|
-
if (delta?.tool_calls?.length) {
|
|
113
|
-
for (const tc of delta.tool_calls) {
|
|
114
|
-
// OpenAI guarantees `index` on every tool_calls delta — it's the
|
|
115
|
-
// only stable correlator across the start-delta (carries `id`) and
|
|
116
|
-
// subsequent arg-only deltas. We thread it through StreamChunk so
|
|
117
|
-
// the agent loop can route arg fragments to the right partial
|
|
118
|
-
// under parallel tool calls.
|
|
119
|
-
const index = typeof tc.index === 'number' ? tc.index : undefined;
|
|
120
|
-
if (tc.id) {
|
|
121
|
-
yield {
|
|
122
|
-
type: 'tool-call-delta',
|
|
123
|
-
toolCall: { id: tc.id, name: tc.function?.name },
|
|
124
|
-
...(index !== undefined ? { toolCallIndex: index } : {}),
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
if (tc.function?.arguments) {
|
|
128
|
-
yield {
|
|
129
|
-
type: 'tool-call-delta',
|
|
130
|
-
text: tc.function.arguments,
|
|
131
|
-
...(index !== undefined ? { toolCallIndex: index } : {}),
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (choice.finish_reason) {
|
|
137
|
-
yield {
|
|
138
|
-
type: 'finish',
|
|
139
|
-
finishReason: choice.finish_reason === 'tool_calls' ? 'tool_calls' : 'stop',
|
|
140
|
-
usage: chunk.usage ? {
|
|
141
|
-
promptTokens: chunk.usage.prompt_tokens ?? 0,
|
|
142
|
-
completionTokens: chunk.usage.completion_tokens ?? 0,
|
|
143
|
-
totalTokens: chunk.usage.total_tokens ?? 0,
|
|
144
|
-
} : undefined,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
// ─── Conversion Helpers ──────────────────────────────────
|
|
151
|
-
function contentToString(content) {
|
|
152
|
-
if (typeof content === 'string')
|
|
153
|
-
return content;
|
|
154
|
-
return content.filter(p => p.type === 'text').map(p => p.text).join('');
|
|
155
|
-
}
|
|
156
|
-
function contentToOpenAIParts(content) {
|
|
157
|
-
if (typeof content === 'string')
|
|
158
|
-
return content;
|
|
159
|
-
return content.map(p => {
|
|
160
|
-
if (p.type === 'text')
|
|
161
|
-
return { type: 'text', text: p.text };
|
|
162
|
-
if (p.type === 'image')
|
|
163
|
-
return { type: 'image_url', image_url: { url: `data:${p.mimeType};base64,${p.data}` } };
|
|
164
|
-
// document — for text-based docs, decode to text; for PDFs, send as image_url (GPT-4o supports)
|
|
165
|
-
if (p.mimeType === 'application/pdf') {
|
|
166
|
-
return { type: 'file', file: { data: p.data, mime_type: p.mimeType } };
|
|
167
|
-
}
|
|
168
|
-
return { type: 'text', text: base64ToUtf8(p.data) };
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Repair tool-call ↔ tool-result adjacency before serializing for an
|
|
173
|
-
* OpenAI-compatible provider.
|
|
174
|
-
*
|
|
175
|
-
* Anthropic carries tool results as content blocks inside user turns, so a
|
|
176
|
-
* loosely-ordered transcript round-trips fine. The OpenAI wire protocol (and
|
|
177
|
-
* strict implementers like DeepSeek) enforce two hard rules:
|
|
178
|
-
*
|
|
179
|
-
* 1. every `role:'tool'` message must immediately follow the `assistant`
|
|
180
|
-
* message whose `tool_calls` declares its `tool_call_id`, and
|
|
181
|
-
* 2. every `tool_calls` entry on an assistant message must be answered by a
|
|
182
|
-
* following `role:'tool'` message before the next assistant/user turn.
|
|
183
|
-
*
|
|
184
|
-
* A persist→resume cycle (client-tool pause, approval round-trip, or an app
|
|
185
|
-
* that re-stores assistant turns without their `toolCalls`) can violate
|
|
186
|
-
* either rule, yielding `400 Messages with role 'tool' must be a response to
|
|
187
|
-
* a preceding message with 'tool_calls'` — or its mirror, an unanswered
|
|
188
|
-
* `tool_calls`. See `docs/plans/2026-06-11-deepseek-tool-transcript-400.md`.
|
|
189
|
-
*
|
|
190
|
-
* This pass enforces BOTH directions:
|
|
191
|
-
* - **Detached / out-of-order results** are pulled up to sit immediately
|
|
192
|
-
* after their parent assistant, in `tool_calls` order.
|
|
193
|
-
* - **Unanswered `tool_calls`** get a synthesized stub result so the
|
|
194
|
-
* request is well-formed (mirrors the placeholder strategy in
|
|
195
|
-
* `resumePendingToolCalls`).
|
|
196
|
-
* - **Orphan tool results** — whose `tool_call_id` is declared by no
|
|
197
|
-
* assistant message anywhere — are dropped; they can never be valid on
|
|
198
|
-
* the wire. (Lossy only when the app already discarded the parent's
|
|
199
|
-
* `toolCalls`; the framework can't reconstruct a deleted call.)
|
|
200
|
-
*
|
|
201
|
-
* Transcripts that already satisfy the invariant pass through unchanged
|
|
202
|
-
* (same message object references), so the common single-run path pays only
|
|
203
|
-
* a linear scan.
|
|
204
|
-
*/
|
|
205
|
-
export function normalizeToolTranscript(messages) {
|
|
206
|
-
// Index tool results by the call id they answer (a FIFO queue per id
|
|
207
|
-
// tolerates pathological duplicate ids without dropping a message), and
|
|
208
|
-
// collect every call id any assistant message declares.
|
|
209
|
-
const resultsByCallId = new Map();
|
|
210
|
-
const declaredCallIds = new Set();
|
|
211
|
-
for (const m of messages) {
|
|
212
|
-
if (m.role === 'tool' && m.toolCallId) {
|
|
213
|
-
const queue = resultsByCallId.get(m.toolCallId);
|
|
214
|
-
if (queue)
|
|
215
|
-
queue.push(m);
|
|
216
|
-
else
|
|
217
|
-
resultsByCallId.set(m.toolCallId, [m]);
|
|
218
|
-
}
|
|
219
|
-
else if (m.role === 'assistant' && m.toolCalls?.length) {
|
|
220
|
-
for (const tc of m.toolCalls)
|
|
221
|
-
declaredCallIds.add(tc.id);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
const out = [];
|
|
225
|
-
for (const m of messages) {
|
|
226
|
-
if (m.role === 'assistant' && m.toolCalls?.length) {
|
|
227
|
-
out.push(m);
|
|
228
|
-
// Emit each declared call's answer adjacent + in declaration order,
|
|
229
|
-
// claiming the real result wherever it sat or synthesizing a stub.
|
|
230
|
-
for (const tc of m.toolCalls) {
|
|
231
|
-
const real = resultsByCallId.get(tc.id)?.shift();
|
|
232
|
-
if (real) {
|
|
233
|
-
out.push(real);
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
out.push({
|
|
237
|
-
role: 'tool',
|
|
238
|
-
toolCallId: tc.id,
|
|
239
|
-
content: '[Rudder] tool result missing — synthesized to satisfy the OpenAI tool-call/tool-result protocol.',
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
// A tool message is emitted only by its parent assistant block above —
|
|
246
|
-
// here it is either already-claimed (skip) or an orphan with no declaring
|
|
247
|
-
// assistant (drop). Either way, never emit it standalone.
|
|
248
|
-
if (m.role === 'tool')
|
|
249
|
-
continue;
|
|
250
|
-
out.push(m);
|
|
251
|
-
}
|
|
252
|
-
return out;
|
|
253
|
-
}
|
|
254
|
-
export function toOpenAIMessages(messages) {
|
|
255
|
-
return normalizeToolTranscript(messages).map(m => {
|
|
256
|
-
if (m.role === 'assistant' && m.toolCalls?.length) {
|
|
257
|
-
return {
|
|
258
|
-
role: 'assistant',
|
|
259
|
-
content: contentToString(m.content) || null,
|
|
260
|
-
tool_calls: m.toolCalls.map(tc => ({
|
|
261
|
-
id: tc.id,
|
|
262
|
-
type: 'function',
|
|
263
|
-
function: { name: tc.name, arguments: JSON.stringify(tc.arguments) },
|
|
264
|
-
})),
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
if (m.role === 'tool') {
|
|
268
|
-
return {
|
|
269
|
-
role: 'tool',
|
|
270
|
-
tool_call_id: m.toolCallId,
|
|
271
|
-
content: typeof m.content === 'string' ? m.content : JSON.stringify(m.content),
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
// User messages with attachments → content array
|
|
275
|
-
if (Array.isArray(m.content)) {
|
|
276
|
-
return { role: m.role, content: contentToOpenAIParts(m.content) };
|
|
277
|
-
}
|
|
278
|
-
return { role: m.role, content: m.content };
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
export function toOpenAITools(tools) {
|
|
282
|
-
return tools.map(t => {
|
|
283
|
-
// Provider-native tool blocks: when a tool carries a recognized
|
|
284
|
-
// `providerHint`, emit OpenAI's native shape instead of the standard
|
|
285
|
-
// function-call schema. Currently:
|
|
286
|
-
// - 'file-search' → { type: 'file_search', vector_store_ids, filters?,
|
|
287
|
-
// max_num_results? }. The model is trained on the
|
|
288
|
-
// native tool — quality is dramatically better
|
|
289
|
-
// than wrapping it as a function call, and the
|
|
290
|
-
// provider runs the search server-side so no
|
|
291
|
-
// client-side execute is needed.
|
|
292
|
-
if (t.providerHint?.type === 'file-search') {
|
|
293
|
-
const vectorStoreIds = t.providerHint['vector_store_ids'] ?? [];
|
|
294
|
-
const block = {
|
|
295
|
-
type: 'file_search',
|
|
296
|
-
vector_store_ids: vectorStoreIds,
|
|
297
|
-
};
|
|
298
|
-
if (t.providerHint['filters'] !== undefined)
|
|
299
|
-
block['filters'] = t.providerHint['filters'];
|
|
300
|
-
if (t.providerHint['max_num_results'] !== undefined)
|
|
301
|
-
block['max_num_results'] = t.providerHint['max_num_results'];
|
|
302
|
-
return block;
|
|
303
|
-
}
|
|
304
|
-
return {
|
|
305
|
-
type: 'function',
|
|
306
|
-
function: { name: t.name, description: t.description, parameters: t.parameters },
|
|
307
|
-
};
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
function toOpenAIToolChoice(choice) {
|
|
311
|
-
if (choice === 'auto')
|
|
312
|
-
return 'auto';
|
|
313
|
-
if (choice === 'required')
|
|
314
|
-
return 'required';
|
|
315
|
-
if (choice === 'none')
|
|
316
|
-
return 'none';
|
|
317
|
-
if (typeof choice === 'object' && 'name' in choice)
|
|
318
|
-
return { type: 'function', function: { name: choice.name } };
|
|
319
|
-
return 'auto';
|
|
320
|
-
}
|
|
321
|
-
// ─── Prompt-cache key ────────────────────────────────────
|
|
322
|
-
//
|
|
323
|
-
// OpenAI caches prompts automatically once they exceed 1024 tokens. The only
|
|
324
|
-
// SDK knob is `prompt_cache_key`: an opaque string that gives OpenAI a routing
|
|
325
|
-
// hint so requests with the same cacheable prefix land on the same backend
|
|
326
|
-
// (which has the prefix already cached). Stable hashing is the goal — not
|
|
327
|
-
// cryptographic strength — so we use cyrb53 over canonical JSON of the
|
|
328
|
-
// regions the agent declared as `cacheable()`.
|
|
329
|
-
//
|
|
330
|
-
// Spec: https://platform.openai.com/docs/guides/prompt-caching
|
|
331
|
-
/**
|
|
332
|
-
* Build a stable `prompt_cache_key` from the regions the agent marked as
|
|
333
|
-
* cacheable. Returns `undefined` if no markers apply (request goes out
|
|
334
|
-
* without a cache key — OpenAI still caches automatically above 1024
|
|
335
|
-
* tokens, just without routing affinity).
|
|
336
|
-
*
|
|
337
|
-
* Exported for unit testing.
|
|
338
|
-
*/
|
|
339
|
-
export function buildPromptCacheKey(messages, tools, cache) {
|
|
340
|
-
if (!cache)
|
|
341
|
-
return undefined;
|
|
342
|
-
const parts = [];
|
|
343
|
-
if (cache.instructions) {
|
|
344
|
-
const sys = messages.find(m => m.role === 'system');
|
|
345
|
-
if (sys)
|
|
346
|
-
parts.push({ s: sys.content });
|
|
347
|
-
}
|
|
348
|
-
if (cache.tools && tools && tools.length > 0) {
|
|
349
|
-
parts.push({ t: tools });
|
|
350
|
-
}
|
|
351
|
-
if (cache.messages && cache.messages > 0) {
|
|
352
|
-
const conv = messages.filter(m => m.role !== 'system');
|
|
353
|
-
const sliced = conv.slice(0, cache.messages);
|
|
354
|
-
if (sliced.length > 0)
|
|
355
|
-
parts.push({ m: sliced });
|
|
356
|
-
}
|
|
357
|
-
if (parts.length === 0)
|
|
358
|
-
return undefined;
|
|
359
|
-
return cyrb53Hex(JSON.stringify(parts));
|
|
360
|
-
}
|
|
361
|
-
function fromOpenAIResponse(response) {
|
|
362
|
-
const choice = response.choices?.[0];
|
|
363
|
-
const message = choice?.message;
|
|
364
|
-
const toolCalls = (message?.tool_calls ?? []).map((tc) => ({
|
|
365
|
-
id: tc.id,
|
|
366
|
-
name: tc.function.name,
|
|
367
|
-
arguments: JSON.parse(tc.function.arguments),
|
|
368
|
-
}));
|
|
369
|
-
return {
|
|
370
|
-
message: {
|
|
371
|
-
role: 'assistant',
|
|
372
|
-
content: message?.content ?? '',
|
|
373
|
-
...(toolCalls.length > 0 ? { toolCalls } : {}),
|
|
374
|
-
},
|
|
375
|
-
usage: {
|
|
376
|
-
promptTokens: response.usage?.prompt_tokens ?? 0,
|
|
377
|
-
completionTokens: response.usage?.completion_tokens ?? 0,
|
|
378
|
-
totalTokens: response.usage?.total_tokens ?? 0,
|
|
379
|
-
},
|
|
380
|
-
finishReason: choice?.finish_reason === 'tool_calls' ? 'tool_calls' : 'stop',
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
// ─── Embedding Adapter ────────────────────────────────────
|
|
384
|
-
class OpenAIEmbeddingAdapter {
|
|
385
|
-
config;
|
|
386
|
-
model;
|
|
387
|
-
constructor(config, model) {
|
|
388
|
-
this.config = config;
|
|
389
|
-
this.model = model;
|
|
390
|
-
}
|
|
391
|
-
async embed(input) {
|
|
392
|
-
const baseUrl = this.config.baseUrl ?? 'https://api.openai.com/v1';
|
|
393
|
-
const inputs = Array.isArray(input) ? input : [input];
|
|
394
|
-
const res = await fetch(`${baseUrl}/embeddings`, {
|
|
395
|
-
method: 'POST',
|
|
396
|
-
headers: {
|
|
397
|
-
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
398
|
-
'Content-Type': 'application/json',
|
|
399
|
-
...(this.config.organization ? { 'OpenAI-Organization': this.config.organization } : {}),
|
|
400
|
-
...(this.config.defaultHeaders ?? {}),
|
|
401
|
-
},
|
|
402
|
-
body: JSON.stringify({ model: this.model, input: inputs }),
|
|
403
|
-
});
|
|
404
|
-
if (!res.ok)
|
|
405
|
-
throw new Error(`[Rudder AI] OpenAI embeddings error: ${res.status} ${await res.text()}`);
|
|
406
|
-
const data = await res.json();
|
|
407
|
-
return {
|
|
408
|
-
embeddings: data.data.map(d => d.embedding),
|
|
409
|
-
usage: { promptTokens: data.usage.prompt_tokens, totalTokens: data.usage.total_tokens },
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
// ─── Image Generation Adapter ────────────────────────────
|
|
414
|
-
const IMAGE_SIZE_MAP = {
|
|
415
|
-
square: '1024x1024',
|
|
416
|
-
landscape: '1792x1024',
|
|
417
|
-
portrait: '1024x1792',
|
|
418
|
-
};
|
|
419
|
-
class OpenAIImageAdapter {
|
|
420
|
-
config;
|
|
421
|
-
model;
|
|
422
|
-
client = null;
|
|
423
|
-
constructor(config, model) {
|
|
424
|
-
this.config = config;
|
|
425
|
-
this.model = model;
|
|
426
|
-
}
|
|
427
|
-
async getClient() {
|
|
428
|
-
if (this.client)
|
|
429
|
-
return this.client;
|
|
430
|
-
const sdk = await import(/* @vite-ignore */ 'openai');
|
|
431
|
-
const OpenAI = sdk.default ?? sdk.OpenAI;
|
|
432
|
-
this.client = new OpenAI({
|
|
433
|
-
apiKey: this.config.apiKey,
|
|
434
|
-
...(this.config.baseUrl ? { baseURL: this.config.baseUrl } : {}),
|
|
435
|
-
...(this.config.organization ? { organization: this.config.organization } : {}),
|
|
436
|
-
...(this.config.defaultHeaders ? { defaultHeaders: this.config.defaultHeaders } : {}),
|
|
437
|
-
});
|
|
438
|
-
return this.client;
|
|
439
|
-
}
|
|
440
|
-
async generate(options) {
|
|
441
|
-
const client = await this.getClient();
|
|
442
|
-
const size = options.size
|
|
443
|
-
? (IMAGE_SIZE_MAP[options.size] ?? options.size)
|
|
444
|
-
: '1024x1024';
|
|
445
|
-
const params = {
|
|
446
|
-
model: this.model,
|
|
447
|
-
prompt: options.prompt,
|
|
448
|
-
size,
|
|
449
|
-
response_format: 'b64_json',
|
|
450
|
-
};
|
|
451
|
-
if (options.n !== undefined)
|
|
452
|
-
params['n'] = options.n;
|
|
453
|
-
if (options.quality)
|
|
454
|
-
params['quality'] = options.quality;
|
|
455
|
-
if (options.style)
|
|
456
|
-
params['style'] = options.style;
|
|
457
|
-
const response = await client.images.generate(params);
|
|
458
|
-
return {
|
|
459
|
-
images: (response.data ?? []).map((img) => ({
|
|
460
|
-
...(img.b64_json ? { base64: img.b64_json } : {}),
|
|
461
|
-
...(img.url ? { url: img.url } : {}),
|
|
462
|
-
...(img.revised_prompt ? { revisedPrompt: img.revised_prompt } : {}),
|
|
463
|
-
})),
|
|
464
|
-
model: this.model,
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
// ─── TTS Adapter ─────────────────────────────────────────
|
|
469
|
-
class OpenAITtsAdapter {
|
|
470
|
-
config;
|
|
471
|
-
model;
|
|
472
|
-
client = null;
|
|
473
|
-
constructor(config, model) {
|
|
474
|
-
this.config = config;
|
|
475
|
-
this.model = model;
|
|
476
|
-
}
|
|
477
|
-
async getClient() {
|
|
478
|
-
if (this.client)
|
|
479
|
-
return this.client;
|
|
480
|
-
const sdk = await import(/* @vite-ignore */ 'openai');
|
|
481
|
-
const OpenAI = sdk.default ?? sdk.OpenAI;
|
|
482
|
-
this.client = new OpenAI({
|
|
483
|
-
apiKey: this.config.apiKey,
|
|
484
|
-
...(this.config.baseUrl ? { baseURL: this.config.baseUrl } : {}),
|
|
485
|
-
...(this.config.organization ? { organization: this.config.organization } : {}),
|
|
486
|
-
...(this.config.defaultHeaders ? { defaultHeaders: this.config.defaultHeaders } : {}),
|
|
487
|
-
});
|
|
488
|
-
return this.client;
|
|
489
|
-
}
|
|
490
|
-
async generate(options) {
|
|
491
|
-
const client = await this.getClient();
|
|
492
|
-
const format = options.format ?? 'mp3';
|
|
493
|
-
const params = {
|
|
494
|
-
model: this.model,
|
|
495
|
-
input: options.text,
|
|
496
|
-
voice: options.voice ?? 'alloy',
|
|
497
|
-
};
|
|
498
|
-
if (options.speed !== undefined)
|
|
499
|
-
params['speed'] = options.speed;
|
|
500
|
-
if (options.format)
|
|
501
|
-
params['response_format'] = options.format;
|
|
502
|
-
const response = await client.audio.speech.create(params);
|
|
503
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
504
|
-
return {
|
|
505
|
-
audio: Buffer.from(arrayBuffer),
|
|
506
|
-
format,
|
|
507
|
-
model: this.model,
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
// ─── STT Adapter ─────────────────────────────────────────
|
|
512
|
-
class OpenAISttAdapter {
|
|
513
|
-
config;
|
|
514
|
-
model;
|
|
515
|
-
client = null;
|
|
516
|
-
constructor(config, model) {
|
|
517
|
-
this.config = config;
|
|
518
|
-
this.model = model;
|
|
519
|
-
}
|
|
520
|
-
async getClient() {
|
|
521
|
-
if (this.client)
|
|
522
|
-
return this.client;
|
|
523
|
-
const sdk = await import(/* @vite-ignore */ 'openai');
|
|
524
|
-
const OpenAI = sdk.default ?? sdk.OpenAI;
|
|
525
|
-
this.client = new OpenAI({
|
|
526
|
-
apiKey: this.config.apiKey,
|
|
527
|
-
...(this.config.baseUrl ? { baseURL: this.config.baseUrl } : {}),
|
|
528
|
-
...(this.config.organization ? { organization: this.config.organization } : {}),
|
|
529
|
-
...(this.config.defaultHeaders ? { defaultHeaders: this.config.defaultHeaders } : {}),
|
|
530
|
-
});
|
|
531
|
-
return this.client;
|
|
532
|
-
}
|
|
533
|
-
async transcribe(options) {
|
|
534
|
-
const client = await this.getClient();
|
|
535
|
-
const file = new File([options.audio], 'audio.mp3', { type: 'audio/mpeg' });
|
|
536
|
-
const params = {
|
|
537
|
-
model: this.model,
|
|
538
|
-
file,
|
|
539
|
-
response_format: 'verbose_json',
|
|
540
|
-
};
|
|
541
|
-
if (options.language)
|
|
542
|
-
params['language'] = options.language;
|
|
543
|
-
if (options.prompt)
|
|
544
|
-
params['prompt'] = options.prompt;
|
|
545
|
-
const response = await client.audio.transcriptions.create(params);
|
|
546
|
-
return {
|
|
547
|
-
text: response.text,
|
|
548
|
-
language: response.language,
|
|
549
|
-
duration: response.duration,
|
|
550
|
-
model: this.model,
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
// ─── Files ──────────────────────────────────────────────
|
|
555
|
-
class OpenAIFileAdapter {
|
|
556
|
-
config;
|
|
557
|
-
client = null;
|
|
558
|
-
constructor(config) {
|
|
559
|
-
this.config = config;
|
|
560
|
-
}
|
|
561
|
-
async getClient() {
|
|
562
|
-
if (this.client)
|
|
563
|
-
return this.client;
|
|
564
|
-
const sdk = await import(/* @vite-ignore */ 'openai');
|
|
565
|
-
const OpenAI = sdk.default ?? sdk.OpenAI;
|
|
566
|
-
this.client = new OpenAI({
|
|
567
|
-
apiKey: this.config.apiKey,
|
|
568
|
-
...(this.config.baseUrl ? { baseURL: this.config.baseUrl } : {}),
|
|
569
|
-
...(this.config.organization ? { organization: this.config.organization } : {}),
|
|
570
|
-
...(this.config.defaultHeaders ? { defaultHeaders: this.config.defaultHeaders } : {}),
|
|
571
|
-
});
|
|
572
|
-
return this.client;
|
|
573
|
-
}
|
|
574
|
-
async upload(options) {
|
|
575
|
-
const client = await this.getClient();
|
|
576
|
-
const { createReadStream } = await import(/* @vite-ignore */ 'node:fs');
|
|
577
|
-
const file = createReadStream(options.filePath);
|
|
578
|
-
const response = await client.files.create({
|
|
579
|
-
file,
|
|
580
|
-
purpose: options.purpose ?? 'assistants',
|
|
581
|
-
});
|
|
582
|
-
return {
|
|
583
|
-
id: response.id,
|
|
584
|
-
filename: response.filename,
|
|
585
|
-
bytes: response.bytes,
|
|
586
|
-
purpose: response.purpose,
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
async list() {
|
|
590
|
-
const client = await this.getClient();
|
|
591
|
-
const response = await client.files.list();
|
|
592
|
-
const files = [];
|
|
593
|
-
for await (const f of response) {
|
|
594
|
-
files.push({
|
|
595
|
-
id: f.id,
|
|
596
|
-
filename: f.filename,
|
|
597
|
-
bytes: f.bytes,
|
|
598
|
-
purpose: f.purpose,
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
return { files };
|
|
602
|
-
}
|
|
603
|
-
async delete(fileId) {
|
|
604
|
-
const client = await this.getClient();
|
|
605
|
-
await client.files.del(fileId);
|
|
606
|
-
}
|
|
607
|
-
async retrieve(fileId) {
|
|
608
|
-
const client = await this.getClient();
|
|
609
|
-
const response = await client.files.content(fileId);
|
|
610
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
611
|
-
return { data: buffer, mimeType: 'application/octet-stream' };
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
// ─── OpenAI Vector Stores (#B8 Phase 1) ──────────────────
|
|
615
|
-
/**
|
|
616
|
-
* OpenAI hosted vector store adapter. Wraps `client.vectorStores.*` and
|
|
617
|
-
* `client.vectorStores.files.*` from the v4+ SDK. Lazy SDK load mirrors
|
|
618
|
-
* the rest of the OpenAI provider.
|
|
619
|
-
*
|
|
620
|
-
* `addFile` defaults to polling until the file is fully indexed
|
|
621
|
-
* (`status === 'completed'`). Pass `{ wait: false }` to fire-and-forget.
|
|
622
|
-
*
|
|
623
|
-
* Local file paths route through OpenAI's Files API first
|
|
624
|
-
* (`client.files.create({ purpose: 'assistants' })`); the resulting
|
|
625
|
-
* `file_id` then attaches to the vector store. Apps that already have
|
|
626
|
-
* a file id pass `{ fileId }` directly.
|
|
627
|
-
*/
|
|
628
|
-
class OpenAIVectorStoreAdapter {
|
|
629
|
-
config;
|
|
630
|
-
client = null;
|
|
631
|
-
constructor(config) {
|
|
632
|
-
this.config = config;
|
|
633
|
-
}
|
|
634
|
-
async getClient() {
|
|
635
|
-
if (this.client)
|
|
636
|
-
return this.client;
|
|
637
|
-
const sdk = await import(/* @vite-ignore */ 'openai');
|
|
638
|
-
const OpenAI = sdk.default ?? sdk.OpenAI;
|
|
639
|
-
this.client = new OpenAI({
|
|
640
|
-
apiKey: this.config.apiKey,
|
|
641
|
-
...(this.config.baseUrl ? { baseURL: this.config.baseUrl } : {}),
|
|
642
|
-
...(this.config.organization ? { organization: this.config.organization } : {}),
|
|
643
|
-
...(this.config.defaultHeaders ? { defaultHeaders: this.config.defaultHeaders } : {}),
|
|
644
|
-
});
|
|
645
|
-
return this.client;
|
|
646
|
-
}
|
|
647
|
-
async create(opts) {
|
|
648
|
-
const client = await this.getClient();
|
|
649
|
-
const params = { name: opts.name };
|
|
650
|
-
if (opts.metadata)
|
|
651
|
-
params['metadata'] = opts.metadata;
|
|
652
|
-
if (opts.expiresAfter)
|
|
653
|
-
params['expires_after'] = opts.expiresAfter;
|
|
654
|
-
const response = await client.vectorStores.create(params);
|
|
655
|
-
return fromOpenAIVectorStore(response);
|
|
656
|
-
}
|
|
657
|
-
async list(opts) {
|
|
658
|
-
const client = await this.getClient();
|
|
659
|
-
const params = {};
|
|
660
|
-
if (opts?.limit !== undefined)
|
|
661
|
-
params['limit'] = opts.limit;
|
|
662
|
-
if (opts?.after !== undefined)
|
|
663
|
-
params['after'] = opts.after;
|
|
664
|
-
if (opts?.before !== undefined)
|
|
665
|
-
params['before'] = opts.before;
|
|
666
|
-
const response = await client.vectorStores.list(params);
|
|
667
|
-
const data = (response.data ?? []);
|
|
668
|
-
return { stores: data.map(d => fromOpenAIVectorStore(d)) };
|
|
669
|
-
}
|
|
670
|
-
async get(id) {
|
|
671
|
-
const client = await this.getClient();
|
|
672
|
-
const response = await client.vectorStores.retrieve(id);
|
|
673
|
-
return fromOpenAIVectorStore(response);
|
|
674
|
-
}
|
|
675
|
-
async delete(id) {
|
|
676
|
-
const client = await this.getClient();
|
|
677
|
-
await client.vectorStores.del(id);
|
|
678
|
-
}
|
|
679
|
-
async addFile(storeId, opts) {
|
|
680
|
-
const client = await this.getClient();
|
|
681
|
-
// Step 1: resolve the file id. If the user passed an existing one we
|
|
682
|
-
// skip the upload; otherwise upload via the standard Files API and
|
|
683
|
-
// reuse the resulting id.
|
|
684
|
-
const fileId = opts.fileId ?? await this.uploadAndGetId(client, opts);
|
|
685
|
-
// Step 2: attach to the store. OpenAI splits attribute + chunking
|
|
686
|
-
// config from the file payload so we pass them as a sibling object.
|
|
687
|
-
const attachParams = { file_id: fileId };
|
|
688
|
-
if (opts.attributes)
|
|
689
|
-
attachParams['attributes'] = opts.attributes;
|
|
690
|
-
if (opts.chunkingStrategy)
|
|
691
|
-
attachParams['chunking_strategy'] = opts.chunkingStrategy;
|
|
692
|
-
const attached = await client.vectorStores.files.create(storeId, attachParams);
|
|
693
|
-
if (opts.wait === false) {
|
|
694
|
-
return fromOpenAIVectorStoreFile(attached, storeId);
|
|
695
|
-
}
|
|
696
|
-
// Step 3: poll until `completed` / `failed` / timeout. Default 2-min
|
|
697
|
-
// budget — enough for typical PDFs but small enough that runaway
|
|
698
|
-
// batch uploads surface a clear error fast.
|
|
699
|
-
const pollInterval = opts.pollInterval ?? 1000;
|
|
700
|
-
const pollTimeout = opts.pollTimeout ?? 120_000;
|
|
701
|
-
const deadline = Date.now() + pollTimeout;
|
|
702
|
-
let current = attached;
|
|
703
|
-
while (true) {
|
|
704
|
-
const info = fromOpenAIVectorStoreFile(current, storeId);
|
|
705
|
-
if (info.status === 'completed' || info.status === 'failed' || info.status === 'cancelled') {
|
|
706
|
-
return info;
|
|
707
|
-
}
|
|
708
|
-
if (Date.now() > deadline) {
|
|
709
|
-
throw new Error(`[Rudder AI] vector-store file ingestion timed out after ${pollTimeout}ms ` +
|
|
710
|
-
`(store=${storeId}, file=${fileId}, status=${info.status}). ` +
|
|
711
|
-
'Increase pollTimeout or set wait: false for fire-and-forget.');
|
|
712
|
-
}
|
|
713
|
-
await sleep(pollInterval);
|
|
714
|
-
current = await client.vectorStores.files.retrieve(storeId, fileId);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
async removeFile(storeId, fileId) {
|
|
718
|
-
const client = await this.getClient();
|
|
719
|
-
await client.vectorStores.files.del(storeId, fileId);
|
|
720
|
-
}
|
|
721
|
-
async listFiles(storeId, opts) {
|
|
722
|
-
const client = await this.getClient();
|
|
723
|
-
const params = {};
|
|
724
|
-
if (opts?.limit !== undefined)
|
|
725
|
-
params['limit'] = opts.limit;
|
|
726
|
-
if (opts?.after !== undefined)
|
|
727
|
-
params['after'] = opts.after;
|
|
728
|
-
if (opts?.before !== undefined)
|
|
729
|
-
params['before'] = opts.before;
|
|
730
|
-
const response = await client.vectorStores.files.list(storeId, params);
|
|
731
|
-
const data = (response.data ?? []);
|
|
732
|
-
return { files: data.map(d => fromOpenAIVectorStoreFile(d, storeId)) };
|
|
733
|
-
}
|
|
734
|
-
/** @internal — upload a local file via the Files API and return the
|
|
735
|
-
* provider's file id. Used when the user passes `filePath` or
|
|
736
|
-
* `fileBuffer` to `addFile` instead of an existing `fileId`. */
|
|
737
|
-
async uploadAndGetId(client, opts) {
|
|
738
|
-
if (opts.filePath) {
|
|
739
|
-
const { createReadStream } = await import(/* @vite-ignore */ 'node:fs');
|
|
740
|
-
const file = createReadStream(opts.filePath);
|
|
741
|
-
const uploaded = await client.files.create({ file, purpose: 'assistants' });
|
|
742
|
-
return uploaded.id;
|
|
743
|
-
}
|
|
744
|
-
if (opts.fileBuffer) {
|
|
745
|
-
const { toFile } = await import(/* @vite-ignore */ 'openai/uploads');
|
|
746
|
-
const file = await toFile(opts.fileBuffer.data, opts.fileBuffer.filename);
|
|
747
|
-
const uploaded = await client.files.create({ file, purpose: 'assistants' });
|
|
748
|
-
return uploaded.id;
|
|
749
|
-
}
|
|
750
|
-
throw new Error('[Rudder AI] addFile requires fileId, filePath, or fileBuffer. ' +
|
|
751
|
-
'Pass an existing OpenAI file id via { fileId } or a local source via { filePath }.');
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
function fromOpenAIVectorStore(raw) {
|
|
755
|
-
const r = raw;
|
|
756
|
-
const fileCount = r.file_counts?.total ??
|
|
757
|
-
(r.file_counts ? (r.file_counts.in_progress ?? 0) + (r.file_counts.completed ?? 0) + (r.file_counts.failed ?? 0) + (r.file_counts.cancelled ?? 0) : 0);
|
|
758
|
-
const result = {
|
|
759
|
-
id: r.id,
|
|
760
|
-
name: r.name,
|
|
761
|
-
createdAt: r.created_at,
|
|
762
|
-
fileCount,
|
|
763
|
-
};
|
|
764
|
-
if (r.usage_bytes !== undefined)
|
|
765
|
-
result.bytesUsed = r.usage_bytes;
|
|
766
|
-
if (r.metadata !== undefined)
|
|
767
|
-
result.metadata = r.metadata;
|
|
768
|
-
return result;
|
|
769
|
-
}
|
|
770
|
-
function fromOpenAIVectorStoreFile(raw, storeId) {
|
|
771
|
-
const r = raw;
|
|
772
|
-
const status = r.status === 'completed' || r.status === 'failed' || r.status === 'cancelled' || r.status === 'in_progress'
|
|
773
|
-
? r.status
|
|
774
|
-
: 'in_progress';
|
|
775
|
-
const result = {
|
|
776
|
-
id: r.id,
|
|
777
|
-
vectorStoreId: storeId,
|
|
778
|
-
status,
|
|
779
|
-
createdAt: r.created_at,
|
|
780
|
-
};
|
|
781
|
-
if (r.usage_bytes !== undefined)
|
|
782
|
-
result.bytes = r.usage_bytes;
|
|
783
|
-
if (r.attributes !== undefined)
|
|
784
|
-
result.attributes = r.attributes;
|
|
785
|
-
if (r.last_error?.message !== undefined)
|
|
786
|
-
result.lastError = r.last_error.message;
|
|
787
|
-
return result;
|
|
788
|
-
}
|
|
789
|
-
function sleep(ms) {
|
|
790
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
791
|
-
}
|
|
792
|
-
//# sourceMappingURL=openai.js.map
|