@elizaos/plugin-elizacloud 2.0.0-beta.1 → 2.0.3-beta.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/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 +2888 -5918
- 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 +881 -3
- package/dist/cloud/index.js.map +9 -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 +5427 -8410
- package/dist/index.js.map +47 -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 +7 -0
- package/dist/models/embeddings.d.ts.map +1 -1
- package/dist/models/embeddings.js +95 -73
- 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 +689 -169
- 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 +113 -1
- package/dist/models/text.d.ts.map +1 -1
- package/dist/models/text.js +453 -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 +2842 -5841
- 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 +81 -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 +178 -78
- 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,84 +244,75 @@ 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
|
-
input: batchTexts
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
continue;
|
|
269
|
+
input: batchTexts,
|
|
270
|
+
dimensions: embeddingDimension
|
|
271
|
+
},
|
|
272
|
+
timeoutMs: EMBED_REQUEST_TIMEOUT_MS
|
|
273
|
+
}), { batch: batchTexts.length, attempt });
|
|
274
|
+
const rateLimitInfo = extractRateLimitInfo(resp);
|
|
275
|
+
if (rateLimitInfo.remainingRequests !== undefined && rateLimitInfo.remainingRequests < 50) {
|
|
276
|
+
logger2.warn(`[BatchEmbeddings] Rate limit: ${rateLimitInfo.remainingRequests}/${rateLimitInfo.limitRequests} requests remaining`);
|
|
265
277
|
}
|
|
266
|
-
const
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
278
|
+
const transient = resp.status === 429 || resp.status === 502 || resp.status === 503 || resp.status === 504;
|
|
279
|
+
if (transient && attempt < EMBED_MAX_ATTEMPTS - 1) {
|
|
280
|
+
const delay = embeddingBackoffMs(attempt, rateLimitInfo.retryAfter);
|
|
281
|
+
logger2.warn(`[BatchEmbeddings] ${resp.status} (attempt ${attempt + 1}/${EMBED_MAX_ATTEMPTS}) — backing off ${delay}ms`);
|
|
282
|
+
await resp.text().catch(() => {
|
|
283
|
+
return;
|
|
284
|
+
});
|
|
285
|
+
await sleep(delay);
|
|
286
|
+
continue;
|
|
273
287
|
}
|
|
274
|
-
|
|
288
|
+
response = resp;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
if (!response) {
|
|
292
|
+
throw new Error("[BatchEmbeddings] No response after retry loop");
|
|
275
293
|
}
|
|
276
294
|
if (!response.ok) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
results[item.originalIndex] = createErrorVector(embeddingDimension, 0.4);
|
|
295
|
+
if (response.status === 401 || response.status === 403) {
|
|
296
|
+
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
297
|
}
|
|
281
|
-
|
|
298
|
+
throw new Error(`[BatchEmbeddings] API error: ${response.status} ${response.statusText}`);
|
|
282
299
|
}
|
|
283
300
|
const data = await response.json();
|
|
284
301
|
if (!data?.data || !Array.isArray(data.data)) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
continue;
|
|
302
|
+
throw new Error("[BatchEmbeddings] API returned invalid response structure");
|
|
303
|
+
}
|
|
304
|
+
if (data.data.length !== batch.length) {
|
|
305
|
+
throw new Error(`[BatchEmbeddings] expected ${batch.length} embeddings, got ${data.data.length}`);
|
|
290
306
|
}
|
|
291
307
|
for (const item of data.data) {
|
|
292
|
-
const
|
|
293
|
-
|
|
308
|
+
const slot = typeof item.index === "number" ? batch[item.index] : undefined;
|
|
309
|
+
if (!slot) {
|
|
310
|
+
throw new Error(`[BatchEmbeddings] response index out of range: ${String(item.index)} (batch size ${batch.length})`);
|
|
311
|
+
}
|
|
312
|
+
if (!Array.isArray(item.embedding) || item.embedding.length !== embeddingDimension) {
|
|
313
|
+
throw new Error(`[BatchEmbeddings] dimension mismatch: model returned ${Array.isArray(item.embedding) ? item.embedding.length : "non-array"}d but agent is configured for ${embeddingDimension}d`);
|
|
314
|
+
}
|
|
315
|
+
results[slot.originalIndex] = item.embedding;
|
|
294
316
|
}
|
|
295
317
|
if (data.usage) {
|
|
296
318
|
const usage = {
|
|
@@ -300,13 +322,11 @@ async function handleBatchTextEmbedding(runtime, texts) {
|
|
|
300
322
|
};
|
|
301
323
|
emitModelUsageEvent(runtime, ModelType.TEXT_EMBEDDING, `batch:${batch.length}`, usage);
|
|
302
324
|
}
|
|
303
|
-
logger2.debug(`[BatchEmbeddings] Got ${batch.length} embeddings (${embeddingDimension}d)
|
|
325
|
+
logger2.debug(`[BatchEmbeddings] Got ${batch.length} embeddings (${embeddingDimension}d)`);
|
|
304
326
|
} catch (error) {
|
|
305
327
|
const message = error instanceof Error ? error.message : String(error);
|
|
306
|
-
logger2.error(`[BatchEmbeddings]
|
|
307
|
-
|
|
308
|
-
results[item.originalIndex] = createErrorVector(embeddingDimension, 0.6);
|
|
309
|
-
}
|
|
328
|
+
logger2.error(`[BatchEmbeddings] Batch failed: ${message}`);
|
|
329
|
+
throw error instanceof Error ? error : new Error(message);
|
|
310
330
|
}
|
|
311
331
|
}
|
|
312
332
|
return results;
|
|
@@ -424,7 +444,10 @@ async function handleImageGeneration(runtime, params) {
|
|
|
424
444
|
}
|
|
425
445
|
async function handleImageDescription(runtime, params) {
|
|
426
446
|
const disableSetting = getSetting(runtime, "DISABLE_IMAGE_DESCRIPTION", "");
|
|
427
|
-
const disabled = disableSetting
|
|
447
|
+
const disabled = [disableSetting, process.env.DISABLE_IMAGE_DESCRIPTION].some((value) => {
|
|
448
|
+
const normalized = value?.trim().toLowerCase();
|
|
449
|
+
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
|
450
|
+
});
|
|
428
451
|
if (disabled) {
|
|
429
452
|
logger4.debug("[ELIZAOS_CLOUD] IMAGE_DESCRIPTION skipped — DISABLE_IMAGE_DESCRIPTION is set");
|
|
430
453
|
return {
|
|
@@ -463,16 +486,21 @@ async function handleImageDescription(runtime, params) {
|
|
|
463
486
|
let response = null;
|
|
464
487
|
let attemptedRetry = false;
|
|
465
488
|
for (let attempt = 0;attempt < 2; attempt++) {
|
|
466
|
-
|
|
467
|
-
json: requestBody
|
|
489
|
+
const attemptResponse = await client.routes.postApiV1ChatCompletionsRaw({
|
|
490
|
+
json: requestBody,
|
|
491
|
+
timeoutMs: resolveCloudTimeoutMs("ELIZAOS_CLOUD_IMAGE_TIMEOUT_MS", 120000)
|
|
468
492
|
});
|
|
469
|
-
if (
|
|
493
|
+
if (!attemptResponse) {
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
response = attemptResponse;
|
|
497
|
+
if (attemptResponse.status !== 429 || attemptedRetry)
|
|
470
498
|
break;
|
|
471
|
-
const headerValue =
|
|
499
|
+
const headerValue = attemptResponse.headers.get("retry-after");
|
|
472
500
|
const headerRetryAfter = headerValue !== null && Number.isFinite(Number(headerValue)) ? Number(headerValue) : undefined;
|
|
473
501
|
let bodyRetryAfter;
|
|
474
502
|
try {
|
|
475
|
-
const peek = await
|
|
503
|
+
const peek = await attemptResponse.clone().json();
|
|
476
504
|
bodyRetryAfter = typeof peek?.retryAfter === "number" && Number.isFinite(peek.retryAfter) ? peek.retryAfter : undefined;
|
|
477
505
|
} catch {}
|
|
478
506
|
const retryAfter = headerRetryAfter ?? bodyRetryAfter ?? 0;
|
|
@@ -485,8 +513,12 @@ async function handleImageDescription(runtime, params) {
|
|
|
485
513
|
logger4.warn(`[ELIZAOS_CLOUD] Image analysis rate-limited (429); upstream retryAfter=${retryAfter || "unknown"}s — failing fast`);
|
|
486
514
|
break;
|
|
487
515
|
}
|
|
488
|
-
if (!response
|
|
489
|
-
|
|
516
|
+
if (!response) {
|
|
517
|
+
throw new Error("ElizaOS Cloud API did not return a response");
|
|
518
|
+
}
|
|
519
|
+
const finalResponse = response;
|
|
520
|
+
if (!finalResponse.ok) {
|
|
521
|
+
const status = finalResponse.status;
|
|
490
522
|
if (status === 402) {
|
|
491
523
|
throw new Error("Eliza Cloud credits exhausted — top up at https://www.elizacloud.ai/dashboard/settings?tab=billing");
|
|
492
524
|
}
|
|
@@ -495,7 +527,7 @@ async function handleImageDescription(runtime, params) {
|
|
|
495
527
|
}
|
|
496
528
|
throw new Error(`ElizaOS Cloud API error: ${status}`);
|
|
497
529
|
}
|
|
498
|
-
const typedResult = await
|
|
530
|
+
const typedResult = await finalResponse.json();
|
|
499
531
|
const content = typedResult.choices?.[0]?.message?.content;
|
|
500
532
|
if (typedResult.usage) {
|
|
501
533
|
emitModelUsageEvent(runtime, ModelType2.IMAGE_DESCRIPTION, typeof params === "string" ? params : params.prompt || "", {
|
|
@@ -523,6 +555,7 @@ async function handleImageDescription(runtime, params) {
|
|
|
523
555
|
|
|
524
556
|
// src/models/research.ts
|
|
525
557
|
import { logger as logger5, ModelType as ModelType3 } from "@elizaos/core";
|
|
558
|
+
var DEFAULT_RESEARCH_TIMEOUT_MS = 600000;
|
|
526
559
|
function normalizeInput(input) {
|
|
527
560
|
if (typeof input !== "string") {
|
|
528
561
|
return input;
|
|
@@ -645,7 +678,8 @@ async function handleResearch(runtime, params) {
|
|
|
645
678
|
requestBody.reasoning = { summary: params.reasoningSummary };
|
|
646
679
|
}
|
|
647
680
|
const response = await createCloudApiClient(runtime).requestRaw("POST", "/responses", {
|
|
648
|
-
json: requestBody
|
|
681
|
+
json: requestBody,
|
|
682
|
+
timeoutMs: resolveCloudTimeoutMs("ELIZAOS_CLOUD_RESEARCH_TIMEOUT_MS", DEFAULT_RESEARCH_TIMEOUT_MS)
|
|
649
683
|
});
|
|
650
684
|
if (!response.ok) {
|
|
651
685
|
const errorText = await response.text();
|
|
@@ -682,17 +716,59 @@ async function handleResearch(runtime, params) {
|
|
|
682
716
|
}
|
|
683
717
|
|
|
684
718
|
// src/models/speech.ts
|
|
685
|
-
import { logger as logger6 } from "@elizaos/core";
|
|
719
|
+
import { isCloudConnected, logger as logger6, toRuntimeSettings } from "@elizaos/core";
|
|
720
|
+
var cloudTtsClientFactory = (runtime) => createElizaCloudClient(runtime);
|
|
721
|
+
function setCloudTtsClientFactoryForTesting(factory) {
|
|
722
|
+
if (factory === null) {
|
|
723
|
+
cloudTtsClientFactory = (runtime) => createElizaCloudClient(runtime);
|
|
724
|
+
} else {
|
|
725
|
+
cloudTtsClientFactory = factory;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
class CloudTtsUnavailableError extends Error {
|
|
730
|
+
constructor(message = "Eliza Cloud is not connected") {
|
|
731
|
+
super(message);
|
|
732
|
+
this.name = "CloudTtsUnavailableError";
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
function normalizeTextInput(input) {
|
|
736
|
+
if (typeof input === "string")
|
|
737
|
+
return { text: input };
|
|
738
|
+
return input;
|
|
739
|
+
}
|
|
740
|
+
function resolveModelId(options) {
|
|
741
|
+
if (options.modelId && options.modelId.trim()) {
|
|
742
|
+
return options.modelId.trim();
|
|
743
|
+
}
|
|
744
|
+
const model = options.model?.trim();
|
|
745
|
+
if (!model)
|
|
746
|
+
return;
|
|
747
|
+
if (model.startsWith("elevenlabs/")) {
|
|
748
|
+
return model.split("/").slice(1).join("/");
|
|
749
|
+
}
|
|
750
|
+
if (model.startsWith("eleven_")) {
|
|
751
|
+
return model;
|
|
752
|
+
}
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
function resolveVoiceId(options) {
|
|
756
|
+
if (options.voiceId && options.voiceId.trim()) {
|
|
757
|
+
return options.voiceId.trim();
|
|
758
|
+
}
|
|
759
|
+
const voice = options.voice?.trim();
|
|
760
|
+
if (!voice)
|
|
761
|
+
return;
|
|
762
|
+
if (voice === "nova")
|
|
763
|
+
return;
|
|
764
|
+
return voice;
|
|
765
|
+
}
|
|
686
766
|
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
767
|
const format = options.format || "mp3";
|
|
692
|
-
const modelId =
|
|
693
|
-
const voiceId =
|
|
768
|
+
const modelId = resolveModelId(options);
|
|
769
|
+
const voiceId = resolveVoiceId(options);
|
|
694
770
|
try {
|
|
695
|
-
const res = await
|
|
771
|
+
const res = await cloudTtsClientFactory(runtime).routes.postApiV1VoiceTts({
|
|
696
772
|
headers: {
|
|
697
773
|
...format === "mp3" ? { Accept: "audio/mpeg" } : {}
|
|
698
774
|
},
|
|
@@ -700,7 +776,8 @@ async function fetchTextToSpeech(runtime, options) {
|
|
|
700
776
|
text: options.text,
|
|
701
777
|
...voiceId ? { voiceId } : {},
|
|
702
778
|
...modelId ? { modelId } : {}
|
|
703
|
-
}
|
|
779
|
+
},
|
|
780
|
+
timeoutMs: resolveCloudTimeoutMs("ELIZAOS_CLOUD_TTS_TIMEOUT_MS", 60000)
|
|
704
781
|
});
|
|
705
782
|
if (!res.ok) {
|
|
706
783
|
const err = await res.text();
|
|
@@ -718,30 +795,117 @@ async function fetchTextToSpeech(runtime, options) {
|
|
|
718
795
|
throw new Error(`Failed to fetch speech from ElizaOS Cloud TTS: ${message}`);
|
|
719
796
|
}
|
|
720
797
|
}
|
|
798
|
+
function toUint8Array(chunk) {
|
|
799
|
+
if (chunk instanceof Uint8Array)
|
|
800
|
+
return chunk;
|
|
801
|
+
if (chunk instanceof ArrayBuffer)
|
|
802
|
+
return new Uint8Array(chunk);
|
|
803
|
+
if (typeof chunk === "string")
|
|
804
|
+
return new TextEncoder().encode(chunk);
|
|
805
|
+
throw new TypeError(`Unexpected TTS chunk type: ${typeof chunk}`);
|
|
806
|
+
}
|
|
807
|
+
function concatChunks(chunks) {
|
|
808
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
809
|
+
const out = new Uint8Array(total);
|
|
810
|
+
let offset = 0;
|
|
811
|
+
for (const chunk of chunks) {
|
|
812
|
+
out.set(chunk, offset);
|
|
813
|
+
offset += chunk.byteLength;
|
|
814
|
+
}
|
|
815
|
+
return out;
|
|
816
|
+
}
|
|
817
|
+
async function webStreamToUint8Array(stream) {
|
|
818
|
+
const reader = stream.getReader();
|
|
819
|
+
const chunks = [];
|
|
820
|
+
try {
|
|
821
|
+
while (true) {
|
|
822
|
+
const result = await reader.read();
|
|
823
|
+
if (result.done)
|
|
824
|
+
break;
|
|
825
|
+
chunks.push(toUint8Array(result.value));
|
|
826
|
+
}
|
|
827
|
+
} finally {
|
|
828
|
+
reader.releaseLock();
|
|
829
|
+
}
|
|
830
|
+
return concatChunks(chunks);
|
|
831
|
+
}
|
|
832
|
+
async function nodeStreamToUint8Array(stream) {
|
|
833
|
+
const chunks = [];
|
|
834
|
+
for await (const chunk of stream) {
|
|
835
|
+
chunks.push(toUint8Array(chunk));
|
|
836
|
+
}
|
|
837
|
+
return concatChunks(chunks);
|
|
838
|
+
}
|
|
839
|
+
function isReadableStream(stream) {
|
|
840
|
+
return typeof stream.getReader === "function";
|
|
841
|
+
}
|
|
842
|
+
async function ttsStreamToBytes(stream) {
|
|
843
|
+
if (isReadableStream(stream)) {
|
|
844
|
+
return webStreamToUint8Array(stream);
|
|
845
|
+
}
|
|
846
|
+
return nodeStreamToUint8Array(stream);
|
|
847
|
+
}
|
|
848
|
+
function buildAudioStreamResult(stream, mimeType) {
|
|
849
|
+
const collected = [];
|
|
850
|
+
let resolveBytes;
|
|
851
|
+
let rejectBytes;
|
|
852
|
+
const bytes = new Promise((resolve, reject) => {
|
|
853
|
+
resolveBytes = resolve;
|
|
854
|
+
rejectBytes = reject;
|
|
855
|
+
});
|
|
856
|
+
async function* generate() {
|
|
857
|
+
try {
|
|
858
|
+
if (isReadableStream(stream)) {
|
|
859
|
+
const reader = stream.getReader();
|
|
860
|
+
try {
|
|
861
|
+
for (;; ) {
|
|
862
|
+
const { value, done } = await reader.read();
|
|
863
|
+
if (done)
|
|
864
|
+
break;
|
|
865
|
+
const chunk = toUint8Array(value);
|
|
866
|
+
collected.push(chunk);
|
|
867
|
+
yield chunk;
|
|
868
|
+
}
|
|
869
|
+
} finally {
|
|
870
|
+
reader.releaseLock();
|
|
871
|
+
}
|
|
872
|
+
} else {
|
|
873
|
+
for await (const value of stream) {
|
|
874
|
+
const chunk = toUint8Array(value);
|
|
875
|
+
collected.push(chunk);
|
|
876
|
+
yield chunk;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
resolveBytes(concatChunks(collected));
|
|
880
|
+
} catch (err) {
|
|
881
|
+
rejectBytes(err);
|
|
882
|
+
throw err;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
return { audioStream: generate(), bytes, mimeType };
|
|
886
|
+
}
|
|
721
887
|
async function handleTextToSpeech(runtime, input) {
|
|
722
|
-
|
|
723
|
-
|
|
888
|
+
if (!isCloudConnected(toRuntimeSettings(runtime))) {
|
|
889
|
+
throw new CloudTtsUnavailableError("Eliza Cloud is not connected — falling through to next TTS handler");
|
|
890
|
+
}
|
|
891
|
+
const options = normalizeTextInput(input);
|
|
892
|
+
const wantsStream = typeof input === "object" && input !== null && input.audioStream === true;
|
|
893
|
+
const resolvedModel = options.modelId || options.model || getSetting(runtime, "ELIZAOS_CLOUD_TTS_MODEL", "eleven_flash_v2_5");
|
|
724
894
|
logger6.log(`[ELIZAOS_CLOUD] Using TEXT_TO_SPEECH model: ${resolvedModel}`);
|
|
725
895
|
try {
|
|
726
896
|
const speechStream = await fetchTextToSpeech(runtime, options);
|
|
727
|
-
|
|
897
|
+
if (wantsStream) {
|
|
898
|
+
const format = options.format || "mp3";
|
|
899
|
+
const mimeType = format === "mp3" ? "audio/mpeg" : `audio/${format}`;
|
|
900
|
+
return buildAudioStreamResult(speechStream, mimeType);
|
|
901
|
+
}
|
|
902
|
+
return ttsStreamToBytes(speechStream);
|
|
728
903
|
} catch (error) {
|
|
729
904
|
const message = error instanceof Error ? error.message : String(error);
|
|
730
905
|
logger6.error(`Error in TEXT_TO_SPEECH: ${message}`);
|
|
731
906
|
throw error;
|
|
732
907
|
}
|
|
733
908
|
}
|
|
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
909
|
// src/utils/responses-output.ts
|
|
746
910
|
function asRecord(value) {
|
|
747
911
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -827,8 +991,11 @@ import {
|
|
|
827
991
|
buildCanonicalSystemPrompt,
|
|
828
992
|
logger as logger7,
|
|
829
993
|
ModelType as ModelType4,
|
|
994
|
+
recordInferenceSpan,
|
|
830
995
|
renderChatMessagesForPrompt,
|
|
831
|
-
resolveEffectiveSystemPrompt
|
|
996
|
+
resolveEffectiveSystemPrompt,
|
|
997
|
+
Semaphore,
|
|
998
|
+
timeInferenceSpan as timeInferenceSpan2
|
|
832
999
|
} from "@elizaos/core";
|
|
833
1000
|
var TEXT_NANO_MODEL_TYPE = ModelType4.TEXT_NANO ?? "TEXT_NANO";
|
|
834
1001
|
var TEXT_MEDIUM_MODEL_TYPE = ModelType4.TEXT_MEDIUM ?? "TEXT_MEDIUM";
|
|
@@ -837,6 +1004,61 @@ var TEXT_LARGE_MODEL_TYPE = ModelType4.TEXT_LARGE;
|
|
|
837
1004
|
var TEXT_MEGA_MODEL_TYPE = ModelType4.TEXT_MEGA ?? "TEXT_MEGA";
|
|
838
1005
|
var RESPONSE_HANDLER_MODEL_TYPE = ModelType4.RESPONSE_HANDLER ?? "RESPONSE_HANDLER";
|
|
839
1006
|
var ACTION_PLANNER_MODEL_TYPE = ModelType4.ACTION_PLANNER ?? "ACTION_PLANNER";
|
|
1007
|
+
var NATIVE_CONCURRENCY_ENV = "ELIZAOS_CLOUD_NATIVE_CONCURRENCY";
|
|
1008
|
+
var DEFAULT_NATIVE_CONCURRENCY = 8;
|
|
1009
|
+
var TEXT_TIMEOUT_ENV = "ELIZAOS_CLOUD_TEXT_TIMEOUT_MS";
|
|
1010
|
+
var DEFAULT_TEXT_TIMEOUT_MS = 120000;
|
|
1011
|
+
function resolveTextTimeoutMs() {
|
|
1012
|
+
const raw = typeof process !== "undefined" ? process.env[TEXT_TIMEOUT_ENV] : undefined;
|
|
1013
|
+
if (raw === undefined || raw.trim() === "")
|
|
1014
|
+
return DEFAULT_TEXT_TIMEOUT_MS;
|
|
1015
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1016
|
+
if (!Number.isFinite(parsed))
|
|
1017
|
+
return DEFAULT_TEXT_TIMEOUT_MS;
|
|
1018
|
+
return parsed <= 0 ? undefined : parsed;
|
|
1019
|
+
}
|
|
1020
|
+
var STREAMING_ENV = "ELIZAOS_CLOUD_STREAMING";
|
|
1021
|
+
function resolveStreamingEnabled() {
|
|
1022
|
+
const raw = typeof process !== "undefined" ? process.env[STREAMING_ENV] : undefined;
|
|
1023
|
+
if (raw === undefined)
|
|
1024
|
+
return true;
|
|
1025
|
+
const v = raw.trim().toLowerCase();
|
|
1026
|
+
return v !== "0" && v !== "false" && v !== "off";
|
|
1027
|
+
}
|
|
1028
|
+
function buildStreamAbortSignal(abortSignal, timeoutMs) {
|
|
1029
|
+
const timeoutSig = typeof timeoutMs === "number" && timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
1030
|
+
if (abortSignal && timeoutSig)
|
|
1031
|
+
return AbortSignal.any([abortSignal, timeoutSig]);
|
|
1032
|
+
return abortSignal ?? timeoutSig;
|
|
1033
|
+
}
|
|
1034
|
+
var nativeChatLimiter = null;
|
|
1035
|
+
function resolveNativeConcurrency() {
|
|
1036
|
+
const raw = typeof process !== "undefined" ? process.env[NATIVE_CONCURRENCY_ENV] : undefined;
|
|
1037
|
+
const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
1038
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_NATIVE_CONCURRENCY;
|
|
1039
|
+
}
|
|
1040
|
+
function getNativeChatLimiter() {
|
|
1041
|
+
if (!nativeChatLimiter) {
|
|
1042
|
+
nativeChatLimiter = new Semaphore(resolveNativeConcurrency());
|
|
1043
|
+
}
|
|
1044
|
+
return nativeChatLimiter;
|
|
1045
|
+
}
|
|
1046
|
+
async function withNativeChatLimit(fn, label = "native") {
|
|
1047
|
+
const limiter = getNativeChatLimiter();
|
|
1048
|
+
const waitStartedAt = Date.now();
|
|
1049
|
+
await limiter.acquire();
|
|
1050
|
+
recordInferenceSpan("cloud.semaphore-wait", Date.now() - waitStartedAt, {
|
|
1051
|
+
route: label
|
|
1052
|
+
});
|
|
1053
|
+
try {
|
|
1054
|
+
return await timeInferenceSpan2(`cloud.http:${label}`, fn, { route: label });
|
|
1055
|
+
} finally {
|
|
1056
|
+
limiter.release();
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
function __resetNativeChatLimiterForTests() {
|
|
1060
|
+
nativeChatLimiter = null;
|
|
1061
|
+
}
|
|
840
1062
|
var REASONING_MODEL_PATTERNS = [
|
|
841
1063
|
"o1",
|
|
842
1064
|
"o3",
|
|
@@ -847,27 +1069,51 @@ var REASONING_MODEL_PATTERNS = [
|
|
|
847
1069
|
"claude-opus-4-7",
|
|
848
1070
|
"gpt-5"
|
|
849
1071
|
];
|
|
850
|
-
var
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1072
|
+
var SPAN_SAMPLER_HONORING_MODEL_PREFIXES = [
|
|
1073
|
+
"vast/eliza-1-",
|
|
1074
|
+
"elizaos/eliza-1-",
|
|
1075
|
+
"eliza-1-"
|
|
1076
|
+
];
|
|
1077
|
+
function isSpanSamplerHonoringModel(modelName) {
|
|
1078
|
+
const lower = modelName.toLowerCase();
|
|
1079
|
+
return SPAN_SAMPLER_HONORING_MODEL_PREFIXES.some((prefix) => lower.startsWith(prefix));
|
|
1080
|
+
}
|
|
1081
|
+
function buildSpanSamplerHeader(plan) {
|
|
1082
|
+
if (!plan || plan.overrides.length === 0)
|
|
1083
|
+
return;
|
|
1084
|
+
const overrides = plan.overrides.map((o) => {
|
|
1085
|
+
const wire = {
|
|
1086
|
+
span_index: o.spanIndex,
|
|
1087
|
+
temperature: o.temperature
|
|
1088
|
+
};
|
|
1089
|
+
if (typeof o.topK === "number")
|
|
1090
|
+
wire.top_k = o.topK;
|
|
1091
|
+
if (typeof o.topP === "number")
|
|
1092
|
+
wire.top_p = o.topP;
|
|
1093
|
+
return wire;
|
|
1094
|
+
});
|
|
1095
|
+
const body = { overrides };
|
|
1096
|
+
if (plan.strict === true)
|
|
1097
|
+
body.strict = true;
|
|
1098
|
+
return JSON.stringify(body);
|
|
1099
|
+
}
|
|
1100
|
+
function extractCostUsd(usage, response) {
|
|
1101
|
+
const fromBody = firstNumber(asRecord2(usage).cost_usd, asRecord2(usage).costUsd, asRecord2(usage).cost);
|
|
1102
|
+
if (typeof fromBody === "number" && Number.isFinite(fromBody)) {
|
|
1103
|
+
return fromBody;
|
|
1104
|
+
}
|
|
1105
|
+
const header = response?.headers?.get?.("X-Eliza-Cost-Usd");
|
|
1106
|
+
if (header) {
|
|
1107
|
+
const parsed = Number(header);
|
|
1108
|
+
if (Number.isFinite(parsed))
|
|
1109
|
+
return parsed;
|
|
860
1110
|
}
|
|
861
|
-
return
|
|
1111
|
+
return;
|
|
862
1112
|
}
|
|
863
1113
|
function isReasoningModel(modelName) {
|
|
864
1114
|
const lower = modelName.toLowerCase();
|
|
865
1115
|
return REASONING_MODEL_PATTERNS.some((pattern) => lower.includes(pattern));
|
|
866
1116
|
}
|
|
867
|
-
function supportsStopSequences(modelName) {
|
|
868
|
-
const lower = modelName.toLowerCase();
|
|
869
|
-
return !RESPONSES_ROUTED_PREFIXES.some((prefix) => lower.startsWith(prefix));
|
|
870
|
-
}
|
|
871
1117
|
function isRecord(value) {
|
|
872
1118
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
873
1119
|
}
|
|
@@ -944,26 +1190,39 @@ function unwrapJsonSchema(value) {
|
|
|
944
1190
|
const record = asRecord2(value);
|
|
945
1191
|
return record.schema ?? record.jsonSchema ?? value;
|
|
946
1192
|
}
|
|
1193
|
+
function normalizeNativeToolEntry(rawTool, fallbackName) {
|
|
1194
|
+
const tool = asRecord2(rawTool);
|
|
1195
|
+
const nested = asRecord2(tool.function);
|
|
1196
|
+
const name = firstString(nested.name, tool.name, fallbackName);
|
|
1197
|
+
if (!name) {
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
const description = firstString(nested.description, tool.description);
|
|
1201
|
+
const inputSchema = unwrapJsonSchema(nested.parameters ?? tool.inputSchema ?? tool.parameters ?? tool.schema ?? { type: "object" });
|
|
1202
|
+
return {
|
|
1203
|
+
type: "function",
|
|
1204
|
+
function: {
|
|
1205
|
+
name,
|
|
1206
|
+
...description ? { description } : {},
|
|
1207
|
+
parameters: inputSchema
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
947
1211
|
function normalizeNativeTools(tools) {
|
|
948
1212
|
if (!tools) {
|
|
949
1213
|
return;
|
|
950
1214
|
}
|
|
951
1215
|
if (Array.isArray(tools)) {
|
|
952
|
-
|
|
1216
|
+
const normalized2 = tools.map((tool) => normalizeNativeToolEntry(tool)).filter((tool) => tool !== undefined);
|
|
1217
|
+
return normalized2.length > 0 ? normalized2 : undefined;
|
|
953
1218
|
}
|
|
954
1219
|
const toolSet = asRecord2(tools);
|
|
955
1220
|
const normalized = [];
|
|
956
1221
|
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
|
-
});
|
|
1222
|
+
const entry = normalizeNativeToolEntry(rawTool, name);
|
|
1223
|
+
if (entry) {
|
|
1224
|
+
normalized.push(entry);
|
|
1225
|
+
}
|
|
967
1226
|
}
|
|
968
1227
|
return normalized.length > 0 ? normalized : undefined;
|
|
969
1228
|
}
|
|
@@ -1189,16 +1448,8 @@ function getModelNameForType(runtime, modelType) {
|
|
|
1189
1448
|
}
|
|
1190
1449
|
}
|
|
1191
1450
|
function buildGenerateParams(runtime, modelType, params) {
|
|
1192
|
-
const paramsWithAttachments = params;
|
|
1193
1451
|
const prompt = params.prompt ?? "";
|
|
1194
|
-
const maxTokens = params.maxTokens ?? 8192;
|
|
1195
|
-
const openai = createOpenAIClient(runtime);
|
|
1196
1452
|
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
1453
|
const systemPrompt = resolveEffectiveSystemPrompt({
|
|
1203
1454
|
params,
|
|
1204
1455
|
fallback: buildCanonicalSystemPrompt({ character: runtime.character })
|
|
@@ -1206,28 +1457,24 @@ function buildGenerateParams(runtime, modelType, params) {
|
|
|
1206
1457
|
const promptText = renderChatMessagesForPrompt(params.messages, {
|
|
1207
1458
|
omitDuplicateSystem: systemPrompt
|
|
1208
1459
|
}) ?? 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 };
|
|
1460
|
+
return { modelName, modelType, prompt: promptText, systemPrompt };
|
|
1220
1461
|
}
|
|
1221
1462
|
async function generateTextWithModel(runtime, modelType, params) {
|
|
1222
1463
|
const { modelName, prompt, systemPrompt } = buildGenerateParams(runtime, modelType, params);
|
|
1223
1464
|
const paramsWithNative = params;
|
|
1224
1465
|
logger7.debug(`[ELIZAOS_CLOUD] Generating text with ${modelType} model: ${modelName}`);
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
}
|
|
1466
|
+
const paramsStreaming = params;
|
|
1467
|
+
const wantsStream = Boolean(paramsStreaming.stream) && paramsStreaming.streamStructured === true && resolveStreamingEnabled();
|
|
1228
1468
|
logger7.log(`[ELIZAOS_CLOUD] Using ${modelType} model: ${modelName}`);
|
|
1229
1469
|
logger7.log(prompt);
|
|
1230
1470
|
if (hasNativeTransportOptions(paramsWithNative)) {
|
|
1471
|
+
if (wantsStream) {
|
|
1472
|
+
return streamNativeChatCompletion(runtime, modelType, paramsWithNative, {
|
|
1473
|
+
modelName,
|
|
1474
|
+
prompt,
|
|
1475
|
+
systemPrompt
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1231
1478
|
const nativeResult = await generateNativeChatCompletion(runtime, modelType, paramsWithNative, {
|
|
1232
1479
|
modelName,
|
|
1233
1480
|
prompt,
|
|
@@ -1255,13 +1502,21 @@ async function generateTextWithModel(runtime, modelType, params) {
|
|
|
1255
1502
|
if (!reasoning && typeof params.temperature === "number") {
|
|
1256
1503
|
requestBody.temperature = params.temperature;
|
|
1257
1504
|
}
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1505
|
+
const responsesHeaders = {
|
|
1506
|
+
"X-Eliza-Llm-Purpose": getPurposeForModelType(modelType),
|
|
1507
|
+
"X-Eliza-Model-Type": modelType
|
|
1508
|
+
};
|
|
1509
|
+
if (isSpanSamplerHonoringModel(modelName)) {
|
|
1510
|
+
const samplerHeader = buildSpanSamplerHeader(params.spanSamplerPlan);
|
|
1511
|
+
if (samplerHeader) {
|
|
1512
|
+
responsesHeaders["x-eliza-span-samplers"] = samplerHeader;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
const response = await withNativeChatLimit(() => createCloudApiClient(runtime).requestRaw("POST", "/responses", {
|
|
1516
|
+
headers: responsesHeaders,
|
|
1517
|
+
json: requestBody,
|
|
1518
|
+
timeoutMs: resolveTextTimeoutMs()
|
|
1519
|
+
}), "responses");
|
|
1265
1520
|
const responseText = await response.text();
|
|
1266
1521
|
let data = {};
|
|
1267
1522
|
if (responseText) {
|
|
@@ -1286,6 +1541,12 @@ async function generateTextWithModel(runtime, modelType, params) {
|
|
|
1286
1541
|
inputTokens: data.usage.input_tokens ?? 0,
|
|
1287
1542
|
outputTokens: data.usage.output_tokens ?? 0,
|
|
1288
1543
|
totalTokens: data.usage.total_tokens ?? 0
|
|
1544
|
+
}, {
|
|
1545
|
+
modelName: getModelNameForType(runtime, modelType),
|
|
1546
|
+
...(() => {
|
|
1547
|
+
const costUsd = extractCostUsd(data.usage, response);
|
|
1548
|
+
return typeof costUsd === "number" ? { costUsd } : {};
|
|
1549
|
+
})()
|
|
1289
1550
|
});
|
|
1290
1551
|
}
|
|
1291
1552
|
const text = extractResponsesOutputText(data);
|
|
@@ -1296,13 +1557,21 @@ async function generateTextWithModel(runtime, modelType, params) {
|
|
|
1296
1557
|
}
|
|
1297
1558
|
async function generateNativeChatCompletion(runtime, modelType, params, context) {
|
|
1298
1559
|
const requestBody = buildNativeRequestBody(params, context.modelName, context.prompt, context.systemPrompt);
|
|
1299
|
-
const
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1560
|
+
const headers = {
|
|
1561
|
+
"X-Eliza-Llm-Purpose": getPurposeForModelType(modelType),
|
|
1562
|
+
"X-Eliza-Model-Type": modelType
|
|
1563
|
+
};
|
|
1564
|
+
if (isSpanSamplerHonoringModel(context.modelName)) {
|
|
1565
|
+
const samplerHeader = buildSpanSamplerHeader(params.spanSamplerPlan);
|
|
1566
|
+
if (samplerHeader) {
|
|
1567
|
+
headers["x-eliza-span-samplers"] = samplerHeader;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
const response = await withNativeChatLimit(() => createCloudApiClient(runtime).requestRaw("POST", "/chat/completions", {
|
|
1571
|
+
headers,
|
|
1572
|
+
json: requestBody,
|
|
1573
|
+
timeoutMs: resolveTextTimeoutMs()
|
|
1574
|
+
}), "chat/completions");
|
|
1306
1575
|
const responseText = await response.text();
|
|
1307
1576
|
let data = {};
|
|
1308
1577
|
if (responseText) {
|
|
@@ -1324,7 +1593,13 @@ async function generateNativeChatCompletion(runtime, modelType, params, context)
|
|
|
1324
1593
|
}
|
|
1325
1594
|
const usage = convertNativeUsage(data.usage);
|
|
1326
1595
|
if (usage) {
|
|
1327
|
-
emitModelUsageEvent(runtime, modelType, context.prompt, usage
|
|
1596
|
+
emitModelUsageEvent(runtime, modelType, context.prompt, usage, {
|
|
1597
|
+
modelName: context.modelName,
|
|
1598
|
+
...(() => {
|
|
1599
|
+
const costUsd = extractCostUsd(data.usage, response);
|
|
1600
|
+
return typeof costUsd === "number" ? { costUsd } : {};
|
|
1601
|
+
})()
|
|
1602
|
+
});
|
|
1328
1603
|
}
|
|
1329
1604
|
const text = extractChatCompletionText(data);
|
|
1330
1605
|
const toolCalls = extractNativeToolCalls(data);
|
|
@@ -1342,6 +1617,251 @@ async function generateNativeChatCompletion(runtime, modelType, params, context)
|
|
|
1342
1617
|
}
|
|
1343
1618
|
};
|
|
1344
1619
|
}
|
|
1620
|
+
function deferred() {
|
|
1621
|
+
let resolve;
|
|
1622
|
+
const promise = new Promise((r) => {
|
|
1623
|
+
resolve = r;
|
|
1624
|
+
});
|
|
1625
|
+
return { promise, resolve };
|
|
1626
|
+
}
|
|
1627
|
+
async function* parseOpenAiSseStream(body) {
|
|
1628
|
+
const reader = body.getReader();
|
|
1629
|
+
const decoder = new TextDecoder;
|
|
1630
|
+
let buffer = "";
|
|
1631
|
+
const handle = (line) => {
|
|
1632
|
+
const trimmed = line.trimStart();
|
|
1633
|
+
if (!trimmed.startsWith("data:"))
|
|
1634
|
+
return null;
|
|
1635
|
+
const payload = trimmed.slice(5).trim();
|
|
1636
|
+
if (payload === "")
|
|
1637
|
+
return null;
|
|
1638
|
+
if (payload === "[DONE]")
|
|
1639
|
+
return "DONE";
|
|
1640
|
+
try {
|
|
1641
|
+
return JSON.parse(payload);
|
|
1642
|
+
} catch {
|
|
1643
|
+
return null;
|
|
1644
|
+
}
|
|
1645
|
+
};
|
|
1646
|
+
try {
|
|
1647
|
+
for (;; ) {
|
|
1648
|
+
const { value, done } = await reader.read();
|
|
1649
|
+
if (done)
|
|
1650
|
+
break;
|
|
1651
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1652
|
+
let nl;
|
|
1653
|
+
while ((nl = buffer.indexOf(`
|
|
1654
|
+
`)) >= 0) {
|
|
1655
|
+
const line = buffer.slice(0, nl);
|
|
1656
|
+
buffer = buffer.slice(nl + 1);
|
|
1657
|
+
const frame = handle(line);
|
|
1658
|
+
if (frame === "DONE")
|
|
1659
|
+
return;
|
|
1660
|
+
if (frame)
|
|
1661
|
+
yield frame;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
const tail = handle(buffer);
|
|
1665
|
+
if (tail && tail !== "DONE")
|
|
1666
|
+
yield tail;
|
|
1667
|
+
} finally {
|
|
1668
|
+
try {
|
|
1669
|
+
await reader.cancel();
|
|
1670
|
+
} catch {}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
function accumulateToolCallDeltas(acc, deltas) {
|
|
1674
|
+
if (!Array.isArray(deltas))
|
|
1675
|
+
return;
|
|
1676
|
+
for (const raw of deltas) {
|
|
1677
|
+
const d = asRecord2(raw);
|
|
1678
|
+
const index = typeof d.index === "number" ? d.index : 0;
|
|
1679
|
+
const cur = acc.get(index) ?? { args: "" };
|
|
1680
|
+
const id = firstString(d.id);
|
|
1681
|
+
if (id)
|
|
1682
|
+
cur.id = id;
|
|
1683
|
+
const fn = recordAt(d, "function");
|
|
1684
|
+
const name = firstString(fn.name);
|
|
1685
|
+
if (name)
|
|
1686
|
+
cur.name = name;
|
|
1687
|
+
if (typeof fn.arguments === "string")
|
|
1688
|
+
cur.args += fn.arguments;
|
|
1689
|
+
acc.set(index, cur);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
function finalizeStreamedToolCalls(acc) {
|
|
1693
|
+
const out = [];
|
|
1694
|
+
for (const [index, c] of [...acc.entries()].sort((a, b) => a[0] - b[0])) {
|
|
1695
|
+
if (!c.name)
|
|
1696
|
+
continue;
|
|
1697
|
+
out.push({
|
|
1698
|
+
type: "tool-call",
|
|
1699
|
+
toolCallId: c.id ?? `call_${c.name}_${index}`,
|
|
1700
|
+
toolName: c.name,
|
|
1701
|
+
input: parseJsonIfPossible(c.args.trim() === "" ? "{}" : c.args)
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1704
|
+
return out;
|
|
1705
|
+
}
|
|
1706
|
+
async function streamNativeChatCompletion(runtime, modelType, params, context) {
|
|
1707
|
+
const requestBody = buildNativeRequestBody(params, context.modelName, context.prompt, context.systemPrompt);
|
|
1708
|
+
requestBody.stream = true;
|
|
1709
|
+
requestBody.stream_options = { include_usage: true };
|
|
1710
|
+
const headers = {
|
|
1711
|
+
"X-Eliza-Llm-Purpose": getPurposeForModelType(modelType),
|
|
1712
|
+
"X-Eliza-Model-Type": modelType
|
|
1713
|
+
};
|
|
1714
|
+
if (isSpanSamplerHonoringModel(context.modelName)) {
|
|
1715
|
+
const samplerHeader = buildSpanSamplerHeader(params.spanSamplerPlan);
|
|
1716
|
+
if (samplerHeader) {
|
|
1717
|
+
headers["x-eliza-span-samplers"] = samplerHeader;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
const abortSignal = params.signal;
|
|
1721
|
+
const signal = buildStreamAbortSignal(abortSignal, resolveTextTimeoutMs());
|
|
1722
|
+
const limiter = getNativeChatLimiter();
|
|
1723
|
+
const waitStartedAt = Date.now();
|
|
1724
|
+
await limiter.acquire();
|
|
1725
|
+
recordInferenceSpan("cloud.semaphore-wait", Date.now() - waitStartedAt, {
|
|
1726
|
+
route: "chat/completions:stream"
|
|
1727
|
+
});
|
|
1728
|
+
let permitReleased = false;
|
|
1729
|
+
const releasePermit = () => {
|
|
1730
|
+
if (!permitReleased) {
|
|
1731
|
+
permitReleased = true;
|
|
1732
|
+
limiter.release();
|
|
1733
|
+
}
|
|
1734
|
+
};
|
|
1735
|
+
let response;
|
|
1736
|
+
try {
|
|
1737
|
+
response = await createCloudApiClient(runtime).requestRaw("POST", "/chat/completions", {
|
|
1738
|
+
headers,
|
|
1739
|
+
json: requestBody,
|
|
1740
|
+
...signal ? { signal } : {}
|
|
1741
|
+
});
|
|
1742
|
+
} catch (err) {
|
|
1743
|
+
releasePermit();
|
|
1744
|
+
throw err;
|
|
1745
|
+
}
|
|
1746
|
+
if (!response.ok) {
|
|
1747
|
+
let errorBody;
|
|
1748
|
+
try {
|
|
1749
|
+
const errText = await response.text();
|
|
1750
|
+
if (errText) {
|
|
1751
|
+
errorBody = JSON.parse(errText).error;
|
|
1752
|
+
}
|
|
1753
|
+
} catch {}
|
|
1754
|
+
releasePermit();
|
|
1755
|
+
const message = typeof errorBody?.message === "string" && errorBody.message.trim() ? errorBody.message.trim() : `elizaOS Cloud error ${response.status}`;
|
|
1756
|
+
const requestError = new Error(message);
|
|
1757
|
+
requestError.status = response.status;
|
|
1758
|
+
if (errorBody)
|
|
1759
|
+
requestError.error = errorBody;
|
|
1760
|
+
throw requestError;
|
|
1761
|
+
}
|
|
1762
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
1763
|
+
const isSse = contentType.includes("text/event-stream") && response.body !== null;
|
|
1764
|
+
if (!isSse) {
|
|
1765
|
+
const bufferedText = await response.text();
|
|
1766
|
+
releasePermit();
|
|
1767
|
+
let data = {};
|
|
1768
|
+
if (bufferedText) {
|
|
1769
|
+
try {
|
|
1770
|
+
data = JSON.parse(bufferedText);
|
|
1771
|
+
} catch (parseErr) {
|
|
1772
|
+
logger7.error(`[ELIZAOS_CLOUD] Failed to parse buffered chat completions JSON: ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
const text = extractChatCompletionText(data);
|
|
1776
|
+
const toolCalls = extractNativeToolCalls(data);
|
|
1777
|
+
const usage = convertNativeUsage(data.usage);
|
|
1778
|
+
if (usage) {
|
|
1779
|
+
emitModelUsageEvent(runtime, modelType, context.prompt, usage, {
|
|
1780
|
+
modelName: context.modelName,
|
|
1781
|
+
...(() => {
|
|
1782
|
+
const costUsd = extractCostUsd(data.usage, response);
|
|
1783
|
+
return typeof costUsd === "number" ? { costUsd } : {};
|
|
1784
|
+
})()
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
if (!text.trim() && toolCalls.length === 0) {
|
|
1788
|
+
throw new Error("elizaOS Cloud returned no text or tool calls");
|
|
1789
|
+
}
|
|
1790
|
+
async function* single() {
|
|
1791
|
+
if (text)
|
|
1792
|
+
yield text;
|
|
1793
|
+
}
|
|
1794
|
+
return {
|
|
1795
|
+
textStream: single(),
|
|
1796
|
+
text: Promise.resolve(text),
|
|
1797
|
+
usage: Promise.resolve(usage),
|
|
1798
|
+
finishReason: Promise.resolve(data.choices?.[0]?.finish_reason),
|
|
1799
|
+
toolCalls: Promise.resolve(toolCalls),
|
|
1800
|
+
providerMetadata: { modelName: context.modelName, usage: data.usage }
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
const body = response.body;
|
|
1804
|
+
const toolAcc = new Map;
|
|
1805
|
+
let accumulated = "";
|
|
1806
|
+
let nativeUsage;
|
|
1807
|
+
let rawUsage;
|
|
1808
|
+
let finishReason;
|
|
1809
|
+
const textD = deferred();
|
|
1810
|
+
const usageD = deferred();
|
|
1811
|
+
const finishD = deferred();
|
|
1812
|
+
const toolCallsD = deferred();
|
|
1813
|
+
async function* generate() {
|
|
1814
|
+
try {
|
|
1815
|
+
for await (const frame of parseOpenAiSseStream(body)) {
|
|
1816
|
+
if (frame.error) {
|
|
1817
|
+
const message = asRecord2(frame.error).message;
|
|
1818
|
+
throw new Error(typeof message === "string" && message.trim() ? message.trim() : "elizaOS Cloud stream error");
|
|
1819
|
+
}
|
|
1820
|
+
const choices = Array.isArray(frame.choices) ? frame.choices : [];
|
|
1821
|
+
const choice = asRecord2(choices[0]);
|
|
1822
|
+
const delta = recordAt(choice, "delta");
|
|
1823
|
+
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
1824
|
+
accumulated += delta.content;
|
|
1825
|
+
yield delta.content;
|
|
1826
|
+
}
|
|
1827
|
+
if (delta.tool_calls) {
|
|
1828
|
+
accumulateToolCallDeltas(toolAcc, delta.tool_calls);
|
|
1829
|
+
}
|
|
1830
|
+
const fr = firstString(choice.finish_reason);
|
|
1831
|
+
if (fr)
|
|
1832
|
+
finishReason = fr;
|
|
1833
|
+
if (frame.usage) {
|
|
1834
|
+
rawUsage = frame.usage;
|
|
1835
|
+
nativeUsage = convertNativeUsage(frame.usage);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
} finally {
|
|
1839
|
+
releasePermit();
|
|
1840
|
+
const toolCalls = finalizeStreamedToolCalls(toolAcc);
|
|
1841
|
+
textD.resolve(accumulated);
|
|
1842
|
+
usageD.resolve(nativeUsage);
|
|
1843
|
+
finishD.resolve(finishReason);
|
|
1844
|
+
toolCallsD.resolve(toolCalls);
|
|
1845
|
+
if (nativeUsage) {
|
|
1846
|
+
emitModelUsageEvent(runtime, modelType, context.prompt, nativeUsage, {
|
|
1847
|
+
modelName: context.modelName,
|
|
1848
|
+
...(() => {
|
|
1849
|
+
const costUsd = extractCostUsd(rawUsage, response);
|
|
1850
|
+
return typeof costUsd === "number" ? { costUsd } : {};
|
|
1851
|
+
})()
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
return {
|
|
1857
|
+
textStream: generate(),
|
|
1858
|
+
text: textD.promise,
|
|
1859
|
+
usage: usageD.promise,
|
|
1860
|
+
finishReason: finishD.promise,
|
|
1861
|
+
toolCalls: toolCallsD.promise,
|
|
1862
|
+
providerMetadata: { modelName: context.modelName }
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1345
1865
|
async function handleTextSmall(runtime, params) {
|
|
1346
1866
|
return generateTextWithModel(runtime, TEXT_SMALL_MODEL_TYPE, params);
|
|
1347
1867
|
}
|
|
@@ -1383,4 +1903,4 @@ export {
|
|
|
1383
1903
|
fetchTextToSpeech
|
|
1384
1904
|
};
|
|
1385
1905
|
|
|
1386
|
-
//# debugId=
|
|
1906
|
+
//# debugId=4FFC473B568B95F864756E2164756E21
|