Files
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

130 lines
3.8 KiB
TypeScript

/**
* Token usage normalization and `MODEL_USED` event emission for LM Studio calls.
*
* LM Studio's OpenAI-compatible API returns usage with the same field names as
* OpenAI (`prompt_tokens`, `completion_tokens`, `total_tokens`), while the AI SDK
* normalizes them to `inputTokens` / `outputTokens` / `totalTokens` before handing
* them to us. We accept either shape and fall back to a length-based estimate when
* a model omits usage (some local builds do).
*/
import type { EventPayload, IAgentRuntime, ModelTypeName } from "@elizaos/core";
import { EventType } from "@elizaos/core";
type ProviderUsage = {
inputTokens?: number;
outputTokens?: number;
promptTokens?: number;
completionTokens?: number;
totalTokens?: number;
};
export type NormalizedUsage = {
promptTokens: number;
completionTokens: number;
totalTokens: number;
estimated?: boolean;
};
function toFiniteNumber(value: unknown): number | undefined {
if (typeof value !== "number" || !Number.isFinite(value)) {
return undefined;
}
return Math.max(0, Math.round(value));
}
export function normalizeTokenUsage(usage: unknown): NormalizedUsage | null {
if (!usage || typeof usage !== "object") {
return null;
}
const record = usage as ProviderUsage;
const promptTokens = toFiniteNumber(record.inputTokens ?? record.promptTokens);
const completionTokens = toFiniteNumber(record.outputTokens ?? record.completionTokens);
const totalTokens = toFiniteNumber(record.totalTokens);
if (promptTokens === undefined && completionTokens === undefined && totalTokens === undefined) {
return null;
}
const normalizedPromptTokens =
promptTokens ??
(completionTokens === undefined && totalTokens !== undefined
? totalTokens
: Math.max(0, (totalTokens ?? 0) - (completionTokens ?? 0)));
const normalizedCompletionTokens =
completionTokens ??
Math.max(0, (totalTokens ?? normalizedPromptTokens) - normalizedPromptTokens);
return {
promptTokens: normalizedPromptTokens,
completionTokens: normalizedCompletionTokens,
totalTokens: totalTokens ?? normalizedPromptTokens + normalizedCompletionTokens,
};
}
export function estimateTokenCount(text: string): number {
return text.length === 0 ? 0 : Math.ceil(text.length / 4);
}
function stringifyForUsage(value: unknown): string {
if (typeof value === "string") {
return value;
}
try {
return JSON.stringify(value);
} catch {
// error-policy:J7 token-count estimation only — a non-serializable value
// degrades to String() so usage telemetry never throws out of a successful
// generation. Not a data path.
return String(value);
}
}
export function estimateUsage(prompt: string, response: unknown): NormalizedUsage {
const promptTokens = estimateTokenCount(prompt);
const completionTokens = estimateTokenCount(stringifyForUsage(response));
return {
promptTokens,
completionTokens,
totalTokens: promptTokens + completionTokens,
estimated: true,
};
}
export function estimateEmbeddingUsage(text: string): NormalizedUsage {
const promptTokens = estimateTokenCount(text);
return {
promptTokens,
completionTokens: 0,
totalTokens: promptTokens,
estimated: true,
};
}
export function emitModelUsed(
runtime: IAgentRuntime,
type: ModelTypeName,
model: string,
usage: NormalizedUsage
): void {
void runtime.emitEvent(
EventType.MODEL_USED as string,
{
runtime,
source: "lmstudio",
provider: "lmstudio",
type,
model,
modelName: model,
tokens: {
prompt: usage.promptTokens,
completion: usage.completionTokens,
total: usage.totalTokens,
...(usage.estimated ? { estimated: true } : {}),
},
...(usage.estimated ? { usageEstimated: true } : {}),
} as EventPayload
);
}