@mastra/core 0.20.0 → 0.20.1-alpha.2
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/CHANGELOG.md +46 -0
- package/dist/agent/agent.d.ts +375 -9
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/index.cjs +11 -11
- package/dist/agent/index.js +2 -2
- package/dist/agent/input-processor/index.cjs +6 -6
- package/dist/agent/input-processor/index.js +1 -1
- package/dist/agent/types.d.ts +66 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/workflows/prepare-stream/index.d.ts +19 -3
- package/dist/agent/workflows/prepare-stream/index.d.ts.map +1 -1
- package/dist/agent/workflows/prepare-stream/prepare-memory-step.d.ts +5 -1
- package/dist/agent/workflows/prepare-stream/prepare-memory-step.d.ts.map +1 -1
- package/dist/agent/workflows/prepare-stream/prepare-tools-step.d.ts +5 -1
- package/dist/agent/workflows/prepare-stream/prepare-tools-step.d.ts.map +1 -1
- package/dist/agent/workflows/prepare-stream/stream-step.d.ts +5 -1
- package/dist/agent/workflows/prepare-stream/stream-step.d.ts.map +1 -1
- package/dist/ai-tracing/index.cjs +32 -32
- package/dist/ai-tracing/index.js +1 -1
- package/dist/{chunk-3NNB72OL.cjs → chunk-33DTPWTJ.cjs} +7 -7
- package/dist/{chunk-3NNB72OL.cjs.map → chunk-33DTPWTJ.cjs.map} +1 -1
- package/dist/{chunk-CJDOU6WP.js → chunk-3KBXOXG6.js} +3 -3
- package/dist/{chunk-CJDOU6WP.js.map → chunk-3KBXOXG6.js.map} +1 -1
- package/dist/{chunk-ZNK5RN5D.cjs → chunk-3Z3DP6S2.cjs} +6 -6
- package/dist/{chunk-ZNK5RN5D.cjs.map → chunk-3Z3DP6S2.cjs.map} +1 -1
- package/dist/{chunk-KGBDRSMX.js → chunk-5I6DXBUR.js} +4 -4
- package/dist/{chunk-KGBDRSMX.js.map → chunk-5I6DXBUR.js.map} +1 -1
- package/dist/{chunk-7EUC32F3.cjs → chunk-6K7IMZVR.cjs} +617 -24
- package/dist/chunk-6K7IMZVR.cjs.map +1 -0
- package/dist/{chunk-FV4QVXO4.js → chunk-6OLRLZJ3.js} +37 -2
- package/dist/chunk-6OLRLZJ3.js.map +1 -0
- package/dist/{chunk-OXLN4CWA.js → chunk-B5GBHE4E.js} +3 -3
- package/dist/{chunk-OXLN4CWA.js.map → chunk-B5GBHE4E.js.map} +1 -1
- package/dist/{chunk-2SH5WPUA.cjs → chunk-BG5FC6ZZ.cjs} +407 -803
- package/dist/chunk-BG5FC6ZZ.cjs.map +1 -0
- package/dist/{chunk-COYTVUIL.js → chunk-EKFF7JLS.js} +610 -17
- package/dist/chunk-EKFF7JLS.js.map +1 -0
- package/dist/{chunk-BOJNXNRV.js → chunk-FJIABZVI.js} +3 -3
- package/dist/{chunk-BOJNXNRV.js.map → chunk-FJIABZVI.js.map} +1 -1
- package/dist/{chunk-RYFQKXXS.js → chunk-H4KO46HZ.js} +3 -3
- package/dist/{chunk-RYFQKXXS.js.map → chunk-H4KO46HZ.js.map} +1 -1
- package/dist/{chunk-T4H33PBR.cjs → chunk-IKLSJCMT.cjs} +360 -69
- package/dist/chunk-IKLSJCMT.cjs.map +1 -0
- package/dist/{chunk-K4AYIXVH.cjs → chunk-L5A4MRCK.cjs} +4 -4
- package/dist/{chunk-K4AYIXVH.cjs.map → chunk-L5A4MRCK.cjs.map} +1 -1
- package/dist/{chunk-DVHBWEYY.cjs → chunk-LCJHFYJS.cjs} +7 -7
- package/dist/{chunk-DVHBWEYY.cjs.map → chunk-LCJHFYJS.cjs.map} +1 -1
- package/dist/{chunk-I6TOPBP6.cjs → chunk-MHHMY2K4.cjs} +49 -18
- package/dist/chunk-MHHMY2K4.cjs.map +1 -0
- package/dist/{chunk-DQISKQDE.js → chunk-OBAFLVGD.js} +342 -51
- package/dist/chunk-OBAFLVGD.js.map +1 -0
- package/dist/{chunk-6R46VE63.js → chunk-OPHFW56S.js} +39 -8
- package/dist/chunk-OPHFW56S.js.map +1 -0
- package/dist/{chunk-LCJP7LWN.cjs → chunk-P4ZPZKZY.cjs} +6 -6
- package/dist/{chunk-LCJP7LWN.cjs.map → chunk-P4ZPZKZY.cjs.map} +1 -1
- package/dist/{chunk-QR5VZWWT.js → chunk-P7WEYMRS.js} +3 -3
- package/dist/{chunk-QR5VZWWT.js.map → chunk-P7WEYMRS.js.map} +1 -1
- package/dist/{chunk-YEEAHLAK.cjs → chunk-PWPESTZZ.cjs} +4 -4
- package/dist/{chunk-YEEAHLAK.cjs.map → chunk-PWPESTZZ.cjs.map} +1 -1
- package/dist/{chunk-7HUKQ6SZ.cjs → chunk-Q3S3BXHO.cjs} +4 -4
- package/dist/{chunk-7HUKQ6SZ.cjs.map → chunk-Q3S3BXHO.cjs.map} +1 -1
- package/dist/{chunk-WP2KQXPV.js → chunk-RMMGYPXG.js} +3 -3
- package/dist/{chunk-WP2KQXPV.js.map → chunk-RMMGYPXG.js.map} +1 -1
- package/dist/{chunk-A4RAEU6X.cjs → chunk-SE4PA467.cjs} +37 -2
- package/dist/chunk-SE4PA467.cjs.map +1 -0
- package/dist/{chunk-AND6J5LG.js → chunk-W2WXYTYI.js} +407 -803
- package/dist/chunk-W2WXYTYI.js.map +1 -0
- package/dist/{chunk-OKIHIKXV.cjs → chunk-WE6ILDQY.cjs} +4 -4
- package/dist/{chunk-OKIHIKXV.cjs.map → chunk-WE6ILDQY.cjs.map} +1 -1
- package/dist/{chunk-ZJ2UFCTS.js → chunk-Y2TIJVKC.js} +3 -3
- package/dist/{chunk-ZJ2UFCTS.js.map → chunk-Y2TIJVKC.js.map} +1 -1
- package/dist/index.cjs +52 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +11 -11
- package/dist/index.js.map +1 -1
- package/dist/integration/index.cjs +3 -3
- package/dist/integration/index.js +1 -1
- package/dist/llm/index.cjs +6 -6
- package/dist/llm/index.d.ts +1 -1
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +1 -1
- package/dist/llm/model/gateway-resolver.d.ts +7 -5
- package/dist/llm/model/gateway-resolver.d.ts.map +1 -1
- package/dist/llm/model/gateways/base.d.ts +8 -6
- package/dist/llm/model/gateways/base.d.ts.map +1 -1
- package/dist/llm/model/gateways/constants.d.ts +3 -0
- package/dist/llm/model/gateways/constants.d.ts.map +1 -0
- package/dist/llm/model/gateways/index.d.ts +5 -0
- package/dist/llm/model/gateways/index.d.ts.map +1 -1
- package/dist/llm/model/gateways/models-dev.d.ts +8 -2
- package/dist/llm/model/gateways/models-dev.d.ts.map +1 -1
- package/dist/llm/model/gateways/netlify.d.ts +11 -2
- package/dist/llm/model/gateways/netlify.d.ts.map +1 -1
- package/dist/llm/model/index.d.ts +1 -1
- package/dist/llm/model/index.d.ts.map +1 -1
- package/dist/llm/model/model.loop.d.ts +1 -1
- package/dist/llm/model/model.loop.d.ts.map +1 -1
- package/dist/llm/model/provider-registry.generated.d.ts +6 -7
- package/dist/llm/model/provider-registry.generated.d.ts.map +1 -1
- package/dist/llm/model/router.d.ts +23 -0
- package/dist/llm/model/router.d.ts.map +1 -0
- package/dist/loop/index.cjs +2 -2
- package/dist/loop/index.js +1 -1
- package/dist/loop/network/index.d.ts +5 -1
- package/dist/loop/network/index.d.ts.map +1 -1
- package/dist/loop/workflows/agentic-execution/index.d.ts +5 -1
- package/dist/loop/workflows/agentic-execution/index.d.ts.map +1 -1
- package/dist/loop/workflows/agentic-execution/llm-execution-step.d.ts +5 -1
- package/dist/loop/workflows/agentic-execution/llm-execution-step.d.ts.map +1 -1
- package/dist/loop/workflows/agentic-execution/llm-mapping-step.d.ts +5 -1
- package/dist/loop/workflows/agentic-execution/llm-mapping-step.d.ts.map +1 -1
- package/dist/loop/workflows/agentic-execution/tool-call-step.d.ts +5 -1
- package/dist/loop/workflows/agentic-execution/tool-call-step.d.ts.map +1 -1
- package/dist/loop/workflows/agentic-loop/index.d.ts +5 -1
- package/dist/loop/workflows/agentic-loop/index.d.ts.map +1 -1
- package/dist/mastra/index.cjs +2 -2
- package/dist/mastra/index.d.ts +695 -12
- package/dist/mastra/index.d.ts.map +1 -1
- package/dist/mastra/index.js +1 -1
- package/dist/memory/index.cjs +4 -4
- package/dist/memory/index.js +1 -1
- package/dist/processors/index.cjs +11 -11
- package/dist/processors/index.js +1 -1
- package/dist/relevance/index.cjs +4 -4
- package/dist/relevance/index.js +1 -1
- package/dist/scores/index.cjs +9 -9
- package/dist/scores/index.js +2 -2
- package/dist/scores/run-experiment/index.d.ts +2 -2
- package/dist/scores/run-experiment/index.d.ts.map +1 -1
- package/dist/scores/scoreTraces/index.cjs +8 -8
- package/dist/scores/scoreTraces/index.js +3 -3
- package/dist/scores/scoreTraces/scoreTracesWorkflow.d.ts +10 -2
- package/dist/scores/scoreTraces/scoreTracesWorkflow.d.ts.map +1 -1
- package/dist/storage/domains/operations/base.d.ts +6 -0
- package/dist/storage/domains/operations/base.d.ts.map +1 -1
- package/dist/storage/index.cjs +11 -3
- package/dist/storage/index.cjs.map +1 -1
- package/dist/storage/index.js +9 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/stream/MastraAgentNetworkStream.d.ts +5 -1
- package/dist/stream/MastraAgentNetworkStream.d.ts.map +1 -1
- package/dist/stream/MastraWorkflowStream.d.ts +3 -3
- package/dist/stream/MastraWorkflowStream.d.ts.map +1 -1
- package/dist/stream/index.cjs +4 -4
- package/dist/stream/index.js +1 -1
- package/dist/test-utils/llm-mock.cjs +2 -2
- package/dist/test-utils/llm-mock.js +1 -1
- package/dist/tools/index.cjs +4 -4
- package/dist/tools/index.js +1 -1
- package/dist/tools/is-vercel-tool.cjs +2 -2
- package/dist/tools/is-vercel-tool.js +1 -1
- package/dist/tools/tool.d.ts +163 -0
- package/dist/tools/tool.d.ts.map +1 -1
- package/dist/utils.cjs +17 -17
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1 -1
- package/dist/workflows/default.d.ts +13 -7
- package/dist/workflows/default.d.ts.map +1 -1
- package/dist/workflows/evented/index.cjs +10 -10
- package/dist/workflows/evented/index.js +1 -1
- package/dist/workflows/evented/step-executor.d.ts +7 -3
- package/dist/workflows/evented/step-executor.d.ts.map +1 -1
- package/dist/workflows/evented/workflow-event-processor/index.d.ts.map +1 -1
- package/dist/workflows/evented/workflow-event-processor/loop.d.ts.map +1 -1
- package/dist/workflows/evented/workflow-event-processor/parallel.d.ts.map +1 -1
- package/dist/workflows/evented/workflow-event-processor/utils.d.ts +1 -1
- package/dist/workflows/evented/workflow.d.ts +23 -19
- package/dist/workflows/evented/workflow.d.ts.map +1 -1
- package/dist/workflows/execution-engine.d.ts +5 -1
- package/dist/workflows/execution-engine.d.ts.map +1 -1
- package/dist/workflows/index.cjs +12 -12
- package/dist/workflows/index.js +1 -1
- package/dist/workflows/legacy/index.cjs +22 -22
- package/dist/workflows/legacy/index.js +1 -1
- package/dist/workflows/step.d.ts +11 -4
- package/dist/workflows/step.d.ts.map +1 -1
- package/dist/workflows/types.d.ts +33 -9
- package/dist/workflows/types.d.ts.map +1 -1
- package/dist/workflows/workflow.d.ts +85 -56
- package/dist/workflows/workflow.d.ts.map +1 -1
- package/dist/workflows/workflow.warning.d.ts +2 -2
- package/dist/workflows/workflow.warning.d.ts.map +1 -1
- package/package.json +9 -4
- package/dist/chunk-2SH5WPUA.cjs.map +0 -1
- package/dist/chunk-45CV4JYJ.cjs +0 -4
- package/dist/chunk-45CV4JYJ.cjs.map +0 -1
- package/dist/chunk-6R46VE63.js.map +0 -1
- package/dist/chunk-7EUC32F3.cjs.map +0 -1
- package/dist/chunk-A4RAEU6X.cjs.map +0 -1
- package/dist/chunk-AND6J5LG.js.map +0 -1
- package/dist/chunk-COYTVUIL.js.map +0 -1
- package/dist/chunk-DQISKQDE.js.map +0 -1
- package/dist/chunk-FV4QVXO4.js.map +0 -1
- package/dist/chunk-I6TOPBP6.cjs.map +0 -1
- package/dist/chunk-RFGQ3EQV.js +0 -3
- package/dist/chunk-RFGQ3EQV.js.map +0 -1
- package/dist/chunk-T4H33PBR.cjs.map +0 -1
- package/dist/llm/model/openai-compatible.d.ts +0 -37
- package/dist/llm/model/openai-compatible.d.ts.map +0 -1
|
@@ -2,6 +2,352 @@
|
|
|
2
2
|
|
|
3
3
|
var chunkUU5L5GDY_cjs = require('./chunk-UU5L5GDY.cjs');
|
|
4
4
|
var chunkWM4VQWOZ_cjs = require('./chunk-WM4VQWOZ.cjs');
|
|
5
|
+
var crypto = require('crypto');
|
|
6
|
+
var anthropicV5 = require('@ai-sdk/anthropic-v5');
|
|
7
|
+
var googleV5 = require('@ai-sdk/google-v5');
|
|
8
|
+
var openaiCompatibleV5 = require('@ai-sdk/openai-compatible-v5');
|
|
9
|
+
var openaiV5 = require('@ai-sdk/openai-v5');
|
|
10
|
+
var xaiV5 = require('@ai-sdk/xai-v5');
|
|
11
|
+
var aiSdkProviderV5 = require('@openrouter/ai-sdk-provider-v5');
|
|
12
|
+
|
|
13
|
+
// src/llm/model/gateway-resolver.ts
|
|
14
|
+
function parseModelRouterId(routerId, gatewayPrefix) {
|
|
15
|
+
if (gatewayPrefix && !routerId.startsWith(`${gatewayPrefix}/`)) {
|
|
16
|
+
throw new Error(`Expected ${gatewayPrefix}/ in model router ID ${routerId}`);
|
|
17
|
+
}
|
|
18
|
+
const idParts = routerId.split("/");
|
|
19
|
+
if (gatewayPrefix && idParts.length < 3) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Expected atleast 3 id parts ${gatewayPrefix}/provider/model, but only saw ${idParts.length} in ${routerId}`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
const providerId = idParts.at(gatewayPrefix ? 1 : 0);
|
|
25
|
+
const modelId = idParts.slice(gatewayPrefix ? 2 : 1).join(`/`);
|
|
26
|
+
if (!routerId.includes(`/`) || !providerId || !modelId) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Attempted to parse provider/model from ${routerId} but this ID doesn't appear to contain a provider`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
providerId,
|
|
33
|
+
modelId
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/llm/model/gateways/base.ts
|
|
38
|
+
var MastraModelGateway = class {
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/llm/model/gateways/constants.ts
|
|
42
|
+
var PROVIDERS_WITH_INSTALLED_PACKAGES = ["anthropic", "google", "openai", "openrouter", "xai"];
|
|
43
|
+
var EXCLUDED_PROVIDERS = ["github-copilot"];
|
|
44
|
+
|
|
45
|
+
// src/llm/model/gateways/models-dev.ts
|
|
46
|
+
var OPENAI_COMPATIBLE_OVERRIDES = {
|
|
47
|
+
cerebras: {
|
|
48
|
+
url: "https://api.cerebras.ai/v1/chat/completions"
|
|
49
|
+
},
|
|
50
|
+
xai: {
|
|
51
|
+
url: "https://api.x.ai/v1/chat/completions"
|
|
52
|
+
},
|
|
53
|
+
mistral: {
|
|
54
|
+
url: "https://api.mistral.ai/v1/chat/completions"
|
|
55
|
+
},
|
|
56
|
+
groq: {
|
|
57
|
+
url: "https://api.groq.com/openai/v1/chat/completions"
|
|
58
|
+
},
|
|
59
|
+
togetherai: {
|
|
60
|
+
url: "https://api.together.xyz/v1/chat/completions"
|
|
61
|
+
},
|
|
62
|
+
deepinfra: {
|
|
63
|
+
url: "https://api.deepinfra.com/v1/openai/chat/completions"
|
|
64
|
+
},
|
|
65
|
+
perplexity: {
|
|
66
|
+
url: "https://api.perplexity.ai/chat/completions"
|
|
67
|
+
},
|
|
68
|
+
vercel: {
|
|
69
|
+
url: "https://ai-gateway.vercel.sh/v1/chat/completions",
|
|
70
|
+
apiKeyEnvVar: "AI_GATEWAY_API_KEY"
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var ModelsDevGateway = class extends MastraModelGateway {
|
|
74
|
+
name = "models.dev";
|
|
75
|
+
prefix = void 0;
|
|
76
|
+
// No prefix for registry gateway
|
|
77
|
+
providerConfigs = {};
|
|
78
|
+
constructor(providerConfigs) {
|
|
79
|
+
super();
|
|
80
|
+
if (providerConfigs) this.providerConfigs = providerConfigs;
|
|
81
|
+
}
|
|
82
|
+
async fetchProviders() {
|
|
83
|
+
console.info("Fetching providers from models.dev API...");
|
|
84
|
+
const response = await fetch("https://models.dev/api.json");
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
throw new Error(`Failed to fetch from models.dev: ${response.statusText}`);
|
|
87
|
+
}
|
|
88
|
+
const data = await response.json();
|
|
89
|
+
const providerConfigs = {};
|
|
90
|
+
for (const [providerId, providerInfo] of Object.entries(data)) {
|
|
91
|
+
if (EXCLUDED_PROVIDERS.includes(providerId)) continue;
|
|
92
|
+
if (!providerInfo || typeof providerInfo !== "object" || !providerInfo.models) continue;
|
|
93
|
+
const normalizedId = providerId;
|
|
94
|
+
const isOpenAICompatible = providerInfo.npm === "@ai-sdk/openai-compatible" || providerInfo.npm === "@ai-sdk/gateway" || // Vercel AI Gateway is OpenAI-compatible
|
|
95
|
+
normalizedId in OPENAI_COMPATIBLE_OVERRIDES;
|
|
96
|
+
const hasInstalledPackage = PROVIDERS_WITH_INSTALLED_PACKAGES.includes(providerId);
|
|
97
|
+
const hasApiAndEnv = providerInfo.api && providerInfo.env && providerInfo.env.length > 0;
|
|
98
|
+
if (isOpenAICompatible || hasInstalledPackage || hasApiAndEnv) {
|
|
99
|
+
const modelIds = Object.keys(providerInfo.models).sort();
|
|
100
|
+
let url = providerInfo.api || OPENAI_COMPATIBLE_OVERRIDES[normalizedId]?.url;
|
|
101
|
+
if (!hasInstalledPackage && url && !url.includes("/chat/completions") && !url.includes("/messages")) {
|
|
102
|
+
url = url.replace(/\/$/, "") + "/chat/completions";
|
|
103
|
+
}
|
|
104
|
+
if (!hasInstalledPackage && !url) {
|
|
105
|
+
console.info(`Skipping ${normalizedId}: No API URL available`);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const apiKeyEnvVar = providerInfo.env?.[0] || `${normalizedId.toUpperCase().replace(/-/g, "_")}_API_KEY`;
|
|
109
|
+
const apiKeyHeader = !hasInstalledPackage ? OPENAI_COMPATIBLE_OVERRIDES[normalizedId]?.apiKeyHeader || "Authorization" : void 0;
|
|
110
|
+
providerConfigs[normalizedId] = {
|
|
111
|
+
url,
|
|
112
|
+
apiKeyEnvVar,
|
|
113
|
+
apiKeyHeader,
|
|
114
|
+
name: providerInfo.name || providerId.charAt(0).toUpperCase() + providerId.slice(1),
|
|
115
|
+
models: modelIds,
|
|
116
|
+
docUrl: providerInfo.doc,
|
|
117
|
+
// Include documentation URL if available
|
|
118
|
+
gateway: `models.dev`
|
|
119
|
+
};
|
|
120
|
+
} else {
|
|
121
|
+
console.info(`Skipped provider ${providerInfo.name}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
this.providerConfigs = providerConfigs;
|
|
125
|
+
console.info(`Found ${Object.keys(providerConfigs).length} OpenAI-compatible providers`);
|
|
126
|
+
console.info("Providers:", Object.keys(providerConfigs).sort());
|
|
127
|
+
return providerConfigs;
|
|
128
|
+
}
|
|
129
|
+
buildUrl(routerId, envVars) {
|
|
130
|
+
const { providerId } = parseModelRouterId(routerId);
|
|
131
|
+
const config = this.providerConfigs[providerId];
|
|
132
|
+
if (!config?.url) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const baseUrlEnvVar = `${providerId.toUpperCase().replace(/-/g, "_")}_BASE_URL`;
|
|
136
|
+
const customBaseUrl = envVars?.[baseUrlEnvVar] || process.env[baseUrlEnvVar];
|
|
137
|
+
return customBaseUrl || config.url;
|
|
138
|
+
}
|
|
139
|
+
getApiKey(modelId) {
|
|
140
|
+
const [provider, model] = modelId.split("/");
|
|
141
|
+
if (!provider || !model) {
|
|
142
|
+
throw new Error(`Could not identify provider from model id ${modelId}`);
|
|
143
|
+
}
|
|
144
|
+
const config = this.providerConfigs[provider];
|
|
145
|
+
if (!config) {
|
|
146
|
+
throw new Error(`Could not find config for provider ${provider} with model id ${modelId}`);
|
|
147
|
+
}
|
|
148
|
+
const apiKey = typeof config.apiKeyEnvVar === `string` ? process.env[config.apiKeyEnvVar] : void 0;
|
|
149
|
+
if (!apiKey) {
|
|
150
|
+
throw new Error(`Could not find API key process.env.${config.apiKeyEnvVar} for model id ${modelId}`);
|
|
151
|
+
}
|
|
152
|
+
return Promise.resolve(apiKey);
|
|
153
|
+
}
|
|
154
|
+
async resolveLanguageModel({
|
|
155
|
+
modelId,
|
|
156
|
+
providerId,
|
|
157
|
+
apiKey
|
|
158
|
+
}) {
|
|
159
|
+
const baseURL = this.buildUrl(`${providerId}/${modelId}`);
|
|
160
|
+
switch (providerId) {
|
|
161
|
+
case "openai":
|
|
162
|
+
return openaiV5.createOpenAI({ apiKey }).responses(modelId);
|
|
163
|
+
case "gemini":
|
|
164
|
+
case "google":
|
|
165
|
+
return googleV5.createGoogleGenerativeAI({
|
|
166
|
+
apiKey
|
|
167
|
+
}).chat(modelId);
|
|
168
|
+
case "anthropic":
|
|
169
|
+
return anthropicV5.createAnthropic({ apiKey })(modelId);
|
|
170
|
+
case "openrouter":
|
|
171
|
+
return aiSdkProviderV5.createOpenRouter({ apiKey })(modelId);
|
|
172
|
+
case "xai":
|
|
173
|
+
return xaiV5.createXai({
|
|
174
|
+
apiKey
|
|
175
|
+
})(modelId);
|
|
176
|
+
default:
|
|
177
|
+
if (!baseURL) throw new Error(`No API URL found for ${providerId}/${modelId}`);
|
|
178
|
+
return openaiCompatibleV5.createOpenAICompatible({ name: providerId, apiKey, baseURL }).chatModel(modelId);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var NetlifyGateway = class extends MastraModelGateway {
|
|
183
|
+
name = "netlify";
|
|
184
|
+
prefix = "netlify";
|
|
185
|
+
// All providers will be prefixed with "netlify/"
|
|
186
|
+
tokenCache = new chunkUU5L5GDY_cjs.InMemoryServerCache();
|
|
187
|
+
async fetchProviders() {
|
|
188
|
+
console.info("Fetching providers from Netlify AI Gateway...");
|
|
189
|
+
const response = await fetch("https://api.netlify.com/api/v1/ai-gateway/providers");
|
|
190
|
+
if (!response.ok) {
|
|
191
|
+
throw new Error(`Failed to fetch from Netlify: ${response.statusText}`);
|
|
192
|
+
}
|
|
193
|
+
const data = await response.json();
|
|
194
|
+
const netlify = {
|
|
195
|
+
apiKeyEnvVar: ["NETLIFY_TOKEN", "NETLIFY_SITE_ID"],
|
|
196
|
+
apiKeyHeader: "Authorization",
|
|
197
|
+
// Netlify uses standard Bearer auth
|
|
198
|
+
name: `Netlify`,
|
|
199
|
+
gateway: `netlify`,
|
|
200
|
+
models: [],
|
|
201
|
+
docUrl: "https://docs.netlify.com/build/ai-gateway/overview/"
|
|
202
|
+
};
|
|
203
|
+
for (const [providerId, provider] of Object.entries(data.providers)) {
|
|
204
|
+
for (const model of provider.models) {
|
|
205
|
+
netlify.models.push(`${providerId}/${model}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
console.info(`Found ${Object.keys(data.providers).length} models via Netlify Gateway`);
|
|
209
|
+
return { netlify };
|
|
210
|
+
}
|
|
211
|
+
async buildUrl(routerId, envVars) {
|
|
212
|
+
const siteId = envVars?.["NETLIFY_SITE_ID"] || process.env["NETLIFY_SITE_ID"];
|
|
213
|
+
const netlifyToken = envVars?.["NETLIFY_TOKEN"] || process.env["NETLIFY_TOKEN"];
|
|
214
|
+
if (!netlifyToken) {
|
|
215
|
+
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
216
|
+
id: "NETLIFY_GATEWAY_NO_TOKEN",
|
|
217
|
+
domain: "LLM",
|
|
218
|
+
category: "UNKNOWN",
|
|
219
|
+
text: `Missing NETLIFY_TOKEN environment variable required for model: ${routerId}`
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (!siteId) {
|
|
223
|
+
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
224
|
+
id: "NETLIFY_GATEWAY_NO_SITE_ID",
|
|
225
|
+
domain: "LLM",
|
|
226
|
+
category: "UNKNOWN",
|
|
227
|
+
text: `Missing NETLIFY_SITE_ID environment variable required for model: ${routerId}`
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
const tokenData = await this.getOrFetchToken(siteId, netlifyToken);
|
|
232
|
+
return tokenData.url.endsWith(`/`) ? tokenData.url.substring(0, tokenData.url.length - 1) : tokenData.url;
|
|
233
|
+
} catch (error) {
|
|
234
|
+
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
235
|
+
id: "NETLIFY_GATEWAY_TOKEN_ERROR",
|
|
236
|
+
domain: "LLM",
|
|
237
|
+
category: "UNKNOWN",
|
|
238
|
+
text: `Failed to get Netlify AI Gateway token for model ${routerId}: ${error instanceof Error ? error.message : String(error)}`
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get cached token or fetch a new site-specific AI Gateway token from Netlify
|
|
244
|
+
*/
|
|
245
|
+
async getOrFetchToken(siteId, netlifyToken) {
|
|
246
|
+
const cacheKey = `netlify-token:${siteId}:${netlifyToken}`;
|
|
247
|
+
const cached = await this.tokenCache.get(cacheKey);
|
|
248
|
+
if (cached && cached.expiresAt > Date.now() / 1e3 + 60) {
|
|
249
|
+
return { token: cached.token, url: cached.url };
|
|
250
|
+
}
|
|
251
|
+
const response = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}/ai-gateway/token`, {
|
|
252
|
+
method: "GET",
|
|
253
|
+
headers: {
|
|
254
|
+
Authorization: `Bearer ${netlifyToken}`
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
if (!response.ok) {
|
|
258
|
+
const error = await response.text();
|
|
259
|
+
throw new Error(`Failed to get Netlify AI Gateway token: ${response.status} ${error}`);
|
|
260
|
+
}
|
|
261
|
+
const tokenResponse = await response.json();
|
|
262
|
+
await this.tokenCache.set(cacheKey, {
|
|
263
|
+
token: tokenResponse.token,
|
|
264
|
+
url: tokenResponse.url,
|
|
265
|
+
expiresAt: tokenResponse.expires_at
|
|
266
|
+
});
|
|
267
|
+
return { token: tokenResponse.token, url: tokenResponse.url };
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Get cached token or fetch a new site-specific AI Gateway token from Netlify
|
|
271
|
+
*/
|
|
272
|
+
async getApiKey(modelId) {
|
|
273
|
+
const siteId = process.env["NETLIFY_SITE_ID"];
|
|
274
|
+
const netlifyToken = process.env["NETLIFY_TOKEN"];
|
|
275
|
+
if (!netlifyToken) {
|
|
276
|
+
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
277
|
+
id: "NETLIFY_GATEWAY_NO_TOKEN",
|
|
278
|
+
domain: "LLM",
|
|
279
|
+
category: "UNKNOWN",
|
|
280
|
+
text: `Missing NETLIFY_TOKEN environment variable required for model: ${modelId}`
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
if (!siteId) {
|
|
284
|
+
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
285
|
+
id: "NETLIFY_GATEWAY_NO_SITE_ID",
|
|
286
|
+
domain: "LLM",
|
|
287
|
+
category: "UNKNOWN",
|
|
288
|
+
text: `Missing NETLIFY_SITE_ID environment variable required for model: ${modelId}`
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
return (await this.getOrFetchToken(siteId, netlifyToken)).token;
|
|
293
|
+
} catch (error) {
|
|
294
|
+
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
295
|
+
id: "NETLIFY_GATEWAY_TOKEN_ERROR",
|
|
296
|
+
domain: "LLM",
|
|
297
|
+
category: "UNKNOWN",
|
|
298
|
+
text: `Failed to get Netlify AI Gateway token for model ${modelId}: ${error instanceof Error ? error.message : String(error)}`
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async resolveLanguageModel({
|
|
303
|
+
modelId,
|
|
304
|
+
providerId,
|
|
305
|
+
apiKey
|
|
306
|
+
}) {
|
|
307
|
+
const baseURL = await this.buildUrl(`${providerId}/${modelId}`);
|
|
308
|
+
switch (providerId) {
|
|
309
|
+
case "openai":
|
|
310
|
+
return openaiV5.createOpenAI({ apiKey, baseURL }).responses(modelId);
|
|
311
|
+
case "gemini":
|
|
312
|
+
return googleV5.createGoogleGenerativeAI({
|
|
313
|
+
baseURL: `${baseURL}/v1beta/`,
|
|
314
|
+
apiKey,
|
|
315
|
+
headers: {
|
|
316
|
+
"user-agent": "google-genai-sdk/"
|
|
317
|
+
}
|
|
318
|
+
}).chat(modelId);
|
|
319
|
+
case "anthropic":
|
|
320
|
+
return anthropicV5.createAnthropic({
|
|
321
|
+
apiKey,
|
|
322
|
+
baseURL: `${baseURL}/v1/`,
|
|
323
|
+
headers: {
|
|
324
|
+
"anthropic-version": "2023-06-01",
|
|
325
|
+
"user-agent": "anthropic/"
|
|
326
|
+
}
|
|
327
|
+
})(modelId);
|
|
328
|
+
default:
|
|
329
|
+
return openaiCompatibleV5.createOpenAICompatible({ name: providerId, apiKey, baseURL }).chatModel(modelId);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// src/llm/model/gateways/index.ts
|
|
335
|
+
function findGatewayForModel(gatewayId, gateways2) {
|
|
336
|
+
const prefixedGateway = gateways2.find((g) => g.prefix && gatewayId.startsWith(`${g.prefix}/`));
|
|
337
|
+
if (prefixedGateway) {
|
|
338
|
+
return prefixedGateway;
|
|
339
|
+
}
|
|
340
|
+
const unprefixedGateways = gateways2.filter((g) => !g.prefix);
|
|
341
|
+
for (const gateway of unprefixedGateways) {
|
|
342
|
+
return gateway;
|
|
343
|
+
}
|
|
344
|
+
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
345
|
+
id: "MODEL_ROUTER_NO_GATEWAY_FOUND",
|
|
346
|
+
category: "USER",
|
|
347
|
+
domain: "MODEL_ROUTER",
|
|
348
|
+
text: `No Mastra model router gateway found for model id ${gatewayId}`
|
|
349
|
+
});
|
|
350
|
+
}
|
|
5
351
|
|
|
6
352
|
// src/llm/model/provider-registry.generated.ts
|
|
7
353
|
var PROVIDER_REGISTRY = {
|
|
@@ -53,7 +399,6 @@ var PROVIDER_REGISTRY = {
|
|
|
53
399
|
xai: {
|
|
54
400
|
url: "https://api.x.ai/v1/chat/completions",
|
|
55
401
|
apiKeyEnvVar: "XAI_API_KEY",
|
|
56
|
-
apiKeyHeader: "Authorization",
|
|
57
402
|
name: "xAI",
|
|
58
403
|
models: [
|
|
59
404
|
"grok-2",
|
|
@@ -139,33 +484,6 @@ var PROVIDER_REGISTRY = {
|
|
|
139
484
|
docUrl: "https://console.groq.com/docs/models",
|
|
140
485
|
gateway: "models.dev"
|
|
141
486
|
},
|
|
142
|
-
"github-copilot": {
|
|
143
|
-
url: "https://api.githubcopilot.com/chat/completions",
|
|
144
|
-
apiKeyEnvVar: "GITHUB_TOKEN",
|
|
145
|
-
apiKeyHeader: "Authorization",
|
|
146
|
-
name: "GitHub Copilot",
|
|
147
|
-
models: [
|
|
148
|
-
"claude-3.5-sonnet",
|
|
149
|
-
"claude-3.7-sonnet",
|
|
150
|
-
"claude-3.7-sonnet-thought",
|
|
151
|
-
"claude-opus-4",
|
|
152
|
-
"claude-opus-41",
|
|
153
|
-
"claude-sonnet-4",
|
|
154
|
-
"claude-sonnet-4.5",
|
|
155
|
-
"gemini-2.0-flash-001",
|
|
156
|
-
"gemini-2.5-pro",
|
|
157
|
-
"gpt-4.1",
|
|
158
|
-
"gpt-4o",
|
|
159
|
-
"gpt-5",
|
|
160
|
-
"gpt-5-mini",
|
|
161
|
-
"grok-code-fast-1",
|
|
162
|
-
"o3",
|
|
163
|
-
"o3-mini",
|
|
164
|
-
"o4-mini"
|
|
165
|
-
],
|
|
166
|
-
docUrl: "https://docs.github.com/en/copilot",
|
|
167
|
-
gateway: "models.dev"
|
|
168
|
-
},
|
|
169
487
|
mistral: {
|
|
170
488
|
url: "https://api.mistral.ai/v1/chat/completions",
|
|
171
489
|
apiKeyEnvVar: "MISTRAL_API_KEY",
|
|
@@ -243,6 +561,7 @@ var PROVIDER_REGISTRY = {
|
|
|
243
561
|
"openai/gpt-4o",
|
|
244
562
|
"openai/gpt-4o-mini",
|
|
245
563
|
"openai/gpt-5",
|
|
564
|
+
"openai/gpt-5-codex",
|
|
246
565
|
"openai/gpt-5-mini",
|
|
247
566
|
"openai/gpt-5-nano",
|
|
248
567
|
"openai/gpt-oss-120b",
|
|
@@ -484,20 +803,22 @@ var PROVIDER_REGISTRY = {
|
|
|
484
803
|
url: "https://opencode.ai/zen/v1/chat/completions",
|
|
485
804
|
apiKeyEnvVar: "OPENCODE_API_KEY",
|
|
486
805
|
apiKeyHeader: "Authorization",
|
|
487
|
-
name: "
|
|
806
|
+
name: "OpenCode Zen",
|
|
488
807
|
models: [
|
|
489
808
|
"claude-3-5-haiku",
|
|
490
809
|
"claude-opus-4-1",
|
|
491
810
|
"claude-sonnet-4",
|
|
492
811
|
"claude-sonnet-4-5",
|
|
493
812
|
"code-supernova",
|
|
813
|
+
"glm-4.6",
|
|
494
814
|
"gpt-5",
|
|
815
|
+
"gpt-5-codex",
|
|
495
816
|
"grok-code",
|
|
496
817
|
"kimi-k2",
|
|
497
818
|
"qwen3-coder",
|
|
498
819
|
"qwen3-max"
|
|
499
820
|
],
|
|
500
|
-
docUrl: "https://opencode.ai/docs",
|
|
821
|
+
docUrl: "https://opencode.ai/docs/zen",
|
|
501
822
|
gateway: "models.dev"
|
|
502
823
|
},
|
|
503
824
|
fastrouter: {
|
|
@@ -525,9 +846,7 @@ var PROVIDER_REGISTRY = {
|
|
|
525
846
|
gateway: "models.dev"
|
|
526
847
|
},
|
|
527
848
|
google: {
|
|
528
|
-
url: "https://generativelanguage.googleapis.com/v1beta/chat/completions",
|
|
529
849
|
apiKeyEnvVar: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
530
|
-
apiKeyHeader: "Authorization",
|
|
531
850
|
name: "Google",
|
|
532
851
|
models: [
|
|
533
852
|
"gemini-1.5-flash",
|
|
@@ -582,11 +901,10 @@ var PROVIDER_REGISTRY = {
|
|
|
582
901
|
gateway: "models.dev"
|
|
583
902
|
},
|
|
584
903
|
openai: {
|
|
585
|
-
url: "https://api.openai.com/v1/chat/completions",
|
|
586
904
|
apiKeyEnvVar: "OPENAI_API_KEY",
|
|
587
|
-
apiKeyHeader: "Authorization",
|
|
588
905
|
name: "OpenAI",
|
|
589
906
|
models: [
|
|
907
|
+
"codex-mini-latest",
|
|
590
908
|
"gpt-3.5-turbo",
|
|
591
909
|
"gpt-4",
|
|
592
910
|
"gpt-4-turbo",
|
|
@@ -600,6 +918,7 @@ var PROVIDER_REGISTRY = {
|
|
|
600
918
|
"gpt-4o-mini",
|
|
601
919
|
"gpt-5",
|
|
602
920
|
"gpt-5-chat-latest",
|
|
921
|
+
"gpt-5-codex",
|
|
603
922
|
"gpt-5-mini",
|
|
604
923
|
"gpt-5-nano",
|
|
605
924
|
"o1",
|
|
@@ -635,9 +954,8 @@ var PROVIDER_REGISTRY = {
|
|
|
635
954
|
gateway: "models.dev"
|
|
636
955
|
},
|
|
637
956
|
openrouter: {
|
|
638
|
-
url: "https://openrouter.ai/api/v1
|
|
957
|
+
url: "https://openrouter.ai/api/v1",
|
|
639
958
|
apiKeyEnvVar: "OPENROUTER_API_KEY",
|
|
640
|
-
apiKeyHeader: "Authorization",
|
|
641
959
|
name: "OpenRouter",
|
|
642
960
|
models: [
|
|
643
961
|
"anthropic/claude-3.5-haiku",
|
|
@@ -697,6 +1015,7 @@ var PROVIDER_REGISTRY = {
|
|
|
697
1015
|
"openai/gpt-4o-mini",
|
|
698
1016
|
"openai/gpt-5",
|
|
699
1017
|
"openai/gpt-5-chat",
|
|
1018
|
+
"openai/gpt-5-codex",
|
|
700
1019
|
"openai/gpt-5-mini",
|
|
701
1020
|
"openai/gpt-5-nano",
|
|
702
1021
|
"openai/gpt-oss-120b",
|
|
@@ -771,7 +1090,8 @@ var PROVIDER_REGISTRY = {
|
|
|
771
1090
|
"hf:moonshotai/Kimi-K2-Instruct",
|
|
772
1091
|
"hf:moonshotai/Kimi-K2-Instruct-0905",
|
|
773
1092
|
"hf:openai/gpt-oss-120b",
|
|
774
|
-
"hf:zai-org/GLM-4.5"
|
|
1093
|
+
"hf:zai-org/GLM-4.5",
|
|
1094
|
+
"hf:zai-org/GLM-4.6"
|
|
775
1095
|
],
|
|
776
1096
|
docUrl: "https://synthetic.new/pricing",
|
|
777
1097
|
gateway: "models.dev"
|
|
@@ -888,9 +1208,7 @@ var PROVIDER_REGISTRY = {
|
|
|
888
1208
|
gateway: "models.dev"
|
|
889
1209
|
},
|
|
890
1210
|
anthropic: {
|
|
891
|
-
url: "https://api.anthropic.com/v1/chat/completions",
|
|
892
1211
|
apiKeyEnvVar: "ANTHROPIC_API_KEY",
|
|
893
|
-
apiKeyHeader: "x-api-key",
|
|
894
1212
|
name: "Anthropic",
|
|
895
1213
|
models: [
|
|
896
1214
|
"claude-3-5-haiku-20241022",
|
|
@@ -977,38 +1295,39 @@ var PROVIDER_REGISTRY = {
|
|
|
977
1295
|
name: "Netlify",
|
|
978
1296
|
gateway: "netlify",
|
|
979
1297
|
models: [
|
|
980
|
-
"anthropic/claude-3-5-haiku-20241022",
|
|
981
1298
|
"anthropic/claude-opus-4-20250514",
|
|
1299
|
+
"anthropic/claude-sonnet-4-5-20250929",
|
|
982
1300
|
"anthropic/claude-sonnet-4-20250514",
|
|
983
1301
|
"anthropic/claude-3-7-sonnet-20250219",
|
|
1302
|
+
"anthropic/claude-3-7-sonnet-latest",
|
|
1303
|
+
"anthropic/claude-3-5-haiku-20241022",
|
|
984
1304
|
"anthropic/claude-3-5-haiku-latest",
|
|
985
1305
|
"anthropic/claude-3-haiku-20240307",
|
|
986
1306
|
"anthropic/claude-opus-4-1-20250805",
|
|
987
|
-
"anthropic/claude-sonnet-4-5-20250929",
|
|
988
|
-
"anthropic/claude-3-7-sonnet-latest",
|
|
989
1307
|
"gemini/gemini-2.5-pro",
|
|
990
|
-
"gemini/gemini-2.5-flash",
|
|
991
|
-
"gemini/gemini-flash-lite-latest",
|
|
992
|
-
"gemini/gemini-2.0-flash-lite",
|
|
993
|
-
"gemini/gemini-2.5-flash-image-preview",
|
|
994
1308
|
"gemini/gemini-flash-latest",
|
|
995
|
-
"gemini/gemini-2.5-flash
|
|
1309
|
+
"gemini/gemini-2.5-flash",
|
|
996
1310
|
"gemini/gemini-2.5-flash-lite-preview-09-2025",
|
|
997
1311
|
"gemini/gemini-2.5-flash-lite",
|
|
1312
|
+
"gemini/gemini-2.5-flash-preview-09-2025",
|
|
1313
|
+
"gemini/gemini-flash-lite-latest",
|
|
998
1314
|
"gemini/gemini-2.0-flash",
|
|
1315
|
+
"gemini/gemini-2.0-flash-lite",
|
|
1316
|
+
"gemini/gemini-2.5-flash-image-preview",
|
|
1317
|
+
"openai/gpt-4.1-mini",
|
|
1318
|
+
"openai/gpt-4.1-nano",
|
|
1319
|
+
"openai/gpt-4o",
|
|
1320
|
+
"openai/gpt-5-pro",
|
|
1321
|
+
"openai/gpt-4o-mini",
|
|
999
1322
|
"openai/o4-mini",
|
|
1000
1323
|
"openai/o3",
|
|
1001
1324
|
"openai/o3-mini",
|
|
1002
1325
|
"openai/codex-mini-latest",
|
|
1326
|
+
"openai/gpt-5",
|
|
1003
1327
|
"openai/gpt-5-codex",
|
|
1004
1328
|
"openai/gpt-5-mini",
|
|
1005
1329
|
"openai/gpt-5-nano",
|
|
1006
|
-
"openai/gpt-4.1"
|
|
1007
|
-
"openai/gpt-4.1-mini",
|
|
1008
|
-
"openai/gpt-4.1-nano",
|
|
1009
|
-
"openai/gpt-4o",
|
|
1010
|
-
"openai/gpt-4o-mini",
|
|
1011
|
-
"openai/gpt-5"
|
|
1330
|
+
"openai/gpt-4.1"
|
|
1012
1331
|
],
|
|
1013
1332
|
docUrl: "https://docs.netlify.com/build/ai-gateway/overview/"
|
|
1014
1333
|
}
|
|
@@ -1034,331 +1353,12 @@ function parseModelString(modelString) {
|
|
|
1034
1353
|
};
|
|
1035
1354
|
}
|
|
1036
1355
|
|
|
1037
|
-
// src/llm/model/
|
|
1038
|
-
var MastraModelGateway = class {
|
|
1039
|
-
};
|
|
1040
|
-
|
|
1041
|
-
// src/llm/model/gateways/models-dev.ts
|
|
1042
|
-
var OPENAI_COMPATIBLE_OVERRIDES = {
|
|
1043
|
-
openai: {
|
|
1044
|
-
url: "https://api.openai.com/v1/chat/completions"
|
|
1045
|
-
},
|
|
1046
|
-
anthropic: {
|
|
1047
|
-
url: "https://api.anthropic.com/v1/chat/completions",
|
|
1048
|
-
apiKeyHeader: "x-api-key"
|
|
1049
|
-
},
|
|
1050
|
-
cerebras: {
|
|
1051
|
-
url: "https://api.cerebras.ai/v1/chat/completions"
|
|
1052
|
-
},
|
|
1053
|
-
xai: {
|
|
1054
|
-
url: "https://api.x.ai/v1/chat/completions"
|
|
1055
|
-
},
|
|
1056
|
-
mistral: {
|
|
1057
|
-
url: "https://api.mistral.ai/v1/chat/completions"
|
|
1058
|
-
},
|
|
1059
|
-
google: {
|
|
1060
|
-
url: "https://generativelanguage.googleapis.com/v1beta/chat/completions"
|
|
1061
|
-
},
|
|
1062
|
-
groq: {
|
|
1063
|
-
url: "https://api.groq.com/openai/v1/chat/completions"
|
|
1064
|
-
},
|
|
1065
|
-
togetherai: {
|
|
1066
|
-
url: "https://api.together.xyz/v1/chat/completions"
|
|
1067
|
-
},
|
|
1068
|
-
deepinfra: {
|
|
1069
|
-
url: "https://api.deepinfra.com/v1/openai/chat/completions"
|
|
1070
|
-
},
|
|
1071
|
-
perplexity: {
|
|
1072
|
-
url: "https://api.perplexity.ai/chat/completions"
|
|
1073
|
-
},
|
|
1074
|
-
vercel: {
|
|
1075
|
-
url: "https://ai-gateway.vercel.sh/v1/chat/completions",
|
|
1076
|
-
apiKeyEnvVar: "AI_GATEWAY_API_KEY"
|
|
1077
|
-
}
|
|
1078
|
-
};
|
|
1079
|
-
var ModelsDevGateway = class extends MastraModelGateway {
|
|
1080
|
-
name = "models.dev";
|
|
1081
|
-
prefix = void 0;
|
|
1082
|
-
// No prefix for registry gateway
|
|
1083
|
-
providerConfigs = {};
|
|
1084
|
-
constructor(providerConfigs) {
|
|
1085
|
-
super();
|
|
1086
|
-
if (providerConfigs) this.providerConfigs = providerConfigs;
|
|
1087
|
-
}
|
|
1088
|
-
async fetchProviders() {
|
|
1089
|
-
console.info("Fetching providers from models.dev API...");
|
|
1090
|
-
const response = await fetch("https://models.dev/api.json");
|
|
1091
|
-
if (!response.ok) {
|
|
1092
|
-
throw new Error(`Failed to fetch from models.dev: ${response.statusText}`);
|
|
1093
|
-
}
|
|
1094
|
-
const data = await response.json();
|
|
1095
|
-
const providerConfigs = {};
|
|
1096
|
-
for (const [providerId, providerInfo] of Object.entries(data)) {
|
|
1097
|
-
if (!providerInfo || typeof providerInfo !== "object" || !providerInfo.models) continue;
|
|
1098
|
-
const normalizedId = providerId;
|
|
1099
|
-
const isOpenAICompatible = providerInfo.npm === "@ai-sdk/openai-compatible" || providerInfo.npm === "@ai-sdk/gateway" || // Vercel AI Gateway is OpenAI-compatible
|
|
1100
|
-
normalizedId in OPENAI_COMPATIBLE_OVERRIDES;
|
|
1101
|
-
const hasApiAndEnv = providerInfo.api && providerInfo.env && providerInfo.env.length > 0;
|
|
1102
|
-
if (isOpenAICompatible || hasApiAndEnv) {
|
|
1103
|
-
const modelIds = Object.keys(providerInfo.models).sort();
|
|
1104
|
-
let url = providerInfo.api || OPENAI_COMPATIBLE_OVERRIDES[normalizedId]?.url;
|
|
1105
|
-
if (url && !url.includes("/chat/completions") && !url.includes("/messages")) {
|
|
1106
|
-
url = url.replace(/\/$/, "") + "/chat/completions";
|
|
1107
|
-
}
|
|
1108
|
-
if (!url) {
|
|
1109
|
-
console.info(`Skipping ${normalizedId}: No API URL available`);
|
|
1110
|
-
continue;
|
|
1111
|
-
}
|
|
1112
|
-
const apiKeyEnvVar = providerInfo.env?.[0] || `${normalizedId.toUpperCase().replace(/-/g, "_")}_API_KEY`;
|
|
1113
|
-
const apiKeyHeader = OPENAI_COMPATIBLE_OVERRIDES[normalizedId]?.apiKeyHeader || "Authorization";
|
|
1114
|
-
providerConfigs[normalizedId] = {
|
|
1115
|
-
url,
|
|
1116
|
-
apiKeyEnvVar,
|
|
1117
|
-
apiKeyHeader,
|
|
1118
|
-
name: providerInfo.name || providerId.charAt(0).toUpperCase() + providerId.slice(1),
|
|
1119
|
-
models: modelIds.filter((id) => !id.includes(`codex`)),
|
|
1120
|
-
// codex requires responses api
|
|
1121
|
-
docUrl: providerInfo.doc,
|
|
1122
|
-
// Include documentation URL if available
|
|
1123
|
-
gateway: `models.dev`
|
|
1124
|
-
};
|
|
1125
|
-
} else {
|
|
1126
|
-
console.info(`Skipped provider ${providerInfo.name}`);
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
this.providerConfigs = providerConfigs;
|
|
1130
|
-
console.info(`Found ${Object.keys(providerConfigs).length} OpenAI-compatible providers`);
|
|
1131
|
-
console.info("Providers:", Object.keys(providerConfigs).sort());
|
|
1132
|
-
return providerConfigs;
|
|
1133
|
-
}
|
|
1134
|
-
buildUrl(modelId, envVars) {
|
|
1135
|
-
const [provider, ...modelParts] = modelId.split("/");
|
|
1136
|
-
if (!provider || !modelParts.length) {
|
|
1137
|
-
return false;
|
|
1138
|
-
}
|
|
1139
|
-
const config = this.providerConfigs[provider];
|
|
1140
|
-
if (!config?.url) {
|
|
1141
|
-
return false;
|
|
1142
|
-
}
|
|
1143
|
-
const baseUrlEnvVar = `${provider.toUpperCase().replace(/-/g, "_")}_BASE_URL`;
|
|
1144
|
-
const customBaseUrl = envVars[baseUrlEnvVar];
|
|
1145
|
-
return customBaseUrl || config.url;
|
|
1146
|
-
}
|
|
1147
|
-
buildHeaders(modelId, envVars) {
|
|
1148
|
-
const [provider] = modelId.split("/");
|
|
1149
|
-
if (!provider) {
|
|
1150
|
-
return {};
|
|
1151
|
-
}
|
|
1152
|
-
const config = this.providerConfigs[provider];
|
|
1153
|
-
if (!config) {
|
|
1154
|
-
return {};
|
|
1155
|
-
}
|
|
1156
|
-
const apiKey = typeof config.apiKeyEnvVar === `string` ? envVars[config.apiKeyEnvVar] : void 0;
|
|
1157
|
-
if (!apiKey) {
|
|
1158
|
-
return {};
|
|
1159
|
-
}
|
|
1160
|
-
const headers = {};
|
|
1161
|
-
if (config.apiKeyHeader === "Authorization" || !config.apiKeyHeader) {
|
|
1162
|
-
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
1163
|
-
} else {
|
|
1164
|
-
headers[config.apiKeyHeader] = apiKey;
|
|
1165
|
-
}
|
|
1166
|
-
if (provider === "anthropic") {
|
|
1167
|
-
headers["anthropic-version"] = "2023-06-01";
|
|
1168
|
-
}
|
|
1169
|
-
return headers;
|
|
1170
|
-
}
|
|
1171
|
-
};
|
|
1172
|
-
|
|
1173
|
-
// src/llm/model/gateways/netlify.ts
|
|
1174
|
-
var NetlifyGateway = class extends MastraModelGateway {
|
|
1175
|
-
name = "netlify";
|
|
1176
|
-
prefix = "netlify";
|
|
1177
|
-
// All providers will be prefixed with "netlify/"
|
|
1178
|
-
tokenCache = new chunkUU5L5GDY_cjs.InMemoryServerCache();
|
|
1179
|
-
async fetchProviders() {
|
|
1180
|
-
console.info("Fetching providers from Netlify AI Gateway...");
|
|
1181
|
-
const response = await fetch("https://api.netlify.com/api/v1/ai-gateway/providers");
|
|
1182
|
-
if (!response.ok) {
|
|
1183
|
-
throw new Error(`Failed to fetch from Netlify: ${response.statusText}`);
|
|
1184
|
-
}
|
|
1185
|
-
const data = await response.json();
|
|
1186
|
-
const netlify = {
|
|
1187
|
-
apiKeyEnvVar: ["NETLIFY_TOKEN", "NETLIFY_SITE_ID"],
|
|
1188
|
-
apiKeyHeader: "Authorization",
|
|
1189
|
-
// Netlify uses standard Bearer auth
|
|
1190
|
-
name: `Netlify`,
|
|
1191
|
-
gateway: `netlify`,
|
|
1192
|
-
models: [],
|
|
1193
|
-
docUrl: "https://docs.netlify.com/build/ai-gateway/overview/"
|
|
1194
|
-
};
|
|
1195
|
-
for (const [providerId, provider] of Object.entries(data.providers)) {
|
|
1196
|
-
for (const model of provider.models) {
|
|
1197
|
-
netlify.models.push(`${providerId}/${model}`);
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
console.info(`Found ${Object.keys(data.providers).length} models via Netlify Gateway`);
|
|
1201
|
-
return { netlify };
|
|
1202
|
-
}
|
|
1203
|
-
async buildUrl(modelId, envVars) {
|
|
1204
|
-
if (!modelId.startsWith(`${this.prefix}/`)) {
|
|
1205
|
-
return false;
|
|
1206
|
-
}
|
|
1207
|
-
const parts = modelId.split("/");
|
|
1208
|
-
if (parts.length < 3) {
|
|
1209
|
-
return false;
|
|
1210
|
-
}
|
|
1211
|
-
const provider = parts[1];
|
|
1212
|
-
if (!provider) {
|
|
1213
|
-
return false;
|
|
1214
|
-
}
|
|
1215
|
-
const siteId = envVars["NETLIFY_SITE_ID"];
|
|
1216
|
-
const netlifyToken = envVars["NETLIFY_TOKEN"];
|
|
1217
|
-
if (!netlifyToken) {
|
|
1218
|
-
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
1219
|
-
id: "NETLIFY_GATEWAY_NO_TOKEN",
|
|
1220
|
-
domain: "LLM",
|
|
1221
|
-
category: "UNKNOWN",
|
|
1222
|
-
text: `Missing NETLIFY_TOKEN environment variable required for model: ${modelId}`
|
|
1223
|
-
});
|
|
1224
|
-
}
|
|
1225
|
-
if (!siteId) {
|
|
1226
|
-
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
1227
|
-
id: "NETLIFY_GATEWAY_NO_SITE_ID",
|
|
1228
|
-
domain: "LLM",
|
|
1229
|
-
category: "UNKNOWN",
|
|
1230
|
-
text: `Missing NETLIFY_SITE_ID environment variable required for model: ${modelId}`
|
|
1231
|
-
});
|
|
1232
|
-
}
|
|
1233
|
-
try {
|
|
1234
|
-
const tokenData = await this.getOrFetchToken(siteId, netlifyToken);
|
|
1235
|
-
return `${tokenData.url}chat/completions`;
|
|
1236
|
-
} catch (error) {
|
|
1237
|
-
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
1238
|
-
id: "NETLIFY_GATEWAY_TOKEN_ERROR",
|
|
1239
|
-
domain: "LLM",
|
|
1240
|
-
category: "UNKNOWN",
|
|
1241
|
-
text: `Failed to get Netlify AI Gateway token for model ${modelId}: ${error instanceof Error ? error.message : String(error)}`
|
|
1242
|
-
});
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
/**
|
|
1246
|
-
* Get cached token or fetch a new site-specific AI Gateway token from Netlify
|
|
1247
|
-
*/
|
|
1248
|
-
async getOrFetchToken(siteId, netlifyToken) {
|
|
1249
|
-
const cacheKey = `netlify-token:${siteId}:${netlifyToken}`;
|
|
1250
|
-
const cached = await this.tokenCache.get(cacheKey);
|
|
1251
|
-
if (cached && cached.expiresAt > Date.now() / 1e3 + 60) {
|
|
1252
|
-
return { token: cached.token, url: cached.url };
|
|
1253
|
-
}
|
|
1254
|
-
const response = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}/ai-gateway/token`, {
|
|
1255
|
-
method: "GET",
|
|
1256
|
-
headers: {
|
|
1257
|
-
Authorization: `Bearer ${netlifyToken}`
|
|
1258
|
-
}
|
|
1259
|
-
});
|
|
1260
|
-
if (!response.ok) {
|
|
1261
|
-
const error = await response.text();
|
|
1262
|
-
throw new Error(`Failed to get Netlify AI Gateway token: ${response.status} ${error}`);
|
|
1263
|
-
}
|
|
1264
|
-
const tokenResponse = await response.json();
|
|
1265
|
-
await this.tokenCache.set(cacheKey, {
|
|
1266
|
-
token: tokenResponse.token,
|
|
1267
|
-
url: tokenResponse.url,
|
|
1268
|
-
expiresAt: tokenResponse.expires_at
|
|
1269
|
-
});
|
|
1270
|
-
return { token: tokenResponse.token, url: tokenResponse.url };
|
|
1271
|
-
}
|
|
1272
|
-
async buildHeaders(modelId, envVars) {
|
|
1273
|
-
const siteId = envVars["NETLIFY_SITE_ID"];
|
|
1274
|
-
const netlifyToken = envVars["NETLIFY_TOKEN"];
|
|
1275
|
-
if (!netlifyToken) {
|
|
1276
|
-
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
1277
|
-
id: "NETLIFY_GATEWAY_NO_TOKEN",
|
|
1278
|
-
domain: "LLM",
|
|
1279
|
-
category: "UNKNOWN",
|
|
1280
|
-
text: `Missing NETLIFY_TOKEN environment variable required for model: ${modelId}`
|
|
1281
|
-
});
|
|
1282
|
-
}
|
|
1283
|
-
if (!siteId) {
|
|
1284
|
-
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
1285
|
-
id: "NETLIFY_GATEWAY_NO_SITE_ID",
|
|
1286
|
-
domain: "LLM",
|
|
1287
|
-
category: "UNKNOWN",
|
|
1288
|
-
text: `Missing NETLIFY_SITE_ID environment variable required for model: ${modelId}`
|
|
1289
|
-
});
|
|
1290
|
-
}
|
|
1291
|
-
try {
|
|
1292
|
-
const tokenData = await this.getOrFetchToken(siteId, netlifyToken);
|
|
1293
|
-
return {
|
|
1294
|
-
Authorization: `Bearer ${tokenData.token}`
|
|
1295
|
-
};
|
|
1296
|
-
} catch (error) {
|
|
1297
|
-
throw new chunkWM4VQWOZ_cjs.MastraError({
|
|
1298
|
-
id: "NETLIFY_GATEWAY_TOKEN_ERROR",
|
|
1299
|
-
domain: "LLM",
|
|
1300
|
-
category: "UNKNOWN",
|
|
1301
|
-
text: `Failed to get Netlify AI Gateway token for model ${modelId}: ${error instanceof Error ? error.message : String(error)}`
|
|
1302
|
-
});
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
};
|
|
1306
|
-
|
|
1307
|
-
// src/llm/model/gateway-resolver.ts
|
|
1356
|
+
// src/llm/model/router.ts
|
|
1308
1357
|
function getStaticProvidersByGateway(name) {
|
|
1309
1358
|
return Object.fromEntries(Object.entries(PROVIDER_REGISTRY).filter(([_provider, config]) => config.gateway === name));
|
|
1310
1359
|
}
|
|
1311
1360
|
var gateways = [new NetlifyGateway(), new ModelsDevGateway(getStaticProvidersByGateway(`models.dev`))];
|
|
1312
|
-
|
|
1313
|
-
const prefixedGateway = gateways.find((g) => g.prefix && modelId.startsWith(`${g.prefix}/`));
|
|
1314
|
-
if (prefixedGateway) {
|
|
1315
|
-
return prefixedGateway;
|
|
1316
|
-
}
|
|
1317
|
-
const unprefixedGateways = gateways.filter((g) => !g.prefix);
|
|
1318
|
-
for (const gateway of unprefixedGateways) {
|
|
1319
|
-
return gateway;
|
|
1320
|
-
}
|
|
1321
|
-
return null;
|
|
1322
|
-
}
|
|
1323
|
-
async function resolveModelConfig(modelId, envVars = process.env) {
|
|
1324
|
-
const gateway = findGatewayForModel(modelId);
|
|
1325
|
-
if (!gateway) {
|
|
1326
|
-
return { url: false, headers: {}, resolvedModelId: modelId };
|
|
1327
|
-
}
|
|
1328
|
-
const url = await gateway.buildUrl(modelId, envVars);
|
|
1329
|
-
if (url === false) {
|
|
1330
|
-
return { url: false, headers: {}, resolvedModelId: modelId };
|
|
1331
|
-
}
|
|
1332
|
-
const headers = gateway.buildHeaders ? await gateway.buildHeaders(modelId, envVars) : {};
|
|
1333
|
-
let resolvedModelId = modelId;
|
|
1334
|
-
const prefix = gateway.prefix ? `${gateway.prefix}/` : null;
|
|
1335
|
-
if (prefix && resolvedModelId.startsWith(prefix)) {
|
|
1336
|
-
resolvedModelId = resolvedModelId.substring(prefix.length);
|
|
1337
|
-
}
|
|
1338
|
-
const firstSlashIndex = resolvedModelId.indexOf("/");
|
|
1339
|
-
if (firstSlashIndex !== -1) {
|
|
1340
|
-
resolvedModelId = resolvedModelId.substring(firstSlashIndex + 1);
|
|
1341
|
-
}
|
|
1342
|
-
return { url, headers, resolvedModelId };
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
// src/llm/model/openai-compatible.ts
|
|
1346
|
-
function resolveApiKey({ provider, apiKey }) {
|
|
1347
|
-
if (apiKey) return apiKey;
|
|
1348
|
-
if (provider) {
|
|
1349
|
-
const config = getProviderConfig(provider);
|
|
1350
|
-
if (typeof config?.apiKeyEnvVar === `string`) {
|
|
1351
|
-
return process.env[config.apiKeyEnvVar];
|
|
1352
|
-
}
|
|
1353
|
-
if (Array.isArray(config?.apiKeyEnvVar)) {
|
|
1354
|
-
for (const key of config.apiKeyEnvVar) {
|
|
1355
|
-
if (process.env[key]) return process.env[key];
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
return void 0;
|
|
1360
|
-
}
|
|
1361
|
-
var OpenAICompatibleModel = class {
|
|
1361
|
+
var ModelRouterLanguageModel = class _ModelRouterLanguageModel {
|
|
1362
1362
|
specificationVersion = "v2";
|
|
1363
1363
|
defaultObjectGenerationMode = "json";
|
|
1364
1364
|
supportsStructuredOutputs = true;
|
|
@@ -1367,215 +1367,29 @@ var OpenAICompatibleModel = class {
|
|
|
1367
1367
|
modelId;
|
|
1368
1368
|
provider;
|
|
1369
1369
|
config;
|
|
1370
|
-
|
|
1371
|
-
// Store the full model ID for gateway resolution
|
|
1370
|
+
gateway;
|
|
1372
1371
|
constructor(config) {
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
}
|
|
1381
|
-
if (isUrl) {
|
|
1382
|
-
parsedConfig = {
|
|
1383
|
-
id: "unknown",
|
|
1384
|
-
url: config
|
|
1385
|
-
};
|
|
1386
|
-
this.provider = "openai-compatible";
|
|
1387
|
-
this.fullModelId = "unknown";
|
|
1388
|
-
this.config = { id: "unknown", url: config };
|
|
1389
|
-
} else {
|
|
1390
|
-
this.fullModelId = config;
|
|
1391
|
-
const firstSlashIndex = config.indexOf("/");
|
|
1392
|
-
if (firstSlashIndex !== -1) {
|
|
1393
|
-
const provider = config.substring(0, firstSlashIndex);
|
|
1394
|
-
const modelId = config.substring(firstSlashIndex + 1);
|
|
1395
|
-
parsedConfig = {
|
|
1396
|
-
id: modelId,
|
|
1397
|
-
apiKey: resolveApiKey({ provider })
|
|
1398
|
-
};
|
|
1399
|
-
this.provider = provider;
|
|
1400
|
-
} else {
|
|
1401
|
-
throw new Error(`Invalid model string: "${config}". Use "provider/model" format or a direct URL.`);
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
} else {
|
|
1405
|
-
parsedConfig = config;
|
|
1406
|
-
this.fullModelId = config.id;
|
|
1407
|
-
const parsed = parseModelString(config.id);
|
|
1408
|
-
this.provider = parsed.provider || "openai-compatible";
|
|
1409
|
-
if (parsed.provider && parsed.modelId !== config.id) {
|
|
1410
|
-
parsedConfig.id = parsed.modelId;
|
|
1411
|
-
}
|
|
1412
|
-
if (!parsedConfig.apiKey) {
|
|
1413
|
-
parsedConfig.apiKey = resolveApiKey({ provider: parsed.provider || void 0 });
|
|
1414
|
-
}
|
|
1372
|
+
if (typeof config === `string`) config = { id: config };
|
|
1373
|
+
const parsedConfig = { ...config, routerId: config.id };
|
|
1374
|
+
this.gateway = findGatewayForModel(config.id, gateways);
|
|
1375
|
+
const parsed = parseModelRouterId(config.id, this.gateway.prefix);
|
|
1376
|
+
this.provider = parsed.providerId || "openai-compatible";
|
|
1377
|
+
if (parsed.providerId && parsed.modelId !== config.id) {
|
|
1378
|
+
parsedConfig.id = parsed.modelId;
|
|
1415
1379
|
}
|
|
1416
1380
|
this.modelId = parsedConfig.id;
|
|
1417
1381
|
this.config = parsedConfig;
|
|
1418
|
-
|
|
1419
|
-
convertMessagesToOpenAI(messages) {
|
|
1420
|
-
return messages.map((msg) => {
|
|
1421
|
-
if (msg.role === "system") {
|
|
1422
|
-
return {
|
|
1423
|
-
role: "system",
|
|
1424
|
-
content: msg.content
|
|
1425
|
-
};
|
|
1426
|
-
}
|
|
1427
|
-
if (msg.role === "user") {
|
|
1428
|
-
const contentParts = msg.content.map((part) => {
|
|
1429
|
-
if (part.type === "text") {
|
|
1430
|
-
return { type: "text", text: part.text };
|
|
1431
|
-
}
|
|
1432
|
-
if (part.type === "file") {
|
|
1433
|
-
return {
|
|
1434
|
-
type: "image_url",
|
|
1435
|
-
image_url: { url: part.data }
|
|
1436
|
-
};
|
|
1437
|
-
}
|
|
1438
|
-
return null;
|
|
1439
|
-
}).filter(Boolean);
|
|
1440
|
-
if (contentParts.every((p) => p?.type === "text")) {
|
|
1441
|
-
return {
|
|
1442
|
-
role: "user",
|
|
1443
|
-
content: contentParts.map((p) => p?.text || "").join("")
|
|
1444
|
-
};
|
|
1445
|
-
}
|
|
1446
|
-
return {
|
|
1447
|
-
role: "user",
|
|
1448
|
-
content: contentParts
|
|
1449
|
-
};
|
|
1450
|
-
}
|
|
1451
|
-
if (msg.role === "assistant") {
|
|
1452
|
-
const textContent = msg.content.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
1453
|
-
const toolCalls = msg.content.filter((part) => part.type === "tool-call").map((part) => ({
|
|
1454
|
-
id: part.toolCallId,
|
|
1455
|
-
type: "function",
|
|
1456
|
-
function: {
|
|
1457
|
-
name: part.toolName,
|
|
1458
|
-
arguments: JSON.stringify(part.input || {})
|
|
1459
|
-
}
|
|
1460
|
-
}));
|
|
1461
|
-
return {
|
|
1462
|
-
role: "assistant",
|
|
1463
|
-
content: textContent || null,
|
|
1464
|
-
...toolCalls.length > 0 && { tool_calls: toolCalls }
|
|
1465
|
-
};
|
|
1466
|
-
}
|
|
1467
|
-
if (msg.role === "tool") {
|
|
1468
|
-
return msg.content.map((toolResponse) => ({
|
|
1469
|
-
role: "tool",
|
|
1470
|
-
tool_call_id: toolResponse.toolCallId,
|
|
1471
|
-
content: JSON.stringify(toolResponse.output)
|
|
1472
|
-
}));
|
|
1473
|
-
}
|
|
1474
|
-
return msg;
|
|
1475
|
-
}).flat();
|
|
1476
|
-
}
|
|
1477
|
-
convertToolsToOpenAI(tools) {
|
|
1478
|
-
if (!tools || Object.keys(tools).length === 0) return void 0;
|
|
1479
|
-
return Object.entries(tools).map(([name, tool]) => {
|
|
1480
|
-
if (tool.type === "function") {
|
|
1481
|
-
return {
|
|
1482
|
-
type: "function",
|
|
1483
|
-
function: {
|
|
1484
|
-
name: tool.name,
|
|
1485
|
-
description: tool.description,
|
|
1486
|
-
parameters: tool.inputSchema || {}
|
|
1487
|
-
}
|
|
1488
|
-
};
|
|
1489
|
-
}
|
|
1490
|
-
return {
|
|
1491
|
-
type: "function",
|
|
1492
|
-
function: {
|
|
1493
|
-
name,
|
|
1494
|
-
description: `Provider tool: ${name}`,
|
|
1495
|
-
parameters: {}
|
|
1496
|
-
}
|
|
1497
|
-
};
|
|
1498
|
-
});
|
|
1499
|
-
}
|
|
1500
|
-
mapFinishReason(reason) {
|
|
1501
|
-
switch (reason) {
|
|
1502
|
-
case "stop":
|
|
1503
|
-
return "stop";
|
|
1504
|
-
case "length":
|
|
1505
|
-
case "max_tokens":
|
|
1506
|
-
return "length";
|
|
1507
|
-
case "tool_calls":
|
|
1508
|
-
case "function_call":
|
|
1509
|
-
return "tool-calls";
|
|
1510
|
-
case "content_filter":
|
|
1511
|
-
return "content-filter";
|
|
1512
|
-
default:
|
|
1513
|
-
return "unknown";
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
/**
|
|
1517
|
-
* Resolve URL and headers for the request
|
|
1518
|
-
* This is called fresh for each request to ensure we get the latest values
|
|
1519
|
-
* (e.g., Netlify tokens can expire and need to be refreshed)
|
|
1520
|
-
*/
|
|
1521
|
-
async resolveRequestConfig() {
|
|
1522
|
-
const shouldUseGateway = !this.config.url;
|
|
1523
|
-
if (shouldUseGateway) {
|
|
1524
|
-
const { url, headers, resolvedModelId } = await resolveModelConfig(this.fullModelId);
|
|
1525
|
-
if (url === false) {
|
|
1526
|
-
throw new Error(`No gateway can handle model: ${this.fullModelId}`);
|
|
1527
|
-
}
|
|
1528
|
-
const finalHeaders = {
|
|
1529
|
-
"Content-Type": "application/json",
|
|
1530
|
-
...headers,
|
|
1531
|
-
...this.config.headers
|
|
1532
|
-
};
|
|
1533
|
-
return { url, headers: finalHeaders, modelId: resolvedModelId };
|
|
1534
|
-
} else {
|
|
1535
|
-
if (!this.config.url) {
|
|
1536
|
-
throw new Error("URL is required for OpenAI-compatible model");
|
|
1537
|
-
}
|
|
1538
|
-
const headers = {
|
|
1539
|
-
"Content-Type": "application/json",
|
|
1540
|
-
...this.config.headers
|
|
1541
|
-
};
|
|
1542
|
-
if (this.config.apiKey) {
|
|
1543
|
-
const providerConfig = this.provider !== "openai-compatible" ? getProviderConfig(this.provider) : void 0;
|
|
1544
|
-
if (providerConfig?.apiKeyHeader === "x-api-key") {
|
|
1545
|
-
headers["x-api-key"] = this.config.apiKey;
|
|
1546
|
-
} else {
|
|
1547
|
-
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
return { url: this.config.url, headers, modelId: this.modelId };
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
validateApiKey() {
|
|
1554
|
-
const willUseGateway = !this.config.url;
|
|
1555
|
-
if (willUseGateway) {
|
|
1556
|
-
return;
|
|
1557
|
-
}
|
|
1558
|
-
if (!this.config.apiKey && this.provider !== "openai-compatible") {
|
|
1559
|
-
const providerConfig = getProviderConfig(this.provider);
|
|
1560
|
-
if (providerConfig?.apiKeyEnvVar) {
|
|
1561
|
-
throw new Error(
|
|
1562
|
-
`API key not found for provider "${this.provider}". Please set the ${providerConfig.apiKeyEnvVar} environment variable.`
|
|
1563
|
-
);
|
|
1564
|
-
} else {
|
|
1565
|
-
throw new Error(
|
|
1566
|
-
`API key not found for provider "${this.provider}". Please provide an API key in the configuration.`
|
|
1567
|
-
);
|
|
1568
|
-
}
|
|
1569
|
-
}
|
|
1382
|
+
this.gateway = findGatewayForModel(parsedConfig.routerId, gateways);
|
|
1570
1383
|
}
|
|
1571
1384
|
async doGenerate() {
|
|
1572
1385
|
throw new Error(
|
|
1573
|
-
"doGenerate is not supported by
|
|
1386
|
+
"doGenerate is not supported by Mastra model router. Mastra only uses streaming (doStream) for all LLM calls."
|
|
1574
1387
|
);
|
|
1575
1388
|
}
|
|
1576
1389
|
async doStream(options) {
|
|
1390
|
+
let apiKey;
|
|
1577
1391
|
try {
|
|
1578
|
-
this.
|
|
1392
|
+
apiKey = await this.gateway.getApiKey(this.config.routerId);
|
|
1579
1393
|
} catch (error) {
|
|
1580
1394
|
return {
|
|
1581
1395
|
stream: new ReadableStream({
|
|
@@ -1585,242 +1399,32 @@ var OpenAICompatibleModel = class {
|
|
|
1585
1399
|
error: error instanceof Error ? error.message : String(error)
|
|
1586
1400
|
});
|
|
1587
1401
|
}
|
|
1588
|
-
})
|
|
1589
|
-
warnings: []
|
|
1590
|
-
};
|
|
1591
|
-
}
|
|
1592
|
-
try {
|
|
1593
|
-
const { url, headers, modelId: resolvedModelId } = await this.resolveRequestConfig();
|
|
1594
|
-
const { prompt, tools, toolChoice, providerOptions } = options;
|
|
1595
|
-
const body = {
|
|
1596
|
-
messages: this.convertMessagesToOpenAI(prompt),
|
|
1597
|
-
model: resolvedModelId,
|
|
1598
|
-
stream: true,
|
|
1599
|
-
...providerOptions
|
|
1600
|
-
};
|
|
1601
|
-
const openAITools = this.convertToolsToOpenAI(tools);
|
|
1602
|
-
if (openAITools) {
|
|
1603
|
-
body.tools = openAITools;
|
|
1604
|
-
if (toolChoice) {
|
|
1605
|
-
body.tool_choice = toolChoice.type === "none" ? "none" : toolChoice.type === "required" ? "required" : toolChoice.type === "auto" ? "auto" : toolChoice.type === "tool" ? { type: "function", function: { name: toolChoice.toolName } } : "auto";
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
if (options.responseFormat?.type === "json") {
|
|
1609
|
-
body.response_format = {
|
|
1610
|
-
type: "json_schema",
|
|
1611
|
-
json_schema: {
|
|
1612
|
-
name: "response",
|
|
1613
|
-
strict: true,
|
|
1614
|
-
schema: options.responseFormat.schema
|
|
1615
|
-
}
|
|
1616
|
-
};
|
|
1617
|
-
}
|
|
1618
|
-
const fetchArgs = {
|
|
1619
|
-
method: "POST",
|
|
1620
|
-
headers,
|
|
1621
|
-
body: JSON.stringify(body),
|
|
1622
|
-
signal: options.abortSignal
|
|
1623
|
-
};
|
|
1624
|
-
const response = await fetch(url, fetchArgs);
|
|
1625
|
-
if (!response.ok) {
|
|
1626
|
-
const error = await response.text();
|
|
1627
|
-
if (response.status === 401 || response.status === 403) {
|
|
1628
|
-
const providerConfig = getProviderConfig(this.provider);
|
|
1629
|
-
if (providerConfig?.apiKeyEnvVar) {
|
|
1630
|
-
throw new Error(
|
|
1631
|
-
`Authentication failed for provider "${this.provider}". Please ensure the ${providerConfig.apiKeyEnvVar} environment variable is set with a valid API key.`
|
|
1632
|
-
);
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
throw new Error(`OpenAI-compatible API error: ${response.status} - ${error}`);
|
|
1636
|
-
}
|
|
1637
|
-
const reader = response.body?.getReader();
|
|
1638
|
-
if (!reader) {
|
|
1639
|
-
throw new Error("Response body is not readable");
|
|
1640
|
-
}
|
|
1641
|
-
const decoder = new TextDecoder();
|
|
1642
|
-
let buffer = "";
|
|
1643
|
-
let sentStart = false;
|
|
1644
|
-
const toolCallBuffers = /* @__PURE__ */ new Map();
|
|
1645
|
-
const mapFinishReason = this.mapFinishReason.bind(this);
|
|
1646
|
-
const modelId = this.modelId;
|
|
1647
|
-
let isActiveText = false;
|
|
1648
|
-
const stream = new ReadableStream({
|
|
1649
|
-
async start(controller) {
|
|
1650
|
-
try {
|
|
1651
|
-
controller.enqueue({
|
|
1652
|
-
type: "stream-start",
|
|
1653
|
-
warnings: []
|
|
1654
|
-
});
|
|
1655
|
-
while (true) {
|
|
1656
|
-
const { done, value } = await reader.read();
|
|
1657
|
-
if (done) {
|
|
1658
|
-
for (const [_, toolCall] of toolCallBuffers) {
|
|
1659
|
-
if (!toolCall.sent && toolCall.id && toolCall.name && toolCall.args) {
|
|
1660
|
-
controller.enqueue({
|
|
1661
|
-
type: "tool-call",
|
|
1662
|
-
toolCallId: toolCall.id,
|
|
1663
|
-
toolName: toolCall.name,
|
|
1664
|
-
input: toolCall.args || "{}"
|
|
1665
|
-
});
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
controller.close();
|
|
1669
|
-
break;
|
|
1670
|
-
}
|
|
1671
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1672
|
-
const lines = buffer.split("\n");
|
|
1673
|
-
buffer = lines.pop() || "";
|
|
1674
|
-
for (const line of lines) {
|
|
1675
|
-
if (line.trim() === "" || line.trim() === "data: [DONE]") {
|
|
1676
|
-
continue;
|
|
1677
|
-
}
|
|
1678
|
-
if (line.startsWith("data: ")) {
|
|
1679
|
-
try {
|
|
1680
|
-
const data = JSON.parse(line.slice(6));
|
|
1681
|
-
if (!sentStart && data.id) {
|
|
1682
|
-
controller.enqueue({
|
|
1683
|
-
type: "response-metadata",
|
|
1684
|
-
id: data.id,
|
|
1685
|
-
modelId: data.model || modelId,
|
|
1686
|
-
timestamp: new Date(data.created ? data.created * 1e3 : Date.now())
|
|
1687
|
-
});
|
|
1688
|
-
sentStart = true;
|
|
1689
|
-
}
|
|
1690
|
-
const choice = data.choices?.[0];
|
|
1691
|
-
if (!choice) continue;
|
|
1692
|
-
if (choice.delta?.content) {
|
|
1693
|
-
if (!isActiveText) {
|
|
1694
|
-
controller.enqueue({ type: "text-start", id: "text-1" });
|
|
1695
|
-
isActiveText = true;
|
|
1696
|
-
}
|
|
1697
|
-
controller.enqueue({
|
|
1698
|
-
type: "text-delta",
|
|
1699
|
-
id: "text-1",
|
|
1700
|
-
delta: choice.delta.content
|
|
1701
|
-
});
|
|
1702
|
-
} else if (isActiveText) {
|
|
1703
|
-
controller.enqueue({ type: "text-end", id: "text-1" });
|
|
1704
|
-
isActiveText = false;
|
|
1705
|
-
}
|
|
1706
|
-
if (choice.delta?.tool_calls) {
|
|
1707
|
-
for (const toolCall of choice.delta.tool_calls) {
|
|
1708
|
-
const index = toolCall.index;
|
|
1709
|
-
if (!toolCallBuffers.has(index)) {
|
|
1710
|
-
if (toolCall.id && toolCall.function?.name) {
|
|
1711
|
-
controller.enqueue({
|
|
1712
|
-
type: "tool-input-start",
|
|
1713
|
-
id: toolCall.id,
|
|
1714
|
-
toolName: toolCall.function.name
|
|
1715
|
-
});
|
|
1716
|
-
}
|
|
1717
|
-
toolCallBuffers.set(index, {
|
|
1718
|
-
id: toolCall.id || "",
|
|
1719
|
-
name: toolCall.function?.name || "",
|
|
1720
|
-
args: ""
|
|
1721
|
-
});
|
|
1722
|
-
}
|
|
1723
|
-
const buffer2 = toolCallBuffers.get(index);
|
|
1724
|
-
if (toolCall.id) {
|
|
1725
|
-
buffer2.id = toolCall.id;
|
|
1726
|
-
}
|
|
1727
|
-
if (toolCall.function?.name) {
|
|
1728
|
-
buffer2.name = toolCall.function.name;
|
|
1729
|
-
}
|
|
1730
|
-
if (toolCall.function?.arguments) {
|
|
1731
|
-
buffer2.args += toolCall.function.arguments;
|
|
1732
|
-
controller.enqueue({
|
|
1733
|
-
type: "tool-input-delta",
|
|
1734
|
-
id: buffer2.id,
|
|
1735
|
-
delta: toolCall.function.arguments
|
|
1736
|
-
});
|
|
1737
|
-
try {
|
|
1738
|
-
JSON.parse(buffer2.args);
|
|
1739
|
-
if (buffer2.id && buffer2.name) {
|
|
1740
|
-
controller.enqueue({
|
|
1741
|
-
type: "tool-input-end",
|
|
1742
|
-
id: buffer2.id
|
|
1743
|
-
});
|
|
1744
|
-
controller.enqueue({
|
|
1745
|
-
type: "tool-call",
|
|
1746
|
-
toolCallId: buffer2.id,
|
|
1747
|
-
toolName: buffer2.name,
|
|
1748
|
-
input: buffer2.args
|
|
1749
|
-
});
|
|
1750
|
-
toolCallBuffers.set(index, {
|
|
1751
|
-
id: buffer2.id,
|
|
1752
|
-
name: buffer2.name,
|
|
1753
|
-
args: buffer2.args,
|
|
1754
|
-
sent: true
|
|
1755
|
-
});
|
|
1756
|
-
}
|
|
1757
|
-
} catch {
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
if (choice.finish_reason) {
|
|
1763
|
-
toolCallBuffers.clear();
|
|
1764
|
-
controller.enqueue({
|
|
1765
|
-
type: "finish",
|
|
1766
|
-
finishReason: mapFinishReason(choice.finish_reason),
|
|
1767
|
-
usage: data.usage ? {
|
|
1768
|
-
inputTokens: data.usage.prompt_tokens || 0,
|
|
1769
|
-
outputTokens: data.usage.completion_tokens || 0,
|
|
1770
|
-
totalTokens: data.usage.total_tokens || 0
|
|
1771
|
-
} : {
|
|
1772
|
-
inputTokens: 0,
|
|
1773
|
-
outputTokens: 0,
|
|
1774
|
-
totalTokens: 0
|
|
1775
|
-
}
|
|
1776
|
-
});
|
|
1777
|
-
}
|
|
1778
|
-
} catch (e) {
|
|
1779
|
-
console.error("Error parsing SSE data:", e);
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
} catch (error) {
|
|
1785
|
-
return {
|
|
1786
|
-
stream: new ReadableStream({
|
|
1787
|
-
start(controller2) {
|
|
1788
|
-
controller2.enqueue({
|
|
1789
|
-
type: "error",
|
|
1790
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1791
|
-
});
|
|
1792
|
-
}
|
|
1793
|
-
}),
|
|
1794
|
-
warnings: []
|
|
1795
|
-
};
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
});
|
|
1799
|
-
return {
|
|
1800
|
-
stream,
|
|
1801
|
-
request: { body: JSON.stringify(body) },
|
|
1802
|
-
response: { headers: Object.fromEntries(response.headers.entries()) },
|
|
1803
|
-
warnings: []
|
|
1804
|
-
};
|
|
1805
|
-
} catch (error) {
|
|
1806
|
-
return {
|
|
1807
|
-
stream: new ReadableStream({
|
|
1808
|
-
start(controller) {
|
|
1809
|
-
controller.enqueue({
|
|
1810
|
-
type: "error",
|
|
1811
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1812
|
-
});
|
|
1813
|
-
}
|
|
1814
|
-
}),
|
|
1815
|
-
warnings: []
|
|
1402
|
+
})
|
|
1816
1403
|
};
|
|
1817
1404
|
}
|
|
1405
|
+
const model = await this.resolveLanguageModel({
|
|
1406
|
+
apiKey,
|
|
1407
|
+
...parseModelRouterId(this.config.routerId, this.gateway.prefix)
|
|
1408
|
+
});
|
|
1409
|
+
return model.doStream(options);
|
|
1410
|
+
}
|
|
1411
|
+
async resolveLanguageModel({
|
|
1412
|
+
modelId,
|
|
1413
|
+
providerId,
|
|
1414
|
+
apiKey
|
|
1415
|
+
}) {
|
|
1416
|
+
const key = crypto.createHash("sha256").update(this.gateway.name + modelId + providerId + apiKey).digest("hex");
|
|
1417
|
+
if (_ModelRouterLanguageModel.modelInstances.has(key)) return _ModelRouterLanguageModel.modelInstances.get(key);
|
|
1418
|
+
const modelInstance = await this.gateway.resolveLanguageModel({ modelId, providerId, apiKey });
|
|
1419
|
+
_ModelRouterLanguageModel.modelInstances.set(key, modelInstance);
|
|
1420
|
+
return modelInstance;
|
|
1818
1421
|
}
|
|
1422
|
+
static modelInstances = /* @__PURE__ */ new Map();
|
|
1819
1423
|
};
|
|
1820
1424
|
|
|
1821
|
-
exports.
|
|
1425
|
+
exports.ModelRouterLanguageModel = ModelRouterLanguageModel;
|
|
1822
1426
|
exports.PROVIDER_REGISTRY = PROVIDER_REGISTRY;
|
|
1823
1427
|
exports.getProviderConfig = getProviderConfig;
|
|
1824
1428
|
exports.parseModelString = parseModelString;
|
|
1825
|
-
//# sourceMappingURL=chunk-
|
|
1826
|
-
//# sourceMappingURL=chunk-
|
|
1429
|
+
//# sourceMappingURL=chunk-BG5FC6ZZ.cjs.map
|
|
1430
|
+
//# sourceMappingURL=chunk-BG5FC6ZZ.cjs.map
|