@jeffusion/bungee-core 6.1.1 → 6.2.1
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/dist/main.js +131 -102
- package/dist/observability-worker.js +21 -1
- package/dist/plugins/anthropic-request-sanitizer/index.js.map +3 -3
- package/dist/plugins/anthropic-tool-name-transformer/index.js.map +3 -3
- package/dist/plugins/chatgpt-oauth/control.js.map +1 -1
- package/dist/plugins/chatgpt-oauth/index.js.map +3 -3
- package/dist/plugins/codex-router/control.js.map +1 -1
- package/dist/plugins/codex-router/index.js.map +3 -3
- package/dist/plugins/deepseek-reasoning-fix/index.js.map +3 -3
- package/dist/plugins/key-access/control.js.map +1 -1
- package/dist/plugins/key-access/index.js.map +3 -3
- package/dist/plugins/key-access/ingress.js.map +1 -1
- package/dist/plugins/key-rate-limit/index.js.map +3 -3
- package/dist/plugins/llm-protocol-adapter/control.js +7 -3
- package/dist/plugins/llm-protocol-adapter/control.js.map +4 -4
- package/dist/plugins/llm-protocol-adapter/index.js +7 -3
- package/dist/plugins/llm-protocol-adapter/index.js.map +6 -6
- package/dist/plugins/llm-tps/control.js +212 -0
- package/dist/plugins/llm-tps/control.js.map +13 -0
- package/dist/plugins/llm-tps/index.js +286 -0
- package/dist/plugins/llm-tps/index.js.map +27 -0
- package/dist/plugins/llm-tps/manifest.json +115 -0
- package/dist/plugins/llm-tps/ui/TpsEndpoints.svelte +9 -0
- package/dist/plugins/llm-tps/ui/TpsOverview.svelte +9 -0
- package/dist/plugins/llm-tps/ui/TpsWidget.svelte +115 -0
- package/dist/plugins/llm-tps/ui/presentation.ts +16 -0
- package/dist/plugins/llm-tps/ui/stats-resource.ts +77 -0
- package/dist/plugins/llm-tps/ui/stats.ts +7 -0
- package/dist/plugins/model-mapping/index.js.map +3 -3
- package/dist/plugins/models-dev/index.js.map +3 -3
- package/dist/plugins/signature-repair/index.js.map +3 -3
- package/dist/plugins/token-budget/control.js.map +1 -1
- package/dist/plugins/token-budget/index.js.map +3 -3
- package/dist/plugins/token-metering/index.js +138 -11
- package/dist/plugins/token-metering/index.js.map +8 -7
- package/dist/plugins/token-stats/control.js.map +1 -1
- package/dist/plugins/token-stats/index.js.map +3 -3
- package/dist/plugins/user-auth/control.js.map +1 -1
- package/dist/plugins/user-auth/index.js.map +3 -3
- package/package.json +2 -2
|
@@ -2663,7 +2663,7 @@ class ResponsesWebSocketMetering {
|
|
|
2663
2663
|
connection.active.delete(responseId);
|
|
2664
2664
|
connection.finished.add(responseId);
|
|
2665
2665
|
const usageComplete = count(usage?.input_tokens) && count(usage?.output_tokens);
|
|
2666
|
-
await this.finish(response, event, type === "response.completed" && usageComplete ? "completed" : "failed", !usageComplete);
|
|
2666
|
+
await this.finish(response, event, type === "response.completed" && usageComplete ? "completed" : "failed", !usageComplete, type === "response.completed" ? body : undefined);
|
|
2667
2667
|
if (connection.finished.size > MAX_TOMBSTONES)
|
|
2668
2668
|
connection.finished.delete(connection.finished.values().next().value);
|
|
2669
2669
|
}
|
|
@@ -2697,23 +2697,23 @@ class ResponsesWebSocketMetering {
|
|
|
2697
2697
|
this.callbacks.prepareRequest(response.requestId);
|
|
2698
2698
|
return await this.emit(response, event, { phase: "selected" }) && await this.emit(response, event, { phase: "request", url: event.upstreamUrl, body: { model: response.model, stream: true } });
|
|
2699
2699
|
}
|
|
2700
|
-
async emit(response, event, phase) {
|
|
2700
|
+
async emit(response, event, phase, settlement) {
|
|
2701
2701
|
if (this.closed || !event.isActive()) {
|
|
2702
2702
|
this.callbacks.discardRequest(response.requestId, response.attemptId);
|
|
2703
2703
|
return false;
|
|
2704
2704
|
}
|
|
2705
|
-
await this.callbacks.emit({ requestId: response.requestId, attemptId: response.attemptId, keyId: event.keyId, routeId: event.routeId, upstreamId: event.upstreamId, isActive: event.isActive, ...phase });
|
|
2705
|
+
await this.callbacks.emit({ requestId: response.requestId, attemptId: response.attemptId, observedAtMonotonicMs: event.observedAtMonotonicMs, keyId: event.keyId, routeId: event.routeId, upstreamId: event.upstreamId, isActive: event.isActive, ...phase }, settlement);
|
|
2706
2706
|
if (this.closed || !event.isActive()) {
|
|
2707
2707
|
this.callbacks.discardRequest(response.requestId, response.attemptId);
|
|
2708
2708
|
return false;
|
|
2709
2709
|
}
|
|
2710
2710
|
return true;
|
|
2711
2711
|
}
|
|
2712
|
-
async finish(response, event, outcome, incomplete) {
|
|
2712
|
+
async finish(response, event, outcome, incomplete, generationResponse) {
|
|
2713
2713
|
this.responseCount--;
|
|
2714
|
-
if (incomplete && !await this.emit(response, event, { phase: "incomplete", reason: "raw-response-incomplete" }))
|
|
2714
|
+
if (incomplete && !await this.emit(response, event, { phase: "incomplete", reason: "raw-response-incomplete" }, { accountingUsageIncomplete: generationResponse !== undefined }))
|
|
2715
2715
|
return;
|
|
2716
|
-
if (!await this.emit(response, event, { phase: "end", sent: true, outcome }))
|
|
2716
|
+
if (!await this.emit(response, event, { phase: "end", sent: true, outcome }, { generationResponse }))
|
|
2717
2717
|
return;
|
|
2718
2718
|
await this.emit(response, event, { phase: "request-end" });
|
|
2719
2719
|
}
|
|
@@ -2741,6 +2741,93 @@ class ResponsesWebSocketMetering {
|
|
|
2741
2741
|
}
|
|
2742
2742
|
}
|
|
2743
2743
|
|
|
2744
|
+
// plugins/token-metering/server/generation-timing.ts
|
|
2745
|
+
var record4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2746
|
+
var text = (value) => typeof value === "string" && value.length > 0;
|
|
2747
|
+
var values = (value) => Array.isArray(value) ? value : [];
|
|
2748
|
+
var parameters = (value) => text(value) || record4(value) && Object.keys(value).length > 0;
|
|
2749
|
+
var content = (value) => text(value) || values(value).some((part) => record4(part) && (text(part.text) || text(part.thinking)));
|
|
2750
|
+
function generationSignals(provider, body, event) {
|
|
2751
|
+
const type = event || body.type;
|
|
2752
|
+
if (provider === "openai") {
|
|
2753
|
+
const choices = values(body.choices).filter(record4);
|
|
2754
|
+
const output = choices.some((choice) => text(choice.text) || record4(choice.delta) && (content(choice.delta.content) || text(choice.delta.reasoning_content) || text(choice.delta.reasoning) || record4(choice.delta.function_call) && text(choice.delta.function_call.arguments) || values(choice.delta.tool_calls).some((tool) => record4(tool) && record4(tool.function) && text(tool.function.arguments)))) || typeof type === "string" && [
|
|
2755
|
+
"response.output_text.delta",
|
|
2756
|
+
"response.reasoning_text.delta",
|
|
2757
|
+
"response.reasoning_summary_text.delta",
|
|
2758
|
+
"response.function_call_arguments.delta",
|
|
2759
|
+
"response.refusal.delta"
|
|
2760
|
+
].includes(type) && text(body.delta);
|
|
2761
|
+
return { output, completed: type === "response.completed" || choices.some((choice) => choice.finish_reason !== undefined && choice.finish_reason !== null) };
|
|
2762
|
+
}
|
|
2763
|
+
if (provider === "anthropic") {
|
|
2764
|
+
const delta = record4(body.delta) ? body.delta : undefined;
|
|
2765
|
+
const block = type === "content_block_start" && record4(body.content_block) ? body.content_block : undefined;
|
|
2766
|
+
return {
|
|
2767
|
+
output: type === "content_block_delta" && !!delta && (delta.type === "text_delta" && text(delta.text) || delta.type === "thinking_delta" && text(delta.thinking) || delta.type === "input_json_delta" && text(delta.partial_json)) || !!block && (text(block.text) || text(block.thinking) || block.type === "tool_use" && parameters(block.input)),
|
|
2768
|
+
completed: type === "message_stop" || type === "message_delta" && !!delta && text(delta.stop_reason)
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
const candidates = values(body.candidates).filter(record4);
|
|
2772
|
+
return {
|
|
2773
|
+
output: candidates.some((candidate) => record4(candidate.content) && values(candidate.content.parts).some((part) => record4(part) && (text(part.text) || record4(part.functionCall) && parameters(part.functionCall.args)))),
|
|
2774
|
+
completed: candidates.some((candidate) => text(candidate.finishReason))
|
|
2775
|
+
};
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
class GenerationTiming {
|
|
2779
|
+
firstOutput;
|
|
2780
|
+
completedAt;
|
|
2781
|
+
terminal = false;
|
|
2782
|
+
missingTime = false;
|
|
2783
|
+
failed = false;
|
|
2784
|
+
branches = new Map;
|
|
2785
|
+
observe(provider, body, at, event) {
|
|
2786
|
+
const type = event || body.type;
|
|
2787
|
+
const response = record4(body.response) ? body.response : undefined;
|
|
2788
|
+
this.failed ||= body.error != null || response?.error != null || type === "error" || type === "response.failed" || type === "response.incomplete" || response?.status === "failed" || response?.status === "incomplete";
|
|
2789
|
+
if (this.terminal)
|
|
2790
|
+
return;
|
|
2791
|
+
const signal = generationSignals(provider, body, event);
|
|
2792
|
+
const valid = at !== undefined && Number.isFinite(at);
|
|
2793
|
+
if (signal.output && this.firstOutput === undefined) {
|
|
2794
|
+
if (valid)
|
|
2795
|
+
this.firstOutput = at;
|
|
2796
|
+
else
|
|
2797
|
+
this.missingTime = true;
|
|
2798
|
+
}
|
|
2799
|
+
const branches = provider === "openai" ? values(body.choices) : provider === "gemini" ? values(body.candidates) : [];
|
|
2800
|
+
if (branches.length) {
|
|
2801
|
+
for (const [position, branch] of branches.entries()) {
|
|
2802
|
+
if (!record4(branch))
|
|
2803
|
+
continue;
|
|
2804
|
+
const id = Number.isSafeInteger(branch.index) && branch.index >= 0 ? branch.index : position;
|
|
2805
|
+
const completed = provider === "openai" ? text(branch.finish_reason) : text(branch.finishReason);
|
|
2806
|
+
if (completed && !valid)
|
|
2807
|
+
this.missingTime = true;
|
|
2808
|
+
const previous = this.branches.get(id);
|
|
2809
|
+
this.branches.set(id, completed ? { completed: true, at } : previous ?? { completed: false });
|
|
2810
|
+
}
|
|
2811
|
+
} else if (signal.completed) {
|
|
2812
|
+
this.terminal = true;
|
|
2813
|
+
if (valid)
|
|
2814
|
+
this.completedAt = at;
|
|
2815
|
+
else
|
|
2816
|
+
this.missingTime = true;
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
duration(endedAt) {
|
|
2820
|
+
if (this.failed || this.missingTime || this.firstOutput === undefined)
|
|
2821
|
+
return;
|
|
2822
|
+
const completedBranches = this.branches.size > 0 && [...this.branches.values()].every((branch) => branch.completed);
|
|
2823
|
+
const end = this.terminal ? this.completedAt : completedBranches ? Math.max(...[...this.branches.values()].map((branch) => branch.at)) : endedAt;
|
|
2824
|
+
if (end === undefined || !Number.isFinite(end))
|
|
2825
|
+
return;
|
|
2826
|
+
const duration = end - this.firstOutput;
|
|
2827
|
+
return Number.isFinite(duration) && duration > 0 ? duration : undefined;
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2744
2831
|
// plugins/token-metering/server/index.ts
|
|
2745
2832
|
var MAX_ACTIVE_ATTEMPTS = 1024;
|
|
2746
2833
|
var LOG_INTERVAL_MS = 60000;
|
|
@@ -2773,6 +2860,7 @@ function createAttempt(event) {
|
|
|
2773
2860
|
keyId: event.keyId ?? null,
|
|
2774
2861
|
routeId: event.routeId || "unknown",
|
|
2775
2862
|
upstreamId: event.upstreamId || "unknown",
|
|
2863
|
+
timing: new GenerationTiming,
|
|
2776
2864
|
provider: "unknown",
|
|
2777
2865
|
sent: false,
|
|
2778
2866
|
llm: false,
|
|
@@ -2780,6 +2868,7 @@ function createAttempt(event) {
|
|
|
2780
2868
|
responseSeen: false,
|
|
2781
2869
|
responseFailed: false,
|
|
2782
2870
|
observationIncomplete: false,
|
|
2871
|
+
generationObservationIncomplete: false,
|
|
2783
2872
|
incompleteReasonLogged: false
|
|
2784
2873
|
};
|
|
2785
2874
|
}
|
|
@@ -2873,6 +2962,34 @@ function finalOutcome(input, canonicalOutcome, officialComplete = false) {
|
|
|
2873
2962
|
}
|
|
2874
2963
|
function finalizeAttempt(input) {
|
|
2875
2964
|
let event = input.observedEvent;
|
|
2965
|
+
const generationComplete = (input.generationCompleted ?? input.endOutcome === "completed") && input.responseSeen && !input.responseFailed && !(input.generationObservationIncomplete ?? input.observationIncomplete) && input.generationDurationMs !== undefined;
|
|
2966
|
+
let generationOutput;
|
|
2967
|
+
if (generationComplete) {
|
|
2968
|
+
try {
|
|
2969
|
+
let measured;
|
|
2970
|
+
if (input.generationResponse) {
|
|
2971
|
+
const measurement = createTokenAccountingSession({
|
|
2972
|
+
provider: "openai",
|
|
2973
|
+
model: input.model,
|
|
2974
|
+
requestId: input.requestId,
|
|
2975
|
+
attemptId: input.attemptId,
|
|
2976
|
+
routeId: input.routeId,
|
|
2977
|
+
upstreamId: input.upstreamId,
|
|
2978
|
+
streaming: true
|
|
2979
|
+
}, { deferFinalization: true });
|
|
2980
|
+
measurement.consumeStreamChunk({ chunk: input.generationResponse });
|
|
2981
|
+
measured = measurement.finalizeCompletedStream();
|
|
2982
|
+
} else
|
|
2983
|
+
measured = input.session?.finalizeCompletedStream();
|
|
2984
|
+
if (measured) {
|
|
2985
|
+
assertCanonicalTokenAccountingEventV2(measured);
|
|
2986
|
+
const output = input.generationResponse ? measured : mergeOfficialUsage({ ...measured }, event);
|
|
2987
|
+
if (output.outputTokens !== undefined && ["official", "local", "heuristic"].includes(output.outputAuthority)) {
|
|
2988
|
+
generationOutput = Object.freeze({ tokens: output.outputTokens, source: output.outputAuthority === "official" ? "official" : "estimated" });
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
} catch {}
|
|
2992
|
+
}
|
|
2876
2993
|
try {
|
|
2877
2994
|
const finalized = input.endOutcome === "completed" && input.responseSeen && !input.responseFailed && !input.observationIncomplete ? input.session?.finalizeCompletedStream() : input.session?.finalizeAbortedStream();
|
|
2878
2995
|
if (finalized) {
|
|
@@ -2914,7 +3031,8 @@ function finalizeAttempt(input) {
|
|
|
2914
3031
|
observationIncomplete: input.observationIncomplete,
|
|
2915
3032
|
complete: outcome === "completed" && event?.inputTokens !== undefined && event?.outputTokens !== undefined && event.inputAuthority !== "partial" && event.outputAuthority !== "partial" && (!input.observationIncomplete || event.inputAuthority === "official" && event.outputAuthority === "official"),
|
|
2916
3033
|
finishedAtMs: input.finishedAtMs,
|
|
2917
|
-
settlementVersion: 1
|
|
3034
|
+
settlementVersion: 1,
|
|
3035
|
+
...generationComplete ? { generationDurationMs: input.generationDurationMs, generationOutput } : {}
|
|
2918
3036
|
});
|
|
2919
3037
|
}
|
|
2920
3038
|
var TokenMeteringPlugin = definePlugin(class {
|
|
@@ -2929,7 +3047,7 @@ var TokenMeteringPlugin = definePlugin(class {
|
|
|
2929
3047
|
requiredPending = new Map;
|
|
2930
3048
|
closed = false;
|
|
2931
3049
|
websocket = new ResponsesWebSocketMetering({
|
|
2932
|
-
emit: (event) => this.handleAttemptObservation(event, true),
|
|
3050
|
+
emit: (event, settlement) => this.handleAttemptObservation(event, true, settlement),
|
|
2933
3051
|
prepareRequest: (requestId) => this.prepareRequest(requestId),
|
|
2934
3052
|
discardRequest: (requestId, attemptId) => {
|
|
2935
3053
|
this.attempts.delete(attemptId);
|
|
@@ -3063,7 +3181,7 @@ var TokenMeteringPlugin = definePlugin(class {
|
|
|
3063
3181
|
});
|
|
3064
3182
|
}
|
|
3065
3183
|
}
|
|
3066
|
-
async handleAttemptObservation(event, websocket = false) {
|
|
3184
|
+
async handleAttemptObservation(event, websocket = false, settlement = {}) {
|
|
3067
3185
|
if (!event.isActive()) {
|
|
3068
3186
|
this.attempts.delete(event.attemptId);
|
|
3069
3187
|
return;
|
|
@@ -3126,7 +3244,7 @@ var TokenMeteringPlugin = definePlugin(class {
|
|
|
3126
3244
|
attempt.session?.consumeRequest({ body: { model } });
|
|
3127
3245
|
}
|
|
3128
3246
|
}
|
|
3129
|
-
attempt.responseFailed ||= event.status >= 400 || body.error
|
|
3247
|
+
attempt.responseFailed ||= event.status >= 400 || body.error != null;
|
|
3130
3248
|
const provider = attempt.provider === "unknown" ? classifyResponse(body, attempt.llm, event.envelope?.event) : attempt.provider;
|
|
3131
3249
|
if (!provider)
|
|
3132
3250
|
return;
|
|
@@ -3138,6 +3256,8 @@ var TokenMeteringPlugin = definePlugin(class {
|
|
|
3138
3256
|
attempt.streaming = event.protocol === "sse";
|
|
3139
3257
|
ensureSession(attempt, provider);
|
|
3140
3258
|
}
|
|
3259
|
+
if (event.protocol === "sse")
|
|
3260
|
+
attempt.timing.observe(provider, body, event.observedAtMonotonicMs, event.envelope?.event);
|
|
3141
3261
|
const session = attempt.session;
|
|
3142
3262
|
if (!session)
|
|
3143
3263
|
return;
|
|
@@ -3151,6 +3271,7 @@ var TokenMeteringPlugin = definePlugin(class {
|
|
|
3151
3271
|
attempt.latestEvent = attempt.responseFailed ? officialOnly(merged) : merged;
|
|
3152
3272
|
} catch (error) {
|
|
3153
3273
|
attempt.observationIncomplete = true;
|
|
3274
|
+
attempt.generationObservationIncomplete = true;
|
|
3154
3275
|
if (attempt.latestEvent)
|
|
3155
3276
|
attempt.latestEvent = officialOnly(attempt.latestEvent);
|
|
3156
3277
|
this.logger.debug("Failed to consume token metering attempt observation", { attemptId: event.attemptId, error });
|
|
@@ -3158,6 +3279,8 @@ var TokenMeteringPlugin = definePlugin(class {
|
|
|
3158
3279
|
return;
|
|
3159
3280
|
}
|
|
3160
3281
|
if (event.phase === "incomplete") {
|
|
3282
|
+
if (!settlement.accountingUsageIncomplete)
|
|
3283
|
+
attempt.generationObservationIncomplete = true;
|
|
3161
3284
|
if (!attempt.observationIncomplete) {
|
|
3162
3285
|
attempt.observationIncomplete = true;
|
|
3163
3286
|
if (!attempt.incompleteReasonLogged) {
|
|
@@ -3192,6 +3315,10 @@ var TokenMeteringPlugin = definePlugin(class {
|
|
|
3192
3315
|
observedEvent: attempt.latestEvent ? { ...attempt.latestEvent } : undefined,
|
|
3193
3316
|
finishedAtMs: Date.now(),
|
|
3194
3317
|
endOutcome: event.outcome,
|
|
3318
|
+
generationDurationMs: attempt.timing.duration(event.observedAtMonotonicMs),
|
|
3319
|
+
generationCompleted: websocket ? settlement.generationResponse !== undefined : event.outcome === "completed",
|
|
3320
|
+
generationResponse: settlement.generationResponse,
|
|
3321
|
+
generationObservationIncomplete: attempt.generationObservationIncomplete,
|
|
3195
3322
|
responseSeen: attempt.responseSeen,
|
|
3196
3323
|
responseFailed: attempt.responseFailed,
|
|
3197
3324
|
observationIncomplete: attempt.observationIncomplete
|
|
@@ -3216,4 +3343,4 @@ export {
|
|
|
3216
3343
|
server_default as default
|
|
3217
3344
|
};
|
|
3218
3345
|
|
|
3219
|
-
//# debugId=
|
|
3346
|
+
//# debugId=DB115B0B5828A5AC64756E2164756E21
|