@elizaos/plugin-elizacloud 2.0.0-beta.1 → 2.0.11-beta.7
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 +20 -44
- package/auto-enable.ts +10 -5
- package/dist/browser/index.browser.js +2 -2
- package/dist/browser/index.browser.js.map +4 -4
- package/dist/cjs/index.node.cjs +2874 -5915
- package/dist/cjs/index.node.js.map +47 -116
- package/dist/cloud/auth-service-types.d.ts +8 -0
- package/dist/cloud/auth-service-types.d.ts.map +1 -0
- package/dist/cloud/auth-service-types.js +36 -0
- package/dist/cloud/auth-service-types.js.map +10 -0
- package/dist/cloud/auth.js +4 -51
- package/dist/cloud/auth.js.map +4 -4
- package/dist/cloud/base-url.d.ts +6 -2
- package/dist/cloud/base-url.d.ts.map +1 -1
- package/dist/cloud/base-url.js +3 -51
- package/dist/cloud/base-url.js.map +3 -3
- package/dist/cloud/bridge-client.d.ts +3 -3
- package/dist/cloud/bridge-client.d.ts.map +1 -1
- package/dist/cloud/bridge-client.js +3 -51
- package/dist/cloud/bridge-client.js.map +3 -3
- package/dist/cloud/clack-observer.d.ts +35 -0
- package/dist/cloud/clack-observer.d.ts.map +1 -0
- package/dist/cloud/clack-observer.js +143 -0
- package/dist/cloud/clack-observer.js.map +10 -0
- package/dist/cloud/cloud-manager.js +45 -92
- package/dist/cloud/cloud-manager.js.map +6 -6
- package/dist/cloud/cloud-wallet.js +2 -4835
- package/dist/cloud/cloud-wallet.js.map +3 -82
- package/dist/cloud/duffel-client.d.ts +181 -0
- package/dist/cloud/duffel-client.d.ts.map +1 -0
- package/dist/cloud/duffel-client.js +506 -0
- package/dist/cloud/duffel-client.js.map +11 -0
- package/dist/cloud/index.d.ts +6 -0
- package/dist/cloud/index.d.ts.map +1 -1
- package/dist/cloud/index.js +1782 -1
- package/dist/cloud/index.js.map +18 -3
- package/dist/cloud/lifeops-schedule-sync-client.d.ts +43 -0
- package/dist/cloud/lifeops-schedule-sync-client.d.ts.map +1 -0
- package/dist/cloud/lifeops-schedule-sync-client.js +180 -0
- package/dist/cloud/lifeops-schedule-sync-client.js.map +11 -0
- package/dist/cloud/lifeops-schedule-sync-contracts.d.ts +89 -0
- package/dist/cloud/lifeops-schedule-sync-contracts.d.ts.map +1 -0
- package/dist/cloud/lifeops-schedule-sync-contracts.js +39 -0
- package/dist/cloud/lifeops-schedule-sync-contracts.js.map +10 -0
- package/dist/cloud/managed-payment-clients.d.ts +166 -0
- package/dist/cloud/managed-payment-clients.d.ts.map +1 -0
- package/dist/cloud/managed-payment-clients.js +238 -0
- package/dist/cloud/managed-payment-clients.js.map +11 -0
- package/dist/cloud/null-observer.d.ts +35 -0
- package/dist/cloud/null-observer.d.ts.map +1 -0
- package/dist/cloud/null-observer.js +45 -0
- package/dist/cloud/null-observer.js.map +10 -0
- package/dist/cloud/setup-observer.d.ts +98 -0
- package/dist/cloud/setup-observer.d.ts.map +1 -0
- package/dist/cloud/setup-observer.js +2 -0
- package/dist/cloud/setup-observer.js.map +9 -0
- package/dist/cloud/validate-url.d.ts.map +1 -1
- package/dist/cloud/validate-url.js +2 -1
- package/dist/cloud/validate-url.js.map +3 -3
- package/dist/cloud/x402-payment-handler.d.ts +85 -0
- package/dist/cloud/x402-payment-handler.d.ts.map +1 -0
- package/dist/cloud/x402-payment-handler.js +119 -0
- package/dist/cloud/x402-payment-handler.js.map +10 -0
- package/dist/cloud-setup.d.ts +36 -0
- package/dist/cloud-setup.d.ts.map +1 -0
- package/dist/{onboarding.js → cloud-setup.js} +139 -139
- package/dist/cloud-setup.js.map +14 -0
- package/dist/cloud-voice-catalog.d.ts +65 -0
- package/dist/cloud-voice-catalog.d.ts.map +1 -0
- package/dist/cloud-voice-catalog.js +278 -0
- package/dist/cloud-voice-catalog.js.map +12 -0
- package/dist/index.browser.d.ts +11 -0
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5416 -8405
- package/dist/index.js.map +48 -116
- package/dist/index.node.d.ts +8 -1
- package/dist/index.node.d.ts.map +1 -1
- package/dist/init.js +17 -4
- package/dist/init.js.map +4 -4
- package/dist/lib/cloud-connection.d.ts +0 -1
- package/dist/lib/cloud-connection.d.ts.map +1 -1
- package/dist/lib/cloud-connection.js +14 -91
- package/dist/lib/cloud-connection.js.map +7 -7
- package/dist/lib/cloud-secrets.d.ts +5 -18
- package/dist/lib/cloud-secrets.d.ts.map +1 -1
- package/dist/lib/cloud-secrets.js +8 -36
- package/dist/lib/cloud-secrets.js.map +3 -3
- package/dist/lib/config-like.d.ts +1 -1
- package/dist/lib/config-like.d.ts.map +1 -1
- package/dist/lib/config-like.js +3 -3
- package/dist/lib/config-like.js.map +3 -3
- package/dist/lib/credential-type-map.d.ts +1 -1
- package/dist/lib/credential-type-map.js.map +1 -1
- package/dist/lib/http.d.ts +0 -11
- package/dist/lib/http.d.ts.map +1 -1
- package/dist/lib/http.js.map +2 -2
- package/dist/lib/server-cloud-tts.d.ts +12 -25
- package/dist/lib/server-cloud-tts.d.ts.map +1 -1
- package/dist/lib/server-cloud-tts.js +31 -329
- package/dist/lib/server-cloud-tts.js.map +4 -7
- package/dist/lib/tts-debug.d.ts +5 -3
- package/dist/lib/tts-debug.d.ts.map +1 -1
- package/dist/lib/tts-debug.js +1 -34
- package/dist/lib/tts-debug.js.map +3 -4
- package/dist/models/embeddings.d.ts.map +1 -1
- package/dist/models/embeddings.js +79 -69
- package/dist/models/embeddings.js.map +6 -6
- package/dist/models/image.d.ts.map +1 -1
- package/dist/models/image.js +42 -15
- package/dist/models/image.js.map +6 -6
- package/dist/models/index.js +676 -166
- package/dist/models/index.js.map +11 -12
- package/dist/models/research.d.ts.map +1 -1
- package/dist/models/research.js +24 -7
- package/dist/models/research.js.map +6 -6
- package/dist/models/speech.d.ts +61 -3
- package/dist/models/speech.d.ts.map +1 -1
- package/dist/models/speech.js +173 -17
- package/dist/models/speech.js.map +5 -5
- package/dist/models/text.d.ts +106 -1
- package/dist/models/text.d.ts.map +1 -1
- package/dist/models/text.js +452 -82
- package/dist/models/text.js.map +7 -8
- package/dist/models/tokenization.d.ts.map +1 -1
- package/dist/models/tokenization.js.map +2 -2
- package/dist/models/transcription.d.ts.map +1 -1
- package/dist/models/transcription.js +20 -6
- package/dist/models/transcription.js.map +5 -5
- package/dist/node/index.node.js +2828 -5838
- package/dist/node/index.node.js.map +47 -116
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +376 -5050
- package/dist/plugin.js.map +16 -92
- package/dist/providers/openai.js +11 -2
- package/dist/providers/openai.js.map +3 -3
- package/dist/register-routes.js +376 -5050
- package/dist/register-routes.js.map +16 -92
- package/dist/routes/cloud-billing-routes.d.ts.map +1 -1
- package/dist/routes/cloud-billing-routes.js +17 -60
- package/dist/routes/cloud-billing-routes.js.map +8 -7
- package/dist/routes/cloud-coding-container-routes.d.ts +8 -0
- package/dist/routes/cloud-coding-container-routes.d.ts.map +1 -0
- package/dist/routes/cloud-coding-container-routes.js +214 -0
- package/dist/routes/cloud-coding-container-routes.js.map +11 -0
- package/dist/routes/cloud-compat-routes.d.ts.map +1 -1
- package/dist/routes/cloud-compat-routes.js +17 -60
- package/dist/routes/cloud-compat-routes.js.map +8 -7
- package/dist/routes/cloud-features-routes.js +2 -2
- package/dist/routes/cloud-features-routes.js.map +4 -4
- package/dist/routes/cloud-relay-routes.d.ts +2 -1
- package/dist/routes/cloud-relay-routes.d.ts.map +1 -1
- package/dist/routes/cloud-relay-routes.js +84 -2
- package/dist/routes/cloud-relay-routes.js.map +5 -4
- package/dist/routes/cloud-routes-autonomous.d.ts +3 -4
- package/dist/routes/cloud-routes-autonomous.d.ts.map +1 -1
- package/dist/routes/cloud-routes-autonomous.js +11 -4893
- package/dist/routes/cloud-routes-autonomous.js.map +8 -87
- package/dist/routes/cloud-routes.d.ts +2 -2
- package/dist/routes/cloud-routes.d.ts.map +1 -1
- package/dist/routes/cloud-routes.js +343 -5058
- package/dist/routes/cloud-routes.js.map +13 -90
- package/dist/routes/cloud-status-routes-autonomous.d.ts +1 -2
- package/dist/routes/cloud-status-routes-autonomous.d.ts.map +1 -1
- package/dist/routes/cloud-status-routes-autonomous.js +4 -51
- package/dist/routes/cloud-status-routes-autonomous.js.map +5 -5
- package/dist/routes/cloud-status-routes.js +14 -90
- package/dist/routes/cloud-status-routes.js.map +7 -7
- package/dist/routes/home-remote-runner-access-url.d.ts +16 -0
- package/dist/routes/home-remote-runner-access-url.d.ts.map +1 -0
- package/dist/routes/home-remote-runner-access-url.js +91 -0
- package/dist/routes/home-remote-runner-access-url.js.map +10 -0
- package/dist/routes/travel-provider-relay-routes.d.ts +9 -0
- package/dist/routes/travel-provider-relay-routes.d.ts.map +1 -0
- package/dist/routes/travel-provider-relay-routes.js +358 -0
- package/dist/routes/travel-provider-relay-routes.js.map +14 -0
- package/dist/services/cloud-auth.d.ts +1 -1
- package/dist/services/cloud-auth.d.ts.map +1 -1
- package/dist/services/cloud-auth.js +7 -2
- package/dist/services/cloud-auth.js.map +4 -4
- package/dist/services/cloud-backup.js.map +2 -2
- package/dist/services/cloud-bootstrap.d.ts.map +1 -1
- package/dist/services/cloud-bootstrap.js.map +2 -2
- package/dist/services/cloud-bridge.js.map +3 -3
- package/dist/services/cloud-container.d.ts +5 -1
- package/dist/services/cloud-container.d.ts.map +1 -1
- package/dist/services/cloud-container.js +52 -1
- package/dist/services/cloud-container.js.map +4 -4
- package/dist/services/cloud-credential-provider.js.map +2 -2
- package/dist/services/cloud-model-registry.js.map +2 -2
- package/dist/types/cloud.d.ts +1 -0
- package/dist/types/cloud.d.ts.map +1 -1
- package/dist/types/cloud.js.map +2 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/cloud-sdk/client.d.ts.map +1 -1
- package/dist/utils/cloud-sdk/client.js +136 -4
- package/dist/utils/cloud-sdk/client.js.map +5 -5
- package/dist/utils/cloud-sdk/http.js.map +1 -1
- package/dist/utils/cloud-sdk/public-routes.d.ts +186 -0
- package/dist/utils/cloud-sdk/public-routes.d.ts.map +1 -1
- package/dist/utils/cloud-sdk/public-routes.js +99 -1
- package/dist/utils/cloud-sdk/public-routes.js.map +3 -3
- package/dist/utils/cloud-sdk/types.d.ts +0 -2
- package/dist/utils/cloud-sdk/types.d.ts.map +1 -1
- package/dist/utils/cloud-sdk/types.js.map +1 -1
- package/dist/utils/config.d.ts +10 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +12 -2
- package/dist/utils/config.js.map +3 -3
- package/dist/utils/events.d.ts +23 -2
- package/dist/utils/events.d.ts.map +1 -1
- package/dist/utils/events.js +5 -3
- package/dist/utils/events.js.map +3 -3
- package/dist/utils/sdk-client.d.ts.map +1 -1
- package/dist/utils/sdk-client.js +17 -4
- package/dist/utils/sdk-client.js.map +4 -4
- package/dist/utils/waifu-metering.d.ts +108 -0
- package/dist/utils/waifu-metering.d.ts.map +1 -0
- package/dist/utils/waifu-metering.js +166 -0
- package/dist/utils/waifu-metering.js.map +10 -0
- package/package.json +51 -22
- package/src/cloud/auth-service-types.ts +24 -0
- package/src/cloud/base-url.ts +6 -62
- package/src/cloud/clack-observer.ts +189 -0
- package/src/cloud/duffel-client.ts +847 -0
- package/src/cloud/index.ts +10 -0
- package/src/cloud/lifeops-schedule-sync-client.ts +245 -0
- package/src/cloud/lifeops-schedule-sync-contracts.ts +124 -0
- package/src/cloud/managed-payment-clients.ts +374 -0
- package/src/cloud/null-observer.ts +45 -0
- package/src/cloud/setup-observer.ts +125 -0
- package/src/cloud/validate-url.ts +7 -1
- package/src/cloud/x402-payment-handler.ts +215 -0
- package/src/cloud-setup.ts +531 -0
- package/src/cloud-voice-catalog.test.ts +254 -0
- package/src/cloud-voice-catalog.ts +246 -0
- package/src/index.browser.ts +29 -0
- package/src/index.node.ts +31 -1
- package/src/index.ts +76 -4
- package/src/lib/cloud-connection.ts +2 -4
- package/src/lib/cloud-secrets.ts +10 -54
- package/src/lib/config-like.ts +1 -1
- package/src/lib/credential-type-map.ts +2 -2
- package/src/lib/http.ts +0 -17
- package/src/lib/server-cloud-tts.ts +33 -341
- package/src/lib/tts-debug.ts +5 -34
- package/src/models/embeddings.ts +140 -76
- package/src/models/image.ts +29 -14
- package/src/models/research.ts +11 -1
- package/src/models/speech.ts +269 -23
- package/src/models/text.ts +704 -110
- package/src/models/tokenization.ts +2 -2
- package/src/models/transcription.ts +7 -3
- package/src/plugin.ts +38 -0
- package/src/routes/cloud-billing-routes.ts +4 -14
- package/src/routes/cloud-coding-container-routes.ts +198 -0
- package/src/routes/cloud-compat-routes.ts +4 -14
- package/src/routes/cloud-features-routes.ts +1 -1
- package/src/routes/cloud-relay-routes.ts +47 -1
- package/src/routes/cloud-routes-autonomous.ts +7 -10
- package/src/routes/cloud-routes.ts +68 -7
- package/src/routes/cloud-status-routes-autonomous.ts +6 -2
- package/src/routes/home-remote-runner-access-url.ts +83 -0
- package/src/routes/travel-provider-relay-routes.ts +193 -0
- package/src/services/cloud-auth.ts +9 -2
- package/src/services/cloud-bootstrap.ts +1 -3
- package/src/services/cloud-bridge.ts +1 -1
- package/src/services/cloud-container.ts +93 -0
- package/src/services/cloud-credential-provider.ts +1 -1
- package/src/services/cloud-model-registry.ts +1 -1
- package/src/types/cloud.ts +22 -0
- package/src/types/index.ts +19 -0
- package/src/utils/cloud-sdk/client.ts +42 -3
- package/src/utils/cloud-sdk/public-routes.ts +168 -0
- package/src/utils/cloud-sdk/types.ts +0 -2
- package/src/utils/config.ts +20 -1
- package/src/utils/events.ts +30 -2
- package/src/utils/sdk-client.ts +5 -1
- package/src/utils/waifu-metering.ts +302 -0
- package/dist/onboarding.d.ts +0 -35
- package/dist/onboarding.d.ts.map +0 -1
- package/dist/onboarding.js.map +0 -14
- package/src/onboarding.ts +0 -396
package/dist/models/index.js
CHANGED
|
@@ -19,7 +19,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
19
19
|
// src/utils/config.ts
|
|
20
20
|
import { logger } from "@elizaos/core";
|
|
21
21
|
import { DEFAULT_ELIZA_CLOUD_TEXT_MODEL } from "@elizaos/core";
|
|
22
|
-
var DEFAULT_ELIZA_CLOUD_LARGE_MODEL = "
|
|
22
|
+
var DEFAULT_ELIZA_CLOUD_LARGE_MODEL = "zai-glm-4.7";
|
|
23
23
|
function getEnvValue(key) {
|
|
24
24
|
if (typeof process === "undefined") {
|
|
25
25
|
return;
|
|
@@ -109,9 +109,18 @@ function getExperimentalTelemetry(runtime) {
|
|
|
109
109
|
const setting = getSetting(runtime, "ELIZAOS_CLOUD_EXPERIMENTAL_TELEMETRY", "false");
|
|
110
110
|
return String(setting).toLowerCase() === "true";
|
|
111
111
|
}
|
|
112
|
+
function resolveCloudTimeoutMs(envKey, defaultMs) {
|
|
113
|
+
const raw = typeof process !== "undefined" ? process.env[envKey] : undefined;
|
|
114
|
+
if (raw === undefined || raw.trim() === "")
|
|
115
|
+
return defaultMs;
|
|
116
|
+
const parsed = Number.parseInt(raw, 10);
|
|
117
|
+
if (!Number.isFinite(parsed))
|
|
118
|
+
return defaultMs;
|
|
119
|
+
return parsed <= 0 ? undefined : parsed;
|
|
120
|
+
}
|
|
112
121
|
|
|
113
122
|
// src/utils/sdk-client.ts
|
|
114
|
-
import {
|
|
123
|
+
import { ElizaCloudClient } from "@elizaos/cloud-sdk";
|
|
115
124
|
function trimTrailingSlash(value) {
|
|
116
125
|
return value.replace(/\/+$/, "");
|
|
117
126
|
}
|
|
@@ -126,7 +135,11 @@ function apiKeyForRuntime(runtime, embedding = false) {
|
|
|
126
135
|
}
|
|
127
136
|
function createCloudApiClient(runtime, embedding = false) {
|
|
128
137
|
const baseUrl = embedding ? getEmbeddingBaseURL(runtime) : getBaseURL(runtime);
|
|
129
|
-
return new
|
|
138
|
+
return new ElizaCloudClient({
|
|
139
|
+
apiBaseUrl: trimTrailingSlash(baseUrl),
|
|
140
|
+
baseUrl: apiBaseToSiteBaseUrl(baseUrl),
|
|
141
|
+
apiKey: apiKeyForRuntime(runtime, embedding)
|
|
142
|
+
}).v1;
|
|
130
143
|
}
|
|
131
144
|
function createElizaCloudClient(runtime) {
|
|
132
145
|
const apiBaseUrl = trimTrailingSlash(getBaseURL(runtime));
|
|
@@ -141,7 +154,7 @@ function createElizaCloudClient(runtime) {
|
|
|
141
154
|
import {
|
|
142
155
|
EventType
|
|
143
156
|
} from "@elizaos/core";
|
|
144
|
-
function emitModelUsageEvent(runtime, type, _prompt, usage) {
|
|
157
|
+
function emitModelUsageEvent(runtime, type, _prompt, usage, meta = {}) {
|
|
145
158
|
const inputTokens = Number(usage.inputTokens || 0);
|
|
146
159
|
const outputTokens = Number(usage.outputTokens || 0);
|
|
147
160
|
const totalTokens = Number(usage.totalTokens != null ? usage.totalTokens : inputTokens + outputTokens);
|
|
@@ -153,14 +166,34 @@ function emitModelUsageEvent(runtime, type, _prompt, usage) {
|
|
|
153
166
|
prompt: inputTokens,
|
|
154
167
|
completion: outputTokens,
|
|
155
168
|
total: totalTokens
|
|
156
|
-
}
|
|
169
|
+
},
|
|
170
|
+
...meta.modelName ? { modelName: meta.modelName } : {},
|
|
171
|
+
...typeof meta.costUsd === "number" && Number.isFinite(meta.costUsd) ? { costUsd: meta.costUsd } : {}
|
|
157
172
|
};
|
|
158
173
|
runtime.emitEvent(EventType.MODEL_USED, payload);
|
|
159
174
|
}
|
|
160
175
|
|
|
161
176
|
// src/models/embeddings.ts
|
|
162
|
-
import {
|
|
177
|
+
import {
|
|
178
|
+
logger as logger2,
|
|
179
|
+
ModelType,
|
|
180
|
+
timeInferenceSpan,
|
|
181
|
+
VECTOR_DIMS
|
|
182
|
+
} from "@elizaos/core";
|
|
163
183
|
var MAX_BATCH_SIZE = 100;
|
|
184
|
+
var EMBED_MAX_ATTEMPTS = 2;
|
|
185
|
+
var EMBED_BACKOFF_BASE_MS = 1000;
|
|
186
|
+
var EMBED_BACKOFF_CAP_MS = 8000;
|
|
187
|
+
var EMBED_REQUEST_TIMEOUT_MS = 60000;
|
|
188
|
+
function embeddingBackoffMs(attempt, retryAfterSec) {
|
|
189
|
+
const exp = EMBED_BACKOFF_BASE_MS * 2 ** attempt;
|
|
190
|
+
const serverHint = typeof retryAfterSec === "number" && retryAfterSec > 0 ? retryAfterSec * 1000 : 0;
|
|
191
|
+
const base = Math.min(EMBED_BACKOFF_CAP_MS, Math.max(exp, serverHint));
|
|
192
|
+
return Math.round(base * (1 + Math.random() * 0.25));
|
|
193
|
+
}
|
|
194
|
+
function sleep(ms) {
|
|
195
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
196
|
+
}
|
|
164
197
|
function extractRateLimitInfo(response) {
|
|
165
198
|
return {
|
|
166
199
|
remainingRequests: parseInt(response.headers.get("x-ratelimit-remaining-requests") || "", 10) || undefined,
|
|
@@ -182,16 +215,16 @@ function getEmbeddingConfig(runtime) {
|
|
|
182
215
|
}
|
|
183
216
|
return { embeddingModelName, embeddingDimension };
|
|
184
217
|
}
|
|
185
|
-
function
|
|
218
|
+
function createInitProbeVector(dimension) {
|
|
186
219
|
const vector = Array(dimension).fill(0);
|
|
187
|
-
vector[0] =
|
|
220
|
+
vector[0] = 0.1;
|
|
188
221
|
return vector;
|
|
189
222
|
}
|
|
190
223
|
async function handleTextEmbedding(runtime, params) {
|
|
191
224
|
const { embeddingDimension } = getEmbeddingConfig(runtime);
|
|
192
225
|
if (params === null) {
|
|
193
226
|
logger2.debug("Creating test embedding for initialization");
|
|
194
|
-
return
|
|
227
|
+
return createInitProbeVector(embeddingDimension);
|
|
195
228
|
}
|
|
196
229
|
let text;
|
|
197
230
|
if (typeof params === "string") {
|
|
@@ -199,12 +232,10 @@ async function handleTextEmbedding(runtime, params) {
|
|
|
199
232
|
} else if (typeof params === "object" && params.text) {
|
|
200
233
|
text = params.text;
|
|
201
234
|
} else {
|
|
202
|
-
|
|
203
|
-
return createErrorVector(embeddingDimension, 0.2);
|
|
235
|
+
throw new Error("Invalid input format for embedding: expected string or { text: string }");
|
|
204
236
|
}
|
|
205
237
|
if (!text.trim()) {
|
|
206
|
-
|
|
207
|
-
return createErrorVector(embeddingDimension, 0.3);
|
|
238
|
+
throw new Error("Cannot generate embedding for empty text");
|
|
208
239
|
}
|
|
209
240
|
const results = await handleBatchTextEmbedding(runtime, [text]);
|
|
210
241
|
return results[0];
|
|
@@ -213,80 +244,61 @@ async function handleBatchTextEmbedding(runtime, texts) {
|
|
|
213
244
|
const { embeddingModelName, embeddingDimension } = getEmbeddingConfig(runtime);
|
|
214
245
|
const client = createCloudApiClient(runtime, true);
|
|
215
246
|
if (!texts || texts.length === 0) {
|
|
216
|
-
logger2.warn("[BatchEmbeddings] Empty texts array");
|
|
217
247
|
return [];
|
|
218
248
|
}
|
|
219
249
|
const validTexts = [];
|
|
220
|
-
const results = new Array(texts.length);
|
|
221
250
|
for (let i = 0;i < texts.length; i++) {
|
|
222
251
|
const text = texts[i]?.trim();
|
|
223
|
-
if (text) {
|
|
224
|
-
|
|
225
|
-
} else {
|
|
226
|
-
results[i] = createErrorVector(embeddingDimension, 0.3);
|
|
252
|
+
if (!text) {
|
|
253
|
+
throw new Error(`Cannot generate embedding for empty text at index ${i}`);
|
|
227
254
|
}
|
|
255
|
+
validTexts.push({ text, originalIndex: i });
|
|
228
256
|
}
|
|
229
|
-
|
|
230
|
-
logger2.warn("[BatchEmbeddings] All texts were empty");
|
|
231
|
-
return results;
|
|
232
|
-
}
|
|
257
|
+
const results = new Array(texts.length);
|
|
233
258
|
for (let batchStart = 0;batchStart < validTexts.length; batchStart += MAX_BATCH_SIZE) {
|
|
234
259
|
const batchEnd = Math.min(batchStart + MAX_BATCH_SIZE, validTexts.length);
|
|
235
260
|
const batch = validTexts.slice(batchStart, batchEnd);
|
|
236
261
|
const batchTexts = batch.map((b) => b.text);
|
|
237
262
|
logger2.info(`[BatchEmbeddings] Processing batch ${Math.floor(batchStart / MAX_BATCH_SIZE) + 1}/${Math.ceil(validTexts.length / MAX_BATCH_SIZE)}: ${batch.length} texts`);
|
|
238
263
|
try {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
input: batchTexts
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
const rateLimitInfo = extractRateLimitInfo(response);
|
|
246
|
-
if (rateLimitInfo.remainingRequests !== undefined && rateLimitInfo.remainingRequests < 50) {
|
|
247
|
-
logger2.warn(`[BatchEmbeddings] Rate limit: ${rateLimitInfo.remainingRequests}/${rateLimitInfo.limitRequests} requests remaining`);
|
|
248
|
-
}
|
|
249
|
-
if (response.status === 429) {
|
|
250
|
-
const retryAfter = rateLimitInfo.retryAfter || 30;
|
|
251
|
-
logger2.warn(`[BatchEmbeddings] Rate limited, waiting ${retryAfter}s...`);
|
|
252
|
-
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
|
|
253
|
-
const retryResponse = await client.requestRaw("POST", "/embeddings", {
|
|
264
|
+
let response = null;
|
|
265
|
+
for (let attempt = 0;attempt < EMBED_MAX_ATTEMPTS; attempt++) {
|
|
266
|
+
const resp = await timeInferenceSpan("cloud.embedding", () => client.requestRaw("POST", "/embeddings", {
|
|
254
267
|
json: {
|
|
255
268
|
model: embeddingModelName,
|
|
256
269
|
input: batchTexts
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
continue;
|
|
270
|
+
},
|
|
271
|
+
timeoutMs: EMBED_REQUEST_TIMEOUT_MS
|
|
272
|
+
}), { batch: batchTexts.length, attempt });
|
|
273
|
+
const rateLimitInfo = extractRateLimitInfo(resp);
|
|
274
|
+
if (rateLimitInfo.remainingRequests !== undefined && rateLimitInfo.remainingRequests < 50) {
|
|
275
|
+
logger2.warn(`[BatchEmbeddings] Rate limit: ${rateLimitInfo.remainingRequests}/${rateLimitInfo.limitRequests} requests remaining`);
|
|
265
276
|
}
|
|
266
|
-
const
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
277
|
+
const transient = resp.status === 429 || resp.status === 502 || resp.status === 503 || resp.status === 504;
|
|
278
|
+
if (transient && attempt < EMBED_MAX_ATTEMPTS - 1) {
|
|
279
|
+
const delay = embeddingBackoffMs(attempt, rateLimitInfo.retryAfter);
|
|
280
|
+
logger2.warn(`[BatchEmbeddings] ${resp.status} (attempt ${attempt + 1}/${EMBED_MAX_ATTEMPTS}) — backing off ${delay}ms`);
|
|
281
|
+
await resp.text().catch(() => {
|
|
282
|
+
return;
|
|
283
|
+
});
|
|
284
|
+
await sleep(delay);
|
|
285
|
+
continue;
|
|
273
286
|
}
|
|
274
|
-
|
|
287
|
+
response = resp;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
if (!response) {
|
|
291
|
+
throw new Error("[BatchEmbeddings] No response after retry loop");
|
|
275
292
|
}
|
|
276
293
|
if (!response.ok) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
results[item.originalIndex] = createErrorVector(embeddingDimension, 0.4);
|
|
294
|
+
if (response.status === 401 || response.status === 403) {
|
|
295
|
+
throw new Error(`[BatchEmbeddings] Authentication failed (${response.status}). ` + `Check ELIZAOS_CLOUD_API_KEY or ELIZAOS_CLOUD_EMBEDDING_API_KEY — ` + `the current key is not authorized for the embedding endpoint.`);
|
|
280
296
|
}
|
|
281
|
-
|
|
297
|
+
throw new Error(`[BatchEmbeddings] API error: ${response.status} ${response.statusText}`);
|
|
282
298
|
}
|
|
283
299
|
const data = await response.json();
|
|
284
300
|
if (!data?.data || !Array.isArray(data.data)) {
|
|
285
|
-
|
|
286
|
-
for (const item of batch) {
|
|
287
|
-
results[item.originalIndex] = createErrorVector(embeddingDimension, 0.5);
|
|
288
|
-
}
|
|
289
|
-
continue;
|
|
301
|
+
throw new Error("[BatchEmbeddings] API returned invalid response structure");
|
|
290
302
|
}
|
|
291
303
|
for (const item of data.data) {
|
|
292
304
|
const originalIndex = batch[item.index].originalIndex;
|
|
@@ -300,13 +312,11 @@ async function handleBatchTextEmbedding(runtime, texts) {
|
|
|
300
312
|
};
|
|
301
313
|
emitModelUsageEvent(runtime, ModelType.TEXT_EMBEDDING, `batch:${batch.length}`, usage);
|
|
302
314
|
}
|
|
303
|
-
logger2.debug(`[BatchEmbeddings] Got ${batch.length} embeddings (${embeddingDimension}d)
|
|
315
|
+
logger2.debug(`[BatchEmbeddings] Got ${batch.length} embeddings (${embeddingDimension}d)`);
|
|
304
316
|
} catch (error) {
|
|
305
317
|
const message = error instanceof Error ? error.message : String(error);
|
|
306
|
-
logger2.error(`[BatchEmbeddings]
|
|
307
|
-
|
|
308
|
-
results[item.originalIndex] = createErrorVector(embeddingDimension, 0.6);
|
|
309
|
-
}
|
|
318
|
+
logger2.error(`[BatchEmbeddings] Batch failed: ${message}`);
|
|
319
|
+
throw error instanceof Error ? error : new Error(message);
|
|
310
320
|
}
|
|
311
321
|
}
|
|
312
322
|
return results;
|
|
@@ -424,7 +434,10 @@ async function handleImageGeneration(runtime, params) {
|
|
|
424
434
|
}
|
|
425
435
|
async function handleImageDescription(runtime, params) {
|
|
426
436
|
const disableSetting = getSetting(runtime, "DISABLE_IMAGE_DESCRIPTION", "");
|
|
427
|
-
const disabled = disableSetting
|
|
437
|
+
const disabled = [disableSetting, process.env.DISABLE_IMAGE_DESCRIPTION].some((value) => {
|
|
438
|
+
const normalized = value?.trim().toLowerCase();
|
|
439
|
+
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
|
440
|
+
});
|
|
428
441
|
if (disabled) {
|
|
429
442
|
logger4.debug("[ELIZAOS_CLOUD] IMAGE_DESCRIPTION skipped — DISABLE_IMAGE_DESCRIPTION is set");
|
|
430
443
|
return {
|
|
@@ -463,16 +476,21 @@ async function handleImageDescription(runtime, params) {
|
|
|
463
476
|
let response = null;
|
|
464
477
|
let attemptedRetry = false;
|
|
465
478
|
for (let attempt = 0;attempt < 2; attempt++) {
|
|
466
|
-
|
|
467
|
-
json: requestBody
|
|
479
|
+
const attemptResponse = await client.routes.postApiV1ChatCompletionsRaw({
|
|
480
|
+
json: requestBody,
|
|
481
|
+
timeoutMs: resolveCloudTimeoutMs("ELIZAOS_CLOUD_IMAGE_TIMEOUT_MS", 120000)
|
|
468
482
|
});
|
|
469
|
-
if (
|
|
483
|
+
if (!attemptResponse) {
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
response = attemptResponse;
|
|
487
|
+
if (attemptResponse.status !== 429 || attemptedRetry)
|
|
470
488
|
break;
|
|
471
|
-
const headerValue =
|
|
489
|
+
const headerValue = attemptResponse.headers.get("retry-after");
|
|
472
490
|
const headerRetryAfter = headerValue !== null && Number.isFinite(Number(headerValue)) ? Number(headerValue) : undefined;
|
|
473
491
|
let bodyRetryAfter;
|
|
474
492
|
try {
|
|
475
|
-
const peek = await
|
|
493
|
+
const peek = await attemptResponse.clone().json();
|
|
476
494
|
bodyRetryAfter = typeof peek?.retryAfter === "number" && Number.isFinite(peek.retryAfter) ? peek.retryAfter : undefined;
|
|
477
495
|
} catch {}
|
|
478
496
|
const retryAfter = headerRetryAfter ?? bodyRetryAfter ?? 0;
|
|
@@ -485,8 +503,12 @@ async function handleImageDescription(runtime, params) {
|
|
|
485
503
|
logger4.warn(`[ELIZAOS_CLOUD] Image analysis rate-limited (429); upstream retryAfter=${retryAfter || "unknown"}s — failing fast`);
|
|
486
504
|
break;
|
|
487
505
|
}
|
|
488
|
-
if (!response
|
|
489
|
-
|
|
506
|
+
if (!response) {
|
|
507
|
+
throw new Error("ElizaOS Cloud API did not return a response");
|
|
508
|
+
}
|
|
509
|
+
const finalResponse = response;
|
|
510
|
+
if (!finalResponse.ok) {
|
|
511
|
+
const status = finalResponse.status;
|
|
490
512
|
if (status === 402) {
|
|
491
513
|
throw new Error("Eliza Cloud credits exhausted — top up at https://www.elizacloud.ai/dashboard/settings?tab=billing");
|
|
492
514
|
}
|
|
@@ -495,7 +517,7 @@ async function handleImageDescription(runtime, params) {
|
|
|
495
517
|
}
|
|
496
518
|
throw new Error(`ElizaOS Cloud API error: ${status}`);
|
|
497
519
|
}
|
|
498
|
-
const typedResult = await
|
|
520
|
+
const typedResult = await finalResponse.json();
|
|
499
521
|
const content = typedResult.choices?.[0]?.message?.content;
|
|
500
522
|
if (typedResult.usage) {
|
|
501
523
|
emitModelUsageEvent(runtime, ModelType2.IMAGE_DESCRIPTION, typeof params === "string" ? params : params.prompt || "", {
|
|
@@ -523,6 +545,7 @@ async function handleImageDescription(runtime, params) {
|
|
|
523
545
|
|
|
524
546
|
// src/models/research.ts
|
|
525
547
|
import { logger as logger5, ModelType as ModelType3 } from "@elizaos/core";
|
|
548
|
+
var DEFAULT_RESEARCH_TIMEOUT_MS = 600000;
|
|
526
549
|
function normalizeInput(input) {
|
|
527
550
|
if (typeof input !== "string") {
|
|
528
551
|
return input;
|
|
@@ -645,7 +668,8 @@ async function handleResearch(runtime, params) {
|
|
|
645
668
|
requestBody.reasoning = { summary: params.reasoningSummary };
|
|
646
669
|
}
|
|
647
670
|
const response = await createCloudApiClient(runtime).requestRaw("POST", "/responses", {
|
|
648
|
-
json: requestBody
|
|
671
|
+
json: requestBody,
|
|
672
|
+
timeoutMs: resolveCloudTimeoutMs("ELIZAOS_CLOUD_RESEARCH_TIMEOUT_MS", DEFAULT_RESEARCH_TIMEOUT_MS)
|
|
649
673
|
});
|
|
650
674
|
if (!response.ok) {
|
|
651
675
|
const errorText = await response.text();
|
|
@@ -682,17 +706,59 @@ async function handleResearch(runtime, params) {
|
|
|
682
706
|
}
|
|
683
707
|
|
|
684
708
|
// src/models/speech.ts
|
|
685
|
-
import { logger as logger6 } from "@elizaos/core";
|
|
709
|
+
import { isCloudConnected, logger as logger6, toRuntimeSettings } from "@elizaos/core";
|
|
710
|
+
var cloudTtsClientFactory = (runtime) => createElizaCloudClient(runtime);
|
|
711
|
+
function setCloudTtsClientFactoryForTesting(factory) {
|
|
712
|
+
if (factory === null) {
|
|
713
|
+
cloudTtsClientFactory = (runtime) => createElizaCloudClient(runtime);
|
|
714
|
+
} else {
|
|
715
|
+
cloudTtsClientFactory = factory;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
class CloudTtsUnavailableError extends Error {
|
|
720
|
+
constructor(message = "Eliza Cloud is not connected") {
|
|
721
|
+
super(message);
|
|
722
|
+
this.name = "CloudTtsUnavailableError";
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
function normalizeTextInput(input) {
|
|
726
|
+
if (typeof input === "string")
|
|
727
|
+
return { text: input };
|
|
728
|
+
return input;
|
|
729
|
+
}
|
|
730
|
+
function resolveModelId(options) {
|
|
731
|
+
if (options.modelId && options.modelId.trim()) {
|
|
732
|
+
return options.modelId.trim();
|
|
733
|
+
}
|
|
734
|
+
const model = options.model?.trim();
|
|
735
|
+
if (!model)
|
|
736
|
+
return;
|
|
737
|
+
if (model.startsWith("elevenlabs/")) {
|
|
738
|
+
return model.split("/").slice(1).join("/");
|
|
739
|
+
}
|
|
740
|
+
if (model.startsWith("eleven_")) {
|
|
741
|
+
return model;
|
|
742
|
+
}
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
function resolveVoiceId(options) {
|
|
746
|
+
if (options.voiceId && options.voiceId.trim()) {
|
|
747
|
+
return options.voiceId.trim();
|
|
748
|
+
}
|
|
749
|
+
const voice = options.voice?.trim();
|
|
750
|
+
if (!voice)
|
|
751
|
+
return;
|
|
752
|
+
if (voice === "nova")
|
|
753
|
+
return;
|
|
754
|
+
return voice;
|
|
755
|
+
}
|
|
686
756
|
async function fetchTextToSpeech(runtime, options) {
|
|
687
|
-
const defaultModel = getSetting(runtime, "ELIZAOS_CLOUD_TTS_MODEL", "gpt-5-mini-tts");
|
|
688
|
-
const defaultVoice = getSetting(runtime, "ELIZAOS_CLOUD_TTS_VOICE", "nova");
|
|
689
|
-
const model = options.model || defaultModel;
|
|
690
|
-
const voice = options.voice || defaultVoice;
|
|
691
757
|
const format = options.format || "mp3";
|
|
692
|
-
const modelId =
|
|
693
|
-
const voiceId =
|
|
758
|
+
const modelId = resolveModelId(options);
|
|
759
|
+
const voiceId = resolveVoiceId(options);
|
|
694
760
|
try {
|
|
695
|
-
const res = await
|
|
761
|
+
const res = await cloudTtsClientFactory(runtime).routes.postApiV1VoiceTts({
|
|
696
762
|
headers: {
|
|
697
763
|
...format === "mp3" ? { Accept: "audio/mpeg" } : {}
|
|
698
764
|
},
|
|
@@ -700,7 +766,8 @@ async function fetchTextToSpeech(runtime, options) {
|
|
|
700
766
|
text: options.text,
|
|
701
767
|
...voiceId ? { voiceId } : {},
|
|
702
768
|
...modelId ? { modelId } : {}
|
|
703
|
-
}
|
|
769
|
+
},
|
|
770
|
+
timeoutMs: resolveCloudTimeoutMs("ELIZAOS_CLOUD_TTS_TIMEOUT_MS", 60000)
|
|
704
771
|
});
|
|
705
772
|
if (!res.ok) {
|
|
706
773
|
const err = await res.text();
|
|
@@ -718,30 +785,117 @@ async function fetchTextToSpeech(runtime, options) {
|
|
|
718
785
|
throw new Error(`Failed to fetch speech from ElizaOS Cloud TTS: ${message}`);
|
|
719
786
|
}
|
|
720
787
|
}
|
|
788
|
+
function toUint8Array(chunk) {
|
|
789
|
+
if (chunk instanceof Uint8Array)
|
|
790
|
+
return chunk;
|
|
791
|
+
if (chunk instanceof ArrayBuffer)
|
|
792
|
+
return new Uint8Array(chunk);
|
|
793
|
+
if (typeof chunk === "string")
|
|
794
|
+
return new TextEncoder().encode(chunk);
|
|
795
|
+
throw new TypeError(`Unexpected TTS chunk type: ${typeof chunk}`);
|
|
796
|
+
}
|
|
797
|
+
function concatChunks(chunks) {
|
|
798
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
799
|
+
const out = new Uint8Array(total);
|
|
800
|
+
let offset = 0;
|
|
801
|
+
for (const chunk of chunks) {
|
|
802
|
+
out.set(chunk, offset);
|
|
803
|
+
offset += chunk.byteLength;
|
|
804
|
+
}
|
|
805
|
+
return out;
|
|
806
|
+
}
|
|
807
|
+
async function webStreamToUint8Array(stream) {
|
|
808
|
+
const reader = stream.getReader();
|
|
809
|
+
const chunks = [];
|
|
810
|
+
try {
|
|
811
|
+
while (true) {
|
|
812
|
+
const result = await reader.read();
|
|
813
|
+
if (result.done)
|
|
814
|
+
break;
|
|
815
|
+
chunks.push(toUint8Array(result.value));
|
|
816
|
+
}
|
|
817
|
+
} finally {
|
|
818
|
+
reader.releaseLock();
|
|
819
|
+
}
|
|
820
|
+
return concatChunks(chunks);
|
|
821
|
+
}
|
|
822
|
+
async function nodeStreamToUint8Array(stream) {
|
|
823
|
+
const chunks = [];
|
|
824
|
+
for await (const chunk of stream) {
|
|
825
|
+
chunks.push(toUint8Array(chunk));
|
|
826
|
+
}
|
|
827
|
+
return concatChunks(chunks);
|
|
828
|
+
}
|
|
829
|
+
function isReadableStream(stream) {
|
|
830
|
+
return typeof stream.getReader === "function";
|
|
831
|
+
}
|
|
832
|
+
async function ttsStreamToBytes(stream) {
|
|
833
|
+
if (isReadableStream(stream)) {
|
|
834
|
+
return webStreamToUint8Array(stream);
|
|
835
|
+
}
|
|
836
|
+
return nodeStreamToUint8Array(stream);
|
|
837
|
+
}
|
|
838
|
+
function buildAudioStreamResult(stream, mimeType) {
|
|
839
|
+
const collected = [];
|
|
840
|
+
let resolveBytes;
|
|
841
|
+
let rejectBytes;
|
|
842
|
+
const bytes = new Promise((resolve, reject) => {
|
|
843
|
+
resolveBytes = resolve;
|
|
844
|
+
rejectBytes = reject;
|
|
845
|
+
});
|
|
846
|
+
async function* generate() {
|
|
847
|
+
try {
|
|
848
|
+
if (isReadableStream(stream)) {
|
|
849
|
+
const reader = stream.getReader();
|
|
850
|
+
try {
|
|
851
|
+
for (;; ) {
|
|
852
|
+
const { value, done } = await reader.read();
|
|
853
|
+
if (done)
|
|
854
|
+
break;
|
|
855
|
+
const chunk = toUint8Array(value);
|
|
856
|
+
collected.push(chunk);
|
|
857
|
+
yield chunk;
|
|
858
|
+
}
|
|
859
|
+
} finally {
|
|
860
|
+
reader.releaseLock();
|
|
861
|
+
}
|
|
862
|
+
} else {
|
|
863
|
+
for await (const value of stream) {
|
|
864
|
+
const chunk = toUint8Array(value);
|
|
865
|
+
collected.push(chunk);
|
|
866
|
+
yield chunk;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
resolveBytes(concatChunks(collected));
|
|
870
|
+
} catch (err) {
|
|
871
|
+
rejectBytes(err);
|
|
872
|
+
throw err;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return { audioStream: generate(), bytes, mimeType };
|
|
876
|
+
}
|
|
721
877
|
async function handleTextToSpeech(runtime, input) {
|
|
722
|
-
|
|
723
|
-
|
|
878
|
+
if (!isCloudConnected(toRuntimeSettings(runtime))) {
|
|
879
|
+
throw new CloudTtsUnavailableError("Eliza Cloud is not connected — falling through to next TTS handler");
|
|
880
|
+
}
|
|
881
|
+
const options = normalizeTextInput(input);
|
|
882
|
+
const wantsStream = typeof input === "object" && input !== null && input.audioStream === true;
|
|
883
|
+
const resolvedModel = options.modelId || options.model || getSetting(runtime, "ELIZAOS_CLOUD_TTS_MODEL", "eleven_flash_v2_5");
|
|
724
884
|
logger6.log(`[ELIZAOS_CLOUD] Using TEXT_TO_SPEECH model: ${resolvedModel}`);
|
|
725
885
|
try {
|
|
726
886
|
const speechStream = await fetchTextToSpeech(runtime, options);
|
|
727
|
-
|
|
887
|
+
if (wantsStream) {
|
|
888
|
+
const format = options.format || "mp3";
|
|
889
|
+
const mimeType = format === "mp3" ? "audio/mpeg" : `audio/${format}`;
|
|
890
|
+
return buildAudioStreamResult(speechStream, mimeType);
|
|
891
|
+
}
|
|
892
|
+
return ttsStreamToBytes(speechStream);
|
|
728
893
|
} catch (error) {
|
|
729
894
|
const message = error instanceof Error ? error.message : String(error);
|
|
730
895
|
logger6.error(`Error in TEXT_TO_SPEECH: ${message}`);
|
|
731
896
|
throw error;
|
|
732
897
|
}
|
|
733
898
|
}
|
|
734
|
-
// src/providers/openai.ts
|
|
735
|
-
import { createOpenAI } from "@ai-sdk/openai";
|
|
736
|
-
function createOpenAIClient(runtime) {
|
|
737
|
-
const baseURL = getBaseURL(runtime);
|
|
738
|
-
const apiKey = getApiKey(runtime) ?? (isProxyMode(runtime) ? "eliza-proxy" : undefined);
|
|
739
|
-
return createOpenAI({
|
|
740
|
-
apiKey: apiKey ?? "",
|
|
741
|
-
baseURL
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
|
|
745
899
|
// src/utils/responses-output.ts
|
|
746
900
|
function asRecord(value) {
|
|
747
901
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -827,8 +981,11 @@ import {
|
|
|
827
981
|
buildCanonicalSystemPrompt,
|
|
828
982
|
logger as logger7,
|
|
829
983
|
ModelType as ModelType4,
|
|
984
|
+
recordInferenceSpan,
|
|
830
985
|
renderChatMessagesForPrompt,
|
|
831
|
-
resolveEffectiveSystemPrompt
|
|
986
|
+
resolveEffectiveSystemPrompt,
|
|
987
|
+
Semaphore,
|
|
988
|
+
timeInferenceSpan as timeInferenceSpan2
|
|
832
989
|
} from "@elizaos/core";
|
|
833
990
|
var TEXT_NANO_MODEL_TYPE = ModelType4.TEXT_NANO ?? "TEXT_NANO";
|
|
834
991
|
var TEXT_MEDIUM_MODEL_TYPE = ModelType4.TEXT_MEDIUM ?? "TEXT_MEDIUM";
|
|
@@ -837,6 +994,61 @@ var TEXT_LARGE_MODEL_TYPE = ModelType4.TEXT_LARGE;
|
|
|
837
994
|
var TEXT_MEGA_MODEL_TYPE = ModelType4.TEXT_MEGA ?? "TEXT_MEGA";
|
|
838
995
|
var RESPONSE_HANDLER_MODEL_TYPE = ModelType4.RESPONSE_HANDLER ?? "RESPONSE_HANDLER";
|
|
839
996
|
var ACTION_PLANNER_MODEL_TYPE = ModelType4.ACTION_PLANNER ?? "ACTION_PLANNER";
|
|
997
|
+
var NATIVE_CONCURRENCY_ENV = "ELIZAOS_CLOUD_NATIVE_CONCURRENCY";
|
|
998
|
+
var DEFAULT_NATIVE_CONCURRENCY = 8;
|
|
999
|
+
var TEXT_TIMEOUT_ENV = "ELIZAOS_CLOUD_TEXT_TIMEOUT_MS";
|
|
1000
|
+
var DEFAULT_TEXT_TIMEOUT_MS = 120000;
|
|
1001
|
+
function resolveTextTimeoutMs() {
|
|
1002
|
+
const raw = typeof process !== "undefined" ? process.env[TEXT_TIMEOUT_ENV] : undefined;
|
|
1003
|
+
if (raw === undefined || raw.trim() === "")
|
|
1004
|
+
return DEFAULT_TEXT_TIMEOUT_MS;
|
|
1005
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1006
|
+
if (!Number.isFinite(parsed))
|
|
1007
|
+
return DEFAULT_TEXT_TIMEOUT_MS;
|
|
1008
|
+
return parsed <= 0 ? undefined : parsed;
|
|
1009
|
+
}
|
|
1010
|
+
var STREAMING_ENV = "ELIZAOS_CLOUD_STREAMING";
|
|
1011
|
+
function resolveStreamingEnabled() {
|
|
1012
|
+
const raw = typeof process !== "undefined" ? process.env[STREAMING_ENV] : undefined;
|
|
1013
|
+
if (raw === undefined)
|
|
1014
|
+
return true;
|
|
1015
|
+
const v = raw.trim().toLowerCase();
|
|
1016
|
+
return v !== "0" && v !== "false" && v !== "off";
|
|
1017
|
+
}
|
|
1018
|
+
function buildStreamAbortSignal(abortSignal, timeoutMs) {
|
|
1019
|
+
const timeoutSig = typeof timeoutMs === "number" && timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
1020
|
+
if (abortSignal && timeoutSig)
|
|
1021
|
+
return AbortSignal.any([abortSignal, timeoutSig]);
|
|
1022
|
+
return abortSignal ?? timeoutSig;
|
|
1023
|
+
}
|
|
1024
|
+
var nativeChatLimiter = null;
|
|
1025
|
+
function resolveNativeConcurrency() {
|
|
1026
|
+
const raw = typeof process !== "undefined" ? process.env[NATIVE_CONCURRENCY_ENV] : undefined;
|
|
1027
|
+
const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
1028
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_NATIVE_CONCURRENCY;
|
|
1029
|
+
}
|
|
1030
|
+
function getNativeChatLimiter() {
|
|
1031
|
+
if (!nativeChatLimiter) {
|
|
1032
|
+
nativeChatLimiter = new Semaphore(resolveNativeConcurrency());
|
|
1033
|
+
}
|
|
1034
|
+
return nativeChatLimiter;
|
|
1035
|
+
}
|
|
1036
|
+
async function withNativeChatLimit(fn, label = "native") {
|
|
1037
|
+
const limiter = getNativeChatLimiter();
|
|
1038
|
+
const waitStartedAt = Date.now();
|
|
1039
|
+
await limiter.acquire();
|
|
1040
|
+
recordInferenceSpan("cloud.semaphore-wait", Date.now() - waitStartedAt, {
|
|
1041
|
+
route: label
|
|
1042
|
+
});
|
|
1043
|
+
try {
|
|
1044
|
+
return await timeInferenceSpan2(`cloud.http:${label}`, fn, { route: label });
|
|
1045
|
+
} finally {
|
|
1046
|
+
limiter.release();
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
function __resetNativeChatLimiterForTests() {
|
|
1050
|
+
nativeChatLimiter = null;
|
|
1051
|
+
}
|
|
840
1052
|
var REASONING_MODEL_PATTERNS = [
|
|
841
1053
|
"o1",
|
|
842
1054
|
"o3",
|
|
@@ -847,27 +1059,51 @@ var REASONING_MODEL_PATTERNS = [
|
|
|
847
1059
|
"claude-opus-4-7",
|
|
848
1060
|
"gpt-5"
|
|
849
1061
|
];
|
|
850
|
-
var
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1062
|
+
var SPAN_SAMPLER_HONORING_MODEL_PREFIXES = [
|
|
1063
|
+
"vast/eliza-1-",
|
|
1064
|
+
"elizaos/eliza-1-",
|
|
1065
|
+
"eliza-1-"
|
|
1066
|
+
];
|
|
1067
|
+
function isSpanSamplerHonoringModel(modelName) {
|
|
1068
|
+
const lower = modelName.toLowerCase();
|
|
1069
|
+
return SPAN_SAMPLER_HONORING_MODEL_PREFIXES.some((prefix) => lower.startsWith(prefix));
|
|
1070
|
+
}
|
|
1071
|
+
function buildSpanSamplerHeader(plan) {
|
|
1072
|
+
if (!plan || plan.overrides.length === 0)
|
|
1073
|
+
return;
|
|
1074
|
+
const overrides = plan.overrides.map((o) => {
|
|
1075
|
+
const wire = {
|
|
1076
|
+
span_index: o.spanIndex,
|
|
1077
|
+
temperature: o.temperature
|
|
1078
|
+
};
|
|
1079
|
+
if (typeof o.topK === "number")
|
|
1080
|
+
wire.top_k = o.topK;
|
|
1081
|
+
if (typeof o.topP === "number")
|
|
1082
|
+
wire.top_p = o.topP;
|
|
1083
|
+
return wire;
|
|
1084
|
+
});
|
|
1085
|
+
const body = { overrides };
|
|
1086
|
+
if (plan.strict === true)
|
|
1087
|
+
body.strict = true;
|
|
1088
|
+
return JSON.stringify(body);
|
|
1089
|
+
}
|
|
1090
|
+
function extractCostUsd(usage, response) {
|
|
1091
|
+
const fromBody = firstNumber(asRecord2(usage).cost_usd, asRecord2(usage).costUsd, asRecord2(usage).cost);
|
|
1092
|
+
if (typeof fromBody === "number" && Number.isFinite(fromBody)) {
|
|
1093
|
+
return fromBody;
|
|
1094
|
+
}
|
|
1095
|
+
const header = response?.headers?.get?.("X-Eliza-Cost-Usd");
|
|
1096
|
+
if (header) {
|
|
1097
|
+
const parsed = Number(header);
|
|
1098
|
+
if (Number.isFinite(parsed))
|
|
1099
|
+
return parsed;
|
|
860
1100
|
}
|
|
861
|
-
return
|
|
1101
|
+
return;
|
|
862
1102
|
}
|
|
863
1103
|
function isReasoningModel(modelName) {
|
|
864
1104
|
const lower = modelName.toLowerCase();
|
|
865
1105
|
return REASONING_MODEL_PATTERNS.some((pattern) => lower.includes(pattern));
|
|
866
1106
|
}
|
|
867
|
-
function supportsStopSequences(modelName) {
|
|
868
|
-
const lower = modelName.toLowerCase();
|
|
869
|
-
return !RESPONSES_ROUTED_PREFIXES.some((prefix) => lower.startsWith(prefix));
|
|
870
|
-
}
|
|
871
1107
|
function isRecord(value) {
|
|
872
1108
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
873
1109
|
}
|
|
@@ -944,26 +1180,39 @@ function unwrapJsonSchema(value) {
|
|
|
944
1180
|
const record = asRecord2(value);
|
|
945
1181
|
return record.schema ?? record.jsonSchema ?? value;
|
|
946
1182
|
}
|
|
1183
|
+
function normalizeNativeToolEntry(rawTool, fallbackName) {
|
|
1184
|
+
const tool = asRecord2(rawTool);
|
|
1185
|
+
const nested = asRecord2(tool.function);
|
|
1186
|
+
const name = firstString(nested.name, tool.name, fallbackName);
|
|
1187
|
+
if (!name) {
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
const description = firstString(nested.description, tool.description);
|
|
1191
|
+
const inputSchema = unwrapJsonSchema(nested.parameters ?? tool.inputSchema ?? tool.parameters ?? tool.schema ?? { type: "object" });
|
|
1192
|
+
return {
|
|
1193
|
+
type: "function",
|
|
1194
|
+
function: {
|
|
1195
|
+
name,
|
|
1196
|
+
...description ? { description } : {},
|
|
1197
|
+
parameters: inputSchema
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
947
1201
|
function normalizeNativeTools(tools) {
|
|
948
1202
|
if (!tools) {
|
|
949
1203
|
return;
|
|
950
1204
|
}
|
|
951
1205
|
if (Array.isArray(tools)) {
|
|
952
|
-
|
|
1206
|
+
const normalized2 = tools.map((tool) => normalizeNativeToolEntry(tool)).filter((tool) => tool !== undefined);
|
|
1207
|
+
return normalized2.length > 0 ? normalized2 : undefined;
|
|
953
1208
|
}
|
|
954
1209
|
const toolSet = asRecord2(tools);
|
|
955
1210
|
const normalized = [];
|
|
956
1211
|
for (const [name, rawTool] of Object.entries(toolSet)) {
|
|
957
|
-
const
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
function: {
|
|
962
|
-
name,
|
|
963
|
-
...typeof tool.description === "string" ? { description: tool.description } : {},
|
|
964
|
-
parameters: inputSchema
|
|
965
|
-
}
|
|
966
|
-
});
|
|
1212
|
+
const entry = normalizeNativeToolEntry(rawTool, name);
|
|
1213
|
+
if (entry) {
|
|
1214
|
+
normalized.push(entry);
|
|
1215
|
+
}
|
|
967
1216
|
}
|
|
968
1217
|
return normalized.length > 0 ? normalized : undefined;
|
|
969
1218
|
}
|
|
@@ -1189,16 +1438,8 @@ function getModelNameForType(runtime, modelType) {
|
|
|
1189
1438
|
}
|
|
1190
1439
|
}
|
|
1191
1440
|
function buildGenerateParams(runtime, modelType, params) {
|
|
1192
|
-
const paramsWithAttachments = params;
|
|
1193
1441
|
const prompt = params.prompt ?? "";
|
|
1194
|
-
const maxTokens = params.maxTokens ?? 8192;
|
|
1195
|
-
const openai = createOpenAIClient(runtime);
|
|
1196
1442
|
const modelName = getModelNameForType(runtime, modelType);
|
|
1197
|
-
const experimentalTelemetry = getExperimentalTelemetry(runtime);
|
|
1198
|
-
const userContent = (paramsWithAttachments.attachments?.length ?? 0) > 0 ? buildUserContent(paramsWithAttachments) : undefined;
|
|
1199
|
-
const model = openai.chat(modelName);
|
|
1200
|
-
const reasoning = isReasoningModel(modelName);
|
|
1201
|
-
const stopSequences = !reasoning && supportsStopSequences(modelName) && Array.isArray(params.stopSequences) && params.stopSequences.length > 0 ? params.stopSequences : undefined;
|
|
1202
1443
|
const systemPrompt = resolveEffectiveSystemPrompt({
|
|
1203
1444
|
params,
|
|
1204
1445
|
fallback: buildCanonicalSystemPrompt({ character: runtime.character })
|
|
@@ -1206,28 +1447,24 @@ function buildGenerateParams(runtime, modelType, params) {
|
|
|
1206
1447
|
const promptText = renderChatMessagesForPrompt(params.messages, {
|
|
1207
1448
|
omitDuplicateSystem: systemPrompt
|
|
1208
1449
|
}) ?? prompt;
|
|
1209
|
-
|
|
1210
|
-
model,
|
|
1211
|
-
...userContent ? { messages: [{ role: "user", content: userContent }] } : { prompt: promptText },
|
|
1212
|
-
system: systemPrompt,
|
|
1213
|
-
...stopSequences ? { stopSequences } : {},
|
|
1214
|
-
maxOutputTokens: maxTokens,
|
|
1215
|
-
experimental_telemetry: {
|
|
1216
|
-
isEnabled: experimentalTelemetry
|
|
1217
|
-
}
|
|
1218
|
-
};
|
|
1219
|
-
return { generateParams, modelName, modelType, prompt: promptText, systemPrompt };
|
|
1450
|
+
return { modelName, modelType, prompt: promptText, systemPrompt };
|
|
1220
1451
|
}
|
|
1221
1452
|
async function generateTextWithModel(runtime, modelType, params) {
|
|
1222
1453
|
const { modelName, prompt, systemPrompt } = buildGenerateParams(runtime, modelType, params);
|
|
1223
1454
|
const paramsWithNative = params;
|
|
1224
1455
|
logger7.debug(`[ELIZAOS_CLOUD] Generating text with ${modelType} model: ${modelName}`);
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
}
|
|
1456
|
+
const paramsStreaming = params;
|
|
1457
|
+
const wantsStream = Boolean(paramsStreaming.stream) && paramsStreaming.streamStructured === true && resolveStreamingEnabled();
|
|
1228
1458
|
logger7.log(`[ELIZAOS_CLOUD] Using ${modelType} model: ${modelName}`);
|
|
1229
1459
|
logger7.log(prompt);
|
|
1230
1460
|
if (hasNativeTransportOptions(paramsWithNative)) {
|
|
1461
|
+
if (wantsStream) {
|
|
1462
|
+
return streamNativeChatCompletion(runtime, modelType, paramsWithNative, {
|
|
1463
|
+
modelName,
|
|
1464
|
+
prompt,
|
|
1465
|
+
systemPrompt
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1231
1468
|
const nativeResult = await generateNativeChatCompletion(runtime, modelType, paramsWithNative, {
|
|
1232
1469
|
modelName,
|
|
1233
1470
|
prompt,
|
|
@@ -1255,13 +1492,21 @@ async function generateTextWithModel(runtime, modelType, params) {
|
|
|
1255
1492
|
if (!reasoning && typeof params.temperature === "number") {
|
|
1256
1493
|
requestBody.temperature = params.temperature;
|
|
1257
1494
|
}
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1495
|
+
const responsesHeaders = {
|
|
1496
|
+
"X-Eliza-Llm-Purpose": getPurposeForModelType(modelType),
|
|
1497
|
+
"X-Eliza-Model-Type": modelType
|
|
1498
|
+
};
|
|
1499
|
+
if (isSpanSamplerHonoringModel(modelName)) {
|
|
1500
|
+
const samplerHeader = buildSpanSamplerHeader(params.spanSamplerPlan);
|
|
1501
|
+
if (samplerHeader) {
|
|
1502
|
+
responsesHeaders["x-eliza-span-samplers"] = samplerHeader;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
const response = await withNativeChatLimit(() => createCloudApiClient(runtime).requestRaw("POST", "/responses", {
|
|
1506
|
+
headers: responsesHeaders,
|
|
1507
|
+
json: requestBody,
|
|
1508
|
+
timeoutMs: resolveTextTimeoutMs()
|
|
1509
|
+
}), "responses");
|
|
1265
1510
|
const responseText = await response.text();
|
|
1266
1511
|
let data = {};
|
|
1267
1512
|
if (responseText) {
|
|
@@ -1286,6 +1531,12 @@ async function generateTextWithModel(runtime, modelType, params) {
|
|
|
1286
1531
|
inputTokens: data.usage.input_tokens ?? 0,
|
|
1287
1532
|
outputTokens: data.usage.output_tokens ?? 0,
|
|
1288
1533
|
totalTokens: data.usage.total_tokens ?? 0
|
|
1534
|
+
}, {
|
|
1535
|
+
modelName: getModelNameForType(runtime, modelType),
|
|
1536
|
+
...(() => {
|
|
1537
|
+
const costUsd = extractCostUsd(data.usage, response);
|
|
1538
|
+
return typeof costUsd === "number" ? { costUsd } : {};
|
|
1539
|
+
})()
|
|
1289
1540
|
});
|
|
1290
1541
|
}
|
|
1291
1542
|
const text = extractResponsesOutputText(data);
|
|
@@ -1296,13 +1547,21 @@ async function generateTextWithModel(runtime, modelType, params) {
|
|
|
1296
1547
|
}
|
|
1297
1548
|
async function generateNativeChatCompletion(runtime, modelType, params, context) {
|
|
1298
1549
|
const requestBody = buildNativeRequestBody(params, context.modelName, context.prompt, context.systemPrompt);
|
|
1299
|
-
const
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1550
|
+
const headers = {
|
|
1551
|
+
"X-Eliza-Llm-Purpose": getPurposeForModelType(modelType),
|
|
1552
|
+
"X-Eliza-Model-Type": modelType
|
|
1553
|
+
};
|
|
1554
|
+
if (isSpanSamplerHonoringModel(context.modelName)) {
|
|
1555
|
+
const samplerHeader = buildSpanSamplerHeader(params.spanSamplerPlan);
|
|
1556
|
+
if (samplerHeader) {
|
|
1557
|
+
headers["x-eliza-span-samplers"] = samplerHeader;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
const response = await withNativeChatLimit(() => createCloudApiClient(runtime).requestRaw("POST", "/chat/completions", {
|
|
1561
|
+
headers,
|
|
1562
|
+
json: requestBody,
|
|
1563
|
+
timeoutMs: resolveTextTimeoutMs()
|
|
1564
|
+
}), "chat/completions");
|
|
1306
1565
|
const responseText = await response.text();
|
|
1307
1566
|
let data = {};
|
|
1308
1567
|
if (responseText) {
|
|
@@ -1324,7 +1583,13 @@ async function generateNativeChatCompletion(runtime, modelType, params, context)
|
|
|
1324
1583
|
}
|
|
1325
1584
|
const usage = convertNativeUsage(data.usage);
|
|
1326
1585
|
if (usage) {
|
|
1327
|
-
emitModelUsageEvent(runtime, modelType, context.prompt, usage
|
|
1586
|
+
emitModelUsageEvent(runtime, modelType, context.prompt, usage, {
|
|
1587
|
+
modelName: context.modelName,
|
|
1588
|
+
...(() => {
|
|
1589
|
+
const costUsd = extractCostUsd(data.usage, response);
|
|
1590
|
+
return typeof costUsd === "number" ? { costUsd } : {};
|
|
1591
|
+
})()
|
|
1592
|
+
});
|
|
1328
1593
|
}
|
|
1329
1594
|
const text = extractChatCompletionText(data);
|
|
1330
1595
|
const toolCalls = extractNativeToolCalls(data);
|
|
@@ -1342,6 +1607,251 @@ async function generateNativeChatCompletion(runtime, modelType, params, context)
|
|
|
1342
1607
|
}
|
|
1343
1608
|
};
|
|
1344
1609
|
}
|
|
1610
|
+
function deferred() {
|
|
1611
|
+
let resolve;
|
|
1612
|
+
const promise = new Promise((r) => {
|
|
1613
|
+
resolve = r;
|
|
1614
|
+
});
|
|
1615
|
+
return { promise, resolve };
|
|
1616
|
+
}
|
|
1617
|
+
async function* parseOpenAiSseStream(body) {
|
|
1618
|
+
const reader = body.getReader();
|
|
1619
|
+
const decoder = new TextDecoder;
|
|
1620
|
+
let buffer = "";
|
|
1621
|
+
const handle = (line) => {
|
|
1622
|
+
const trimmed = line.trimStart();
|
|
1623
|
+
if (!trimmed.startsWith("data:"))
|
|
1624
|
+
return null;
|
|
1625
|
+
const payload = trimmed.slice(5).trim();
|
|
1626
|
+
if (payload === "")
|
|
1627
|
+
return null;
|
|
1628
|
+
if (payload === "[DONE]")
|
|
1629
|
+
return "DONE";
|
|
1630
|
+
try {
|
|
1631
|
+
return JSON.parse(payload);
|
|
1632
|
+
} catch {
|
|
1633
|
+
return null;
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
try {
|
|
1637
|
+
for (;; ) {
|
|
1638
|
+
const { value, done } = await reader.read();
|
|
1639
|
+
if (done)
|
|
1640
|
+
break;
|
|
1641
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1642
|
+
let nl;
|
|
1643
|
+
while ((nl = buffer.indexOf(`
|
|
1644
|
+
`)) >= 0) {
|
|
1645
|
+
const line = buffer.slice(0, nl);
|
|
1646
|
+
buffer = buffer.slice(nl + 1);
|
|
1647
|
+
const frame = handle(line);
|
|
1648
|
+
if (frame === "DONE")
|
|
1649
|
+
return;
|
|
1650
|
+
if (frame)
|
|
1651
|
+
yield frame;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
const tail = handle(buffer);
|
|
1655
|
+
if (tail && tail !== "DONE")
|
|
1656
|
+
yield tail;
|
|
1657
|
+
} finally {
|
|
1658
|
+
try {
|
|
1659
|
+
await reader.cancel();
|
|
1660
|
+
} catch {}
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
function accumulateToolCallDeltas(acc, deltas) {
|
|
1664
|
+
if (!Array.isArray(deltas))
|
|
1665
|
+
return;
|
|
1666
|
+
for (const raw of deltas) {
|
|
1667
|
+
const d = asRecord2(raw);
|
|
1668
|
+
const index = typeof d.index === "number" ? d.index : 0;
|
|
1669
|
+
const cur = acc.get(index) ?? { args: "" };
|
|
1670
|
+
const id = firstString(d.id);
|
|
1671
|
+
if (id)
|
|
1672
|
+
cur.id = id;
|
|
1673
|
+
const fn = recordAt(d, "function");
|
|
1674
|
+
const name = firstString(fn.name);
|
|
1675
|
+
if (name)
|
|
1676
|
+
cur.name = name;
|
|
1677
|
+
if (typeof fn.arguments === "string")
|
|
1678
|
+
cur.args += fn.arguments;
|
|
1679
|
+
acc.set(index, cur);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
function finalizeStreamedToolCalls(acc) {
|
|
1683
|
+
const out = [];
|
|
1684
|
+
for (const [index, c] of [...acc.entries()].sort((a, b) => a[0] - b[0])) {
|
|
1685
|
+
if (!c.name)
|
|
1686
|
+
continue;
|
|
1687
|
+
out.push({
|
|
1688
|
+
type: "tool-call",
|
|
1689
|
+
toolCallId: c.id ?? `call_${c.name}_${index}`,
|
|
1690
|
+
toolName: c.name,
|
|
1691
|
+
input: parseJsonIfPossible(c.args.trim() === "" ? "{}" : c.args)
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
return out;
|
|
1695
|
+
}
|
|
1696
|
+
async function streamNativeChatCompletion(runtime, modelType, params, context) {
|
|
1697
|
+
const requestBody = buildNativeRequestBody(params, context.modelName, context.prompt, context.systemPrompt);
|
|
1698
|
+
requestBody.stream = true;
|
|
1699
|
+
requestBody.stream_options = { include_usage: true };
|
|
1700
|
+
const headers = {
|
|
1701
|
+
"X-Eliza-Llm-Purpose": getPurposeForModelType(modelType),
|
|
1702
|
+
"X-Eliza-Model-Type": modelType
|
|
1703
|
+
};
|
|
1704
|
+
if (isSpanSamplerHonoringModel(context.modelName)) {
|
|
1705
|
+
const samplerHeader = buildSpanSamplerHeader(params.spanSamplerPlan);
|
|
1706
|
+
if (samplerHeader) {
|
|
1707
|
+
headers["x-eliza-span-samplers"] = samplerHeader;
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
const abortSignal = params.signal;
|
|
1711
|
+
const signal = buildStreamAbortSignal(abortSignal, resolveTextTimeoutMs());
|
|
1712
|
+
const limiter = getNativeChatLimiter();
|
|
1713
|
+
const waitStartedAt = Date.now();
|
|
1714
|
+
await limiter.acquire();
|
|
1715
|
+
recordInferenceSpan("cloud.semaphore-wait", Date.now() - waitStartedAt, {
|
|
1716
|
+
route: "chat/completions:stream"
|
|
1717
|
+
});
|
|
1718
|
+
let permitReleased = false;
|
|
1719
|
+
const releasePermit = () => {
|
|
1720
|
+
if (!permitReleased) {
|
|
1721
|
+
permitReleased = true;
|
|
1722
|
+
limiter.release();
|
|
1723
|
+
}
|
|
1724
|
+
};
|
|
1725
|
+
let response;
|
|
1726
|
+
try {
|
|
1727
|
+
response = await createCloudApiClient(runtime).requestRaw("POST", "/chat/completions", {
|
|
1728
|
+
headers,
|
|
1729
|
+
json: requestBody,
|
|
1730
|
+
...signal ? { signal } : {}
|
|
1731
|
+
});
|
|
1732
|
+
} catch (err) {
|
|
1733
|
+
releasePermit();
|
|
1734
|
+
throw err;
|
|
1735
|
+
}
|
|
1736
|
+
if (!response.ok) {
|
|
1737
|
+
let errorBody;
|
|
1738
|
+
try {
|
|
1739
|
+
const errText = await response.text();
|
|
1740
|
+
if (errText) {
|
|
1741
|
+
errorBody = JSON.parse(errText).error;
|
|
1742
|
+
}
|
|
1743
|
+
} catch {}
|
|
1744
|
+
releasePermit();
|
|
1745
|
+
const message = typeof errorBody?.message === "string" && errorBody.message.trim() ? errorBody.message.trim() : `elizaOS Cloud error ${response.status}`;
|
|
1746
|
+
const requestError = new Error(message);
|
|
1747
|
+
requestError.status = response.status;
|
|
1748
|
+
if (errorBody)
|
|
1749
|
+
requestError.error = errorBody;
|
|
1750
|
+
throw requestError;
|
|
1751
|
+
}
|
|
1752
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
1753
|
+
const isSse = contentType.includes("text/event-stream") && response.body !== null;
|
|
1754
|
+
if (!isSse) {
|
|
1755
|
+
const bufferedText = await response.text();
|
|
1756
|
+
releasePermit();
|
|
1757
|
+
let data = {};
|
|
1758
|
+
if (bufferedText) {
|
|
1759
|
+
try {
|
|
1760
|
+
data = JSON.parse(bufferedText);
|
|
1761
|
+
} catch (parseErr) {
|
|
1762
|
+
logger7.error(`[ELIZAOS_CLOUD] Failed to parse buffered chat completions JSON: ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
const text = extractChatCompletionText(data);
|
|
1766
|
+
const toolCalls = extractNativeToolCalls(data);
|
|
1767
|
+
const usage = convertNativeUsage(data.usage);
|
|
1768
|
+
if (usage) {
|
|
1769
|
+
emitModelUsageEvent(runtime, modelType, context.prompt, usage, {
|
|
1770
|
+
modelName: context.modelName,
|
|
1771
|
+
...(() => {
|
|
1772
|
+
const costUsd = extractCostUsd(data.usage, response);
|
|
1773
|
+
return typeof costUsd === "number" ? { costUsd } : {};
|
|
1774
|
+
})()
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
if (!text.trim() && toolCalls.length === 0) {
|
|
1778
|
+
throw new Error("elizaOS Cloud returned no text or tool calls");
|
|
1779
|
+
}
|
|
1780
|
+
async function* single() {
|
|
1781
|
+
if (text)
|
|
1782
|
+
yield text;
|
|
1783
|
+
}
|
|
1784
|
+
return {
|
|
1785
|
+
textStream: single(),
|
|
1786
|
+
text: Promise.resolve(text),
|
|
1787
|
+
usage: Promise.resolve(usage),
|
|
1788
|
+
finishReason: Promise.resolve(data.choices?.[0]?.finish_reason),
|
|
1789
|
+
toolCalls: Promise.resolve(toolCalls),
|
|
1790
|
+
providerMetadata: { modelName: context.modelName, usage: data.usage }
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
const body = response.body;
|
|
1794
|
+
const toolAcc = new Map;
|
|
1795
|
+
let accumulated = "";
|
|
1796
|
+
let nativeUsage;
|
|
1797
|
+
let rawUsage;
|
|
1798
|
+
let finishReason;
|
|
1799
|
+
const textD = deferred();
|
|
1800
|
+
const usageD = deferred();
|
|
1801
|
+
const finishD = deferred();
|
|
1802
|
+
const toolCallsD = deferred();
|
|
1803
|
+
async function* generate() {
|
|
1804
|
+
try {
|
|
1805
|
+
for await (const frame of parseOpenAiSseStream(body)) {
|
|
1806
|
+
if (frame.error) {
|
|
1807
|
+
const message = asRecord2(frame.error).message;
|
|
1808
|
+
throw new Error(typeof message === "string" && message.trim() ? message.trim() : "elizaOS Cloud stream error");
|
|
1809
|
+
}
|
|
1810
|
+
const choices = Array.isArray(frame.choices) ? frame.choices : [];
|
|
1811
|
+
const choice = asRecord2(choices[0]);
|
|
1812
|
+
const delta = recordAt(choice, "delta");
|
|
1813
|
+
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
1814
|
+
accumulated += delta.content;
|
|
1815
|
+
yield delta.content;
|
|
1816
|
+
}
|
|
1817
|
+
if (delta.tool_calls) {
|
|
1818
|
+
accumulateToolCallDeltas(toolAcc, delta.tool_calls);
|
|
1819
|
+
}
|
|
1820
|
+
const fr = firstString(choice.finish_reason);
|
|
1821
|
+
if (fr)
|
|
1822
|
+
finishReason = fr;
|
|
1823
|
+
if (frame.usage) {
|
|
1824
|
+
rawUsage = frame.usage;
|
|
1825
|
+
nativeUsage = convertNativeUsage(frame.usage);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
} finally {
|
|
1829
|
+
releasePermit();
|
|
1830
|
+
const toolCalls = finalizeStreamedToolCalls(toolAcc);
|
|
1831
|
+
textD.resolve(accumulated);
|
|
1832
|
+
usageD.resolve(nativeUsage);
|
|
1833
|
+
finishD.resolve(finishReason);
|
|
1834
|
+
toolCallsD.resolve(toolCalls);
|
|
1835
|
+
if (nativeUsage) {
|
|
1836
|
+
emitModelUsageEvent(runtime, modelType, context.prompt, nativeUsage, {
|
|
1837
|
+
modelName: context.modelName,
|
|
1838
|
+
...(() => {
|
|
1839
|
+
const costUsd = extractCostUsd(rawUsage, response);
|
|
1840
|
+
return typeof costUsd === "number" ? { costUsd } : {};
|
|
1841
|
+
})()
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
return {
|
|
1847
|
+
textStream: generate(),
|
|
1848
|
+
text: textD.promise,
|
|
1849
|
+
usage: usageD.promise,
|
|
1850
|
+
finishReason: finishD.promise,
|
|
1851
|
+
toolCalls: toolCallsD.promise,
|
|
1852
|
+
providerMetadata: { modelName: context.modelName }
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1345
1855
|
async function handleTextSmall(runtime, params) {
|
|
1346
1856
|
return generateTextWithModel(runtime, TEXT_SMALL_MODEL_TYPE, params);
|
|
1347
1857
|
}
|
|
@@ -1383,4 +1893,4 @@ export {
|
|
|
1383
1893
|
fetchTextToSpeech
|
|
1384
1894
|
};
|
|
1385
1895
|
|
|
1386
|
-
//# debugId=
|
|
1896
|
+
//# debugId=14309D70FEEE136A64756E2164756E21
|