@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
package/dist/anthropic/index.mjs
CHANGED
|
@@ -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();
|
|
@@ -1613,7 +2256,123 @@ var AnthropicPromptCompressionStats = class {
|
|
|
1613
2256
|
return this.totalOriginalCharacters > 0 ? this.totalCharactersSaved / this.totalOriginalCharacters : 0;
|
|
1614
2257
|
}
|
|
1615
2258
|
};
|
|
2259
|
+
var DeferredAnthropicApiPromise = class extends Promise {
|
|
2260
|
+
responseEnvelope;
|
|
2261
|
+
constructor(execute) {
|
|
2262
|
+
super((resolve) => resolve(void 0));
|
|
2263
|
+
this.responseEnvelope = Promise.resolve().then(execute);
|
|
2264
|
+
}
|
|
2265
|
+
then(onfulfilled, onrejected) {
|
|
2266
|
+
return this.responseEnvelope.then((result) => result.data).then(onfulfilled, onrejected);
|
|
2267
|
+
}
|
|
2268
|
+
catch(onrejected) {
|
|
2269
|
+
return this.then(void 0, onrejected);
|
|
2270
|
+
}
|
|
2271
|
+
finally(onfinally) {
|
|
2272
|
+
return this.responseEnvelope.then((result) => result.data).finally(onfinally ?? void 0);
|
|
2273
|
+
}
|
|
2274
|
+
withResponse() {
|
|
2275
|
+
return this.responseEnvelope;
|
|
2276
|
+
}
|
|
2277
|
+
async asResponse() {
|
|
2278
|
+
const result = await this.responseEnvelope;
|
|
2279
|
+
if (!("response" in result)) {
|
|
2280
|
+
throw new TypeError("The wrapped Anthropic result does not expose a raw response");
|
|
2281
|
+
}
|
|
2282
|
+
return result.response;
|
|
2283
|
+
}
|
|
2284
|
+
};
|
|
2285
|
+
async function resolveAnthropicResponseEnvelope(value) {
|
|
2286
|
+
if (isObjectRecord(value) && typeof value.withResponse === "function") {
|
|
2287
|
+
const withResponse = value.withResponse;
|
|
2288
|
+
const result = await withResponse.call(value);
|
|
2289
|
+
if (!isObjectRecord(result) || !("data" in result)) {
|
|
2290
|
+
throw new TypeError("Anthropic create().withResponse() returned an invalid result");
|
|
2291
|
+
}
|
|
2292
|
+
return result;
|
|
2293
|
+
}
|
|
2294
|
+
return { data: await value };
|
|
2295
|
+
}
|
|
1616
2296
|
var USAGETAP_CORRELATION_HEADER = "x-usage-correlation-id";
|
|
2297
|
+
function withSampling(client, options = {}) {
|
|
2298
|
+
if (!client?.messages || typeof client.messages.create !== "function" || !options) {
|
|
2299
|
+
throw new UsageTapError(
|
|
2300
|
+
"USAGETAP_BAD_REQUEST",
|
|
2301
|
+
"withSampling requires an Anthropic client and sampling options"
|
|
2302
|
+
);
|
|
2303
|
+
}
|
|
2304
|
+
const { apiKey, usageTapClient, ...policy } = options;
|
|
2305
|
+
const localPolicy = typeof policy.rate === "number" ? policy : void 0;
|
|
2306
|
+
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey, sampling: localPolicy });
|
|
2307
|
+
const originalCreate = client.messages.create.bind(client.messages);
|
|
2308
|
+
const create = (params, requestOptions) => {
|
|
2309
|
+
return new DeferredAnthropicApiPromise(async () => {
|
|
2310
|
+
const { usageTap: callContext = {}, ...providerOptions } = requestOptions ?? {};
|
|
2311
|
+
const decision = params.stream === true ? Promise.resolve(false) : usageTap.shouldSampleAsync({
|
|
2312
|
+
customerId: callContext.customerId,
|
|
2313
|
+
feature: callContext.feature,
|
|
2314
|
+
input: params
|
|
2315
|
+
}, localPolicy);
|
|
2316
|
+
const startedAt = Date.now();
|
|
2317
|
+
try {
|
|
2318
|
+
const result = await resolveAnthropicResponseEnvelope(originalCreate(
|
|
2319
|
+
params,
|
|
2320
|
+
Object.keys(providerOptions).length ? providerOptions : void 0
|
|
2321
|
+
));
|
|
2322
|
+
const response = result.data;
|
|
2323
|
+
const selected = await decision;
|
|
2324
|
+
if (selected) {
|
|
2325
|
+
const record = isObjectRecord(response) ? response : {};
|
|
2326
|
+
await usageTap.captureSample({
|
|
2327
|
+
customerId: callContext.customerId,
|
|
2328
|
+
feature: callContext.feature,
|
|
2329
|
+
environment: callContext.environment,
|
|
2330
|
+
tags: callContext.tags,
|
|
2331
|
+
provider: "anthropic",
|
|
2332
|
+
model: readString(record.model) ?? readString(params.model),
|
|
2333
|
+
input: params,
|
|
2334
|
+
output: response,
|
|
2335
|
+
usage: record.usage,
|
|
2336
|
+
latencyMs: Date.now() - startedAt
|
|
2337
|
+
}).catch(() => void 0);
|
|
2338
|
+
}
|
|
2339
|
+
return result;
|
|
2340
|
+
} catch (error) {
|
|
2341
|
+
const selected = await decision;
|
|
2342
|
+
if (selected) {
|
|
2343
|
+
await usageTap.captureSample({
|
|
2344
|
+
customerId: callContext.customerId,
|
|
2345
|
+
feature: callContext.feature,
|
|
2346
|
+
environment: callContext.environment,
|
|
2347
|
+
tags: callContext.tags,
|
|
2348
|
+
provider: "anthropic",
|
|
2349
|
+
model: readString(params.model),
|
|
2350
|
+
input: params,
|
|
2351
|
+
latencyMs: Date.now() - startedAt,
|
|
2352
|
+
error: error instanceof Error ? { name: error.name, message: error.message } : { message: String(error) }
|
|
2353
|
+
}).catch(() => void 0);
|
|
2354
|
+
}
|
|
2355
|
+
throw error;
|
|
2356
|
+
}
|
|
2357
|
+
});
|
|
2358
|
+
};
|
|
2359
|
+
const messages = new Proxy(client.messages, {
|
|
2360
|
+
get(target, prop, receiver) {
|
|
2361
|
+
if (prop === "create") return create;
|
|
2362
|
+
return safeReflectGet(target, prop, receiver);
|
|
2363
|
+
}
|
|
2364
|
+
});
|
|
2365
|
+
return new Proxy(client, {
|
|
2366
|
+
get(target, prop, receiver) {
|
|
2367
|
+
if (prop === "messages") return messages;
|
|
2368
|
+
if (prop === "unwrap") return () => target;
|
|
2369
|
+
return safeReflectGet(target, prop, receiver);
|
|
2370
|
+
}
|
|
2371
|
+
});
|
|
2372
|
+
}
|
|
2373
|
+
function safeReflectGet(target, prop, receiver) {
|
|
2374
|
+
return Reflect.get(target, prop, receiver);
|
|
2375
|
+
}
|
|
1617
2376
|
function withCompression(client, options = {}) {
|
|
1618
2377
|
if (!client?.messages || typeof client.messages.create !== "function") {
|
|
1619
2378
|
throw new UsageTapError(
|
|
@@ -1624,17 +2383,24 @@ function withCompression(client, options = {}) {
|
|
|
1624
2383
|
const {
|
|
1625
2384
|
apiKey,
|
|
1626
2385
|
usageTapClient,
|
|
2386
|
+
sampling,
|
|
1627
2387
|
...compressionOverrides
|
|
1628
2388
|
} = options;
|
|
1629
2389
|
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey });
|
|
1630
2390
|
const compression = {
|
|
1631
2391
|
provider: "usagetap",
|
|
2392
|
+
roles: { user: { enabled: true, aggressiveness: 0.2 } },
|
|
1632
2393
|
minContextTokens: DEFAULT_PROMPT_COMPRESSION_MIN_CONTEXT_TOKENS,
|
|
1633
2394
|
...compressionOverrides
|
|
1634
2395
|
};
|
|
1635
2396
|
const originalCreate = client.messages.create.bind(client.messages);
|
|
1636
2397
|
const wrappedCreate = (params, requestOptions) => {
|
|
1637
|
-
return
|
|
2398
|
+
return new DeferredAnthropicApiPromise(async () => {
|
|
2399
|
+
const outcome = await compressAnthropicParams(params, usageTap, compression);
|
|
2400
|
+
return resolveAnthropicResponseEnvelope(
|
|
2401
|
+
originalCreate(outcome.params, requestOptions)
|
|
2402
|
+
);
|
|
2403
|
+
});
|
|
1638
2404
|
};
|
|
1639
2405
|
const proxiedMessages = new Proxy(client.messages, {
|
|
1640
2406
|
get(target, prop, receiver) {
|
|
@@ -1642,13 +2408,18 @@ function withCompression(client, options = {}) {
|
|
|
1642
2408
|
return Reflect.get(target, prop, receiver);
|
|
1643
2409
|
}
|
|
1644
2410
|
});
|
|
1645
|
-
|
|
2411
|
+
const compressed = new Proxy(client, {
|
|
1646
2412
|
get(target, prop, receiver) {
|
|
1647
2413
|
if (prop === "messages") return proxiedMessages;
|
|
1648
2414
|
if (prop === "unwrap") return () => target;
|
|
1649
2415
|
return Reflect.get(target, prop, receiver);
|
|
1650
2416
|
}
|
|
1651
2417
|
});
|
|
2418
|
+
return sampling ? withSampling(compressed, {
|
|
2419
|
+
...sampling === true ? {} : sampling,
|
|
2420
|
+
apiKey,
|
|
2421
|
+
usageTapClient
|
|
2422
|
+
}) : compressed;
|
|
1652
2423
|
}
|
|
1653
2424
|
function withMetering(client, customer) {
|
|
1654
2425
|
const config = typeof customer === "string" ? { customerId: customer } : customer;
|
|
@@ -1663,6 +2434,7 @@ function withMetering(client, customer) {
|
|
|
1663
2434
|
usageTapClient,
|
|
1664
2435
|
applyVendorHints,
|
|
1665
2436
|
promptCompression,
|
|
2437
|
+
sampling,
|
|
1666
2438
|
...defaultContext
|
|
1667
2439
|
} = config;
|
|
1668
2440
|
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey });
|
|
@@ -1670,7 +2442,8 @@ function withMetering(client, customer) {
|
|
|
1670
2442
|
return wrapAnthropic(client, usageTap, {
|
|
1671
2443
|
defaultContext,
|
|
1672
2444
|
applyVendorHints,
|
|
1673
|
-
promptCompression: normalizedCompression
|
|
2445
|
+
promptCompression: normalizedCompression,
|
|
2446
|
+
sampling
|
|
1674
2447
|
});
|
|
1675
2448
|
}
|
|
1676
2449
|
function wrapAnthropic(client, usageTap, options = {}) {
|
|
@@ -1683,6 +2456,7 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1683
2456
|
const defaultContext = options.defaultContext;
|
|
1684
2457
|
const applyVendorHints = options.applyVendorHints !== false;
|
|
1685
2458
|
const defaultPromptCompression = normalizePromptCompressionOptions(options.promptCompression);
|
|
2459
|
+
const defaultSampling = normalizeMeteredAnthropicSampling(options.sampling);
|
|
1686
2460
|
const promptCompressionStats = new AnthropicPromptCompressionStats();
|
|
1687
2461
|
const proxiedMessages = createMessagesProxy(
|
|
1688
2462
|
client.messages,
|
|
@@ -1690,7 +2464,8 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1690
2464
|
defaultContext,
|
|
1691
2465
|
applyVendorHints,
|
|
1692
2466
|
defaultPromptCompression,
|
|
1693
|
-
promptCompressionStats
|
|
2467
|
+
promptCompressionStats,
|
|
2468
|
+
defaultSampling
|
|
1694
2469
|
);
|
|
1695
2470
|
const handler = {
|
|
1696
2471
|
get(target, prop, receiver) {
|
|
@@ -1708,7 +2483,7 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1708
2483
|
};
|
|
1709
2484
|
return new Proxy(client, handler);
|
|
1710
2485
|
}
|
|
1711
|
-
function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
2486
|
+
function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling) {
|
|
1712
2487
|
if (typeof resource.create !== "function") {
|
|
1713
2488
|
throw new UsageTapError("USAGETAP_BAD_REQUEST", "wrapAnthropic requires client.messages.create");
|
|
1714
2489
|
}
|
|
@@ -1720,19 +2495,22 @@ function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHint
|
|
|
1720
2495
|
withUsage,
|
|
1721
2496
|
promptCompression
|
|
1722
2497
|
} = splitUsageOptions(options);
|
|
1723
|
-
return
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2498
|
+
return new DeferredAnthropicApiPromise(
|
|
2499
|
+
() => invokeMessagesCreate({
|
|
2500
|
+
params,
|
|
2501
|
+
requestOptions,
|
|
2502
|
+
usageContext,
|
|
2503
|
+
withUsage,
|
|
2504
|
+
promptCompression,
|
|
2505
|
+
originalCreate,
|
|
2506
|
+
usageTap,
|
|
2507
|
+
defaultContext,
|
|
2508
|
+
applyVendorHints,
|
|
2509
|
+
defaultPromptCompression,
|
|
2510
|
+
promptCompressionStats,
|
|
2511
|
+
defaultSampling
|
|
2512
|
+
})
|
|
2513
|
+
);
|
|
1736
2514
|
});
|
|
1737
2515
|
const handler = {
|
|
1738
2516
|
get(target, prop, receiver) {
|
|
@@ -1745,13 +2523,25 @@ function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHint
|
|
|
1745
2523
|
return new Proxy(resource, handler);
|
|
1746
2524
|
}
|
|
1747
2525
|
async function invokeMessagesCreate(args) {
|
|
1748
|
-
const beginRequest = resolveBeginRequest(
|
|
2526
|
+
const beginRequest = resolveBeginRequest(
|
|
2527
|
+
args.defaultContext,
|
|
2528
|
+
args.usageContext
|
|
2529
|
+
);
|
|
1749
2530
|
const begin = await args.usageTap.beginCall(
|
|
1750
2531
|
beginRequest,
|
|
1751
2532
|
beginCallOptions(args.withUsage)
|
|
1752
2533
|
);
|
|
1753
2534
|
const state = createCallState(beginRequest, begin);
|
|
1754
2535
|
const ctx = createUsageContext(state);
|
|
2536
|
+
const sampleStartedAt = Date.now();
|
|
2537
|
+
const wantsStream = isStreamingRequest(args.params);
|
|
2538
|
+
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredAnthropicSampleDecision({
|
|
2539
|
+
usageTap: args.usageTap,
|
|
2540
|
+
sampling: args.defaultSampling,
|
|
2541
|
+
beginRequest,
|
|
2542
|
+
input: args.params
|
|
2543
|
+
});
|
|
2544
|
+
let providerCompleted = false;
|
|
1755
2545
|
try {
|
|
1756
2546
|
const hintedParams = args.applyVendorHints ? applyAnthropicVendorHints(args.params, ctx.begin.data.vendorHints) : args.params;
|
|
1757
2547
|
const finalParams = await compressAnthropicParamsForCall({
|
|
@@ -1764,21 +2554,38 @@ async function invokeMessagesCreate(args) {
|
|
|
1764
2554
|
withUsage: args.withUsage,
|
|
1765
2555
|
operation: "messages.create"
|
|
1766
2556
|
});
|
|
2557
|
+
ctx.setUsage(anthropicRequestExecutionMetadata(finalParams));
|
|
1767
2558
|
const request = attachCorrelationHeader(args.requestOptions, ctx.begin.correlationId);
|
|
1768
|
-
const
|
|
2559
|
+
const providerResult = await resolveAnthropicResponseEnvelope(
|
|
2560
|
+
args.originalCreate(finalParams, request)
|
|
2561
|
+
);
|
|
2562
|
+
const response = providerResult.data;
|
|
2563
|
+
providerCompleted = true;
|
|
1769
2564
|
if (isStreamingRequest(finalParams)) {
|
|
1770
2565
|
ensureAsyncIterable(response, "messages.create");
|
|
1771
|
-
return
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
2566
|
+
return {
|
|
2567
|
+
...providerResult,
|
|
2568
|
+
data: wrapAnthropicStreamForUsageTap(
|
|
2569
|
+
response,
|
|
2570
|
+
state,
|
|
2571
|
+
args.usageTap,
|
|
2572
|
+
args.withUsage,
|
|
2573
|
+
readString(finalParams.model)
|
|
2574
|
+
)
|
|
2575
|
+
};
|
|
1778
2576
|
}
|
|
1779
2577
|
inferAnthropicUsage(response, readString(finalParams.model), ctx);
|
|
2578
|
+
await captureMeteredAnthropicSample({
|
|
2579
|
+
usageTap: args.usageTap,
|
|
2580
|
+
sampling: args.defaultSampling,
|
|
2581
|
+
decision: sampleDecision,
|
|
2582
|
+
state,
|
|
2583
|
+
input: args.params,
|
|
2584
|
+
response,
|
|
2585
|
+
startedAt: sampleStartedAt
|
|
2586
|
+
});
|
|
1780
2587
|
await finalizeCall(state, args.usageTap, args.withUsage);
|
|
1781
|
-
return
|
|
2588
|
+
return providerResult;
|
|
1782
2589
|
} catch (error) {
|
|
1783
2590
|
if (!state.error) {
|
|
1784
2591
|
state.error = {
|
|
@@ -1786,10 +2593,80 @@ async function invokeMessagesCreate(args) {
|
|
|
1786
2593
|
message: error instanceof Error ? error.message : String(error)
|
|
1787
2594
|
};
|
|
1788
2595
|
}
|
|
2596
|
+
if (!providerCompleted) {
|
|
2597
|
+
await captureMeteredAnthropicSample({
|
|
2598
|
+
usageTap: args.usageTap,
|
|
2599
|
+
sampling: args.defaultSampling,
|
|
2600
|
+
decision: sampleDecision,
|
|
2601
|
+
state,
|
|
2602
|
+
input: args.params,
|
|
2603
|
+
error,
|
|
2604
|
+
startedAt: sampleStartedAt
|
|
2605
|
+
});
|
|
2606
|
+
}
|
|
1789
2607
|
await finalizeCall(state, args.usageTap, args.withUsage);
|
|
1790
2608
|
throw error;
|
|
1791
2609
|
}
|
|
1792
2610
|
}
|
|
2611
|
+
function normalizeMeteredAnthropicSampling(options) {
|
|
2612
|
+
if (!options) return void 0;
|
|
2613
|
+
if (options === true) return {};
|
|
2614
|
+
return {
|
|
2615
|
+
policy: typeof options.rate === "number" ? options : void 0
|
|
2616
|
+
};
|
|
2617
|
+
}
|
|
2618
|
+
function startMeteredAnthropicSampleDecision({
|
|
2619
|
+
usageTap,
|
|
2620
|
+
sampling,
|
|
2621
|
+
beginRequest,
|
|
2622
|
+
input
|
|
2623
|
+
}) {
|
|
2624
|
+
if (!sampling) return Promise.resolve(false);
|
|
2625
|
+
return usageTap.shouldSampleAsync(
|
|
2626
|
+
{
|
|
2627
|
+
customerId: beginRequest.customerId,
|
|
2628
|
+
feature: beginRequest.feature,
|
|
2629
|
+
input
|
|
2630
|
+
},
|
|
2631
|
+
sampling.policy
|
|
2632
|
+
);
|
|
2633
|
+
}
|
|
2634
|
+
async function captureMeteredAnthropicSample({
|
|
2635
|
+
usageTap,
|
|
2636
|
+
sampling,
|
|
2637
|
+
decision,
|
|
2638
|
+
state,
|
|
2639
|
+
input,
|
|
2640
|
+
response,
|
|
2641
|
+
error,
|
|
2642
|
+
startedAt
|
|
2643
|
+
}) {
|
|
2644
|
+
if (!sampling) return;
|
|
2645
|
+
let selected = false;
|
|
2646
|
+
try {
|
|
2647
|
+
selected = await decision;
|
|
2648
|
+
} catch {
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
if (!selected) return;
|
|
2652
|
+
const record = isObjectRecord(response) ? response : {};
|
|
2653
|
+
await usageTap.captureSample({
|
|
2654
|
+
sampleId: state.begin.data.callId,
|
|
2655
|
+
callId: state.begin.data.callId,
|
|
2656
|
+
customerId: state.beginRequest.customerId,
|
|
2657
|
+
feature: state.beginRequest.feature,
|
|
2658
|
+
tags: state.beginRequest.tags,
|
|
2659
|
+
provider: "anthropic",
|
|
2660
|
+
model: readString(record.model) ?? readString(input.model),
|
|
2661
|
+
input,
|
|
2662
|
+
...response === void 0 ? {} : { output: response },
|
|
2663
|
+
usage: record.usage,
|
|
2664
|
+
latencyMs: Date.now() - startedAt,
|
|
2665
|
+
...error === void 0 ? {} : {
|
|
2666
|
+
error: error instanceof Error ? { name: error.name, message: error.message } : { message: String(error) }
|
|
2667
|
+
}
|
|
2668
|
+
}).catch(() => void 0);
|
|
2669
|
+
}
|
|
1793
2670
|
async function compressAnthropicParamsForCall(args) {
|
|
1794
2671
|
const compression = resolveEffectivePromptCompressionOptions(
|
|
1795
2672
|
args.defaultPromptCompression,
|
|
@@ -1869,6 +2746,10 @@ async function compressAnthropicParams(params, usageTap, compression, signal) {
|
|
|
1869
2746
|
const result = await usageTap.compressPromptMessages(source, {
|
|
1870
2747
|
provider: "usagetap",
|
|
1871
2748
|
failOpen: compression.failOpen,
|
|
2749
|
+
mode: compression.mode,
|
|
2750
|
+
latencyBudgetMs: compression.latencyBudgetMs,
|
|
2751
|
+
compactEmptyUserMessages: compression.compactEmptyUserMessages,
|
|
2752
|
+
compactDuplicateUserTextParts: compression.compactDuplicateUserTextParts,
|
|
1872
2753
|
aggressiveness: resolveMessageEndpointAggressiveness(compression),
|
|
1873
2754
|
signal
|
|
1874
2755
|
});
|
|
@@ -2257,6 +3138,8 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2257
3138
|
if (requested) begin.requested = requested;
|
|
2258
3139
|
const feature = current.feature ?? base.feature;
|
|
2259
3140
|
if (feature) begin.feature = feature;
|
|
3141
|
+
const runId = current.runId ?? base.runId;
|
|
3142
|
+
if (runId) begin.runId = runId;
|
|
2260
3143
|
const idempotency = current.idempotency ?? base.idempotency;
|
|
2261
3144
|
if (idempotency) begin.idempotency = idempotency;
|
|
2262
3145
|
const idempotencyKey = current.idempotencyKey ?? base.idempotencyKey;
|
|
@@ -2265,10 +3148,14 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2265
3148
|
if (customerName) begin.customerName = customerName;
|
|
2266
3149
|
const customerEmail = current.customerEmail ?? base.customerEmail;
|
|
2267
3150
|
if (customerEmail) begin.customerEmail = customerEmail;
|
|
3151
|
+
const customerUserId = current.customerUserId ?? base.customerUserId;
|
|
3152
|
+
if (customerUserId) begin.customerUserId = customerUserId;
|
|
3153
|
+
const customerUserName = current.customerUserName ?? base.customerUserName;
|
|
3154
|
+
if (customerUserName) begin.customerUserName = customerUserName;
|
|
3155
|
+
const customerUserEmail = current.customerUserEmail ?? base.customerUserEmail;
|
|
3156
|
+
if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
|
|
2268
3157
|
const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
|
|
2269
3158
|
if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
|
|
2270
|
-
const holdUsd = current.holdUsd ?? base.holdUsd;
|
|
2271
|
-
if (typeof holdUsd === "number") begin.holdUsd = holdUsd;
|
|
2272
3159
|
const batch = current.batch ?? base.batch;
|
|
2273
3160
|
if (typeof batch === "boolean") begin.batch = batch;
|
|
2274
3161
|
const pricingMode = current.pricingMode ?? base.pricingMode;
|
|
@@ -2280,6 +3167,11 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2280
3167
|
}
|
|
2281
3168
|
function createCallState(beginRequest, begin) {
|
|
2282
3169
|
const usage = {};
|
|
3170
|
+
const pricingMode = begin.data.pricingMode ?? beginRequest.pricingMode ?? (beginRequest.batch === true ? "batch" : beginRequest.batch === false ? "standard" : void 0);
|
|
3171
|
+
if (pricingMode) {
|
|
3172
|
+
usage.pricingMode = pricingMode;
|
|
3173
|
+
usage.batch = pricingMode === "batch";
|
|
3174
|
+
}
|
|
2283
3175
|
const stripeCustomerId = typeof begin.data.stripeCustomerId === "string" ? begin.data.stripeCustomerId : typeof beginRequest.stripeCustomerId === "string" ? beginRequest.stripeCustomerId : void 0;
|
|
2284
3176
|
if (stripeCustomerId) {
|
|
2285
3177
|
usage.stripeCustomerId = stripeCustomerId;
|
|
@@ -2343,10 +3235,12 @@ function inferAnthropicUsage(response, fallbackModel, ctx) {
|
|
|
2343
3235
|
}
|
|
2344
3236
|
function extractAnthropicUsage(payload, fallbackModel) {
|
|
2345
3237
|
if (!isObjectRecord(payload)) {
|
|
2346
|
-
return fallbackModel ? { modelUsed: fallbackModel } :
|
|
3238
|
+
return { providerUsed: "anthropic", ...fallbackModel ? { modelUsed: fallbackModel } : {} };
|
|
2347
3239
|
}
|
|
2348
3240
|
const usage = findAnthropicUsageRecord(payload);
|
|
2349
|
-
const result = {
|
|
3241
|
+
const result = {
|
|
3242
|
+
providerUsed: "anthropic"
|
|
3243
|
+
};
|
|
2350
3244
|
const model = readString(payload.model) ?? readString(payload.message?.model) ?? fallbackModel;
|
|
2351
3245
|
if (model) {
|
|
2352
3246
|
result.modelUsed = model;
|
|
@@ -2356,6 +3250,21 @@ function extractAnthropicUsage(payload, fallbackModel) {
|
|
|
2356
3250
|
}
|
|
2357
3251
|
return Object.keys(result).length ? result : void 0;
|
|
2358
3252
|
}
|
|
3253
|
+
function anthropicRequestExecutionMetadata(params) {
|
|
3254
|
+
const outputConfig = isObjectRecord(params.output_config) ? params.output_config : void 0;
|
|
3255
|
+
const thinking = isObjectRecord(params.thinking) ? params.thinking : void 0;
|
|
3256
|
+
const rawEffort = outputConfig?.effort;
|
|
3257
|
+
const effort = rawEffort === "none" || rawEffort === "minimal" || rawEffort === "low" || rawEffort === "medium" || rawEffort === "high" || rawEffort === "xhigh" || rawEffort === "max" ? rawEffort : void 0;
|
|
3258
|
+
const mode = readString(thinking?.type);
|
|
3259
|
+
const rawBudget = readNumber(thinking?.budget_tokens);
|
|
3260
|
+
const budget = rawBudget !== void 0 && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
|
|
3261
|
+
return {
|
|
3262
|
+
providerUsed: "anthropic",
|
|
3263
|
+
...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
|
|
3264
|
+
...mode ? { reasoningMode: mode } : {},
|
|
3265
|
+
...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
2359
3268
|
function findAnthropicUsageRecord(payload) {
|
|
2360
3269
|
if (isObjectRecord(payload.usage)) {
|
|
2361
3270
|
return payload.usage;
|
|
@@ -2366,10 +3275,6 @@ function findAnthropicUsageRecord(payload) {
|
|
|
2366
3275
|
return void 0;
|
|
2367
3276
|
}
|
|
2368
3277
|
function applyAnthropicUsageRecord(usage, usageRecord) {
|
|
2369
|
-
const inputTokens = readNumber(usageRecord.input_tokens ?? usageRecord.prompt_tokens);
|
|
2370
|
-
if (inputTokens !== void 0) {
|
|
2371
|
-
usage.inputTokens = inputTokens;
|
|
2372
|
-
}
|
|
2373
3278
|
const outputTokens = readNumber(usageRecord.output_tokens ?? usageRecord.completion_tokens);
|
|
2374
3279
|
if (outputTokens !== void 0) {
|
|
2375
3280
|
usage.responseTokens = outputTokens;
|
|
@@ -2380,6 +3285,26 @@ function applyAnthropicUsageRecord(usage, usageRecord) {
|
|
|
2380
3285
|
if (cachedInputTokens !== void 0) {
|
|
2381
3286
|
usage.cachedInputTokens = cachedInputTokens;
|
|
2382
3287
|
}
|
|
3288
|
+
const cacheWriteInputTokens = readNumber(
|
|
3289
|
+
usageRecord.cache_creation_input_tokens ?? usageRecord.prompt_cache_miss_tokens ?? usageRecord.cache_write_tokens
|
|
3290
|
+
);
|
|
3291
|
+
if (cacheWriteInputTokens !== void 0) {
|
|
3292
|
+
usage.cacheWriteInputTokens = cacheWriteInputTokens;
|
|
3293
|
+
}
|
|
3294
|
+
if (isObjectRecord(usageRecord.cache_creation)) {
|
|
3295
|
+
const cacheWrite5m = readNumber(
|
|
3296
|
+
usageRecord.cache_creation.ephemeral_5m_input_tokens
|
|
3297
|
+
);
|
|
3298
|
+
const cacheWrite1h = readNumber(
|
|
3299
|
+
usageRecord.cache_creation.ephemeral_1h_input_tokens
|
|
3300
|
+
);
|
|
3301
|
+
if (cacheWrite5m !== void 0) usage.cacheWrite5mInputTokens = cacheWrite5m;
|
|
3302
|
+
if (cacheWrite1h !== void 0) usage.cacheWrite1hInputTokens = cacheWrite1h;
|
|
3303
|
+
}
|
|
3304
|
+
const baseInputTokens = readNumber(usageRecord.input_tokens ?? usageRecord.prompt_tokens);
|
|
3305
|
+
if (baseInputTokens !== void 0) {
|
|
3306
|
+
usage.inputTokens = baseInputTokens + (cachedInputTokens ?? 0) + (cacheWriteInputTokens ?? 0);
|
|
3307
|
+
}
|
|
2383
3308
|
}
|
|
2384
3309
|
function accumulateAnthropicStreamUsage(chunk, fallbackModel, state) {
|
|
2385
3310
|
const usage = extractAnthropicUsage(chunk, fallbackModel);
|
|
@@ -2603,6 +3528,6 @@ function isIteratorResult(value) {
|
|
|
2603
3528
|
return isObjectRecord(value) && "done" in value;
|
|
2604
3529
|
}
|
|
2605
3530
|
|
|
2606
|
-
export { AnthropicPromptCompressionStats, withCompression, withMetering, wrapAnthropic };
|
|
3531
|
+
export { AnthropicPromptCompressionStats, withCompression, withMetering, withSampling, wrapAnthropic };
|
|
2607
3532
|
//# sourceMappingURL=index.mjs.map
|
|
2608
3533
|
//# sourceMappingURL=index.mjs.map
|