@usagetap/sdk 1.3.2 → 1.7.0
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 +372 -39
- package/dist/adapters/anthropic.cjs +995 -69
- package/dist/adapters/anthropic.cjs.map +1 -1
- package/dist/adapters/anthropic.d.cts +45 -3
- package/dist/adapters/anthropic.d.ts +45 -3
- package/dist/adapters/anthropic.mjs +995 -70
- package/dist/adapters/anthropic.mjs.map +1 -1
- package/dist/adapters/openai.cjs +1208 -106
- package/dist/adapters/openai.cjs.map +1 -1
- package/dist/adapters/openai.d.cts +46 -3
- package/dist/adapters/openai.d.ts +46 -3
- package/dist/adapters/openai.mjs +1208 -107
- package/dist/adapters/openai.mjs.map +1 -1
- package/dist/adapters/openrouter.cjs +3912 -53
- package/dist/adapters/openrouter.cjs.map +1 -1
- package/dist/adapters/openrouter.d.cts +6 -3
- package/dist/adapters/openrouter.d.ts +6 -3
- package/dist/adapters/openrouter.mjs +3910 -54
- package/dist/adapters/openrouter.mjs.map +1 -1
- package/dist/anthropic/index.cjs +995 -69
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.d.cts +2 -2
- package/dist/anthropic/index.d.ts +2 -2
- package/dist/anthropic/index.mjs +995 -70
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/client-C0UiaqVB.d.cts +1305 -0
- package/dist/client-C0UiaqVB.d.ts +1305 -0
- package/dist/express/index.cjs +399 -64
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.d.cts +2 -2
- package/dist/express/index.d.ts +2 -2
- package/dist/express/index.mjs +399 -64
- package/dist/express/index.mjs.map +1 -1
- package/dist/index.cjs +1044 -163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -5
- package/dist/index.d.ts +16 -5
- package/dist/index.mjs +1044 -163
- package/dist/index.mjs.map +1 -1
- package/dist/openai/index.cjs +1209 -107
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.d.cts +2 -2
- package/dist/openai/index.d.ts +2 -2
- package/dist/openai/index.mjs +1209 -108
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openrouter/index.cjs +1226 -109
- package/dist/openrouter/index.cjs.map +1 -1
- package/dist/openrouter/index.d.cts +3 -3
- package/dist/openrouter/index.d.ts +3 -3
- package/dist/openrouter/index.mjs +1224 -108
- package/dist/openrouter/index.mjs.map +1 -1
- package/dist/react/index.cjs +19 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +17 -4
- package/dist/react/index.d.ts +17 -4
- package/dist/react/index.mjs +19 -1
- package/dist/react/index.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/client-BD8O2J8Z.d.cts +0 -668
- package/dist/client-BD8O2J8Z.d.ts +0 -668
|
@@ -240,6 +240,11 @@ async function compressMessagesWithUsageTap(options) {
|
|
|
240
240
|
aggressiveness,
|
|
241
241
|
"UsageTap prompt message compression"
|
|
242
242
|
);
|
|
243
|
+
if (options.latencyBudgetMs !== void 0 && (!Number.isFinite(options.latencyBudgetMs) || options.latencyBudgetMs < 0)) {
|
|
244
|
+
throw new Error(
|
|
245
|
+
"UsageTap prompt message compression latencyBudgetMs must be a non-negative number"
|
|
246
|
+
);
|
|
247
|
+
}
|
|
243
248
|
const original = stableStringifyInput(options.input);
|
|
244
249
|
const headers = {
|
|
245
250
|
"content-type": "application/json"
|
|
@@ -254,7 +259,15 @@ async function compressMessagesWithUsageTap(options) {
|
|
|
254
259
|
headers,
|
|
255
260
|
body: JSON.stringify({
|
|
256
261
|
...cloneInputRecord(options.input),
|
|
257
|
-
compression_settings: {
|
|
262
|
+
compression_settings: {
|
|
263
|
+
aggressiveness,
|
|
264
|
+
...options.mode === void 0 ? {} : { mode: options.mode },
|
|
265
|
+
...options.latencyBudgetMs === void 0 ? {} : { latency_budget_ms: options.latencyBudgetMs },
|
|
266
|
+
...options.compactEmptyUserMessages === void 0 ? {} : { compact_empty_user_messages: options.compactEmptyUserMessages },
|
|
267
|
+
...options.compactDuplicateUserTextParts === void 0 ? {} : {
|
|
268
|
+
compact_duplicate_user_text_parts: options.compactDuplicateUserTextParts
|
|
269
|
+
}
|
|
270
|
+
}
|
|
258
271
|
}),
|
|
259
272
|
signal: options.signal
|
|
260
273
|
}
|
|
@@ -750,10 +763,369 @@ function scalarToToon(value) {
|
|
|
750
763
|
return JSON.stringify(text);
|
|
751
764
|
}
|
|
752
765
|
|
|
766
|
+
// src/resources.ts
|
|
767
|
+
var CANONICAL_MEDIA_TYPE = "application/vnd.usagetap.v1+json";
|
|
768
|
+
var DEFAULT_GATEWAY_BASE_URL = "https://gateway.usagetap.com";
|
|
769
|
+
function normalizedBaseUrl(value) {
|
|
770
|
+
return `${value.replace(/\/+$/, "")}/`;
|
|
771
|
+
}
|
|
772
|
+
function errorMessage(payload, status) {
|
|
773
|
+
if (payload && typeof payload === "object") {
|
|
774
|
+
const record = payload;
|
|
775
|
+
const error = record.error;
|
|
776
|
+
if (error && typeof error === "object") {
|
|
777
|
+
const message2 = error.message;
|
|
778
|
+
if (typeof message2 === "string" && message2) return message2;
|
|
779
|
+
}
|
|
780
|
+
const message = record.message;
|
|
781
|
+
if (typeof message === "string" && message) return message;
|
|
782
|
+
}
|
|
783
|
+
return `UsageTap request failed with HTTP ${status}`;
|
|
784
|
+
}
|
|
785
|
+
function errorCode(status) {
|
|
786
|
+
if (status === 401 || status === 403) return "USAGETAP_AUTH_ERROR";
|
|
787
|
+
if (status === 429) return "USAGETAP_RATE_LIMITED";
|
|
788
|
+
if (status >= 500) return "USAGETAP_SERVER_ERROR";
|
|
789
|
+
return "USAGETAP_BAD_REQUEST";
|
|
790
|
+
}
|
|
791
|
+
var ResourceTransport = class {
|
|
792
|
+
baseUrl;
|
|
793
|
+
apiKey;
|
|
794
|
+
fetchImpl;
|
|
795
|
+
defaultHeaders;
|
|
796
|
+
sdkVersion;
|
|
797
|
+
constructor(baseUrl, config) {
|
|
798
|
+
this.baseUrl = normalizedBaseUrl(baseUrl);
|
|
799
|
+
this.apiKey = config.apiKey;
|
|
800
|
+
this.fetchImpl = config.fetchImpl;
|
|
801
|
+
this.defaultHeaders = config.headers ?? {};
|
|
802
|
+
this.sdkVersion = config.sdkVersion;
|
|
803
|
+
}
|
|
804
|
+
async request(request) {
|
|
805
|
+
const body = request.body === void 0 ? void 0 : JSON.stringify(request.body);
|
|
806
|
+
const headers = {
|
|
807
|
+
...this.defaultHeaders,
|
|
808
|
+
accept: request.response === "data" ? CANONICAL_MEDIA_TYPE : request.response === "ndjson" ? "application/x-ndjson" : "application/json",
|
|
809
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
810
|
+
"x-usage-sdk": `js/${this.sdkVersion}`,
|
|
811
|
+
...body ? { "content-type": "application/json" } : {},
|
|
812
|
+
...request.options?.idempotencyKey ? { "idempotency-key": request.options.idempotencyKey } : {},
|
|
813
|
+
...request.options?.headers
|
|
814
|
+
};
|
|
815
|
+
let response;
|
|
816
|
+
try {
|
|
817
|
+
response = await this.fetchImpl(
|
|
818
|
+
new URL(request.path.replace(/^\/+/, ""), this.baseUrl),
|
|
819
|
+
{
|
|
820
|
+
method: request.method,
|
|
821
|
+
headers,
|
|
822
|
+
body,
|
|
823
|
+
signal: request.options?.signal
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
} catch (error) {
|
|
827
|
+
throw new UsageTapError(
|
|
828
|
+
"USAGETAP_NETWORK_ERROR",
|
|
829
|
+
"Failed to reach UsageTap",
|
|
830
|
+
{ retryable: true, cause: error }
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
const requestId = response.headers.get("x-request-id") ?? response.headers.get("x-usage-correlation-id") ?? void 0;
|
|
834
|
+
const text = await response.text();
|
|
835
|
+
let payload;
|
|
836
|
+
if (text && request.response !== "ndjson") {
|
|
837
|
+
try {
|
|
838
|
+
payload = JSON.parse(text);
|
|
839
|
+
} catch (error) {
|
|
840
|
+
throw new UsageTapError(
|
|
841
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
842
|
+
"UsageTap returned invalid JSON",
|
|
843
|
+
{ status: response.status, correlationId: requestId, cause: error }
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (!response.ok) {
|
|
848
|
+
throw new UsageTapError(
|
|
849
|
+
errorCode(response.status),
|
|
850
|
+
errorMessage(payload, response.status),
|
|
851
|
+
{
|
|
852
|
+
status: response.status,
|
|
853
|
+
retryable: response.status === 429 || response.status >= 500,
|
|
854
|
+
correlationId: requestId,
|
|
855
|
+
details: payload && typeof payload === "object" ? payload : void 0
|
|
856
|
+
}
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
if (request.response === "ndjson") {
|
|
860
|
+
if (!text.trim()) return [];
|
|
861
|
+
try {
|
|
862
|
+
return text.trim().split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line));
|
|
863
|
+
} catch (error) {
|
|
864
|
+
throw new UsageTapError(
|
|
865
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
866
|
+
"UsageTap returned invalid NDJSON",
|
|
867
|
+
{ status: response.status, correlationId: requestId, cause: error }
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (request.response === "data") {
|
|
872
|
+
if (!payload || typeof payload !== "object" || !("data" in payload)) {
|
|
873
|
+
throw new UsageTapError(
|
|
874
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
875
|
+
"UsageTap response missing data",
|
|
876
|
+
{ status: response.status, correlationId: requestId }
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
return payload.data;
|
|
880
|
+
}
|
|
881
|
+
if (payload === void 0) {
|
|
882
|
+
throw new UsageTapError(
|
|
883
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
884
|
+
"UsageTap response was empty",
|
|
885
|
+
{ status: response.status, correlationId: requestId }
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
return payload;
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
function resourceId(value, keys, label) {
|
|
892
|
+
const id = typeof value === "string" ? value : keys.map((key) => value[key]).find((candidate) => Boolean(candidate?.trim()));
|
|
893
|
+
if (!id?.trim()) {
|
|
894
|
+
throw new UsageTapError(
|
|
895
|
+
"USAGETAP_BAD_REQUEST",
|
|
896
|
+
`${label} requires a non-empty ID`
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
return id.trim();
|
|
900
|
+
}
|
|
901
|
+
function terminalSummary(status) {
|
|
902
|
+
return status === "COMPLETE" || status === "FAILED";
|
|
903
|
+
}
|
|
904
|
+
function terminalGatewayBatch(status) {
|
|
905
|
+
return ["completed", "failed", "expired", "cancelled"].includes(status);
|
|
906
|
+
}
|
|
907
|
+
function validateWaitOptions(options) {
|
|
908
|
+
const pollIntervalMs = options.pollIntervalMs ?? 1500;
|
|
909
|
+
const timeoutMs = options.timeoutMs ?? 3 * 6e4;
|
|
910
|
+
if (!Number.isFinite(pollIntervalMs) || pollIntervalMs < 0) {
|
|
911
|
+
throw new UsageTapError(
|
|
912
|
+
"USAGETAP_BAD_REQUEST",
|
|
913
|
+
"pollIntervalMs must be a non-negative number"
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs < 1) {
|
|
917
|
+
throw new UsageTapError(
|
|
918
|
+
"USAGETAP_BAD_REQUEST",
|
|
919
|
+
"timeoutMs must be a positive number"
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
return { pollIntervalMs, timeoutMs };
|
|
923
|
+
}
|
|
924
|
+
var SummarizationResource = class {
|
|
925
|
+
summaries;
|
|
926
|
+
batches;
|
|
927
|
+
profiles;
|
|
928
|
+
measurements;
|
|
929
|
+
transport;
|
|
930
|
+
constructor(config) {
|
|
931
|
+
this.transport = new ResourceTransport(config.apiBaseUrl, config);
|
|
932
|
+
this.summaries = {
|
|
933
|
+
create: (params, options) => this.transport.request({
|
|
934
|
+
method: "POST",
|
|
935
|
+
path: "/v1/compression/summaries",
|
|
936
|
+
body: params,
|
|
937
|
+
options,
|
|
938
|
+
response: "data"
|
|
939
|
+
}),
|
|
940
|
+
retrieve: (jobId, options) => this.transport.request({
|
|
941
|
+
method: "GET",
|
|
942
|
+
path: `/v1/compression/jobs/${encodeURIComponent(
|
|
943
|
+
resourceId(jobId, ["jobId"], "summaries.retrieve")
|
|
944
|
+
)}`,
|
|
945
|
+
options,
|
|
946
|
+
response: "data"
|
|
947
|
+
}),
|
|
948
|
+
wait: (job, options) => this.waitForSummary(job, options)
|
|
949
|
+
};
|
|
950
|
+
this.batches = {
|
|
951
|
+
create: (params, options) => this.transport.request({
|
|
952
|
+
method: "POST",
|
|
953
|
+
path: "/v1/compression/batches",
|
|
954
|
+
body: params,
|
|
955
|
+
options,
|
|
956
|
+
response: "data"
|
|
957
|
+
}),
|
|
958
|
+
retrieve: (batchId, options) => this.transport.request({
|
|
959
|
+
method: "GET",
|
|
960
|
+
path: `/v1/compression/batches/${encodeURIComponent(
|
|
961
|
+
resourceId(batchId, ["batchId"], "summarization.batches.retrieve")
|
|
962
|
+
)}`,
|
|
963
|
+
options,
|
|
964
|
+
response: "data"
|
|
965
|
+
}),
|
|
966
|
+
wait: (batch, options) => this.waitForBatch(batch, options)
|
|
967
|
+
};
|
|
968
|
+
this.profiles = {
|
|
969
|
+
retrieve: (profile, options) => this.transport.request({
|
|
970
|
+
method: "GET",
|
|
971
|
+
path: `/v1/compression/profiles/${encodeURIComponent(
|
|
972
|
+
resourceId(profile, [], "summarization.profiles.retrieve")
|
|
973
|
+
)}`,
|
|
974
|
+
options,
|
|
975
|
+
response: "data"
|
|
976
|
+
})
|
|
977
|
+
};
|
|
978
|
+
this.measurements = {
|
|
979
|
+
create: (params, options) => this.transport.request({
|
|
980
|
+
method: "POST",
|
|
981
|
+
path: "/v1/compression/measurements",
|
|
982
|
+
body: params,
|
|
983
|
+
options,
|
|
984
|
+
response: "data"
|
|
985
|
+
})
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
async waitForSummary(value, options = {}) {
|
|
989
|
+
const { pollIntervalMs, timeoutMs } = validateWaitOptions(options);
|
|
990
|
+
const deadline = Date.now() + timeoutMs;
|
|
991
|
+
let job = typeof value === "string" ? await this.summaries.retrieve(value, options) : value;
|
|
992
|
+
while (!terminalSummary(job.status)) {
|
|
993
|
+
if (Date.now() >= deadline) {
|
|
994
|
+
throw new UsageTapError(
|
|
995
|
+
"USAGETAP_RETRY_EXHAUSTED",
|
|
996
|
+
`Summarization job ${job.jobId} did not finish before timeout`,
|
|
997
|
+
{ retryable: true }
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
await sleep(pollIntervalMs, options.signal);
|
|
1001
|
+
job = await this.summaries.retrieve(job.jobId, options);
|
|
1002
|
+
}
|
|
1003
|
+
return job;
|
|
1004
|
+
}
|
|
1005
|
+
async waitForBatch(value, options = {}) {
|
|
1006
|
+
const { pollIntervalMs, timeoutMs } = validateWaitOptions(options);
|
|
1007
|
+
const deadline = Date.now() + timeoutMs;
|
|
1008
|
+
let batch = typeof value === "string" ? await this.batches.retrieve(value, options) : value;
|
|
1009
|
+
while (!terminalSummary(batch.status)) {
|
|
1010
|
+
if (Date.now() >= deadline) {
|
|
1011
|
+
throw new UsageTapError(
|
|
1012
|
+
"USAGETAP_RETRY_EXHAUSTED",
|
|
1013
|
+
`Summarization batch ${batch.batchId} did not finish before timeout`,
|
|
1014
|
+
{ retryable: true }
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
await sleep(pollIntervalMs, options.signal);
|
|
1018
|
+
batch = await this.batches.retrieve(batch.batchId, options);
|
|
1019
|
+
}
|
|
1020
|
+
return batch;
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
var GatewayResource = class {
|
|
1024
|
+
chat;
|
|
1025
|
+
/** Buffered OpenAI Responses-compatible requests. */
|
|
1026
|
+
responses;
|
|
1027
|
+
models;
|
|
1028
|
+
batches;
|
|
1029
|
+
transport;
|
|
1030
|
+
idempotencyGenerator;
|
|
1031
|
+
constructor(config) {
|
|
1032
|
+
this.transport = new ResourceTransport(
|
|
1033
|
+
config.gatewayBaseUrl ?? DEFAULT_GATEWAY_BASE_URL,
|
|
1034
|
+
config
|
|
1035
|
+
);
|
|
1036
|
+
this.idempotencyGenerator = config.idempotencyGenerator ?? createIdempotencyKey;
|
|
1037
|
+
this.chat = {
|
|
1038
|
+
completions: {
|
|
1039
|
+
create: (params, options) => this.transport.request({
|
|
1040
|
+
method: "POST",
|
|
1041
|
+
path: "/v1/chat/completions",
|
|
1042
|
+
body: params,
|
|
1043
|
+
options,
|
|
1044
|
+
response: "json"
|
|
1045
|
+
})
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
this.responses = {
|
|
1049
|
+
create: (params, options) => this.transport.request({
|
|
1050
|
+
method: "POST",
|
|
1051
|
+
path: "/v1/responses",
|
|
1052
|
+
body: params,
|
|
1053
|
+
options,
|
|
1054
|
+
response: "json"
|
|
1055
|
+
})
|
|
1056
|
+
};
|
|
1057
|
+
this.models = {
|
|
1058
|
+
list: (options) => this.transport.request({
|
|
1059
|
+
method: "GET",
|
|
1060
|
+
path: "/v1/models",
|
|
1061
|
+
options,
|
|
1062
|
+
response: "json"
|
|
1063
|
+
})
|
|
1064
|
+
};
|
|
1065
|
+
this.batches = {
|
|
1066
|
+
create: (params, options = {}) => this.transport.request({
|
|
1067
|
+
method: "POST",
|
|
1068
|
+
path: "/v1/batches",
|
|
1069
|
+
body: params,
|
|
1070
|
+
options: {
|
|
1071
|
+
...options,
|
|
1072
|
+
idempotencyKey: options.idempotencyKey ?? this.idempotencyGenerator()
|
|
1073
|
+
},
|
|
1074
|
+
response: "json"
|
|
1075
|
+
}),
|
|
1076
|
+
retrieve: (batchId, options) => this.transport.request({
|
|
1077
|
+
method: "GET",
|
|
1078
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1079
|
+
resourceId(batchId, ["id"], "gateway.batches.retrieve")
|
|
1080
|
+
)}`,
|
|
1081
|
+
options,
|
|
1082
|
+
response: "json"
|
|
1083
|
+
}),
|
|
1084
|
+
wait: (batch, options) => this.waitForBatch(batch, options),
|
|
1085
|
+
cancel: (batchId, options) => this.transport.request({
|
|
1086
|
+
method: "POST",
|
|
1087
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1088
|
+
resourceId(batchId, ["id"], "gateway.batches.cancel")
|
|
1089
|
+
)}/cancel`,
|
|
1090
|
+
options,
|
|
1091
|
+
response: "json"
|
|
1092
|
+
}),
|
|
1093
|
+
results: (batchId, options) => this.transport.request({
|
|
1094
|
+
method: "GET",
|
|
1095
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1096
|
+
resourceId(batchId, ["id"], "gateway.batches.results")
|
|
1097
|
+
)}/results`,
|
|
1098
|
+
options,
|
|
1099
|
+
response: "ndjson"
|
|
1100
|
+
})
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
async waitForBatch(value, options = {}) {
|
|
1104
|
+
const { pollIntervalMs, timeoutMs } = validateWaitOptions(options);
|
|
1105
|
+
const deadline = Date.now() + timeoutMs;
|
|
1106
|
+
let batch = typeof value === "string" ? await this.batches.retrieve(value, options) : value;
|
|
1107
|
+
while (!terminalGatewayBatch(batch.status)) {
|
|
1108
|
+
if (Date.now() >= deadline) {
|
|
1109
|
+
throw new UsageTapError(
|
|
1110
|
+
"USAGETAP_RETRY_EXHAUSTED",
|
|
1111
|
+
`Gateway batch ${batch.id} did not finish before timeout`,
|
|
1112
|
+
{ retryable: true }
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
await sleep(pollIntervalMs, options.signal);
|
|
1116
|
+
batch = await this.batches.retrieve(batch.id, options);
|
|
1117
|
+
}
|
|
1118
|
+
return batch;
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
|
|
753
1122
|
// src/client.ts
|
|
754
1123
|
var CALL_BEGIN_PATH = "call_begin";
|
|
755
1124
|
var CALL_END_PATH = "call_end";
|
|
756
1125
|
var COMPRESS_PROMPT_PATH = "compress_prompt";
|
|
1126
|
+
var SAMPLES_PATH = "samples";
|
|
1127
|
+
var SAMPLING_SETTINGS_PATH = "sampling/settings";
|
|
1128
|
+
var SAMPLING_DECIDE_PATH = "sampling/decide";
|
|
757
1129
|
var CHECK_USAGE_PATH = "customers/{customerId}/usage";
|
|
758
1130
|
var CREATE_CUSTOMER_PATH = "customers";
|
|
759
1131
|
var CHANGE_PLAN_PATH = "customers/{customerId}/change_plan";
|
|
@@ -764,11 +1136,16 @@ var CORRELATION_HEADER = "x-usage-correlation-id";
|
|
|
764
1136
|
var IDEMPOTENCY_HEADER = "idempotency-key";
|
|
765
1137
|
var SDK_HEADER = "x-usage-sdk";
|
|
766
1138
|
var USER_AGENT = "UsageTapClient";
|
|
767
|
-
var
|
|
1139
|
+
var CANONICAL_MEDIA_TYPE2 = "application/vnd.usagetap.v1+json";
|
|
768
1140
|
var DEFAULT_BASE_URL = "https://api.usagetap.com";
|
|
769
|
-
var
|
|
1141
|
+
var DEFAULT_RUN_INACTIVITY_MS = 60 * 60 * 1e3;
|
|
1142
|
+
var SDK_VERSION = "1.7.0" ;
|
|
770
1143
|
var HAS_WINDOW = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
771
1144
|
var UsageTapClient = class {
|
|
1145
|
+
/** OpenAI-compatible chat, model, and native batch operations. */
|
|
1146
|
+
gateway;
|
|
1147
|
+
/** Published-profile context summarization operations. */
|
|
1148
|
+
summarization;
|
|
772
1149
|
apiKey;
|
|
773
1150
|
baseUrl;
|
|
774
1151
|
fetchImpl;
|
|
@@ -793,6 +1170,11 @@ var UsageTapClient = class {
|
|
|
793
1170
|
usageTapCompressionMessagesEndpoint;
|
|
794
1171
|
usageTapCompressionModel;
|
|
795
1172
|
usageTapCompressionAggressiveness;
|
|
1173
|
+
sampling;
|
|
1174
|
+
samplingSettingsCacheMs;
|
|
1175
|
+
circuitBreaker;
|
|
1176
|
+
circuitBreakerRuns = /* @__PURE__ */ new Map();
|
|
1177
|
+
samplingSettingsCache;
|
|
796
1178
|
constructor(options = {}) {
|
|
797
1179
|
const apiKey = options.apiKey?.trim() || readEnvironmentVariable("USAGETAP_API_KEY");
|
|
798
1180
|
const baseUrl = options.baseUrl?.trim() || readEnvironmentVariable("USAGETAP_BASE_URL") || DEFAULT_BASE_URL;
|
|
@@ -815,10 +1197,21 @@ var UsageTapClient = class {
|
|
|
815
1197
|
"A global fetch implementation was not found. Pass fetchImpl in UsageTapClientOptions."
|
|
816
1198
|
);
|
|
817
1199
|
}
|
|
818
|
-
const
|
|
819
|
-
this.baseUrl = new URL(
|
|
1200
|
+
const normalizedBaseUrl2 = normalizeBaseUrl(baseUrl);
|
|
1201
|
+
this.baseUrl = new URL(normalizedBaseUrl2);
|
|
820
1202
|
this.apiKey = apiKey;
|
|
821
1203
|
this.fetchImpl = wrapFetchImplementation(fetchCandidate, !options.fetchImpl);
|
|
1204
|
+
const resourceConfig = {
|
|
1205
|
+
apiKey,
|
|
1206
|
+
apiBaseUrl: normalizedBaseUrl2,
|
|
1207
|
+
gatewayBaseUrl: options.gatewayBaseUrl?.trim() || readEnvironmentVariable("USAGETAP_GATEWAY_URL"),
|
|
1208
|
+
fetchImpl: this.fetchImpl,
|
|
1209
|
+
headers: options.headers,
|
|
1210
|
+
sdkVersion: SDK_VERSION,
|
|
1211
|
+
idempotencyGenerator: options.idempotencyGenerator
|
|
1212
|
+
};
|
|
1213
|
+
this.gateway = new GatewayResource(resourceConfig);
|
|
1214
|
+
this.summarization = new SummarizationResource(resourceConfig);
|
|
822
1215
|
this.defaultFeature = options.defaultFeature;
|
|
823
1216
|
this.defaultTags = options.defaultTags?.length ? dedupeStrings(options.defaultTags) : void 0;
|
|
824
1217
|
this.defaultHeaders = options.headers ? normalizeHeaderDictionary(options.headers) : {};
|
|
@@ -840,11 +1233,122 @@ var UsageTapClient = class {
|
|
|
840
1233
|
this.usageTapCompressionMessagesEndpoint = options.usageTapCompressionMessagesEndpoint;
|
|
841
1234
|
this.usageTapCompressionModel = options.usageTapCompressionModel;
|
|
842
1235
|
this.usageTapCompressionAggressiveness = options.usageTapCompressionAggressiveness;
|
|
1236
|
+
this.sampling = options.sampling;
|
|
1237
|
+
this.samplingSettingsCacheMs = Number.isFinite(options.samplingSettingsCacheMs) ? Math.max(0, Number(options.samplingSettingsCacheMs)) : 5 * 60 * 1e3;
|
|
1238
|
+
if (options.circuitBreaker) {
|
|
1239
|
+
const maxCallsPerRun = options.circuitBreaker.maxCallsPerRun;
|
|
1240
|
+
if (!Number.isInteger(maxCallsPerRun) || maxCallsPerRun < 1) {
|
|
1241
|
+
throw new UsageTapError(
|
|
1242
|
+
"USAGETAP_BAD_REQUEST",
|
|
1243
|
+
"circuitBreaker.maxCallsPerRun must be a positive integer"
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1246
|
+
const runInactivityMs = options.circuitBreaker.runInactivityMs ?? DEFAULT_RUN_INACTIVITY_MS;
|
|
1247
|
+
if (!Number.isFinite(runInactivityMs) || runInactivityMs < 1) {
|
|
1248
|
+
throw new UsageTapError(
|
|
1249
|
+
"USAGETAP_BAD_REQUEST",
|
|
1250
|
+
"circuitBreaker.runInactivityMs must be a positive number"
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
this.circuitBreaker = {
|
|
1254
|
+
maxCallsPerRun,
|
|
1255
|
+
runInactivityMs
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
shouldSample(request, policy = this.sampling || void 0) {
|
|
1260
|
+
if (!policy) return false;
|
|
1261
|
+
const rate = Math.min(1, Math.max(0, Number(policy.rate) || 0));
|
|
1262
|
+
if (rate <= 0) return false;
|
|
1263
|
+
const customerId = request.customerId?.trim();
|
|
1264
|
+
if (customerId && policy.customers?.exclude?.includes(customerId)) return false;
|
|
1265
|
+
const feature = request.feature?.trim();
|
|
1266
|
+
if (feature && policy.features?.exclude?.includes(feature)) return false;
|
|
1267
|
+
const included = policy.features?.include?.filter(Boolean) ?? [];
|
|
1268
|
+
if (included.length > 0 && (!feature || !included.includes(feature))) return false;
|
|
1269
|
+
const minimum = Math.max(0, Math.round(policy.minInputTokens ?? 0));
|
|
1270
|
+
if (minimum > 0 && estimatePromptTokens(request.input) < minimum) return false;
|
|
1271
|
+
return (policy.random ?? Math.random)() < rate;
|
|
1272
|
+
}
|
|
1273
|
+
async getSamplingSettings(options = {}) {
|
|
1274
|
+
const now = Date.now();
|
|
1275
|
+
if (!options.forceRefresh && this.samplingSettingsCache && this.samplingSettingsCache.expiresAtMs > now) {
|
|
1276
|
+
return {
|
|
1277
|
+
result: { status: "ACCEPTED", code: "SAMPLING_SETTINGS_CACHED" },
|
|
1278
|
+
data: this.samplingSettingsCache.settings,
|
|
1279
|
+
correlationId: options.correlationId ?? "local-cache"
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
const response = await this.requestGet(
|
|
1283
|
+
SAMPLING_SETTINGS_PATH,
|
|
1284
|
+
{
|
|
1285
|
+
signal: options.signal,
|
|
1286
|
+
headers: options.headers,
|
|
1287
|
+
retries: options.retries,
|
|
1288
|
+
correlationId: options.correlationId
|
|
1289
|
+
}
|
|
1290
|
+
);
|
|
1291
|
+
const serverCacheMs = Math.max(0, Number(response.data.cacheSeconds) || 0) * 1e3;
|
|
1292
|
+
const cacheMs = Math.min(this.samplingSettingsCacheMs, serverCacheMs);
|
|
1293
|
+
this.samplingSettingsCache = {
|
|
1294
|
+
settings: response.data,
|
|
1295
|
+
expiresAtMs: now + cacheMs
|
|
1296
|
+
};
|
|
1297
|
+
return response;
|
|
1298
|
+
}
|
|
1299
|
+
async shouldSampleAsync(request, policy) {
|
|
1300
|
+
if (policy) return this.shouldSample(request, policy);
|
|
1301
|
+
if (this.sampling === false) return false;
|
|
1302
|
+
if (this.sampling) return this.shouldSample(request, this.sampling);
|
|
1303
|
+
try {
|
|
1304
|
+
const settings = await this.getSamplingSettings();
|
|
1305
|
+
return this.shouldSample(request, settings.data);
|
|
1306
|
+
} catch {
|
|
1307
|
+
return false;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
async decideSample(request, options = {}) {
|
|
1311
|
+
const hasTokens = Number.isFinite(request.inputTokens) && Number(request.inputTokens) >= 0;
|
|
1312
|
+
const hasCharacters = Number.isFinite(request.inputCharacters) && Number(request.inputCharacters) >= 0;
|
|
1313
|
+
if (!hasTokens && !hasCharacters) {
|
|
1314
|
+
throw new UsageTapError(
|
|
1315
|
+
"USAGETAP_BAD_REQUEST",
|
|
1316
|
+
"decideSample requires inputTokens or inputCharacters"
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
return this.request(
|
|
1320
|
+
SAMPLING_DECIDE_PATH,
|
|
1321
|
+
request,
|
|
1322
|
+
options
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
async captureSample(request, options = {}) {
|
|
1326
|
+
if (!request || request.input === void 0) {
|
|
1327
|
+
throw new UsageTapError(
|
|
1328
|
+
"USAGETAP_BAD_REQUEST",
|
|
1329
|
+
"captureSample requires input"
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
if (!request.provider?.trim()) {
|
|
1333
|
+
throw new UsageTapError(
|
|
1334
|
+
"USAGETAP_BAD_REQUEST",
|
|
1335
|
+
"captureSample requires provider"
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
const sampleId = request.sampleId?.trim() || this.idempotencyGenerator();
|
|
1339
|
+
return this.request(
|
|
1340
|
+
SAMPLES_PATH,
|
|
1341
|
+
{ ...request, sampleId },
|
|
1342
|
+
{ ...options, idempotencyKey: sampleId }
|
|
1343
|
+
);
|
|
843
1344
|
}
|
|
844
1345
|
async beginCall(request, options = {}) {
|
|
845
1346
|
const idempotencyKey = request.idempotencyKey ?? request.idempotency ?? (this.autoIdempotency ? this.idempotencyGenerator() : void 0);
|
|
1347
|
+
this.reserveRunCall(request, idempotencyKey);
|
|
1348
|
+
const apiRequest = { ...request };
|
|
1349
|
+
delete apiRequest.runId;
|
|
846
1350
|
const payload = {
|
|
847
|
-
...
|
|
1351
|
+
...apiRequest,
|
|
848
1352
|
feature: request.feature ?? this.defaultFeature,
|
|
849
1353
|
tags: this.mergeTags(request.tags)
|
|
850
1354
|
};
|
|
@@ -862,6 +1366,28 @@ var UsageTapClient = class {
|
|
|
862
1366
|
);
|
|
863
1367
|
return response;
|
|
864
1368
|
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Inspect a configured run circuit breaker without consuming another call.
|
|
1371
|
+
*/
|
|
1372
|
+
canRunContinue(request) {
|
|
1373
|
+
const identity = this.resolveRunIdentity(request);
|
|
1374
|
+
if (!identity || !this.circuitBreaker) {
|
|
1375
|
+
throw new UsageTapError(
|
|
1376
|
+
"USAGETAP_BAD_REQUEST",
|
|
1377
|
+
"canRunContinue requires circuitBreaker configuration and a non-empty runId"
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
this.expireInactiveRuns();
|
|
1381
|
+
const calls = this.circuitBreakerRuns.get(identity.key)?.calls ?? 0;
|
|
1382
|
+
return this.createCircuitBreakerDecision(identity.customerId, identity.runId, calls);
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Release local state after a workflow finishes. Returns true when state existed.
|
|
1386
|
+
*/
|
|
1387
|
+
resetRun(request) {
|
|
1388
|
+
const identity = this.resolveRunIdentity(request);
|
|
1389
|
+
return identity ? this.circuitBreakerRuns.delete(identity.key) : false;
|
|
1390
|
+
}
|
|
865
1391
|
async promptCompress(request, options = {}) {
|
|
866
1392
|
if (!request?.callId) {
|
|
867
1393
|
throw new UsageTapError(
|
|
@@ -959,6 +1485,10 @@ var UsageTapClient = class {
|
|
|
959
1485
|
usageTapCompressionMessagesEndpoint: this.usageTapCompressionMessagesEndpoint,
|
|
960
1486
|
aggressiveness: options.aggressiveness ?? this.aggressiveness,
|
|
961
1487
|
usageTapCompressionAggressiveness: options.usageTapCompressionAggressiveness ?? this.usageTapCompressionAggressiveness,
|
|
1488
|
+
mode: options.mode,
|
|
1489
|
+
latencyBudgetMs: options.latencyBudgetMs,
|
|
1490
|
+
compactEmptyUserMessages: options.compactEmptyUserMessages,
|
|
1491
|
+
compactDuplicateUserTextParts: options.compactDuplicateUserTextParts,
|
|
962
1492
|
fetchImpl: this.fetchImpl,
|
|
963
1493
|
signal: options.signal,
|
|
964
1494
|
failOpen: options.failOpen
|
|
@@ -1000,14 +1530,29 @@ var UsageTapClient = class {
|
|
|
1000
1530
|
callId: request.callId,
|
|
1001
1531
|
feature: feature ?? this.defaultFeature,
|
|
1002
1532
|
tags: tags ?? this.defaultTags,
|
|
1533
|
+
providerUsed: request.providerUsed,
|
|
1003
1534
|
modelUsed: request.modelUsed,
|
|
1535
|
+
reasoningEffort: request.reasoningEffort,
|
|
1536
|
+
reasoningEffortSource: request.reasoningEffortSource,
|
|
1537
|
+
reasoningMode: request.reasoningMode,
|
|
1538
|
+
reasoningBudgetTokens: request.reasoningBudgetTokens,
|
|
1004
1539
|
metrics: {
|
|
1005
1540
|
inputTokens: request.inputTokens,
|
|
1006
1541
|
responseTokens: request.responseTokens,
|
|
1007
1542
|
cachedInputTokens: request.cachedInputTokens,
|
|
1543
|
+
cacheWriteInputTokens: request.cacheWriteInputTokens,
|
|
1544
|
+
cacheWrite5mInputTokens: request.cacheWrite5mInputTokens,
|
|
1545
|
+
cacheWrite1hInputTokens: request.cacheWrite1hInputTokens,
|
|
1008
1546
|
reasoningTokens: request.reasoningTokens,
|
|
1009
1547
|
searches: request.searches,
|
|
1010
1548
|
audioSeconds: request.audioSeconds,
|
|
1549
|
+
imageInputCount: request.imageInputCount,
|
|
1550
|
+
imageInputTokens: request.imageInputTokens,
|
|
1551
|
+
imageOutputCount: request.imageOutputCount,
|
|
1552
|
+
imageOutputTokens: request.imageOutputTokens,
|
|
1553
|
+
audioInputTokens: request.audioInputTokens,
|
|
1554
|
+
cachedAudioInputTokens: request.cachedAudioInputTokens,
|
|
1555
|
+
audioOutputTokens: request.audioOutputTokens,
|
|
1011
1556
|
costUsd: response.data.costUSD
|
|
1012
1557
|
},
|
|
1013
1558
|
correlationId: response.correlationId
|
|
@@ -1094,10 +1639,10 @@ var UsageTapClient = class {
|
|
|
1094
1639
|
"incrementCustomMeter requires meterSlot"
|
|
1095
1640
|
);
|
|
1096
1641
|
}
|
|
1097
|
-
if (!["CUSTOM1", "CUSTOM2"].includes(request.meterSlot)) {
|
|
1642
|
+
if (!["CUSTOM1", "CUSTOM2", "AGENTIC_API"].includes(request.meterSlot)) {
|
|
1098
1643
|
throw new UsageTapError(
|
|
1099
1644
|
"USAGETAP_BAD_REQUEST",
|
|
1100
|
-
"meterSlot must be CUSTOM1 or
|
|
1645
|
+
"meterSlot must be CUSTOM1, CUSTOM2 or AGENTIC_API"
|
|
1101
1646
|
);
|
|
1102
1647
|
}
|
|
1103
1648
|
if (typeof request.amount !== "number" || !Number.isFinite(request.amount) || request.amount <= 0) {
|
|
@@ -1112,6 +1657,15 @@ var UsageTapClient = class {
|
|
|
1112
1657
|
meterSlot: request.meterSlot,
|
|
1113
1658
|
amount: request.amount
|
|
1114
1659
|
};
|
|
1660
|
+
if (request.customerUserId) {
|
|
1661
|
+
payload.customerUserId = request.customerUserId;
|
|
1662
|
+
}
|
|
1663
|
+
if (request.customerUserName) {
|
|
1664
|
+
payload.customerUserName = request.customerUserName;
|
|
1665
|
+
}
|
|
1666
|
+
if (request.customerUserEmail) {
|
|
1667
|
+
payload.customerUserEmail = request.customerUserEmail;
|
|
1668
|
+
}
|
|
1115
1669
|
if (request.feature) {
|
|
1116
1670
|
payload.feature = request.feature;
|
|
1117
1671
|
}
|
|
@@ -1148,6 +1702,11 @@ var UsageTapClient = class {
|
|
|
1148
1702
|
const beginPayload = idempotencyKey ? { ...beginRequest, idempotencyKey, idempotency: idempotencyKey } : { ...beginRequest };
|
|
1149
1703
|
const beginResponse = await this.beginCall(beginPayload, options);
|
|
1150
1704
|
let usage = {};
|
|
1705
|
+
const pricingMode = beginResponse.data.pricingMode ?? beginRequest.pricingMode ?? (beginRequest.batch === true ? "batch" : beginRequest.batch === false ? "standard" : void 0);
|
|
1706
|
+
if (pricingMode) {
|
|
1707
|
+
usage.pricingMode = pricingMode;
|
|
1708
|
+
usage.batch = pricingMode === "batch";
|
|
1709
|
+
}
|
|
1151
1710
|
const initialStripeCustomerId = typeof beginResponse.data.stripeCustomerId === "string" ? beginResponse.data.stripeCustomerId : typeof beginRequest.stripeCustomerId === "string" ? beginRequest.stripeCustomerId : void 0;
|
|
1152
1711
|
if (initialStripeCustomerId) {
|
|
1153
1712
|
usage = { ...usage, stripeCustomerId: initialStripeCustomerId };
|
|
@@ -1156,6 +1715,28 @@ var UsageTapClient = class {
|
|
|
1156
1715
|
let handlerResult;
|
|
1157
1716
|
let handlerError;
|
|
1158
1717
|
let endCallError;
|
|
1718
|
+
let finalizationDeferred = false;
|
|
1719
|
+
let finalizationPromise;
|
|
1720
|
+
const finalize = () => {
|
|
1721
|
+
if (!finalizationPromise) {
|
|
1722
|
+
finalizationPromise = this.endCall(
|
|
1723
|
+
{
|
|
1724
|
+
callId: beginResponse.data.callId,
|
|
1725
|
+
// Pass context for metric tracking
|
|
1726
|
+
customerId: beginRequest.customerId,
|
|
1727
|
+
feature: beginRequest.feature ?? this.defaultFeature,
|
|
1728
|
+
tags: beginRequest.tags ?? this.defaultTags,
|
|
1729
|
+
...usage,
|
|
1730
|
+
error: errorPayload
|
|
1731
|
+
},
|
|
1732
|
+
{
|
|
1733
|
+
...options,
|
|
1734
|
+
correlationId: beginResponse.correlationId
|
|
1735
|
+
}
|
|
1736
|
+
).then(() => void 0);
|
|
1737
|
+
}
|
|
1738
|
+
return finalizationPromise;
|
|
1739
|
+
};
|
|
1159
1740
|
const context = {
|
|
1160
1741
|
begin: beginResponse,
|
|
1161
1742
|
setUsage: (u) => {
|
|
@@ -1163,6 +1744,10 @@ var UsageTapClient = class {
|
|
|
1163
1744
|
},
|
|
1164
1745
|
setError: (err) => {
|
|
1165
1746
|
errorPayload = err;
|
|
1747
|
+
},
|
|
1748
|
+
deferFinalization: () => {
|
|
1749
|
+
finalizationDeferred = true;
|
|
1750
|
+
return finalize;
|
|
1166
1751
|
}
|
|
1167
1752
|
};
|
|
1168
1753
|
try {
|
|
@@ -1176,24 +1761,12 @@ var UsageTapClient = class {
|
|
|
1176
1761
|
};
|
|
1177
1762
|
}
|
|
1178
1763
|
} finally {
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
feature: beginRequest.feature ?? this.defaultFeature,
|
|
1186
|
-
tags: beginRequest.tags ?? this.defaultTags,
|
|
1187
|
-
...usage,
|
|
1188
|
-
error: errorPayload
|
|
1189
|
-
},
|
|
1190
|
-
{
|
|
1191
|
-
...options,
|
|
1192
|
-
correlationId: beginResponse.correlationId
|
|
1193
|
-
}
|
|
1194
|
-
);
|
|
1195
|
-
} catch (error) {
|
|
1196
|
-
endCallError = error;
|
|
1764
|
+
if (handlerError || !finalizationDeferred) {
|
|
1765
|
+
try {
|
|
1766
|
+
await finalize();
|
|
1767
|
+
} catch (error) {
|
|
1768
|
+
endCallError = error;
|
|
1769
|
+
}
|
|
1197
1770
|
}
|
|
1198
1771
|
}
|
|
1199
1772
|
if (handlerError) {
|
|
@@ -1215,17 +1788,86 @@ var UsageTapClient = class {
|
|
|
1215
1788
|
toPromptCompressionTelemetry(result) {
|
|
1216
1789
|
return {
|
|
1217
1790
|
provider: result.provider,
|
|
1218
|
-
originalCharacters: result.originalCharacters,
|
|
1219
|
-
compressedCharacters: result.compressedCharacters,
|
|
1220
|
-
savedCharacters: result.savedCharacters,
|
|
1221
1791
|
originalTokens: result.originalTokens,
|
|
1222
1792
|
compressedTokens: result.compressedTokens,
|
|
1223
1793
|
savedTokens: result.savedTokens,
|
|
1224
1794
|
tokenSavingsRatio: result.tokenSavingsRatio,
|
|
1225
|
-
savingsRatio: result.savingsRatio,
|
|
1226
1795
|
techniques: result.techniques
|
|
1227
1796
|
};
|
|
1228
1797
|
}
|
|
1798
|
+
reserveRunCall(request, idempotencyKey) {
|
|
1799
|
+
const identity = this.resolveRunIdentity(request);
|
|
1800
|
+
if (!identity || !this.circuitBreaker) return;
|
|
1801
|
+
this.expireInactiveRuns();
|
|
1802
|
+
const now = Date.now();
|
|
1803
|
+
const state = this.circuitBreakerRuns.get(identity.key) ?? {
|
|
1804
|
+
calls: 0,
|
|
1805
|
+
reservationKeys: /* @__PURE__ */ new Set(),
|
|
1806
|
+
lastSeenAtMs: now
|
|
1807
|
+
};
|
|
1808
|
+
const reservationKey = idempotencyKey ?? this.idempotencyGenerator();
|
|
1809
|
+
state.lastSeenAtMs = now;
|
|
1810
|
+
if (state.reservationKeys.has(reservationKey)) {
|
|
1811
|
+
this.circuitBreakerRuns.set(identity.key, state);
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
const decision = this.createCircuitBreakerDecision(
|
|
1815
|
+
identity.customerId,
|
|
1816
|
+
identity.runId,
|
|
1817
|
+
state.calls
|
|
1818
|
+
);
|
|
1819
|
+
if (!decision.allowed) {
|
|
1820
|
+
throw new UsageTapError(
|
|
1821
|
+
"USAGETAP_CIRCUIT_OPEN",
|
|
1822
|
+
`Run ${identity.runId} reached its ${decision.limit}-call circuit-breaker limit`,
|
|
1823
|
+
{
|
|
1824
|
+
details: {
|
|
1825
|
+
reason: decision.reason,
|
|
1826
|
+
customerId: identity.customerId,
|
|
1827
|
+
runId: identity.runId,
|
|
1828
|
+
calls: decision.calls,
|
|
1829
|
+
limit: decision.limit,
|
|
1830
|
+
remaining: decision.remaining
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
);
|
|
1834
|
+
}
|
|
1835
|
+
state.calls += 1;
|
|
1836
|
+
state.reservationKeys.add(reservationKey);
|
|
1837
|
+
this.circuitBreakerRuns.set(identity.key, state);
|
|
1838
|
+
}
|
|
1839
|
+
resolveRunIdentity(request) {
|
|
1840
|
+
const customerId = request.customerId?.trim();
|
|
1841
|
+
const runId = request.runId?.trim();
|
|
1842
|
+
if (!customerId || !runId) return void 0;
|
|
1843
|
+
return {
|
|
1844
|
+
key: `${customerId}\0${runId}`,
|
|
1845
|
+
customerId,
|
|
1846
|
+
runId
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
createCircuitBreakerDecision(customerId, runId, calls) {
|
|
1850
|
+
const limit = this.circuitBreaker?.maxCallsPerRun ?? 0;
|
|
1851
|
+
const allowed = calls < limit;
|
|
1852
|
+
return {
|
|
1853
|
+
allowed,
|
|
1854
|
+
...allowed ? {} : { reason: "max_calls_per_run" },
|
|
1855
|
+
customerId,
|
|
1856
|
+
runId,
|
|
1857
|
+
calls,
|
|
1858
|
+
limit,
|
|
1859
|
+
remaining: Math.max(0, limit - calls)
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
expireInactiveRuns() {
|
|
1863
|
+
if (!this.circuitBreaker || this.circuitBreakerRuns.size === 0) return;
|
|
1864
|
+
const expiredBefore = Date.now() - this.circuitBreaker.runInactivityMs;
|
|
1865
|
+
for (const [key, state] of this.circuitBreakerRuns) {
|
|
1866
|
+
if (state.lastSeenAtMs < expiredBefore) {
|
|
1867
|
+
this.circuitBreakerRuns.delete(key);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1229
1871
|
async request(path, payload, options) {
|
|
1230
1872
|
const url = new URL(path, this.baseUrl).toString();
|
|
1231
1873
|
const body = payload !== void 0 ? JSON.stringify(payload) : void 0;
|
|
@@ -1413,7 +2055,7 @@ var UsageTapClient = class {
|
|
|
1413
2055
|
...this.defaultHeaders,
|
|
1414
2056
|
[SDK_HEADER]: `js/${SDK_VERSION}`,
|
|
1415
2057
|
"content-type": "application/json",
|
|
1416
|
-
accept:
|
|
2058
|
+
accept: CANONICAL_MEDIA_TYPE2
|
|
1417
2059
|
};
|
|
1418
2060
|
if (!HAS_WINDOW) {
|
|
1419
2061
|
headers["user-agent"] = `${USER_AGENT}/${SDK_VERSION}`;
|
|
@@ -1466,7 +2108,8 @@ var UsageTapClient = class {
|
|
|
1466
2108
|
}
|
|
1467
2109
|
toHttpError(status, payload, correlationId) {
|
|
1468
2110
|
const code = mapStatusToErrorCode(status);
|
|
1469
|
-
const
|
|
2111
|
+
const apiCode = payload?.error?.code ?? payload?.result?.code ?? "UNKNOWN";
|
|
2112
|
+
const retryable = isRetryableStatus(status) || isRetryableApiCode(apiCode);
|
|
1470
2113
|
const message = payload?.error?.message ?? payload?.result?.message ?? `UsageTap responded with HTTP ${status}`;
|
|
1471
2114
|
return new UsageTapError(code, message, {
|
|
1472
2115
|
status,
|
|
@@ -1499,7 +2142,7 @@ function isRetryableStatus(status) {
|
|
|
1499
2142
|
}
|
|
1500
2143
|
function isRetryableApiCode(code) {
|
|
1501
2144
|
const normalized = code.toUpperCase();
|
|
1502
|
-
return normalized.includes("TRANSIENT") || normalized.includes("RETRY") || normalized.includes("TIMEOUT") || normalized.includes("THROTTLE") || normalized.includes("RATE_LIMIT");
|
|
2145
|
+
return normalized === "PAYG_CONFLICT" || normalized.includes("TRANSIENT") || normalized.includes("RETRY") || normalized.includes("TIMEOUT") || normalized.includes("THROTTLE") || normalized.includes("RATE_LIMIT");
|
|
1503
2146
|
}
|
|
1504
2147
|
function mapApiCodeToError(code) {
|
|
1505
2148
|
const normalized = code.toUpperCase();
|
|
@@ -1614,17 +2257,18 @@ var OpenAIPromptCompressionStats = class {
|
|
|
1614
2257
|
}
|
|
1615
2258
|
};
|
|
1616
2259
|
function createOpenAIAdapter(init) {
|
|
1617
|
-
const { client, usageTap } = init;
|
|
2260
|
+
const { client, usageTap, provider = "openai" } = init;
|
|
1618
2261
|
return {
|
|
1619
2262
|
async invoke(params) {
|
|
1620
2263
|
const result = await usageTap.withUsage(
|
|
1621
2264
|
params.begin,
|
|
1622
2265
|
async (ctx) => {
|
|
2266
|
+
ctx.setUsage({ providerUsed: provider });
|
|
1623
2267
|
const response = await params.call(client, {
|
|
1624
2268
|
hints: ctx.begin.data.vendorHints,
|
|
1625
2269
|
begin: ctx.begin
|
|
1626
2270
|
});
|
|
1627
|
-
tryInferUsage(response, ctx.begin.data.vendorHints, params.extractUsage, ctx);
|
|
2271
|
+
tryInferUsage(response, ctx.begin.data.vendorHints, params.extractUsage, ctx, provider);
|
|
1628
2272
|
return {
|
|
1629
2273
|
data: response,
|
|
1630
2274
|
begin: ctx.begin
|
|
@@ -1638,24 +2282,29 @@ function createOpenAIAdapter(init) {
|
|
|
1638
2282
|
const result = await usageTap.withUsage(
|
|
1639
2283
|
params.begin,
|
|
1640
2284
|
async (ctx) => {
|
|
2285
|
+
const settle = deferUsageFinalization(ctx);
|
|
2286
|
+
ctx.setUsage({ providerUsed: provider });
|
|
1641
2287
|
const { stream, onComplete } = await params.call(client, {
|
|
1642
2288
|
hints: ctx.begin.data.vendorHints,
|
|
1643
2289
|
begin: ctx.begin
|
|
1644
2290
|
});
|
|
1645
|
-
const wrapped = wrapStreamForUsageTap(stream, async () => {
|
|
1646
|
-
if (!onComplete) return;
|
|
2291
|
+
const wrapped = wrapStreamForUsageTap(stream, async (termination) => {
|
|
1647
2292
|
try {
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
2293
|
+
if (termination === "complete" && onComplete) {
|
|
2294
|
+
const maybeUsage = await onComplete();
|
|
2295
|
+
if (maybeUsage) {
|
|
2296
|
+
ctx.setUsage(maybeUsage);
|
|
2297
|
+
}
|
|
1651
2298
|
}
|
|
1652
2299
|
} catch (error) {
|
|
1653
2300
|
ctx.setError({
|
|
1654
2301
|
code: "USAGE_FINALIZE_ERROR",
|
|
1655
2302
|
message: error instanceof Error ? error.message : String(error)
|
|
1656
2303
|
});
|
|
2304
|
+
await settle();
|
|
1657
2305
|
throw error;
|
|
1658
2306
|
}
|
|
2307
|
+
await settle();
|
|
1659
2308
|
}, ctx);
|
|
1660
2309
|
const finalize = async () => {
|
|
1661
2310
|
await wrapped.__usageTapFinalize?.();
|
|
@@ -1763,6 +2412,181 @@ async function pipeToResponse(stream, res, options = {}) {
|
|
|
1763
2412
|
}
|
|
1764
2413
|
}
|
|
1765
2414
|
var USAGETAP_CORRELATION_HEADER = "x-usage-correlation-id";
|
|
2415
|
+
function withSampling(client, options = {}) {
|
|
2416
|
+
if (!client || !options) {
|
|
2417
|
+
throw new UsageTapError(
|
|
2418
|
+
"USAGETAP_BAD_REQUEST",
|
|
2419
|
+
"withSampling requires an OpenAI-compatible client and sampling options"
|
|
2420
|
+
);
|
|
2421
|
+
}
|
|
2422
|
+
const { apiKey, usageTapClient, provider = "openai", ...policy } = options;
|
|
2423
|
+
const localPolicy = typeof policy.rate === "number" ? policy : void 0;
|
|
2424
|
+
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey, sampling: localPolicy });
|
|
2425
|
+
const wrapCreate = (create) => async (params, requestOptions) => {
|
|
2426
|
+
const { usageTap: callContextRaw, ...providerOptions } = requestOptions ?? {};
|
|
2427
|
+
const callContext = isObjectRecord(callContextRaw) ? callContextRaw : {};
|
|
2428
|
+
const streaming = params.stream === true;
|
|
2429
|
+
const decision = streaming ? Promise.resolve(false) : usageTap.shouldSampleAsync({
|
|
2430
|
+
customerId: readString(callContext.customerId),
|
|
2431
|
+
feature: readString(callContext.feature),
|
|
2432
|
+
input: params
|
|
2433
|
+
}, localPolicy);
|
|
2434
|
+
const startedAt = Date.now();
|
|
2435
|
+
try {
|
|
2436
|
+
const response = await create(
|
|
2437
|
+
params,
|
|
2438
|
+
Object.keys(providerOptions).length ? providerOptions : void 0
|
|
2439
|
+
);
|
|
2440
|
+
const selected = await decision;
|
|
2441
|
+
if (selected) {
|
|
2442
|
+
const record = isObjectRecord(response) ? response : {};
|
|
2443
|
+
await usageTap.captureSample({
|
|
2444
|
+
customerId: readString(callContext.customerId),
|
|
2445
|
+
feature: readString(callContext.feature),
|
|
2446
|
+
environment: readString(callContext.environment),
|
|
2447
|
+
tags: readStringArray(callContext.tags),
|
|
2448
|
+
provider,
|
|
2449
|
+
model: readString(record.model) ?? readString(params.model),
|
|
2450
|
+
input: params,
|
|
2451
|
+
output: response,
|
|
2452
|
+
usage: record.usage,
|
|
2453
|
+
latencyMs: Date.now() - startedAt
|
|
2454
|
+
}).catch(() => void 0);
|
|
2455
|
+
}
|
|
2456
|
+
return response;
|
|
2457
|
+
} catch (error) {
|
|
2458
|
+
const selected = await decision;
|
|
2459
|
+
if (selected) {
|
|
2460
|
+
await usageTap.captureSample({
|
|
2461
|
+
customerId: readString(callContext.customerId),
|
|
2462
|
+
feature: readString(callContext.feature),
|
|
2463
|
+
environment: readString(callContext.environment),
|
|
2464
|
+
tags: readStringArray(callContext.tags),
|
|
2465
|
+
provider,
|
|
2466
|
+
model: readString(params.model),
|
|
2467
|
+
input: params,
|
|
2468
|
+
latencyMs: Date.now() - startedAt,
|
|
2469
|
+
error: serializeSamplingError(error)
|
|
2470
|
+
}).catch(() => void 0);
|
|
2471
|
+
}
|
|
2472
|
+
throw error;
|
|
2473
|
+
}
|
|
2474
|
+
};
|
|
2475
|
+
const chat = client.chat?.completions ? new Proxy(client.chat, {
|
|
2476
|
+
get(target, prop, receiver) {
|
|
2477
|
+
if (prop !== "completions") return safeReflectGet(target, prop, receiver);
|
|
2478
|
+
const completions = target.completions;
|
|
2479
|
+
return new Proxy(completions, {
|
|
2480
|
+
get(completionTarget, completionProp, completionReceiver) {
|
|
2481
|
+
if (completionProp === "create") {
|
|
2482
|
+
return wrapCreate(
|
|
2483
|
+
completionTarget.create.bind(completionTarget)
|
|
2484
|
+
);
|
|
2485
|
+
}
|
|
2486
|
+
return safeReflectGet(
|
|
2487
|
+
completionTarget,
|
|
2488
|
+
completionProp,
|
|
2489
|
+
completionReceiver
|
|
2490
|
+
);
|
|
2491
|
+
}
|
|
2492
|
+
});
|
|
2493
|
+
}
|
|
2494
|
+
}) : void 0;
|
|
2495
|
+
const responses = typeof client.responses !== "undefined" && client.responses ? new Proxy(client.responses, {
|
|
2496
|
+
get(target, prop, receiver) {
|
|
2497
|
+
if (prop === "create") {
|
|
2498
|
+
const create = Reflect.get(target, prop, receiver);
|
|
2499
|
+
return wrapCreate(create.bind(target));
|
|
2500
|
+
}
|
|
2501
|
+
return safeReflectGet(target, prop, receiver);
|
|
2502
|
+
}
|
|
2503
|
+
}) : void 0;
|
|
2504
|
+
return new Proxy(client, {
|
|
2505
|
+
get(target, prop, receiver) {
|
|
2506
|
+
if (prop === "chat" && chat) return chat;
|
|
2507
|
+
if (prop === "responses" && responses) return responses;
|
|
2508
|
+
if (prop === "unwrap") return () => target;
|
|
2509
|
+
return safeReflectGet(target, prop, receiver);
|
|
2510
|
+
}
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
function safeReflectGet(target, prop, receiver) {
|
|
2514
|
+
return Reflect.get(target, prop, receiver);
|
|
2515
|
+
}
|
|
2516
|
+
function readStringArray(value) {
|
|
2517
|
+
if (!Array.isArray(value)) return void 0;
|
|
2518
|
+
const strings = value.filter((item) => typeof item === "string");
|
|
2519
|
+
return strings.length ? strings : void 0;
|
|
2520
|
+
}
|
|
2521
|
+
function readString(value) {
|
|
2522
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
2523
|
+
}
|
|
2524
|
+
function serializeSamplingError(error) {
|
|
2525
|
+
if (error instanceof Error) {
|
|
2526
|
+
return { name: error.name, message: error.message };
|
|
2527
|
+
}
|
|
2528
|
+
return { message: String(error) };
|
|
2529
|
+
}
|
|
2530
|
+
function normalizeMeteredOpenAISampling(options) {
|
|
2531
|
+
if (!options) return void 0;
|
|
2532
|
+
if (options === true) return { provider: "openai" };
|
|
2533
|
+
const { provider = "openai", ...policyFields } = options;
|
|
2534
|
+
return {
|
|
2535
|
+
provider,
|
|
2536
|
+
policy: typeof policyFields.rate === "number" ? policyFields : void 0
|
|
2537
|
+
};
|
|
2538
|
+
}
|
|
2539
|
+
function startMeteredOpenAISampleDecision({
|
|
2540
|
+
usageTap,
|
|
2541
|
+
sampling,
|
|
2542
|
+
beginRequest,
|
|
2543
|
+
input
|
|
2544
|
+
}) {
|
|
2545
|
+
if (!sampling) return Promise.resolve(false);
|
|
2546
|
+
return usageTap.shouldSampleAsync(
|
|
2547
|
+
{
|
|
2548
|
+
customerId: beginRequest.customerId,
|
|
2549
|
+
feature: beginRequest.feature,
|
|
2550
|
+
input
|
|
2551
|
+
},
|
|
2552
|
+
sampling.policy
|
|
2553
|
+
);
|
|
2554
|
+
}
|
|
2555
|
+
async function captureMeteredOpenAISample({
|
|
2556
|
+
usageTap,
|
|
2557
|
+
sampling,
|
|
2558
|
+
decision,
|
|
2559
|
+
ctx,
|
|
2560
|
+
beginRequest,
|
|
2561
|
+
input,
|
|
2562
|
+
response,
|
|
2563
|
+
error,
|
|
2564
|
+
startedAt
|
|
2565
|
+
}) {
|
|
2566
|
+
if (!sampling) return;
|
|
2567
|
+
let selected = false;
|
|
2568
|
+
try {
|
|
2569
|
+
selected = await decision;
|
|
2570
|
+
} catch {
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
if (!selected) return;
|
|
2574
|
+
const record = isObjectRecord(response) ? response : {};
|
|
2575
|
+
await usageTap.captureSample({
|
|
2576
|
+
sampleId: ctx.begin.data.callId,
|
|
2577
|
+
callId: ctx.begin.data.callId,
|
|
2578
|
+
customerId: beginRequest.customerId,
|
|
2579
|
+
feature: beginRequest.feature,
|
|
2580
|
+
tags: beginRequest.tags,
|
|
2581
|
+
provider: sampling.provider,
|
|
2582
|
+
model: readString(record.model) ?? readString(input.model),
|
|
2583
|
+
input,
|
|
2584
|
+
...response === void 0 ? {} : { output: response },
|
|
2585
|
+
usage: record.usage,
|
|
2586
|
+
latencyMs: Date.now() - startedAt,
|
|
2587
|
+
...error === void 0 ? {} : { error: serializeSamplingError(error) }
|
|
2588
|
+
}).catch(() => void 0);
|
|
2589
|
+
}
|
|
1766
2590
|
function withCompression(client, options = {}) {
|
|
1767
2591
|
if (!client) {
|
|
1768
2592
|
throw new UsageTapError(
|
|
@@ -1773,11 +2597,13 @@ function withCompression(client, options = {}) {
|
|
|
1773
2597
|
const {
|
|
1774
2598
|
apiKey,
|
|
1775
2599
|
usageTapClient,
|
|
2600
|
+
sampling,
|
|
1776
2601
|
...compressionOverrides
|
|
1777
2602
|
} = options;
|
|
1778
2603
|
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey });
|
|
1779
2604
|
const compression = {
|
|
1780
2605
|
provider: "usagetap",
|
|
2606
|
+
roles: { user: { enabled: true, aggressiveness: 0.2 } },
|
|
1781
2607
|
minContextTokens: DEFAULT_PROMPT_COMPRESSION_MIN_CONTEXT_TOKENS,
|
|
1782
2608
|
...compressionOverrides
|
|
1783
2609
|
};
|
|
@@ -1787,7 +2613,7 @@ function withCompression(client, options = {}) {
|
|
|
1787
2613
|
usageTap,
|
|
1788
2614
|
compression
|
|
1789
2615
|
) : void 0;
|
|
1790
|
-
|
|
2616
|
+
const compressed = new Proxy(client, {
|
|
1791
2617
|
get(target, prop, receiver) {
|
|
1792
2618
|
if (prop === "chat" && proxiedChat) {
|
|
1793
2619
|
return proxiedChat;
|
|
@@ -1801,6 +2627,11 @@ function withCompression(client, options = {}) {
|
|
|
1801
2627
|
return Reflect.get(target, prop, receiver);
|
|
1802
2628
|
}
|
|
1803
2629
|
});
|
|
2630
|
+
return sampling ? withSampling(compressed, {
|
|
2631
|
+
...sampling === true ? {} : sampling,
|
|
2632
|
+
apiKey,
|
|
2633
|
+
usageTapClient
|
|
2634
|
+
}) : compressed;
|
|
1804
2635
|
}
|
|
1805
2636
|
function withMetering(client, customer) {
|
|
1806
2637
|
const config = typeof customer === "string" ? { customerId: customer } : customer;
|
|
@@ -1815,6 +2646,8 @@ function withMetering(client, customer) {
|
|
|
1815
2646
|
usageTapClient,
|
|
1816
2647
|
applyVendorHints,
|
|
1817
2648
|
promptCompression,
|
|
2649
|
+
sampling,
|
|
2650
|
+
provider,
|
|
1818
2651
|
...defaultContext
|
|
1819
2652
|
} = config;
|
|
1820
2653
|
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey });
|
|
@@ -1822,7 +2655,9 @@ function withMetering(client, customer) {
|
|
|
1822
2655
|
return wrapOpenAI(client, usageTap, {
|
|
1823
2656
|
defaultContext,
|
|
1824
2657
|
applyVendorHints,
|
|
1825
|
-
promptCompression: normalizedCompression
|
|
2658
|
+
promptCompression: normalizedCompression,
|
|
2659
|
+
sampling,
|
|
2660
|
+
provider
|
|
1826
2661
|
});
|
|
1827
2662
|
}
|
|
1828
2663
|
function wrapOpenAI(client, usageTap, options = {}) {
|
|
@@ -1832,6 +2667,8 @@ function wrapOpenAI(client, usageTap, options = {}) {
|
|
|
1832
2667
|
const defaultContext = options.defaultContext;
|
|
1833
2668
|
const applyVendorHints = options.applyVendorHints !== false;
|
|
1834
2669
|
const defaultPromptCompression = normalizePromptCompressionOptions(options.promptCompression);
|
|
2670
|
+
const defaultSampling = normalizeMeteredOpenAISampling(options.sampling);
|
|
2671
|
+
const provider = options.provider ?? "openai";
|
|
1835
2672
|
const promptCompressionStats = new OpenAIPromptCompressionStats();
|
|
1836
2673
|
const proxiedChat = client.chat ? createChatProxy(
|
|
1837
2674
|
client.chat,
|
|
@@ -1839,7 +2676,9 @@ function wrapOpenAI(client, usageTap, options = {}) {
|
|
|
1839
2676
|
defaultContext,
|
|
1840
2677
|
applyVendorHints,
|
|
1841
2678
|
defaultPromptCompression,
|
|
1842
|
-
promptCompressionStats
|
|
2679
|
+
promptCompressionStats,
|
|
2680
|
+
defaultSampling,
|
|
2681
|
+
provider
|
|
1843
2682
|
) : void 0;
|
|
1844
2683
|
const proxiedResponses = typeof client.responses !== "undefined" ? createResponsesProxy(
|
|
1845
2684
|
client.responses,
|
|
@@ -1847,7 +2686,9 @@ function wrapOpenAI(client, usageTap, options = {}) {
|
|
|
1847
2686
|
defaultContext,
|
|
1848
2687
|
applyVendorHints,
|
|
1849
2688
|
defaultPromptCompression,
|
|
1850
|
-
promptCompressionStats
|
|
2689
|
+
promptCompressionStats,
|
|
2690
|
+
defaultSampling,
|
|
2691
|
+
provider
|
|
1851
2692
|
) : void 0;
|
|
1852
2693
|
const handler = {
|
|
1853
2694
|
get(target, prop, receiver) {
|
|
@@ -1915,14 +2756,16 @@ function createCompressionOnlyResponsesProxy(resource, usageTap, compression) {
|
|
|
1915
2756
|
}
|
|
1916
2757
|
});
|
|
1917
2758
|
}
|
|
1918
|
-
function createChatProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
2759
|
+
function createChatProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling, provider) {
|
|
1919
2760
|
const completions = createChatCompletionsProxy(
|
|
1920
2761
|
resource.completions,
|
|
1921
2762
|
usageTap,
|
|
1922
2763
|
defaultContext,
|
|
1923
2764
|
applyVendorHints,
|
|
1924
2765
|
defaultPromptCompression,
|
|
1925
|
-
promptCompressionStats
|
|
2766
|
+
promptCompressionStats,
|
|
2767
|
+
defaultSampling,
|
|
2768
|
+
provider
|
|
1926
2769
|
);
|
|
1927
2770
|
const handler = {
|
|
1928
2771
|
get(target, prop, receiver) {
|
|
@@ -1934,7 +2777,7 @@ function createChatProxy(resource, usageTap, defaultContext, applyVendorHints, d
|
|
|
1934
2777
|
};
|
|
1935
2778
|
return new Proxy(resource, handler);
|
|
1936
2779
|
}
|
|
1937
|
-
function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
2780
|
+
function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling, provider) {
|
|
1938
2781
|
if (!resource || typeof resource !== "object") {
|
|
1939
2782
|
return void 0;
|
|
1940
2783
|
}
|
|
@@ -1949,9 +2792,20 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
1949
2792
|
withUsage,
|
|
1950
2793
|
promptCompression
|
|
1951
2794
|
} = splitUsageOptions(options);
|
|
1952
|
-
const beginRequest =
|
|
2795
|
+
const beginRequest = responsesBeginRequest(
|
|
2796
|
+
resolveBeginRequest(defaultContext, usageContext),
|
|
2797
|
+
params
|
|
2798
|
+
);
|
|
1953
2799
|
const wantsStream = isStreamingRequest(params);
|
|
1954
2800
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
2801
|
+
const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
|
|
2802
|
+
const sampleStartedAt = Date.now();
|
|
2803
|
+
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
|
|
2804
|
+
usageTap,
|
|
2805
|
+
sampling: defaultSampling,
|
|
2806
|
+
beginRequest,
|
|
2807
|
+
input: params
|
|
2808
|
+
});
|
|
1955
2809
|
const hintedParams = applyVendorHints ? applyResponsesVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
1956
2810
|
const finalParams = await compressResponsesParamsForCall({
|
|
1957
2811
|
params: hintedParams,
|
|
@@ -1963,25 +2817,67 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
1963
2817
|
withUsage,
|
|
1964
2818
|
operation: "responses.create"
|
|
1965
2819
|
});
|
|
2820
|
+
ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
|
|
1966
2821
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
1967
2822
|
if (wantsStream) {
|
|
1968
2823
|
const apiPromise2 = originalCreate(finalParams, request);
|
|
1969
2824
|
const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
|
|
1970
2825
|
ensureAsyncIterable(rawStream, "responses.create");
|
|
1971
|
-
const wrappedStream = wrapStreamForUsageTap(rawStream, async () => {
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2826
|
+
const wrappedStream = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
2827
|
+
try {
|
|
2828
|
+
if (termination === "complete") {
|
|
2829
|
+
const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
|
|
2830
|
+
if (usage) {
|
|
2831
|
+
ctx.setUsage(usage);
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
} catch (error) {
|
|
2835
|
+
ctx.setError({
|
|
2836
|
+
code: "USAGE_FINALIZE_ERROR",
|
|
2837
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2838
|
+
});
|
|
2839
|
+
throw error;
|
|
2840
|
+
} finally {
|
|
2841
|
+
await settle?.();
|
|
1975
2842
|
}
|
|
1976
|
-
}, ctx)
|
|
2843
|
+
}, ctx, (chunk) => {
|
|
2844
|
+
tryInferUsageFromStreamChunk(
|
|
2845
|
+
chunk,
|
|
2846
|
+
ctx.begin.data.vendorHints,
|
|
2847
|
+
ctx,
|
|
2848
|
+
provider
|
|
2849
|
+
);
|
|
2850
|
+
});
|
|
1977
2851
|
return wrappedStream;
|
|
1978
2852
|
});
|
|
1979
2853
|
return wrappedPromise2;
|
|
1980
2854
|
}
|
|
1981
2855
|
const apiPromise = originalCreate(finalParams, request);
|
|
1982
|
-
const wrappedPromise = transformApiPromise(apiPromise, (response) => {
|
|
1983
|
-
tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx);
|
|
2856
|
+
const wrappedPromise = transformApiPromise(apiPromise, async (response) => {
|
|
2857
|
+
tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx, provider);
|
|
2858
|
+
await captureMeteredOpenAISample({
|
|
2859
|
+
usageTap,
|
|
2860
|
+
sampling: defaultSampling,
|
|
2861
|
+
decision: sampleDecision,
|
|
2862
|
+
ctx,
|
|
2863
|
+
beginRequest,
|
|
2864
|
+
input: params,
|
|
2865
|
+
response,
|
|
2866
|
+
startedAt: sampleStartedAt
|
|
2867
|
+
});
|
|
1984
2868
|
return response;
|
|
2869
|
+
}, async (error) => {
|
|
2870
|
+
await captureMeteredOpenAISample({
|
|
2871
|
+
usageTap,
|
|
2872
|
+
sampling: defaultSampling,
|
|
2873
|
+
decision: sampleDecision,
|
|
2874
|
+
ctx,
|
|
2875
|
+
beginRequest,
|
|
2876
|
+
input: params,
|
|
2877
|
+
error,
|
|
2878
|
+
startedAt: sampleStartedAt
|
|
2879
|
+
});
|
|
2880
|
+
throw error;
|
|
1985
2881
|
});
|
|
1986
2882
|
return wrappedPromise;
|
|
1987
2883
|
}, withUsage);
|
|
@@ -1996,7 +2892,7 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
1996
2892
|
};
|
|
1997
2893
|
return new Proxy(resource, handler);
|
|
1998
2894
|
}
|
|
1999
|
-
function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
2895
|
+
function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling, provider) {
|
|
2000
2896
|
const originalCreate = resource.create.bind(resource);
|
|
2001
2897
|
const streamCandidate = resource.stream;
|
|
2002
2898
|
const originalStream = typeof streamCandidate === "function" ? streamCandidate.bind(resource) : void 0;
|
|
@@ -2010,8 +2906,16 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2010
2906
|
const beginRequest = resolveBeginRequest(defaultContext, usageContext);
|
|
2011
2907
|
const wantsStream = isStreamingRequest(params);
|
|
2012
2908
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
2909
|
+
const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
|
|
2910
|
+
const sampleStartedAt = Date.now();
|
|
2911
|
+
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
|
|
2912
|
+
usageTap,
|
|
2913
|
+
sampling: defaultSampling,
|
|
2914
|
+
beginRequest,
|
|
2915
|
+
input: params
|
|
2916
|
+
});
|
|
2013
2917
|
const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
2014
|
-
const
|
|
2918
|
+
const compressedParams = await compressChatParamsForCall({
|
|
2015
2919
|
params: hintedParams,
|
|
2016
2920
|
usageTap,
|
|
2017
2921
|
ctx,
|
|
@@ -2021,25 +2925,68 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2021
2925
|
withUsage,
|
|
2022
2926
|
operation: "chat.completions.create"
|
|
2023
2927
|
});
|
|
2928
|
+
const finalParams = wantsStream ? ensureOpenAIStreamUsage(compressedParams) : compressedParams;
|
|
2929
|
+
ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
|
|
2024
2930
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
2025
2931
|
if (wantsStream) {
|
|
2026
2932
|
const apiPromise2 = originalCreate(finalParams, request);
|
|
2027
2933
|
const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
|
|
2028
2934
|
ensureAsyncIterable(rawStream, "chat.completions.create");
|
|
2029
|
-
const wrappedStream2 = wrapStreamForUsageTap(rawStream, async () => {
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2935
|
+
const wrappedStream2 = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
2936
|
+
try {
|
|
2937
|
+
if (termination === "complete") {
|
|
2938
|
+
const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
|
|
2939
|
+
if (usage) {
|
|
2940
|
+
ctx.setUsage(usage);
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
} catch (error) {
|
|
2944
|
+
ctx.setError({
|
|
2945
|
+
code: "USAGE_FINALIZE_ERROR",
|
|
2946
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2947
|
+
});
|
|
2948
|
+
throw error;
|
|
2949
|
+
} finally {
|
|
2950
|
+
await settle?.();
|
|
2033
2951
|
}
|
|
2034
|
-
}, ctx)
|
|
2952
|
+
}, ctx, (chunk) => {
|
|
2953
|
+
tryInferUsageFromStreamChunk(
|
|
2954
|
+
chunk,
|
|
2955
|
+
ctx.begin.data.vendorHints,
|
|
2956
|
+
ctx,
|
|
2957
|
+
provider
|
|
2958
|
+
);
|
|
2959
|
+
});
|
|
2035
2960
|
return wrappedStream2;
|
|
2036
2961
|
});
|
|
2037
2962
|
return wrappedPromise2;
|
|
2038
2963
|
}
|
|
2039
2964
|
const apiPromise = originalCreate(finalParams, request);
|
|
2040
|
-
const wrappedPromise = transformApiPromise(apiPromise, (response) => {
|
|
2041
|
-
tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx);
|
|
2965
|
+
const wrappedPromise = transformApiPromise(apiPromise, async (response) => {
|
|
2966
|
+
tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx, provider);
|
|
2967
|
+
await captureMeteredOpenAISample({
|
|
2968
|
+
usageTap,
|
|
2969
|
+
sampling: defaultSampling,
|
|
2970
|
+
decision: sampleDecision,
|
|
2971
|
+
ctx,
|
|
2972
|
+
beginRequest,
|
|
2973
|
+
input: params,
|
|
2974
|
+
response,
|
|
2975
|
+
startedAt: sampleStartedAt
|
|
2976
|
+
});
|
|
2042
2977
|
return response;
|
|
2978
|
+
}, async (error) => {
|
|
2979
|
+
await captureMeteredOpenAISample({
|
|
2980
|
+
usageTap,
|
|
2981
|
+
sampling: defaultSampling,
|
|
2982
|
+
decision: sampleDecision,
|
|
2983
|
+
ctx,
|
|
2984
|
+
beginRequest,
|
|
2985
|
+
input: params,
|
|
2986
|
+
error,
|
|
2987
|
+
startedAt: sampleStartedAt
|
|
2988
|
+
});
|
|
2989
|
+
throw error;
|
|
2043
2990
|
});
|
|
2044
2991
|
return wrappedPromise;
|
|
2045
2992
|
}, withUsage);
|
|
@@ -2053,8 +3000,9 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2053
3000
|
} = splitUsageOptions(options);
|
|
2054
3001
|
const beginRequest = resolveBeginRequest(defaultContext, usageContext);
|
|
2055
3002
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
3003
|
+
const settle = deferUsageFinalization(ctx);
|
|
2056
3004
|
const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
2057
|
-
const
|
|
3005
|
+
const compressedParams = await compressChatParamsForCall({
|
|
2058
3006
|
params: hintedParams,
|
|
2059
3007
|
usageTap,
|
|
2060
3008
|
ctx,
|
|
@@ -2064,16 +3012,41 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2064
3012
|
withUsage,
|
|
2065
3013
|
operation: "chat.completions.stream"
|
|
2066
3014
|
});
|
|
3015
|
+
const finalParams = ensureOpenAIStreamUsage(compressedParams);
|
|
3016
|
+
ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
|
|
2067
3017
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
2068
3018
|
const apiPromise = originalStream(finalParams, request);
|
|
2069
3019
|
const wrappedPromise = transformApiPromise(apiPromise, (rawStream) => {
|
|
2070
3020
|
ensureAsyncIterable(rawStream, "chat.completions.stream");
|
|
2071
|
-
const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async () => {
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
3021
|
+
const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
3022
|
+
try {
|
|
3023
|
+
if (termination === "complete") {
|
|
3024
|
+
const usage = await extractUsageFromStream(
|
|
3025
|
+
rawStream,
|
|
3026
|
+
ctx.begin.data.vendorHints,
|
|
3027
|
+
provider
|
|
3028
|
+
);
|
|
3029
|
+
if (usage) {
|
|
3030
|
+
ctx.setUsage(usage);
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
} catch (error) {
|
|
3034
|
+
ctx.setError({
|
|
3035
|
+
code: "USAGE_FINALIZE_ERROR",
|
|
3036
|
+
message: error instanceof Error ? error.message : String(error)
|
|
3037
|
+
});
|
|
3038
|
+
throw error;
|
|
3039
|
+
} finally {
|
|
3040
|
+
await settle();
|
|
2075
3041
|
}
|
|
2076
|
-
}, ctx)
|
|
3042
|
+
}, ctx, (chunk) => {
|
|
3043
|
+
tryInferUsageFromStreamChunk(
|
|
3044
|
+
chunk,
|
|
3045
|
+
ctx.begin.data.vendorHints,
|
|
3046
|
+
ctx,
|
|
3047
|
+
provider
|
|
3048
|
+
);
|
|
3049
|
+
});
|
|
2077
3050
|
return wrappedStreamInner;
|
|
2078
3051
|
});
|
|
2079
3052
|
return wrappedPromise;
|
|
@@ -2195,6 +3168,10 @@ async function compressChatParams(params, usageTap, compression, signal) {
|
|
|
2195
3168
|
const result = await usageTap.compressPromptMessages(source, {
|
|
2196
3169
|
provider: "usagetap",
|
|
2197
3170
|
failOpen: compression.failOpen,
|
|
3171
|
+
mode: compression.mode,
|
|
3172
|
+
latencyBudgetMs: compression.latencyBudgetMs,
|
|
3173
|
+
compactEmptyUserMessages: compression.compactEmptyUserMessages,
|
|
3174
|
+
compactDuplicateUserTextParts: compression.compactDuplicateUserTextParts,
|
|
2198
3175
|
aggressiveness: resolveMessageEndpointAggressiveness(compression),
|
|
2199
3176
|
signal
|
|
2200
3177
|
});
|
|
@@ -2664,19 +3641,52 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2664
3641
|
if (requested) begin.requested = requested;
|
|
2665
3642
|
const feature = current.feature ?? base.feature;
|
|
2666
3643
|
if (feature) begin.feature = feature;
|
|
3644
|
+
const runId = current.runId ?? base.runId;
|
|
3645
|
+
if (runId) begin.runId = runId;
|
|
2667
3646
|
const idempotency = current.idempotency ?? base.idempotency;
|
|
2668
3647
|
if (idempotency) begin.idempotency = idempotency;
|
|
2669
3648
|
const customerName = current.customerName ?? base.customerName;
|
|
2670
3649
|
if (customerName) begin.customerName = customerName;
|
|
2671
3650
|
const customerEmail = current.customerEmail ?? base.customerEmail;
|
|
2672
3651
|
if (customerEmail) begin.customerEmail = customerEmail;
|
|
3652
|
+
const customerUserId = current.customerUserId ?? base.customerUserId;
|
|
3653
|
+
if (customerUserId) begin.customerUserId = customerUserId;
|
|
3654
|
+
const customerUserName = current.customerUserName ?? base.customerUserName;
|
|
3655
|
+
if (customerUserName) begin.customerUserName = customerUserName;
|
|
3656
|
+
const customerUserEmail = current.customerUserEmail ?? base.customerUserEmail;
|
|
3657
|
+
if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
|
|
3658
|
+
const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
|
|
3659
|
+
if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
|
|
3660
|
+
const batch = current.batch ?? base.batch;
|
|
3661
|
+
if (typeof batch === "boolean") begin.batch = batch;
|
|
3662
|
+
const pricingMode = current.pricingMode ?? base.pricingMode;
|
|
3663
|
+
if (pricingMode) begin.pricingMode = pricingMode;
|
|
2673
3664
|
if (tags?.length) {
|
|
2674
3665
|
begin.tags = tags;
|
|
2675
3666
|
}
|
|
2676
3667
|
return begin;
|
|
2677
3668
|
}
|
|
2678
|
-
function
|
|
2679
|
-
|
|
3669
|
+
function responsesBeginRequest(begin, params) {
|
|
3670
|
+
if (!responsesRequestUsesWebSearch(params)) return begin;
|
|
3671
|
+
return {
|
|
3672
|
+
...begin,
|
|
3673
|
+
requested: {
|
|
3674
|
+
...begin.requested ?? {},
|
|
3675
|
+
search: true
|
|
3676
|
+
}
|
|
3677
|
+
};
|
|
3678
|
+
}
|
|
3679
|
+
function responsesRequestUsesWebSearch(params) {
|
|
3680
|
+
if (!params || typeof params !== "object") return false;
|
|
3681
|
+
const tools = params.tools;
|
|
3682
|
+
return Array.isArray(tools) && tools.some((tool) => {
|
|
3683
|
+
if (!tool || typeof tool !== "object") return false;
|
|
3684
|
+
const type = tool.type;
|
|
3685
|
+
return type === "web_search" || type === "web_search_preview";
|
|
3686
|
+
});
|
|
3687
|
+
}
|
|
3688
|
+
function transformApiPromise(apiPromise, onResolve, onReject) {
|
|
3689
|
+
const resolvedPromise = Promise.resolve(apiPromise).then(onResolve, onReject);
|
|
2680
3690
|
if (isObjectRecord(apiPromise)) {
|
|
2681
3691
|
const proto = Object.getPrototypeOf(apiPromise);
|
|
2682
3692
|
if (proto) {
|
|
@@ -2811,12 +3821,12 @@ function applyResponsesVendorHints(params, hints) {
|
|
|
2811
3821
|
}
|
|
2812
3822
|
return next;
|
|
2813
3823
|
}
|
|
2814
|
-
async function extractUsageFromStream(stream, hints) {
|
|
3824
|
+
async function extractUsageFromStream(stream, hints, provider = "openai") {
|
|
2815
3825
|
const finalPayload = await resolveStreamFinalPayload(stream);
|
|
2816
3826
|
if (!finalPayload) {
|
|
2817
3827
|
return void 0;
|
|
2818
3828
|
}
|
|
2819
|
-
return inferUsageFromResponse(finalPayload, hints);
|
|
3829
|
+
return inferUsageFromResponse(finalPayload, hints, provider);
|
|
2820
3830
|
}
|
|
2821
3831
|
async function resolveStreamFinalPayload(stream) {
|
|
2822
3832
|
if (!stream || typeof stream !== "object") {
|
|
@@ -2889,14 +3899,14 @@ function setHeaderIfPossible(res, key, value) {
|
|
|
2889
3899
|
res.setHeader(key, value);
|
|
2890
3900
|
}
|
|
2891
3901
|
}
|
|
2892
|
-
function tryInferUsage(response, hints, extractor, ctx) {
|
|
3902
|
+
function tryInferUsage(response, hints, extractor, ctx, provider = "openai") {
|
|
2893
3903
|
const explicit = extractor?.(response);
|
|
2894
|
-
const inferred = explicit ?? inferUsageFromResponse(response, hints);
|
|
3904
|
+
const inferred = explicit ?? inferUsageFromResponse(response, hints, provider);
|
|
2895
3905
|
if (inferred) {
|
|
2896
3906
|
ctx.setUsage(inferred);
|
|
2897
3907
|
}
|
|
2898
3908
|
}
|
|
2899
|
-
function inferUsageFromResponse(response, hints) {
|
|
3909
|
+
function inferUsageFromResponse(response, hints, provider = "openai") {
|
|
2900
3910
|
if (!response || typeof response !== "object") {
|
|
2901
3911
|
return void 0;
|
|
2902
3912
|
}
|
|
@@ -2904,32 +3914,110 @@ function inferUsageFromResponse(response, hints) {
|
|
|
2904
3914
|
if (!candidate.usage) {
|
|
2905
3915
|
return void 0;
|
|
2906
3916
|
}
|
|
2907
|
-
const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
|
|
3917
|
+
const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.input_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
|
|
3918
|
+
const cacheWriteInputTokens = candidate.usage.prompt_tokens_details?.cache_write_tokens ?? candidate.usage.prompt_tokens_details?.cache_creation_tokens ?? candidate.usage.input_tokens_details?.cache_write_tokens ?? candidate.usage.input_tokens_details?.cache_creation_tokens ?? candidate.usage.cache_creation_input_tokens ?? candidate.usage.cache_write_input_tokens ?? candidate.usage.cache_write_tokens;
|
|
3919
|
+
const cacheWrite5mInputTokens = candidate.usage.cache_write_5m_input_tokens ?? candidate.usage.cache_creation?.ephemeral_5m_input_tokens;
|
|
3920
|
+
const cacheWrite1hInputTokens = candidate.usage.cache_write_1h_input_tokens ?? candidate.usage.cache_creation?.ephemeral_1h_input_tokens;
|
|
3921
|
+
const outputSearches = Array.isArray(candidate.output) ? candidate.output.filter((item) => item?.type === "web_search_call").length : 0;
|
|
3922
|
+
const searches = candidate.usage.searches ?? candidate.usage.web_search_queries ?? candidate.usage.server_tool_use?.web_search_requests ?? outputSearches;
|
|
3923
|
+
const responseEffort = normalizeExecutionReasoningEffort(
|
|
3924
|
+
candidate.reasoning?.effort
|
|
3925
|
+
);
|
|
2908
3926
|
return {
|
|
3927
|
+
providerUsed: provider,
|
|
2909
3928
|
modelUsed: candidate.model ?? hints?.preferredModel,
|
|
2910
|
-
inputTokens: candidate.usage.prompt_tokens,
|
|
2911
|
-
responseTokens: candidate.usage.completion_tokens,
|
|
2912
|
-
cachedInputTokens
|
|
3929
|
+
inputTokens: candidate.usage.prompt_tokens ?? candidate.usage.input_tokens,
|
|
3930
|
+
responseTokens: candidate.usage.completion_tokens ?? candidate.usage.output_tokens,
|
|
3931
|
+
cachedInputTokens,
|
|
3932
|
+
cacheWriteInputTokens,
|
|
3933
|
+
cacheWrite5mInputTokens,
|
|
3934
|
+
cacheWrite1hInputTokens,
|
|
3935
|
+
audioInputTokens: candidate.usage.prompt_tokens_details?.audio_tokens ?? candidate.usage.input_tokens_details?.audio_tokens,
|
|
3936
|
+
cachedAudioInputTokens: candidate.usage.prompt_tokens_details?.cached_audio_tokens ?? candidate.usage.input_tokens_details?.cached_audio_tokens ?? candidate.usage.prompt_tokens_details?.cached_tokens_details?.audio_tokens ?? candidate.usage.input_tokens_details?.cached_tokens_details?.audio_tokens,
|
|
3937
|
+
imageInputTokens: candidate.usage.prompt_tokens_details?.image_tokens ?? candidate.usage.input_tokens_details?.image_tokens,
|
|
3938
|
+
imageOutputTokens: candidate.usage.completion_tokens_details?.image_tokens ?? candidate.usage.output_tokens_details?.image_tokens,
|
|
3939
|
+
audioOutputTokens: candidate.usage.completion_tokens_details?.audio_tokens ?? candidate.usage.output_tokens_details?.audio_tokens,
|
|
3940
|
+
reasoningTokens: candidate.usage.completion_tokens_details?.reasoning_tokens ?? candidate.usage.output_tokens_details?.reasoning_tokens,
|
|
3941
|
+
...responseEffort ? {
|
|
3942
|
+
reasoningEffort: responseEffort,
|
|
3943
|
+
reasoningEffortSource: "provider_response"
|
|
3944
|
+
} : {},
|
|
3945
|
+
...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {},
|
|
3946
|
+
...typeof searches === "number" && searches > 0 ? { searches } : {}
|
|
2913
3947
|
};
|
|
2914
3948
|
}
|
|
2915
|
-
function
|
|
3949
|
+
function normalizeExecutionReasoningEffort(value) {
|
|
3950
|
+
return value === "none" || value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max" ? value : void 0;
|
|
3951
|
+
}
|
|
3952
|
+
function openAIRequestExecutionMetadata(params, provider) {
|
|
3953
|
+
const record = params && typeof params === "object" ? params : {};
|
|
3954
|
+
const reasoning = record.reasoning && typeof record.reasoning === "object" ? record.reasoning : void 0;
|
|
3955
|
+
const effort = normalizeExecutionReasoningEffort(
|
|
3956
|
+
record.reasoning_effort ?? reasoning?.effort ?? record.thinking_level
|
|
3957
|
+
);
|
|
3958
|
+
const mode = typeof reasoning?.type === "string" ? reasoning.type : typeof reasoning?.mode === "string" ? reasoning.mode : void 0;
|
|
3959
|
+
const rawBudget = reasoning?.budget_tokens ?? record.thinking_budget ?? record.thinking_budget_tokens;
|
|
3960
|
+
const budget = typeof rawBudget === "number" && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
|
|
3961
|
+
return {
|
|
3962
|
+
providerUsed: provider,
|
|
3963
|
+
...typeof record.model === "string" ? { modelUsed: record.model } : {},
|
|
3964
|
+
...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
|
|
3965
|
+
...mode ? { reasoningMode: mode } : {},
|
|
3966
|
+
...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
|
|
3967
|
+
};
|
|
3968
|
+
}
|
|
3969
|
+
function tryInferUsageFromStreamChunk(chunk, hints, ctx, provider) {
|
|
3970
|
+
const payload = isObjectRecord(chunk) && isObjectRecord(chunk.response) ? chunk.response : chunk;
|
|
3971
|
+
const inferred = inferUsageFromResponse(payload, hints, provider);
|
|
3972
|
+
if (inferred) {
|
|
3973
|
+
ctx.setUsage(inferred);
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3976
|
+
function ensureOpenAIStreamUsage(params) {
|
|
3977
|
+
if (!isObjectRecord(params)) {
|
|
3978
|
+
return params;
|
|
3979
|
+
}
|
|
3980
|
+
const streamOptions = isObjectRecord(params.stream_options) ? params.stream_options : {};
|
|
3981
|
+
return {
|
|
3982
|
+
...params,
|
|
3983
|
+
stream_options: {
|
|
3984
|
+
...streamOptions,
|
|
3985
|
+
include_usage: true
|
|
3986
|
+
}
|
|
3987
|
+
};
|
|
3988
|
+
}
|
|
3989
|
+
function deferUsageFinalization(ctx) {
|
|
3990
|
+
return ctx.deferFinalization?.() ?? (() => Promise.resolve());
|
|
3991
|
+
}
|
|
3992
|
+
function wrapStreamForUsageTap(source, finalize, ctx, onChunk) {
|
|
2916
3993
|
const getIterator = source[Symbol.asyncIterator];
|
|
2917
3994
|
if (typeof getIterator !== "function") {
|
|
2918
3995
|
throw new TypeError("Stream is not async iterable");
|
|
2919
3996
|
}
|
|
2920
3997
|
const iterator = getIterator.call(source);
|
|
2921
3998
|
let completed = false;
|
|
2922
|
-
const invokeFinalize = async () => {
|
|
3999
|
+
const invokeFinalize = async (termination, error) => {
|
|
2923
4000
|
if (completed) return;
|
|
2924
4001
|
completed = true;
|
|
4002
|
+
if (termination === "cancel" || termination === "manual") {
|
|
4003
|
+
ctx.setError({
|
|
4004
|
+
code: "STREAM_ABORTED",
|
|
4005
|
+
message: termination === "cancel" ? "Provider stream consumption was cancelled before completion" : "Provider stream was finalized before completion"
|
|
4006
|
+
});
|
|
4007
|
+
} else if (termination === "error") {
|
|
4008
|
+
ctx.setError({
|
|
4009
|
+
code: "VENDOR_ERROR",
|
|
4010
|
+
message: error instanceof Error ? error.message : String(error)
|
|
4011
|
+
});
|
|
4012
|
+
}
|
|
2925
4013
|
try {
|
|
2926
|
-
await finalize();
|
|
2927
|
-
} catch (
|
|
4014
|
+
await finalize(termination);
|
|
4015
|
+
} catch (error2) {
|
|
2928
4016
|
ctx.setError({
|
|
2929
4017
|
code: "USAGE_FINALIZE_ERROR",
|
|
2930
|
-
message:
|
|
4018
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
2931
4019
|
});
|
|
2932
|
-
throw
|
|
4020
|
+
throw error2;
|
|
2933
4021
|
}
|
|
2934
4022
|
};
|
|
2935
4023
|
const prototype = Object.getPrototypeOf(source) ?? Object.prototype;
|
|
@@ -2953,12 +4041,15 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
|
|
|
2953
4041
|
value: async (...args) => {
|
|
2954
4042
|
try {
|
|
2955
4043
|
const result = await iterator.next(...args);
|
|
4044
|
+
if (!result.done) {
|
|
4045
|
+
onChunk?.(result.value);
|
|
4046
|
+
}
|
|
2956
4047
|
if (result.done) {
|
|
2957
|
-
await invokeFinalize();
|
|
4048
|
+
await invokeFinalize("complete");
|
|
2958
4049
|
}
|
|
2959
4050
|
return result;
|
|
2960
4051
|
} catch (error) {
|
|
2961
|
-
await invokeFinalize().catch(() => void 0);
|
|
4052
|
+
await invokeFinalize("error", error).catch(() => void 0);
|
|
2962
4053
|
throw error;
|
|
2963
4054
|
}
|
|
2964
4055
|
},
|
|
@@ -2967,39 +4058,49 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
|
|
|
2967
4058
|
});
|
|
2968
4059
|
Object.defineProperty(wrapped, "return", {
|
|
2969
4060
|
value: async (value) => {
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
4061
|
+
try {
|
|
4062
|
+
if (typeof iterator.return === "function") {
|
|
4063
|
+
const rawResult = await iterator.return(value);
|
|
4064
|
+
if (!isIteratorResult(rawResult)) {
|
|
4065
|
+
throw new TypeError("Iterator.return() returned an invalid result");
|
|
4066
|
+
}
|
|
4067
|
+
await invokeFinalize("cancel");
|
|
4068
|
+
return rawResult;
|
|
2974
4069
|
}
|
|
2975
|
-
await invokeFinalize();
|
|
2976
|
-
return
|
|
4070
|
+
await invokeFinalize("cancel");
|
|
4071
|
+
return { done: true, value };
|
|
4072
|
+
} catch (error) {
|
|
4073
|
+
await invokeFinalize("error", error).catch(() => void 0);
|
|
4074
|
+
throw error;
|
|
2977
4075
|
}
|
|
2978
|
-
await invokeFinalize();
|
|
2979
|
-
return { done: true, value };
|
|
2980
4076
|
},
|
|
2981
4077
|
configurable: true,
|
|
2982
4078
|
writable: true
|
|
2983
4079
|
});
|
|
2984
4080
|
Object.defineProperty(wrapped, "throw", {
|
|
2985
4081
|
value: async (error) => {
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
4082
|
+
try {
|
|
4083
|
+
if (typeof iterator.throw === "function") {
|
|
4084
|
+
const rawResult = await iterator.throw(error);
|
|
4085
|
+
if (!isIteratorResult(rawResult)) {
|
|
4086
|
+
throw new TypeError("Iterator.throw() returned an invalid result");
|
|
4087
|
+
}
|
|
4088
|
+
await invokeFinalize("error", error);
|
|
4089
|
+
return rawResult;
|
|
2990
4090
|
}
|
|
2991
|
-
await invokeFinalize();
|
|
2992
|
-
|
|
4091
|
+
await invokeFinalize("error", error);
|
|
4092
|
+
throw error;
|
|
4093
|
+
} catch (thrownError) {
|
|
4094
|
+
await invokeFinalize("error", thrownError).catch(() => void 0);
|
|
4095
|
+
throw thrownError;
|
|
2993
4096
|
}
|
|
2994
|
-
await invokeFinalize();
|
|
2995
|
-
throw error;
|
|
2996
4097
|
},
|
|
2997
4098
|
configurable: true,
|
|
2998
4099
|
writable: true
|
|
2999
4100
|
});
|
|
3000
4101
|
Object.defineProperty(wrapped, "__usageTapFinalize", {
|
|
3001
4102
|
value: async () => {
|
|
3002
|
-
await invokeFinalize();
|
|
4103
|
+
await invokeFinalize("manual");
|
|
3003
4104
|
},
|
|
3004
4105
|
configurable: true
|
|
3005
4106
|
});
|
|
@@ -3011,9 +4112,24 @@ function isIteratorResult(value) {
|
|
|
3011
4112
|
|
|
3012
4113
|
// src/adapters/openrouter.ts
|
|
3013
4114
|
function createOpenRouterAdapter(init) {
|
|
3014
|
-
return createOpenAIAdapter(init);
|
|
4115
|
+
return createOpenAIAdapter({ ...init, provider: "openrouter" });
|
|
4116
|
+
}
|
|
4117
|
+
function withMetering2(client, customer) {
|
|
4118
|
+
return withMetering(
|
|
4119
|
+
client,
|
|
4120
|
+
typeof customer === "string" ? { customerId: customer, provider: "openrouter" } : { ...customer, provider: "openrouter" }
|
|
4121
|
+
);
|
|
4122
|
+
}
|
|
4123
|
+
function wrapOpenAI2(client, usageTap, options = {}) {
|
|
4124
|
+
return wrapOpenAI(client, usageTap, {
|
|
4125
|
+
...options,
|
|
4126
|
+
provider: "openrouter"
|
|
4127
|
+
});
|
|
4128
|
+
}
|
|
4129
|
+
function withSampling2(client, options = {}) {
|
|
4130
|
+
return withSampling(client, { ...options, provider: "openrouter" });
|
|
3015
4131
|
}
|
|
3016
4132
|
|
|
3017
|
-
export { createOpenRouterAdapter, withCompression, withMetering, wrapOpenAI };
|
|
4133
|
+
export { createOpenRouterAdapter, withCompression, withMetering2 as withMetering, withSampling2 as withSampling, wrapOpenAI2 as wrapOpenAI };
|
|
3018
4134
|
//# sourceMappingURL=index.mjs.map
|
|
3019
4135
|
//# sourceMappingURL=index.mjs.map
|