@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.cjs
CHANGED
|
@@ -242,6 +242,11 @@ async function compressMessagesWithUsageTap(options) {
|
|
|
242
242
|
aggressiveness,
|
|
243
243
|
"UsageTap prompt message compression"
|
|
244
244
|
);
|
|
245
|
+
if (options.latencyBudgetMs !== void 0 && (!Number.isFinite(options.latencyBudgetMs) || options.latencyBudgetMs < 0)) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
"UsageTap prompt message compression latencyBudgetMs must be a non-negative number"
|
|
248
|
+
);
|
|
249
|
+
}
|
|
245
250
|
const original = stableStringifyInput(options.input);
|
|
246
251
|
const headers = {
|
|
247
252
|
"content-type": "application/json"
|
|
@@ -256,7 +261,15 @@ async function compressMessagesWithUsageTap(options) {
|
|
|
256
261
|
headers,
|
|
257
262
|
body: JSON.stringify({
|
|
258
263
|
...cloneInputRecord(options.input),
|
|
259
|
-
compression_settings: {
|
|
264
|
+
compression_settings: {
|
|
265
|
+
aggressiveness,
|
|
266
|
+
...options.mode === void 0 ? {} : { mode: options.mode },
|
|
267
|
+
...options.latencyBudgetMs === void 0 ? {} : { latency_budget_ms: options.latencyBudgetMs },
|
|
268
|
+
...options.compactEmptyUserMessages === void 0 ? {} : { compact_empty_user_messages: options.compactEmptyUserMessages },
|
|
269
|
+
...options.compactDuplicateUserTextParts === void 0 ? {} : {
|
|
270
|
+
compact_duplicate_user_text_parts: options.compactDuplicateUserTextParts
|
|
271
|
+
}
|
|
272
|
+
}
|
|
260
273
|
}),
|
|
261
274
|
signal: options.signal
|
|
262
275
|
}
|
|
@@ -752,10 +765,369 @@ function scalarToToon(value) {
|
|
|
752
765
|
return JSON.stringify(text);
|
|
753
766
|
}
|
|
754
767
|
|
|
768
|
+
// src/resources.ts
|
|
769
|
+
var CANONICAL_MEDIA_TYPE = "application/vnd.usagetap.v1+json";
|
|
770
|
+
var DEFAULT_GATEWAY_BASE_URL = "https://gateway.usagetap.com";
|
|
771
|
+
function normalizedBaseUrl(value) {
|
|
772
|
+
return `${value.replace(/\/+$/, "")}/`;
|
|
773
|
+
}
|
|
774
|
+
function errorMessage(payload, status) {
|
|
775
|
+
if (payload && typeof payload === "object") {
|
|
776
|
+
const record = payload;
|
|
777
|
+
const error = record.error;
|
|
778
|
+
if (error && typeof error === "object") {
|
|
779
|
+
const message2 = error.message;
|
|
780
|
+
if (typeof message2 === "string" && message2) return message2;
|
|
781
|
+
}
|
|
782
|
+
const message = record.message;
|
|
783
|
+
if (typeof message === "string" && message) return message;
|
|
784
|
+
}
|
|
785
|
+
return `UsageTap request failed with HTTP ${status}`;
|
|
786
|
+
}
|
|
787
|
+
function errorCode(status) {
|
|
788
|
+
if (status === 401 || status === 403) return "USAGETAP_AUTH_ERROR";
|
|
789
|
+
if (status === 429) return "USAGETAP_RATE_LIMITED";
|
|
790
|
+
if (status >= 500) return "USAGETAP_SERVER_ERROR";
|
|
791
|
+
return "USAGETAP_BAD_REQUEST";
|
|
792
|
+
}
|
|
793
|
+
var ResourceTransport = class {
|
|
794
|
+
baseUrl;
|
|
795
|
+
apiKey;
|
|
796
|
+
fetchImpl;
|
|
797
|
+
defaultHeaders;
|
|
798
|
+
sdkVersion;
|
|
799
|
+
constructor(baseUrl, config) {
|
|
800
|
+
this.baseUrl = normalizedBaseUrl(baseUrl);
|
|
801
|
+
this.apiKey = config.apiKey;
|
|
802
|
+
this.fetchImpl = config.fetchImpl;
|
|
803
|
+
this.defaultHeaders = config.headers ?? {};
|
|
804
|
+
this.sdkVersion = config.sdkVersion;
|
|
805
|
+
}
|
|
806
|
+
async request(request) {
|
|
807
|
+
const body = request.body === void 0 ? void 0 : JSON.stringify(request.body);
|
|
808
|
+
const headers = {
|
|
809
|
+
...this.defaultHeaders,
|
|
810
|
+
accept: request.response === "data" ? CANONICAL_MEDIA_TYPE : request.response === "ndjson" ? "application/x-ndjson" : "application/json",
|
|
811
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
812
|
+
"x-usage-sdk": `js/${this.sdkVersion}`,
|
|
813
|
+
...body ? { "content-type": "application/json" } : {},
|
|
814
|
+
...request.options?.idempotencyKey ? { "idempotency-key": request.options.idempotencyKey } : {},
|
|
815
|
+
...request.options?.headers
|
|
816
|
+
};
|
|
817
|
+
let response;
|
|
818
|
+
try {
|
|
819
|
+
response = await this.fetchImpl(
|
|
820
|
+
new URL(request.path.replace(/^\/+/, ""), this.baseUrl),
|
|
821
|
+
{
|
|
822
|
+
method: request.method,
|
|
823
|
+
headers,
|
|
824
|
+
body,
|
|
825
|
+
signal: request.options?.signal
|
|
826
|
+
}
|
|
827
|
+
);
|
|
828
|
+
} catch (error) {
|
|
829
|
+
throw new UsageTapError(
|
|
830
|
+
"USAGETAP_NETWORK_ERROR",
|
|
831
|
+
"Failed to reach UsageTap",
|
|
832
|
+
{ retryable: true, cause: error }
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
const requestId = response.headers.get("x-request-id") ?? response.headers.get("x-usage-correlation-id") ?? void 0;
|
|
836
|
+
const text = await response.text();
|
|
837
|
+
let payload;
|
|
838
|
+
if (text && request.response !== "ndjson") {
|
|
839
|
+
try {
|
|
840
|
+
payload = JSON.parse(text);
|
|
841
|
+
} catch (error) {
|
|
842
|
+
throw new UsageTapError(
|
|
843
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
844
|
+
"UsageTap returned invalid JSON",
|
|
845
|
+
{ status: response.status, correlationId: requestId, cause: error }
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
if (!response.ok) {
|
|
850
|
+
throw new UsageTapError(
|
|
851
|
+
errorCode(response.status),
|
|
852
|
+
errorMessage(payload, response.status),
|
|
853
|
+
{
|
|
854
|
+
status: response.status,
|
|
855
|
+
retryable: response.status === 429 || response.status >= 500,
|
|
856
|
+
correlationId: requestId,
|
|
857
|
+
details: payload && typeof payload === "object" ? payload : void 0
|
|
858
|
+
}
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
if (request.response === "ndjson") {
|
|
862
|
+
if (!text.trim()) return [];
|
|
863
|
+
try {
|
|
864
|
+
return text.trim().split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line));
|
|
865
|
+
} catch (error) {
|
|
866
|
+
throw new UsageTapError(
|
|
867
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
868
|
+
"UsageTap returned invalid NDJSON",
|
|
869
|
+
{ status: response.status, correlationId: requestId, cause: error }
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (request.response === "data") {
|
|
874
|
+
if (!payload || typeof payload !== "object" || !("data" in payload)) {
|
|
875
|
+
throw new UsageTapError(
|
|
876
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
877
|
+
"UsageTap response missing data",
|
|
878
|
+
{ status: response.status, correlationId: requestId }
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
return payload.data;
|
|
882
|
+
}
|
|
883
|
+
if (payload === void 0) {
|
|
884
|
+
throw new UsageTapError(
|
|
885
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
886
|
+
"UsageTap response was empty",
|
|
887
|
+
{ status: response.status, correlationId: requestId }
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
return payload;
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
function resourceId(value, keys, label) {
|
|
894
|
+
const id = typeof value === "string" ? value : keys.map((key) => value[key]).find((candidate) => Boolean(candidate?.trim()));
|
|
895
|
+
if (!id?.trim()) {
|
|
896
|
+
throw new UsageTapError(
|
|
897
|
+
"USAGETAP_BAD_REQUEST",
|
|
898
|
+
`${label} requires a non-empty ID`
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
return id.trim();
|
|
902
|
+
}
|
|
903
|
+
function terminalSummary(status) {
|
|
904
|
+
return status === "COMPLETE" || status === "FAILED";
|
|
905
|
+
}
|
|
906
|
+
function terminalGatewayBatch(status) {
|
|
907
|
+
return ["completed", "failed", "expired", "cancelled"].includes(status);
|
|
908
|
+
}
|
|
909
|
+
function validateWaitOptions(options) {
|
|
910
|
+
const pollIntervalMs = options.pollIntervalMs ?? 1500;
|
|
911
|
+
const timeoutMs = options.timeoutMs ?? 3 * 6e4;
|
|
912
|
+
if (!Number.isFinite(pollIntervalMs) || pollIntervalMs < 0) {
|
|
913
|
+
throw new UsageTapError(
|
|
914
|
+
"USAGETAP_BAD_REQUEST",
|
|
915
|
+
"pollIntervalMs must be a non-negative number"
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs < 1) {
|
|
919
|
+
throw new UsageTapError(
|
|
920
|
+
"USAGETAP_BAD_REQUEST",
|
|
921
|
+
"timeoutMs must be a positive number"
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
return { pollIntervalMs, timeoutMs };
|
|
925
|
+
}
|
|
926
|
+
var SummarizationResource = class {
|
|
927
|
+
summaries;
|
|
928
|
+
batches;
|
|
929
|
+
profiles;
|
|
930
|
+
measurements;
|
|
931
|
+
transport;
|
|
932
|
+
constructor(config) {
|
|
933
|
+
this.transport = new ResourceTransport(config.apiBaseUrl, config);
|
|
934
|
+
this.summaries = {
|
|
935
|
+
create: (params, options) => this.transport.request({
|
|
936
|
+
method: "POST",
|
|
937
|
+
path: "/v1/compression/summaries",
|
|
938
|
+
body: params,
|
|
939
|
+
options,
|
|
940
|
+
response: "data"
|
|
941
|
+
}),
|
|
942
|
+
retrieve: (jobId, options) => this.transport.request({
|
|
943
|
+
method: "GET",
|
|
944
|
+
path: `/v1/compression/jobs/${encodeURIComponent(
|
|
945
|
+
resourceId(jobId, ["jobId"], "summaries.retrieve")
|
|
946
|
+
)}`,
|
|
947
|
+
options,
|
|
948
|
+
response: "data"
|
|
949
|
+
}),
|
|
950
|
+
wait: (job, options) => this.waitForSummary(job, options)
|
|
951
|
+
};
|
|
952
|
+
this.batches = {
|
|
953
|
+
create: (params, options) => this.transport.request({
|
|
954
|
+
method: "POST",
|
|
955
|
+
path: "/v1/compression/batches",
|
|
956
|
+
body: params,
|
|
957
|
+
options,
|
|
958
|
+
response: "data"
|
|
959
|
+
}),
|
|
960
|
+
retrieve: (batchId, options) => this.transport.request({
|
|
961
|
+
method: "GET",
|
|
962
|
+
path: `/v1/compression/batches/${encodeURIComponent(
|
|
963
|
+
resourceId(batchId, ["batchId"], "summarization.batches.retrieve")
|
|
964
|
+
)}`,
|
|
965
|
+
options,
|
|
966
|
+
response: "data"
|
|
967
|
+
}),
|
|
968
|
+
wait: (batch, options) => this.waitForBatch(batch, options)
|
|
969
|
+
};
|
|
970
|
+
this.profiles = {
|
|
971
|
+
retrieve: (profile, options) => this.transport.request({
|
|
972
|
+
method: "GET",
|
|
973
|
+
path: `/v1/compression/profiles/${encodeURIComponent(
|
|
974
|
+
resourceId(profile, [], "summarization.profiles.retrieve")
|
|
975
|
+
)}`,
|
|
976
|
+
options,
|
|
977
|
+
response: "data"
|
|
978
|
+
})
|
|
979
|
+
};
|
|
980
|
+
this.measurements = {
|
|
981
|
+
create: (params, options) => this.transport.request({
|
|
982
|
+
method: "POST",
|
|
983
|
+
path: "/v1/compression/measurements",
|
|
984
|
+
body: params,
|
|
985
|
+
options,
|
|
986
|
+
response: "data"
|
|
987
|
+
})
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
async waitForSummary(value, options = {}) {
|
|
991
|
+
const { pollIntervalMs, timeoutMs } = validateWaitOptions(options);
|
|
992
|
+
const deadline = Date.now() + timeoutMs;
|
|
993
|
+
let job = typeof value === "string" ? await this.summaries.retrieve(value, options) : value;
|
|
994
|
+
while (!terminalSummary(job.status)) {
|
|
995
|
+
if (Date.now() >= deadline) {
|
|
996
|
+
throw new UsageTapError(
|
|
997
|
+
"USAGETAP_RETRY_EXHAUSTED",
|
|
998
|
+
`Summarization job ${job.jobId} did not finish before timeout`,
|
|
999
|
+
{ retryable: true }
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
await sleep(pollIntervalMs, options.signal);
|
|
1003
|
+
job = await this.summaries.retrieve(job.jobId, options);
|
|
1004
|
+
}
|
|
1005
|
+
return job;
|
|
1006
|
+
}
|
|
1007
|
+
async waitForBatch(value, options = {}) {
|
|
1008
|
+
const { pollIntervalMs, timeoutMs } = validateWaitOptions(options);
|
|
1009
|
+
const deadline = Date.now() + timeoutMs;
|
|
1010
|
+
let batch = typeof value === "string" ? await this.batches.retrieve(value, options) : value;
|
|
1011
|
+
while (!terminalSummary(batch.status)) {
|
|
1012
|
+
if (Date.now() >= deadline) {
|
|
1013
|
+
throw new UsageTapError(
|
|
1014
|
+
"USAGETAP_RETRY_EXHAUSTED",
|
|
1015
|
+
`Summarization batch ${batch.batchId} did not finish before timeout`,
|
|
1016
|
+
{ retryable: true }
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
await sleep(pollIntervalMs, options.signal);
|
|
1020
|
+
batch = await this.batches.retrieve(batch.batchId, options);
|
|
1021
|
+
}
|
|
1022
|
+
return batch;
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
var GatewayResource = class {
|
|
1026
|
+
chat;
|
|
1027
|
+
/** Buffered OpenAI Responses-compatible requests. */
|
|
1028
|
+
responses;
|
|
1029
|
+
models;
|
|
1030
|
+
batches;
|
|
1031
|
+
transport;
|
|
1032
|
+
idempotencyGenerator;
|
|
1033
|
+
constructor(config) {
|
|
1034
|
+
this.transport = new ResourceTransport(
|
|
1035
|
+
config.gatewayBaseUrl ?? DEFAULT_GATEWAY_BASE_URL,
|
|
1036
|
+
config
|
|
1037
|
+
);
|
|
1038
|
+
this.idempotencyGenerator = config.idempotencyGenerator ?? createIdempotencyKey;
|
|
1039
|
+
this.chat = {
|
|
1040
|
+
completions: {
|
|
1041
|
+
create: (params, options) => this.transport.request({
|
|
1042
|
+
method: "POST",
|
|
1043
|
+
path: "/v1/chat/completions",
|
|
1044
|
+
body: params,
|
|
1045
|
+
options,
|
|
1046
|
+
response: "json"
|
|
1047
|
+
})
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
this.responses = {
|
|
1051
|
+
create: (params, options) => this.transport.request({
|
|
1052
|
+
method: "POST",
|
|
1053
|
+
path: "/v1/responses",
|
|
1054
|
+
body: params,
|
|
1055
|
+
options,
|
|
1056
|
+
response: "json"
|
|
1057
|
+
})
|
|
1058
|
+
};
|
|
1059
|
+
this.models = {
|
|
1060
|
+
list: (options) => this.transport.request({
|
|
1061
|
+
method: "GET",
|
|
1062
|
+
path: "/v1/models",
|
|
1063
|
+
options,
|
|
1064
|
+
response: "json"
|
|
1065
|
+
})
|
|
1066
|
+
};
|
|
1067
|
+
this.batches = {
|
|
1068
|
+
create: (params, options = {}) => this.transport.request({
|
|
1069
|
+
method: "POST",
|
|
1070
|
+
path: "/v1/batches",
|
|
1071
|
+
body: params,
|
|
1072
|
+
options: {
|
|
1073
|
+
...options,
|
|
1074
|
+
idempotencyKey: options.idempotencyKey ?? this.idempotencyGenerator()
|
|
1075
|
+
},
|
|
1076
|
+
response: "json"
|
|
1077
|
+
}),
|
|
1078
|
+
retrieve: (batchId, options) => this.transport.request({
|
|
1079
|
+
method: "GET",
|
|
1080
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1081
|
+
resourceId(batchId, ["id"], "gateway.batches.retrieve")
|
|
1082
|
+
)}`,
|
|
1083
|
+
options,
|
|
1084
|
+
response: "json"
|
|
1085
|
+
}),
|
|
1086
|
+
wait: (batch, options) => this.waitForBatch(batch, options),
|
|
1087
|
+
cancel: (batchId, options) => this.transport.request({
|
|
1088
|
+
method: "POST",
|
|
1089
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1090
|
+
resourceId(batchId, ["id"], "gateway.batches.cancel")
|
|
1091
|
+
)}/cancel`,
|
|
1092
|
+
options,
|
|
1093
|
+
response: "json"
|
|
1094
|
+
}),
|
|
1095
|
+
results: (batchId, options) => this.transport.request({
|
|
1096
|
+
method: "GET",
|
|
1097
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1098
|
+
resourceId(batchId, ["id"], "gateway.batches.results")
|
|
1099
|
+
)}/results`,
|
|
1100
|
+
options,
|
|
1101
|
+
response: "ndjson"
|
|
1102
|
+
})
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
async waitForBatch(value, options = {}) {
|
|
1106
|
+
const { pollIntervalMs, timeoutMs } = validateWaitOptions(options);
|
|
1107
|
+
const deadline = Date.now() + timeoutMs;
|
|
1108
|
+
let batch = typeof value === "string" ? await this.batches.retrieve(value, options) : value;
|
|
1109
|
+
while (!terminalGatewayBatch(batch.status)) {
|
|
1110
|
+
if (Date.now() >= deadline) {
|
|
1111
|
+
throw new UsageTapError(
|
|
1112
|
+
"USAGETAP_RETRY_EXHAUSTED",
|
|
1113
|
+
`Gateway batch ${batch.id} did not finish before timeout`,
|
|
1114
|
+
{ retryable: true }
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
await sleep(pollIntervalMs, options.signal);
|
|
1118
|
+
batch = await this.batches.retrieve(batch.id, options);
|
|
1119
|
+
}
|
|
1120
|
+
return batch;
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
|
|
755
1124
|
// src/client.ts
|
|
756
1125
|
var CALL_BEGIN_PATH = "call_begin";
|
|
757
1126
|
var CALL_END_PATH = "call_end";
|
|
758
1127
|
var COMPRESS_PROMPT_PATH = "compress_prompt";
|
|
1128
|
+
var SAMPLES_PATH = "samples";
|
|
1129
|
+
var SAMPLING_SETTINGS_PATH = "sampling/settings";
|
|
1130
|
+
var SAMPLING_DECIDE_PATH = "sampling/decide";
|
|
759
1131
|
var CHECK_USAGE_PATH = "customers/{customerId}/usage";
|
|
760
1132
|
var CREATE_CUSTOMER_PATH = "customers";
|
|
761
1133
|
var CHANGE_PLAN_PATH = "customers/{customerId}/change_plan";
|
|
@@ -766,11 +1138,16 @@ var CORRELATION_HEADER = "x-usage-correlation-id";
|
|
|
766
1138
|
var IDEMPOTENCY_HEADER = "idempotency-key";
|
|
767
1139
|
var SDK_HEADER = "x-usage-sdk";
|
|
768
1140
|
var USER_AGENT = "UsageTapClient";
|
|
769
|
-
var
|
|
1141
|
+
var CANONICAL_MEDIA_TYPE2 = "application/vnd.usagetap.v1+json";
|
|
770
1142
|
var DEFAULT_BASE_URL = "https://api.usagetap.com";
|
|
771
|
-
var
|
|
1143
|
+
var DEFAULT_RUN_INACTIVITY_MS = 60 * 60 * 1e3;
|
|
1144
|
+
var SDK_VERSION = "1.7.0" ;
|
|
772
1145
|
var HAS_WINDOW = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
773
1146
|
var UsageTapClient = class {
|
|
1147
|
+
/** OpenAI-compatible chat, model, and native batch operations. */
|
|
1148
|
+
gateway;
|
|
1149
|
+
/** Published-profile context summarization operations. */
|
|
1150
|
+
summarization;
|
|
774
1151
|
apiKey;
|
|
775
1152
|
baseUrl;
|
|
776
1153
|
fetchImpl;
|
|
@@ -795,6 +1172,11 @@ var UsageTapClient = class {
|
|
|
795
1172
|
usageTapCompressionMessagesEndpoint;
|
|
796
1173
|
usageTapCompressionModel;
|
|
797
1174
|
usageTapCompressionAggressiveness;
|
|
1175
|
+
sampling;
|
|
1176
|
+
samplingSettingsCacheMs;
|
|
1177
|
+
circuitBreaker;
|
|
1178
|
+
circuitBreakerRuns = /* @__PURE__ */ new Map();
|
|
1179
|
+
samplingSettingsCache;
|
|
798
1180
|
constructor(options = {}) {
|
|
799
1181
|
const apiKey = options.apiKey?.trim() || readEnvironmentVariable("USAGETAP_API_KEY");
|
|
800
1182
|
const baseUrl = options.baseUrl?.trim() || readEnvironmentVariable("USAGETAP_BASE_URL") || DEFAULT_BASE_URL;
|
|
@@ -817,10 +1199,21 @@ var UsageTapClient = class {
|
|
|
817
1199
|
"A global fetch implementation was not found. Pass fetchImpl in UsageTapClientOptions."
|
|
818
1200
|
);
|
|
819
1201
|
}
|
|
820
|
-
const
|
|
821
|
-
this.baseUrl = new URL(
|
|
1202
|
+
const normalizedBaseUrl2 = normalizeBaseUrl(baseUrl);
|
|
1203
|
+
this.baseUrl = new URL(normalizedBaseUrl2);
|
|
822
1204
|
this.apiKey = apiKey;
|
|
823
1205
|
this.fetchImpl = wrapFetchImplementation(fetchCandidate, !options.fetchImpl);
|
|
1206
|
+
const resourceConfig = {
|
|
1207
|
+
apiKey,
|
|
1208
|
+
apiBaseUrl: normalizedBaseUrl2,
|
|
1209
|
+
gatewayBaseUrl: options.gatewayBaseUrl?.trim() || readEnvironmentVariable("USAGETAP_GATEWAY_URL"),
|
|
1210
|
+
fetchImpl: this.fetchImpl,
|
|
1211
|
+
headers: options.headers,
|
|
1212
|
+
sdkVersion: SDK_VERSION,
|
|
1213
|
+
idempotencyGenerator: options.idempotencyGenerator
|
|
1214
|
+
};
|
|
1215
|
+
this.gateway = new GatewayResource(resourceConfig);
|
|
1216
|
+
this.summarization = new SummarizationResource(resourceConfig);
|
|
824
1217
|
this.defaultFeature = options.defaultFeature;
|
|
825
1218
|
this.defaultTags = options.defaultTags?.length ? dedupeStrings(options.defaultTags) : void 0;
|
|
826
1219
|
this.defaultHeaders = options.headers ? normalizeHeaderDictionary(options.headers) : {};
|
|
@@ -842,11 +1235,122 @@ var UsageTapClient = class {
|
|
|
842
1235
|
this.usageTapCompressionMessagesEndpoint = options.usageTapCompressionMessagesEndpoint;
|
|
843
1236
|
this.usageTapCompressionModel = options.usageTapCompressionModel;
|
|
844
1237
|
this.usageTapCompressionAggressiveness = options.usageTapCompressionAggressiveness;
|
|
1238
|
+
this.sampling = options.sampling;
|
|
1239
|
+
this.samplingSettingsCacheMs = Number.isFinite(options.samplingSettingsCacheMs) ? Math.max(0, Number(options.samplingSettingsCacheMs)) : 5 * 60 * 1e3;
|
|
1240
|
+
if (options.circuitBreaker) {
|
|
1241
|
+
const maxCallsPerRun = options.circuitBreaker.maxCallsPerRun;
|
|
1242
|
+
if (!Number.isInteger(maxCallsPerRun) || maxCallsPerRun < 1) {
|
|
1243
|
+
throw new UsageTapError(
|
|
1244
|
+
"USAGETAP_BAD_REQUEST",
|
|
1245
|
+
"circuitBreaker.maxCallsPerRun must be a positive integer"
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
const runInactivityMs = options.circuitBreaker.runInactivityMs ?? DEFAULT_RUN_INACTIVITY_MS;
|
|
1249
|
+
if (!Number.isFinite(runInactivityMs) || runInactivityMs < 1) {
|
|
1250
|
+
throw new UsageTapError(
|
|
1251
|
+
"USAGETAP_BAD_REQUEST",
|
|
1252
|
+
"circuitBreaker.runInactivityMs must be a positive number"
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
this.circuitBreaker = {
|
|
1256
|
+
maxCallsPerRun,
|
|
1257
|
+
runInactivityMs
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
shouldSample(request, policy = this.sampling || void 0) {
|
|
1262
|
+
if (!policy) return false;
|
|
1263
|
+
const rate = Math.min(1, Math.max(0, Number(policy.rate) || 0));
|
|
1264
|
+
if (rate <= 0) return false;
|
|
1265
|
+
const customerId = request.customerId?.trim();
|
|
1266
|
+
if (customerId && policy.customers?.exclude?.includes(customerId)) return false;
|
|
1267
|
+
const feature = request.feature?.trim();
|
|
1268
|
+
if (feature && policy.features?.exclude?.includes(feature)) return false;
|
|
1269
|
+
const included = policy.features?.include?.filter(Boolean) ?? [];
|
|
1270
|
+
if (included.length > 0 && (!feature || !included.includes(feature))) return false;
|
|
1271
|
+
const minimum = Math.max(0, Math.round(policy.minInputTokens ?? 0));
|
|
1272
|
+
if (minimum > 0 && estimatePromptTokens(request.input) < minimum) return false;
|
|
1273
|
+
return (policy.random ?? Math.random)() < rate;
|
|
1274
|
+
}
|
|
1275
|
+
async getSamplingSettings(options = {}) {
|
|
1276
|
+
const now = Date.now();
|
|
1277
|
+
if (!options.forceRefresh && this.samplingSettingsCache && this.samplingSettingsCache.expiresAtMs > now) {
|
|
1278
|
+
return {
|
|
1279
|
+
result: { status: "ACCEPTED", code: "SAMPLING_SETTINGS_CACHED" },
|
|
1280
|
+
data: this.samplingSettingsCache.settings,
|
|
1281
|
+
correlationId: options.correlationId ?? "local-cache"
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
const response = await this.requestGet(
|
|
1285
|
+
SAMPLING_SETTINGS_PATH,
|
|
1286
|
+
{
|
|
1287
|
+
signal: options.signal,
|
|
1288
|
+
headers: options.headers,
|
|
1289
|
+
retries: options.retries,
|
|
1290
|
+
correlationId: options.correlationId
|
|
1291
|
+
}
|
|
1292
|
+
);
|
|
1293
|
+
const serverCacheMs = Math.max(0, Number(response.data.cacheSeconds) || 0) * 1e3;
|
|
1294
|
+
const cacheMs = Math.min(this.samplingSettingsCacheMs, serverCacheMs);
|
|
1295
|
+
this.samplingSettingsCache = {
|
|
1296
|
+
settings: response.data,
|
|
1297
|
+
expiresAtMs: now + cacheMs
|
|
1298
|
+
};
|
|
1299
|
+
return response;
|
|
1300
|
+
}
|
|
1301
|
+
async shouldSampleAsync(request, policy) {
|
|
1302
|
+
if (policy) return this.shouldSample(request, policy);
|
|
1303
|
+
if (this.sampling === false) return false;
|
|
1304
|
+
if (this.sampling) return this.shouldSample(request, this.sampling);
|
|
1305
|
+
try {
|
|
1306
|
+
const settings = await this.getSamplingSettings();
|
|
1307
|
+
return this.shouldSample(request, settings.data);
|
|
1308
|
+
} catch {
|
|
1309
|
+
return false;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
async decideSample(request, options = {}) {
|
|
1313
|
+
const hasTokens = Number.isFinite(request.inputTokens) && Number(request.inputTokens) >= 0;
|
|
1314
|
+
const hasCharacters = Number.isFinite(request.inputCharacters) && Number(request.inputCharacters) >= 0;
|
|
1315
|
+
if (!hasTokens && !hasCharacters) {
|
|
1316
|
+
throw new UsageTapError(
|
|
1317
|
+
"USAGETAP_BAD_REQUEST",
|
|
1318
|
+
"decideSample requires inputTokens or inputCharacters"
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
return this.request(
|
|
1322
|
+
SAMPLING_DECIDE_PATH,
|
|
1323
|
+
request,
|
|
1324
|
+
options
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1327
|
+
async captureSample(request, options = {}) {
|
|
1328
|
+
if (!request || request.input === void 0) {
|
|
1329
|
+
throw new UsageTapError(
|
|
1330
|
+
"USAGETAP_BAD_REQUEST",
|
|
1331
|
+
"captureSample requires input"
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
if (!request.provider?.trim()) {
|
|
1335
|
+
throw new UsageTapError(
|
|
1336
|
+
"USAGETAP_BAD_REQUEST",
|
|
1337
|
+
"captureSample requires provider"
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
const sampleId = request.sampleId?.trim() || this.idempotencyGenerator();
|
|
1341
|
+
return this.request(
|
|
1342
|
+
SAMPLES_PATH,
|
|
1343
|
+
{ ...request, sampleId },
|
|
1344
|
+
{ ...options, idempotencyKey: sampleId }
|
|
1345
|
+
);
|
|
845
1346
|
}
|
|
846
1347
|
async beginCall(request, options = {}) {
|
|
847
1348
|
const idempotencyKey = request.idempotencyKey ?? request.idempotency ?? (this.autoIdempotency ? this.idempotencyGenerator() : void 0);
|
|
1349
|
+
this.reserveRunCall(request, idempotencyKey);
|
|
1350
|
+
const apiRequest = { ...request };
|
|
1351
|
+
delete apiRequest.runId;
|
|
848
1352
|
const payload = {
|
|
849
|
-
...
|
|
1353
|
+
...apiRequest,
|
|
850
1354
|
feature: request.feature ?? this.defaultFeature,
|
|
851
1355
|
tags: this.mergeTags(request.tags)
|
|
852
1356
|
};
|
|
@@ -864,6 +1368,28 @@ var UsageTapClient = class {
|
|
|
864
1368
|
);
|
|
865
1369
|
return response;
|
|
866
1370
|
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Inspect a configured run circuit breaker without consuming another call.
|
|
1373
|
+
*/
|
|
1374
|
+
canRunContinue(request) {
|
|
1375
|
+
const identity = this.resolveRunIdentity(request);
|
|
1376
|
+
if (!identity || !this.circuitBreaker) {
|
|
1377
|
+
throw new UsageTapError(
|
|
1378
|
+
"USAGETAP_BAD_REQUEST",
|
|
1379
|
+
"canRunContinue requires circuitBreaker configuration and a non-empty runId"
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
this.expireInactiveRuns();
|
|
1383
|
+
const calls = this.circuitBreakerRuns.get(identity.key)?.calls ?? 0;
|
|
1384
|
+
return this.createCircuitBreakerDecision(identity.customerId, identity.runId, calls);
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Release local state after a workflow finishes. Returns true when state existed.
|
|
1388
|
+
*/
|
|
1389
|
+
resetRun(request) {
|
|
1390
|
+
const identity = this.resolveRunIdentity(request);
|
|
1391
|
+
return identity ? this.circuitBreakerRuns.delete(identity.key) : false;
|
|
1392
|
+
}
|
|
867
1393
|
async promptCompress(request, options = {}) {
|
|
868
1394
|
if (!request?.callId) {
|
|
869
1395
|
throw new UsageTapError(
|
|
@@ -961,6 +1487,10 @@ var UsageTapClient = class {
|
|
|
961
1487
|
usageTapCompressionMessagesEndpoint: this.usageTapCompressionMessagesEndpoint,
|
|
962
1488
|
aggressiveness: options.aggressiveness ?? this.aggressiveness,
|
|
963
1489
|
usageTapCompressionAggressiveness: options.usageTapCompressionAggressiveness ?? this.usageTapCompressionAggressiveness,
|
|
1490
|
+
mode: options.mode,
|
|
1491
|
+
latencyBudgetMs: options.latencyBudgetMs,
|
|
1492
|
+
compactEmptyUserMessages: options.compactEmptyUserMessages,
|
|
1493
|
+
compactDuplicateUserTextParts: options.compactDuplicateUserTextParts,
|
|
964
1494
|
fetchImpl: this.fetchImpl,
|
|
965
1495
|
signal: options.signal,
|
|
966
1496
|
failOpen: options.failOpen
|
|
@@ -1002,14 +1532,29 @@ var UsageTapClient = class {
|
|
|
1002
1532
|
callId: request.callId,
|
|
1003
1533
|
feature: feature ?? this.defaultFeature,
|
|
1004
1534
|
tags: tags ?? this.defaultTags,
|
|
1535
|
+
providerUsed: request.providerUsed,
|
|
1005
1536
|
modelUsed: request.modelUsed,
|
|
1537
|
+
reasoningEffort: request.reasoningEffort,
|
|
1538
|
+
reasoningEffortSource: request.reasoningEffortSource,
|
|
1539
|
+
reasoningMode: request.reasoningMode,
|
|
1540
|
+
reasoningBudgetTokens: request.reasoningBudgetTokens,
|
|
1006
1541
|
metrics: {
|
|
1007
1542
|
inputTokens: request.inputTokens,
|
|
1008
1543
|
responseTokens: request.responseTokens,
|
|
1009
1544
|
cachedInputTokens: request.cachedInputTokens,
|
|
1545
|
+
cacheWriteInputTokens: request.cacheWriteInputTokens,
|
|
1546
|
+
cacheWrite5mInputTokens: request.cacheWrite5mInputTokens,
|
|
1547
|
+
cacheWrite1hInputTokens: request.cacheWrite1hInputTokens,
|
|
1010
1548
|
reasoningTokens: request.reasoningTokens,
|
|
1011
1549
|
searches: request.searches,
|
|
1012
1550
|
audioSeconds: request.audioSeconds,
|
|
1551
|
+
imageInputCount: request.imageInputCount,
|
|
1552
|
+
imageInputTokens: request.imageInputTokens,
|
|
1553
|
+
imageOutputCount: request.imageOutputCount,
|
|
1554
|
+
imageOutputTokens: request.imageOutputTokens,
|
|
1555
|
+
audioInputTokens: request.audioInputTokens,
|
|
1556
|
+
cachedAudioInputTokens: request.cachedAudioInputTokens,
|
|
1557
|
+
audioOutputTokens: request.audioOutputTokens,
|
|
1013
1558
|
costUsd: response.data.costUSD
|
|
1014
1559
|
},
|
|
1015
1560
|
correlationId: response.correlationId
|
|
@@ -1096,10 +1641,10 @@ var UsageTapClient = class {
|
|
|
1096
1641
|
"incrementCustomMeter requires meterSlot"
|
|
1097
1642
|
);
|
|
1098
1643
|
}
|
|
1099
|
-
if (!["CUSTOM1", "CUSTOM2"].includes(request.meterSlot)) {
|
|
1644
|
+
if (!["CUSTOM1", "CUSTOM2", "AGENTIC_API"].includes(request.meterSlot)) {
|
|
1100
1645
|
throw new UsageTapError(
|
|
1101
1646
|
"USAGETAP_BAD_REQUEST",
|
|
1102
|
-
"meterSlot must be CUSTOM1 or
|
|
1647
|
+
"meterSlot must be CUSTOM1, CUSTOM2 or AGENTIC_API"
|
|
1103
1648
|
);
|
|
1104
1649
|
}
|
|
1105
1650
|
if (typeof request.amount !== "number" || !Number.isFinite(request.amount) || request.amount <= 0) {
|
|
@@ -1114,6 +1659,15 @@ var UsageTapClient = class {
|
|
|
1114
1659
|
meterSlot: request.meterSlot,
|
|
1115
1660
|
amount: request.amount
|
|
1116
1661
|
};
|
|
1662
|
+
if (request.customerUserId) {
|
|
1663
|
+
payload.customerUserId = request.customerUserId;
|
|
1664
|
+
}
|
|
1665
|
+
if (request.customerUserName) {
|
|
1666
|
+
payload.customerUserName = request.customerUserName;
|
|
1667
|
+
}
|
|
1668
|
+
if (request.customerUserEmail) {
|
|
1669
|
+
payload.customerUserEmail = request.customerUserEmail;
|
|
1670
|
+
}
|
|
1117
1671
|
if (request.feature) {
|
|
1118
1672
|
payload.feature = request.feature;
|
|
1119
1673
|
}
|
|
@@ -1150,6 +1704,11 @@ var UsageTapClient = class {
|
|
|
1150
1704
|
const beginPayload = idempotencyKey ? { ...beginRequest, idempotencyKey, idempotency: idempotencyKey } : { ...beginRequest };
|
|
1151
1705
|
const beginResponse = await this.beginCall(beginPayload, options);
|
|
1152
1706
|
let usage = {};
|
|
1707
|
+
const pricingMode = beginResponse.data.pricingMode ?? beginRequest.pricingMode ?? (beginRequest.batch === true ? "batch" : beginRequest.batch === false ? "standard" : void 0);
|
|
1708
|
+
if (pricingMode) {
|
|
1709
|
+
usage.pricingMode = pricingMode;
|
|
1710
|
+
usage.batch = pricingMode === "batch";
|
|
1711
|
+
}
|
|
1153
1712
|
const initialStripeCustomerId = typeof beginResponse.data.stripeCustomerId === "string" ? beginResponse.data.stripeCustomerId : typeof beginRequest.stripeCustomerId === "string" ? beginRequest.stripeCustomerId : void 0;
|
|
1154
1713
|
if (initialStripeCustomerId) {
|
|
1155
1714
|
usage = { ...usage, stripeCustomerId: initialStripeCustomerId };
|
|
@@ -1158,6 +1717,28 @@ var UsageTapClient = class {
|
|
|
1158
1717
|
let handlerResult;
|
|
1159
1718
|
let handlerError;
|
|
1160
1719
|
let endCallError;
|
|
1720
|
+
let finalizationDeferred = false;
|
|
1721
|
+
let finalizationPromise;
|
|
1722
|
+
const finalize = () => {
|
|
1723
|
+
if (!finalizationPromise) {
|
|
1724
|
+
finalizationPromise = this.endCall(
|
|
1725
|
+
{
|
|
1726
|
+
callId: beginResponse.data.callId,
|
|
1727
|
+
// Pass context for metric tracking
|
|
1728
|
+
customerId: beginRequest.customerId,
|
|
1729
|
+
feature: beginRequest.feature ?? this.defaultFeature,
|
|
1730
|
+
tags: beginRequest.tags ?? this.defaultTags,
|
|
1731
|
+
...usage,
|
|
1732
|
+
error: errorPayload
|
|
1733
|
+
},
|
|
1734
|
+
{
|
|
1735
|
+
...options,
|
|
1736
|
+
correlationId: beginResponse.correlationId
|
|
1737
|
+
}
|
|
1738
|
+
).then(() => void 0);
|
|
1739
|
+
}
|
|
1740
|
+
return finalizationPromise;
|
|
1741
|
+
};
|
|
1161
1742
|
const context = {
|
|
1162
1743
|
begin: beginResponse,
|
|
1163
1744
|
setUsage: (u) => {
|
|
@@ -1165,6 +1746,10 @@ var UsageTapClient = class {
|
|
|
1165
1746
|
},
|
|
1166
1747
|
setError: (err) => {
|
|
1167
1748
|
errorPayload = err;
|
|
1749
|
+
},
|
|
1750
|
+
deferFinalization: () => {
|
|
1751
|
+
finalizationDeferred = true;
|
|
1752
|
+
return finalize;
|
|
1168
1753
|
}
|
|
1169
1754
|
};
|
|
1170
1755
|
try {
|
|
@@ -1178,24 +1763,12 @@ var UsageTapClient = class {
|
|
|
1178
1763
|
};
|
|
1179
1764
|
}
|
|
1180
1765
|
} finally {
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
feature: beginRequest.feature ?? this.defaultFeature,
|
|
1188
|
-
tags: beginRequest.tags ?? this.defaultTags,
|
|
1189
|
-
...usage,
|
|
1190
|
-
error: errorPayload
|
|
1191
|
-
},
|
|
1192
|
-
{
|
|
1193
|
-
...options,
|
|
1194
|
-
correlationId: beginResponse.correlationId
|
|
1195
|
-
}
|
|
1196
|
-
);
|
|
1197
|
-
} catch (error) {
|
|
1198
|
-
endCallError = error;
|
|
1766
|
+
if (handlerError || !finalizationDeferred) {
|
|
1767
|
+
try {
|
|
1768
|
+
await finalize();
|
|
1769
|
+
} catch (error) {
|
|
1770
|
+
endCallError = error;
|
|
1771
|
+
}
|
|
1199
1772
|
}
|
|
1200
1773
|
}
|
|
1201
1774
|
if (handlerError) {
|
|
@@ -1217,17 +1790,86 @@ var UsageTapClient = class {
|
|
|
1217
1790
|
toPromptCompressionTelemetry(result) {
|
|
1218
1791
|
return {
|
|
1219
1792
|
provider: result.provider,
|
|
1220
|
-
originalCharacters: result.originalCharacters,
|
|
1221
|
-
compressedCharacters: result.compressedCharacters,
|
|
1222
|
-
savedCharacters: result.savedCharacters,
|
|
1223
1793
|
originalTokens: result.originalTokens,
|
|
1224
1794
|
compressedTokens: result.compressedTokens,
|
|
1225
1795
|
savedTokens: result.savedTokens,
|
|
1226
1796
|
tokenSavingsRatio: result.tokenSavingsRatio,
|
|
1227
|
-
savingsRatio: result.savingsRatio,
|
|
1228
1797
|
techniques: result.techniques
|
|
1229
1798
|
};
|
|
1230
1799
|
}
|
|
1800
|
+
reserveRunCall(request, idempotencyKey) {
|
|
1801
|
+
const identity = this.resolveRunIdentity(request);
|
|
1802
|
+
if (!identity || !this.circuitBreaker) return;
|
|
1803
|
+
this.expireInactiveRuns();
|
|
1804
|
+
const now = Date.now();
|
|
1805
|
+
const state = this.circuitBreakerRuns.get(identity.key) ?? {
|
|
1806
|
+
calls: 0,
|
|
1807
|
+
reservationKeys: /* @__PURE__ */ new Set(),
|
|
1808
|
+
lastSeenAtMs: now
|
|
1809
|
+
};
|
|
1810
|
+
const reservationKey = idempotencyKey ?? this.idempotencyGenerator();
|
|
1811
|
+
state.lastSeenAtMs = now;
|
|
1812
|
+
if (state.reservationKeys.has(reservationKey)) {
|
|
1813
|
+
this.circuitBreakerRuns.set(identity.key, state);
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
const decision = this.createCircuitBreakerDecision(
|
|
1817
|
+
identity.customerId,
|
|
1818
|
+
identity.runId,
|
|
1819
|
+
state.calls
|
|
1820
|
+
);
|
|
1821
|
+
if (!decision.allowed) {
|
|
1822
|
+
throw new UsageTapError(
|
|
1823
|
+
"USAGETAP_CIRCUIT_OPEN",
|
|
1824
|
+
`Run ${identity.runId} reached its ${decision.limit}-call circuit-breaker limit`,
|
|
1825
|
+
{
|
|
1826
|
+
details: {
|
|
1827
|
+
reason: decision.reason,
|
|
1828
|
+
customerId: identity.customerId,
|
|
1829
|
+
runId: identity.runId,
|
|
1830
|
+
calls: decision.calls,
|
|
1831
|
+
limit: decision.limit,
|
|
1832
|
+
remaining: decision.remaining
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
state.calls += 1;
|
|
1838
|
+
state.reservationKeys.add(reservationKey);
|
|
1839
|
+
this.circuitBreakerRuns.set(identity.key, state);
|
|
1840
|
+
}
|
|
1841
|
+
resolveRunIdentity(request) {
|
|
1842
|
+
const customerId = request.customerId?.trim();
|
|
1843
|
+
const runId = request.runId?.trim();
|
|
1844
|
+
if (!customerId || !runId) return void 0;
|
|
1845
|
+
return {
|
|
1846
|
+
key: `${customerId}\0${runId}`,
|
|
1847
|
+
customerId,
|
|
1848
|
+
runId
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
createCircuitBreakerDecision(customerId, runId, calls) {
|
|
1852
|
+
const limit = this.circuitBreaker?.maxCallsPerRun ?? 0;
|
|
1853
|
+
const allowed = calls < limit;
|
|
1854
|
+
return {
|
|
1855
|
+
allowed,
|
|
1856
|
+
...allowed ? {} : { reason: "max_calls_per_run" },
|
|
1857
|
+
customerId,
|
|
1858
|
+
runId,
|
|
1859
|
+
calls,
|
|
1860
|
+
limit,
|
|
1861
|
+
remaining: Math.max(0, limit - calls)
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
expireInactiveRuns() {
|
|
1865
|
+
if (!this.circuitBreaker || this.circuitBreakerRuns.size === 0) return;
|
|
1866
|
+
const expiredBefore = Date.now() - this.circuitBreaker.runInactivityMs;
|
|
1867
|
+
for (const [key, state] of this.circuitBreakerRuns) {
|
|
1868
|
+
if (state.lastSeenAtMs < expiredBefore) {
|
|
1869
|
+
this.circuitBreakerRuns.delete(key);
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1231
1873
|
async request(path, payload, options) {
|
|
1232
1874
|
const url = new URL(path, this.baseUrl).toString();
|
|
1233
1875
|
const body = payload !== void 0 ? JSON.stringify(payload) : void 0;
|
|
@@ -1415,7 +2057,7 @@ var UsageTapClient = class {
|
|
|
1415
2057
|
...this.defaultHeaders,
|
|
1416
2058
|
[SDK_HEADER]: `js/${SDK_VERSION}`,
|
|
1417
2059
|
"content-type": "application/json",
|
|
1418
|
-
accept:
|
|
2060
|
+
accept: CANONICAL_MEDIA_TYPE2
|
|
1419
2061
|
};
|
|
1420
2062
|
if (!HAS_WINDOW) {
|
|
1421
2063
|
headers["user-agent"] = `${USER_AGENT}/${SDK_VERSION}`;
|
|
@@ -1468,7 +2110,8 @@ var UsageTapClient = class {
|
|
|
1468
2110
|
}
|
|
1469
2111
|
toHttpError(status, payload, correlationId) {
|
|
1470
2112
|
const code = mapStatusToErrorCode(status);
|
|
1471
|
-
const
|
|
2113
|
+
const apiCode = payload?.error?.code ?? payload?.result?.code ?? "UNKNOWN";
|
|
2114
|
+
const retryable = isRetryableStatus(status) || isRetryableApiCode(apiCode);
|
|
1472
2115
|
const message = payload?.error?.message ?? payload?.result?.message ?? `UsageTap responded with HTTP ${status}`;
|
|
1473
2116
|
return new UsageTapError(code, message, {
|
|
1474
2117
|
status,
|
|
@@ -1501,7 +2144,7 @@ function isRetryableStatus(status) {
|
|
|
1501
2144
|
}
|
|
1502
2145
|
function isRetryableApiCode(code) {
|
|
1503
2146
|
const normalized = code.toUpperCase();
|
|
1504
|
-
return normalized.includes("TRANSIENT") || normalized.includes("RETRY") || normalized.includes("TIMEOUT") || normalized.includes("THROTTLE") || normalized.includes("RATE_LIMIT");
|
|
2147
|
+
return normalized === "PAYG_CONFLICT" || normalized.includes("TRANSIENT") || normalized.includes("RETRY") || normalized.includes("TIMEOUT") || normalized.includes("THROTTLE") || normalized.includes("RATE_LIMIT");
|
|
1505
2148
|
}
|
|
1506
2149
|
function mapApiCodeToError(code) {
|
|
1507
2150
|
const normalized = code.toUpperCase();
|
|
@@ -1615,7 +2258,123 @@ var AnthropicPromptCompressionStats = class {
|
|
|
1615
2258
|
return this.totalOriginalCharacters > 0 ? this.totalCharactersSaved / this.totalOriginalCharacters : 0;
|
|
1616
2259
|
}
|
|
1617
2260
|
};
|
|
2261
|
+
var DeferredAnthropicApiPromise = class extends Promise {
|
|
2262
|
+
responseEnvelope;
|
|
2263
|
+
constructor(execute) {
|
|
2264
|
+
super((resolve) => resolve(void 0));
|
|
2265
|
+
this.responseEnvelope = Promise.resolve().then(execute);
|
|
2266
|
+
}
|
|
2267
|
+
then(onfulfilled, onrejected) {
|
|
2268
|
+
return this.responseEnvelope.then((result) => result.data).then(onfulfilled, onrejected);
|
|
2269
|
+
}
|
|
2270
|
+
catch(onrejected) {
|
|
2271
|
+
return this.then(void 0, onrejected);
|
|
2272
|
+
}
|
|
2273
|
+
finally(onfinally) {
|
|
2274
|
+
return this.responseEnvelope.then((result) => result.data).finally(onfinally ?? void 0);
|
|
2275
|
+
}
|
|
2276
|
+
withResponse() {
|
|
2277
|
+
return this.responseEnvelope;
|
|
2278
|
+
}
|
|
2279
|
+
async asResponse() {
|
|
2280
|
+
const result = await this.responseEnvelope;
|
|
2281
|
+
if (!("response" in result)) {
|
|
2282
|
+
throw new TypeError("The wrapped Anthropic result does not expose a raw response");
|
|
2283
|
+
}
|
|
2284
|
+
return result.response;
|
|
2285
|
+
}
|
|
2286
|
+
};
|
|
2287
|
+
async function resolveAnthropicResponseEnvelope(value) {
|
|
2288
|
+
if (isObjectRecord(value) && typeof value.withResponse === "function") {
|
|
2289
|
+
const withResponse = value.withResponse;
|
|
2290
|
+
const result = await withResponse.call(value);
|
|
2291
|
+
if (!isObjectRecord(result) || !("data" in result)) {
|
|
2292
|
+
throw new TypeError("Anthropic create().withResponse() returned an invalid result");
|
|
2293
|
+
}
|
|
2294
|
+
return result;
|
|
2295
|
+
}
|
|
2296
|
+
return { data: await value };
|
|
2297
|
+
}
|
|
1618
2298
|
var USAGETAP_CORRELATION_HEADER = "x-usage-correlation-id";
|
|
2299
|
+
function withSampling(client, options = {}) {
|
|
2300
|
+
if (!client?.messages || typeof client.messages.create !== "function" || !options) {
|
|
2301
|
+
throw new UsageTapError(
|
|
2302
|
+
"USAGETAP_BAD_REQUEST",
|
|
2303
|
+
"withSampling requires an Anthropic client and sampling options"
|
|
2304
|
+
);
|
|
2305
|
+
}
|
|
2306
|
+
const { apiKey, usageTapClient, ...policy } = options;
|
|
2307
|
+
const localPolicy = typeof policy.rate === "number" ? policy : void 0;
|
|
2308
|
+
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey, sampling: localPolicy });
|
|
2309
|
+
const originalCreate = client.messages.create.bind(client.messages);
|
|
2310
|
+
const create = (params, requestOptions) => {
|
|
2311
|
+
return new DeferredAnthropicApiPromise(async () => {
|
|
2312
|
+
const { usageTap: callContext = {}, ...providerOptions } = requestOptions ?? {};
|
|
2313
|
+
const decision = params.stream === true ? Promise.resolve(false) : usageTap.shouldSampleAsync({
|
|
2314
|
+
customerId: callContext.customerId,
|
|
2315
|
+
feature: callContext.feature,
|
|
2316
|
+
input: params
|
|
2317
|
+
}, localPolicy);
|
|
2318
|
+
const startedAt = Date.now();
|
|
2319
|
+
try {
|
|
2320
|
+
const result = await resolveAnthropicResponseEnvelope(originalCreate(
|
|
2321
|
+
params,
|
|
2322
|
+
Object.keys(providerOptions).length ? providerOptions : void 0
|
|
2323
|
+
));
|
|
2324
|
+
const response = result.data;
|
|
2325
|
+
const selected = await decision;
|
|
2326
|
+
if (selected) {
|
|
2327
|
+
const record = isObjectRecord(response) ? response : {};
|
|
2328
|
+
await usageTap.captureSample({
|
|
2329
|
+
customerId: callContext.customerId,
|
|
2330
|
+
feature: callContext.feature,
|
|
2331
|
+
environment: callContext.environment,
|
|
2332
|
+
tags: callContext.tags,
|
|
2333
|
+
provider: "anthropic",
|
|
2334
|
+
model: readString(record.model) ?? readString(params.model),
|
|
2335
|
+
input: params,
|
|
2336
|
+
output: response,
|
|
2337
|
+
usage: record.usage,
|
|
2338
|
+
latencyMs: Date.now() - startedAt
|
|
2339
|
+
}).catch(() => void 0);
|
|
2340
|
+
}
|
|
2341
|
+
return result;
|
|
2342
|
+
} catch (error) {
|
|
2343
|
+
const selected = await decision;
|
|
2344
|
+
if (selected) {
|
|
2345
|
+
await usageTap.captureSample({
|
|
2346
|
+
customerId: callContext.customerId,
|
|
2347
|
+
feature: callContext.feature,
|
|
2348
|
+
environment: callContext.environment,
|
|
2349
|
+
tags: callContext.tags,
|
|
2350
|
+
provider: "anthropic",
|
|
2351
|
+
model: readString(params.model),
|
|
2352
|
+
input: params,
|
|
2353
|
+
latencyMs: Date.now() - startedAt,
|
|
2354
|
+
error: error instanceof Error ? { name: error.name, message: error.message } : { message: String(error) }
|
|
2355
|
+
}).catch(() => void 0);
|
|
2356
|
+
}
|
|
2357
|
+
throw error;
|
|
2358
|
+
}
|
|
2359
|
+
});
|
|
2360
|
+
};
|
|
2361
|
+
const messages = new Proxy(client.messages, {
|
|
2362
|
+
get(target, prop, receiver) {
|
|
2363
|
+
if (prop === "create") return create;
|
|
2364
|
+
return safeReflectGet(target, prop, receiver);
|
|
2365
|
+
}
|
|
2366
|
+
});
|
|
2367
|
+
return new Proxy(client, {
|
|
2368
|
+
get(target, prop, receiver) {
|
|
2369
|
+
if (prop === "messages") return messages;
|
|
2370
|
+
if (prop === "unwrap") return () => target;
|
|
2371
|
+
return safeReflectGet(target, prop, receiver);
|
|
2372
|
+
}
|
|
2373
|
+
});
|
|
2374
|
+
}
|
|
2375
|
+
function safeReflectGet(target, prop, receiver) {
|
|
2376
|
+
return Reflect.get(target, prop, receiver);
|
|
2377
|
+
}
|
|
1619
2378
|
function withCompression(client, options = {}) {
|
|
1620
2379
|
if (!client?.messages || typeof client.messages.create !== "function") {
|
|
1621
2380
|
throw new UsageTapError(
|
|
@@ -1626,17 +2385,24 @@ function withCompression(client, options = {}) {
|
|
|
1626
2385
|
const {
|
|
1627
2386
|
apiKey,
|
|
1628
2387
|
usageTapClient,
|
|
2388
|
+
sampling,
|
|
1629
2389
|
...compressionOverrides
|
|
1630
2390
|
} = options;
|
|
1631
2391
|
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey });
|
|
1632
2392
|
const compression = {
|
|
1633
2393
|
provider: "usagetap",
|
|
2394
|
+
roles: { user: { enabled: true, aggressiveness: 0.2 } },
|
|
1634
2395
|
minContextTokens: DEFAULT_PROMPT_COMPRESSION_MIN_CONTEXT_TOKENS,
|
|
1635
2396
|
...compressionOverrides
|
|
1636
2397
|
};
|
|
1637
2398
|
const originalCreate = client.messages.create.bind(client.messages);
|
|
1638
2399
|
const wrappedCreate = (params, requestOptions) => {
|
|
1639
|
-
return
|
|
2400
|
+
return new DeferredAnthropicApiPromise(async () => {
|
|
2401
|
+
const outcome = await compressAnthropicParams(params, usageTap, compression);
|
|
2402
|
+
return resolveAnthropicResponseEnvelope(
|
|
2403
|
+
originalCreate(outcome.params, requestOptions)
|
|
2404
|
+
);
|
|
2405
|
+
});
|
|
1640
2406
|
};
|
|
1641
2407
|
const proxiedMessages = new Proxy(client.messages, {
|
|
1642
2408
|
get(target, prop, receiver) {
|
|
@@ -1644,13 +2410,18 @@ function withCompression(client, options = {}) {
|
|
|
1644
2410
|
return Reflect.get(target, prop, receiver);
|
|
1645
2411
|
}
|
|
1646
2412
|
});
|
|
1647
|
-
|
|
2413
|
+
const compressed = new Proxy(client, {
|
|
1648
2414
|
get(target, prop, receiver) {
|
|
1649
2415
|
if (prop === "messages") return proxiedMessages;
|
|
1650
2416
|
if (prop === "unwrap") return () => target;
|
|
1651
2417
|
return Reflect.get(target, prop, receiver);
|
|
1652
2418
|
}
|
|
1653
2419
|
});
|
|
2420
|
+
return sampling ? withSampling(compressed, {
|
|
2421
|
+
...sampling === true ? {} : sampling,
|
|
2422
|
+
apiKey,
|
|
2423
|
+
usageTapClient
|
|
2424
|
+
}) : compressed;
|
|
1654
2425
|
}
|
|
1655
2426
|
function withMetering(client, customer) {
|
|
1656
2427
|
const config = typeof customer === "string" ? { customerId: customer } : customer;
|
|
@@ -1665,6 +2436,7 @@ function withMetering(client, customer) {
|
|
|
1665
2436
|
usageTapClient,
|
|
1666
2437
|
applyVendorHints,
|
|
1667
2438
|
promptCompression,
|
|
2439
|
+
sampling,
|
|
1668
2440
|
...defaultContext
|
|
1669
2441
|
} = config;
|
|
1670
2442
|
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey });
|
|
@@ -1672,7 +2444,8 @@ function withMetering(client, customer) {
|
|
|
1672
2444
|
return wrapAnthropic(client, usageTap, {
|
|
1673
2445
|
defaultContext,
|
|
1674
2446
|
applyVendorHints,
|
|
1675
|
-
promptCompression: normalizedCompression
|
|
2447
|
+
promptCompression: normalizedCompression,
|
|
2448
|
+
sampling
|
|
1676
2449
|
});
|
|
1677
2450
|
}
|
|
1678
2451
|
function wrapAnthropic(client, usageTap, options = {}) {
|
|
@@ -1685,6 +2458,7 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1685
2458
|
const defaultContext = options.defaultContext;
|
|
1686
2459
|
const applyVendorHints = options.applyVendorHints !== false;
|
|
1687
2460
|
const defaultPromptCompression = normalizePromptCompressionOptions(options.promptCompression);
|
|
2461
|
+
const defaultSampling = normalizeMeteredAnthropicSampling(options.sampling);
|
|
1688
2462
|
const promptCompressionStats = new AnthropicPromptCompressionStats();
|
|
1689
2463
|
const proxiedMessages = createMessagesProxy(
|
|
1690
2464
|
client.messages,
|
|
@@ -1692,7 +2466,8 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1692
2466
|
defaultContext,
|
|
1693
2467
|
applyVendorHints,
|
|
1694
2468
|
defaultPromptCompression,
|
|
1695
|
-
promptCompressionStats
|
|
2469
|
+
promptCompressionStats,
|
|
2470
|
+
defaultSampling
|
|
1696
2471
|
);
|
|
1697
2472
|
const handler = {
|
|
1698
2473
|
get(target, prop, receiver) {
|
|
@@ -1710,7 +2485,7 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1710
2485
|
};
|
|
1711
2486
|
return new Proxy(client, handler);
|
|
1712
2487
|
}
|
|
1713
|
-
function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
2488
|
+
function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling) {
|
|
1714
2489
|
if (typeof resource.create !== "function") {
|
|
1715
2490
|
throw new UsageTapError("USAGETAP_BAD_REQUEST", "wrapAnthropic requires client.messages.create");
|
|
1716
2491
|
}
|
|
@@ -1722,19 +2497,22 @@ function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHint
|
|
|
1722
2497
|
withUsage,
|
|
1723
2498
|
promptCompression
|
|
1724
2499
|
} = splitUsageOptions(options);
|
|
1725
|
-
return
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
2500
|
+
return new DeferredAnthropicApiPromise(
|
|
2501
|
+
() => invokeMessagesCreate({
|
|
2502
|
+
params,
|
|
2503
|
+
requestOptions,
|
|
2504
|
+
usageContext,
|
|
2505
|
+
withUsage,
|
|
2506
|
+
promptCompression,
|
|
2507
|
+
originalCreate,
|
|
2508
|
+
usageTap,
|
|
2509
|
+
defaultContext,
|
|
2510
|
+
applyVendorHints,
|
|
2511
|
+
defaultPromptCompression,
|
|
2512
|
+
promptCompressionStats,
|
|
2513
|
+
defaultSampling
|
|
2514
|
+
})
|
|
2515
|
+
);
|
|
1738
2516
|
});
|
|
1739
2517
|
const handler = {
|
|
1740
2518
|
get(target, prop, receiver) {
|
|
@@ -1747,13 +2525,25 @@ function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHint
|
|
|
1747
2525
|
return new Proxy(resource, handler);
|
|
1748
2526
|
}
|
|
1749
2527
|
async function invokeMessagesCreate(args) {
|
|
1750
|
-
const beginRequest = resolveBeginRequest(
|
|
2528
|
+
const beginRequest = resolveBeginRequest(
|
|
2529
|
+
args.defaultContext,
|
|
2530
|
+
args.usageContext
|
|
2531
|
+
);
|
|
1751
2532
|
const begin = await args.usageTap.beginCall(
|
|
1752
2533
|
beginRequest,
|
|
1753
2534
|
beginCallOptions(args.withUsage)
|
|
1754
2535
|
);
|
|
1755
2536
|
const state = createCallState(beginRequest, begin);
|
|
1756
2537
|
const ctx = createUsageContext(state);
|
|
2538
|
+
const sampleStartedAt = Date.now();
|
|
2539
|
+
const wantsStream = isStreamingRequest(args.params);
|
|
2540
|
+
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredAnthropicSampleDecision({
|
|
2541
|
+
usageTap: args.usageTap,
|
|
2542
|
+
sampling: args.defaultSampling,
|
|
2543
|
+
beginRequest,
|
|
2544
|
+
input: args.params
|
|
2545
|
+
});
|
|
2546
|
+
let providerCompleted = false;
|
|
1757
2547
|
try {
|
|
1758
2548
|
const hintedParams = args.applyVendorHints ? applyAnthropicVendorHints(args.params, ctx.begin.data.vendorHints) : args.params;
|
|
1759
2549
|
const finalParams = await compressAnthropicParamsForCall({
|
|
@@ -1766,21 +2556,38 @@ async function invokeMessagesCreate(args) {
|
|
|
1766
2556
|
withUsage: args.withUsage,
|
|
1767
2557
|
operation: "messages.create"
|
|
1768
2558
|
});
|
|
2559
|
+
ctx.setUsage(anthropicRequestExecutionMetadata(finalParams));
|
|
1769
2560
|
const request = attachCorrelationHeader(args.requestOptions, ctx.begin.correlationId);
|
|
1770
|
-
const
|
|
2561
|
+
const providerResult = await resolveAnthropicResponseEnvelope(
|
|
2562
|
+
args.originalCreate(finalParams, request)
|
|
2563
|
+
);
|
|
2564
|
+
const response = providerResult.data;
|
|
2565
|
+
providerCompleted = true;
|
|
1771
2566
|
if (isStreamingRequest(finalParams)) {
|
|
1772
2567
|
ensureAsyncIterable(response, "messages.create");
|
|
1773
|
-
return
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
2568
|
+
return {
|
|
2569
|
+
...providerResult,
|
|
2570
|
+
data: wrapAnthropicStreamForUsageTap(
|
|
2571
|
+
response,
|
|
2572
|
+
state,
|
|
2573
|
+
args.usageTap,
|
|
2574
|
+
args.withUsage,
|
|
2575
|
+
readString(finalParams.model)
|
|
2576
|
+
)
|
|
2577
|
+
};
|
|
1780
2578
|
}
|
|
1781
2579
|
inferAnthropicUsage(response, readString(finalParams.model), ctx);
|
|
2580
|
+
await captureMeteredAnthropicSample({
|
|
2581
|
+
usageTap: args.usageTap,
|
|
2582
|
+
sampling: args.defaultSampling,
|
|
2583
|
+
decision: sampleDecision,
|
|
2584
|
+
state,
|
|
2585
|
+
input: args.params,
|
|
2586
|
+
response,
|
|
2587
|
+
startedAt: sampleStartedAt
|
|
2588
|
+
});
|
|
1782
2589
|
await finalizeCall(state, args.usageTap, args.withUsage);
|
|
1783
|
-
return
|
|
2590
|
+
return providerResult;
|
|
1784
2591
|
} catch (error) {
|
|
1785
2592
|
if (!state.error) {
|
|
1786
2593
|
state.error = {
|
|
@@ -1788,10 +2595,80 @@ async function invokeMessagesCreate(args) {
|
|
|
1788
2595
|
message: error instanceof Error ? error.message : String(error)
|
|
1789
2596
|
};
|
|
1790
2597
|
}
|
|
2598
|
+
if (!providerCompleted) {
|
|
2599
|
+
await captureMeteredAnthropicSample({
|
|
2600
|
+
usageTap: args.usageTap,
|
|
2601
|
+
sampling: args.defaultSampling,
|
|
2602
|
+
decision: sampleDecision,
|
|
2603
|
+
state,
|
|
2604
|
+
input: args.params,
|
|
2605
|
+
error,
|
|
2606
|
+
startedAt: sampleStartedAt
|
|
2607
|
+
});
|
|
2608
|
+
}
|
|
1791
2609
|
await finalizeCall(state, args.usageTap, args.withUsage);
|
|
1792
2610
|
throw error;
|
|
1793
2611
|
}
|
|
1794
2612
|
}
|
|
2613
|
+
function normalizeMeteredAnthropicSampling(options) {
|
|
2614
|
+
if (!options) return void 0;
|
|
2615
|
+
if (options === true) return {};
|
|
2616
|
+
return {
|
|
2617
|
+
policy: typeof options.rate === "number" ? options : void 0
|
|
2618
|
+
};
|
|
2619
|
+
}
|
|
2620
|
+
function startMeteredAnthropicSampleDecision({
|
|
2621
|
+
usageTap,
|
|
2622
|
+
sampling,
|
|
2623
|
+
beginRequest,
|
|
2624
|
+
input
|
|
2625
|
+
}) {
|
|
2626
|
+
if (!sampling) return Promise.resolve(false);
|
|
2627
|
+
return usageTap.shouldSampleAsync(
|
|
2628
|
+
{
|
|
2629
|
+
customerId: beginRequest.customerId,
|
|
2630
|
+
feature: beginRequest.feature,
|
|
2631
|
+
input
|
|
2632
|
+
},
|
|
2633
|
+
sampling.policy
|
|
2634
|
+
);
|
|
2635
|
+
}
|
|
2636
|
+
async function captureMeteredAnthropicSample({
|
|
2637
|
+
usageTap,
|
|
2638
|
+
sampling,
|
|
2639
|
+
decision,
|
|
2640
|
+
state,
|
|
2641
|
+
input,
|
|
2642
|
+
response,
|
|
2643
|
+
error,
|
|
2644
|
+
startedAt
|
|
2645
|
+
}) {
|
|
2646
|
+
if (!sampling) return;
|
|
2647
|
+
let selected = false;
|
|
2648
|
+
try {
|
|
2649
|
+
selected = await decision;
|
|
2650
|
+
} catch {
|
|
2651
|
+
return;
|
|
2652
|
+
}
|
|
2653
|
+
if (!selected) return;
|
|
2654
|
+
const record = isObjectRecord(response) ? response : {};
|
|
2655
|
+
await usageTap.captureSample({
|
|
2656
|
+
sampleId: state.begin.data.callId,
|
|
2657
|
+
callId: state.begin.data.callId,
|
|
2658
|
+
customerId: state.beginRequest.customerId,
|
|
2659
|
+
feature: state.beginRequest.feature,
|
|
2660
|
+
tags: state.beginRequest.tags,
|
|
2661
|
+
provider: "anthropic",
|
|
2662
|
+
model: readString(record.model) ?? readString(input.model),
|
|
2663
|
+
input,
|
|
2664
|
+
...response === void 0 ? {} : { output: response },
|
|
2665
|
+
usage: record.usage,
|
|
2666
|
+
latencyMs: Date.now() - startedAt,
|
|
2667
|
+
...error === void 0 ? {} : {
|
|
2668
|
+
error: error instanceof Error ? { name: error.name, message: error.message } : { message: String(error) }
|
|
2669
|
+
}
|
|
2670
|
+
}).catch(() => void 0);
|
|
2671
|
+
}
|
|
1795
2672
|
async function compressAnthropicParamsForCall(args) {
|
|
1796
2673
|
const compression = resolveEffectivePromptCompressionOptions(
|
|
1797
2674
|
args.defaultPromptCompression,
|
|
@@ -1871,6 +2748,10 @@ async function compressAnthropicParams(params, usageTap, compression, signal) {
|
|
|
1871
2748
|
const result = await usageTap.compressPromptMessages(source, {
|
|
1872
2749
|
provider: "usagetap",
|
|
1873
2750
|
failOpen: compression.failOpen,
|
|
2751
|
+
mode: compression.mode,
|
|
2752
|
+
latencyBudgetMs: compression.latencyBudgetMs,
|
|
2753
|
+
compactEmptyUserMessages: compression.compactEmptyUserMessages,
|
|
2754
|
+
compactDuplicateUserTextParts: compression.compactDuplicateUserTextParts,
|
|
1874
2755
|
aggressiveness: resolveMessageEndpointAggressiveness(compression),
|
|
1875
2756
|
signal
|
|
1876
2757
|
});
|
|
@@ -2259,6 +3140,8 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2259
3140
|
if (requested) begin.requested = requested;
|
|
2260
3141
|
const feature = current.feature ?? base.feature;
|
|
2261
3142
|
if (feature) begin.feature = feature;
|
|
3143
|
+
const runId = current.runId ?? base.runId;
|
|
3144
|
+
if (runId) begin.runId = runId;
|
|
2262
3145
|
const idempotency = current.idempotency ?? base.idempotency;
|
|
2263
3146
|
if (idempotency) begin.idempotency = idempotency;
|
|
2264
3147
|
const idempotencyKey = current.idempotencyKey ?? base.idempotencyKey;
|
|
@@ -2267,10 +3150,14 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2267
3150
|
if (customerName) begin.customerName = customerName;
|
|
2268
3151
|
const customerEmail = current.customerEmail ?? base.customerEmail;
|
|
2269
3152
|
if (customerEmail) begin.customerEmail = customerEmail;
|
|
3153
|
+
const customerUserId = current.customerUserId ?? base.customerUserId;
|
|
3154
|
+
if (customerUserId) begin.customerUserId = customerUserId;
|
|
3155
|
+
const customerUserName = current.customerUserName ?? base.customerUserName;
|
|
3156
|
+
if (customerUserName) begin.customerUserName = customerUserName;
|
|
3157
|
+
const customerUserEmail = current.customerUserEmail ?? base.customerUserEmail;
|
|
3158
|
+
if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
|
|
2270
3159
|
const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
|
|
2271
3160
|
if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
|
|
2272
|
-
const holdUsd = current.holdUsd ?? base.holdUsd;
|
|
2273
|
-
if (typeof holdUsd === "number") begin.holdUsd = holdUsd;
|
|
2274
3161
|
const batch = current.batch ?? base.batch;
|
|
2275
3162
|
if (typeof batch === "boolean") begin.batch = batch;
|
|
2276
3163
|
const pricingMode = current.pricingMode ?? base.pricingMode;
|
|
@@ -2282,6 +3169,11 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2282
3169
|
}
|
|
2283
3170
|
function createCallState(beginRequest, begin) {
|
|
2284
3171
|
const usage = {};
|
|
3172
|
+
const pricingMode = begin.data.pricingMode ?? beginRequest.pricingMode ?? (beginRequest.batch === true ? "batch" : beginRequest.batch === false ? "standard" : void 0);
|
|
3173
|
+
if (pricingMode) {
|
|
3174
|
+
usage.pricingMode = pricingMode;
|
|
3175
|
+
usage.batch = pricingMode === "batch";
|
|
3176
|
+
}
|
|
2285
3177
|
const stripeCustomerId = typeof begin.data.stripeCustomerId === "string" ? begin.data.stripeCustomerId : typeof beginRequest.stripeCustomerId === "string" ? beginRequest.stripeCustomerId : void 0;
|
|
2286
3178
|
if (stripeCustomerId) {
|
|
2287
3179
|
usage.stripeCustomerId = stripeCustomerId;
|
|
@@ -2345,10 +3237,12 @@ function inferAnthropicUsage(response, fallbackModel, ctx) {
|
|
|
2345
3237
|
}
|
|
2346
3238
|
function extractAnthropicUsage(payload, fallbackModel) {
|
|
2347
3239
|
if (!isObjectRecord(payload)) {
|
|
2348
|
-
return fallbackModel ? { modelUsed: fallbackModel } :
|
|
3240
|
+
return { providerUsed: "anthropic", ...fallbackModel ? { modelUsed: fallbackModel } : {} };
|
|
2349
3241
|
}
|
|
2350
3242
|
const usage = findAnthropicUsageRecord(payload);
|
|
2351
|
-
const result = {
|
|
3243
|
+
const result = {
|
|
3244
|
+
providerUsed: "anthropic"
|
|
3245
|
+
};
|
|
2352
3246
|
const model = readString(payload.model) ?? readString(payload.message?.model) ?? fallbackModel;
|
|
2353
3247
|
if (model) {
|
|
2354
3248
|
result.modelUsed = model;
|
|
@@ -2358,6 +3252,21 @@ function extractAnthropicUsage(payload, fallbackModel) {
|
|
|
2358
3252
|
}
|
|
2359
3253
|
return Object.keys(result).length ? result : void 0;
|
|
2360
3254
|
}
|
|
3255
|
+
function anthropicRequestExecutionMetadata(params) {
|
|
3256
|
+
const outputConfig = isObjectRecord(params.output_config) ? params.output_config : void 0;
|
|
3257
|
+
const thinking = isObjectRecord(params.thinking) ? params.thinking : void 0;
|
|
3258
|
+
const rawEffort = outputConfig?.effort;
|
|
3259
|
+
const effort = rawEffort === "none" || rawEffort === "minimal" || rawEffort === "low" || rawEffort === "medium" || rawEffort === "high" || rawEffort === "xhigh" || rawEffort === "max" ? rawEffort : void 0;
|
|
3260
|
+
const mode = readString(thinking?.type);
|
|
3261
|
+
const rawBudget = readNumber(thinking?.budget_tokens);
|
|
3262
|
+
const budget = rawBudget !== void 0 && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
|
|
3263
|
+
return {
|
|
3264
|
+
providerUsed: "anthropic",
|
|
3265
|
+
...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
|
|
3266
|
+
...mode ? { reasoningMode: mode } : {},
|
|
3267
|
+
...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
2361
3270
|
function findAnthropicUsageRecord(payload) {
|
|
2362
3271
|
if (isObjectRecord(payload.usage)) {
|
|
2363
3272
|
return payload.usage;
|
|
@@ -2368,10 +3277,6 @@ function findAnthropicUsageRecord(payload) {
|
|
|
2368
3277
|
return void 0;
|
|
2369
3278
|
}
|
|
2370
3279
|
function applyAnthropicUsageRecord(usage, usageRecord) {
|
|
2371
|
-
const inputTokens = readNumber(usageRecord.input_tokens ?? usageRecord.prompt_tokens);
|
|
2372
|
-
if (inputTokens !== void 0) {
|
|
2373
|
-
usage.inputTokens = inputTokens;
|
|
2374
|
-
}
|
|
2375
3280
|
const outputTokens = readNumber(usageRecord.output_tokens ?? usageRecord.completion_tokens);
|
|
2376
3281
|
if (outputTokens !== void 0) {
|
|
2377
3282
|
usage.responseTokens = outputTokens;
|
|
@@ -2382,6 +3287,26 @@ function applyAnthropicUsageRecord(usage, usageRecord) {
|
|
|
2382
3287
|
if (cachedInputTokens !== void 0) {
|
|
2383
3288
|
usage.cachedInputTokens = cachedInputTokens;
|
|
2384
3289
|
}
|
|
3290
|
+
const cacheWriteInputTokens = readNumber(
|
|
3291
|
+
usageRecord.cache_creation_input_tokens ?? usageRecord.prompt_cache_miss_tokens ?? usageRecord.cache_write_tokens
|
|
3292
|
+
);
|
|
3293
|
+
if (cacheWriteInputTokens !== void 0) {
|
|
3294
|
+
usage.cacheWriteInputTokens = cacheWriteInputTokens;
|
|
3295
|
+
}
|
|
3296
|
+
if (isObjectRecord(usageRecord.cache_creation)) {
|
|
3297
|
+
const cacheWrite5m = readNumber(
|
|
3298
|
+
usageRecord.cache_creation.ephemeral_5m_input_tokens
|
|
3299
|
+
);
|
|
3300
|
+
const cacheWrite1h = readNumber(
|
|
3301
|
+
usageRecord.cache_creation.ephemeral_1h_input_tokens
|
|
3302
|
+
);
|
|
3303
|
+
if (cacheWrite5m !== void 0) usage.cacheWrite5mInputTokens = cacheWrite5m;
|
|
3304
|
+
if (cacheWrite1h !== void 0) usage.cacheWrite1hInputTokens = cacheWrite1h;
|
|
3305
|
+
}
|
|
3306
|
+
const baseInputTokens = readNumber(usageRecord.input_tokens ?? usageRecord.prompt_tokens);
|
|
3307
|
+
if (baseInputTokens !== void 0) {
|
|
3308
|
+
usage.inputTokens = baseInputTokens + (cachedInputTokens ?? 0) + (cacheWriteInputTokens ?? 0);
|
|
3309
|
+
}
|
|
2385
3310
|
}
|
|
2386
3311
|
function accumulateAnthropicStreamUsage(chunk, fallbackModel, state) {
|
|
2387
3312
|
const usage = extractAnthropicUsage(chunk, fallbackModel);
|
|
@@ -2608,6 +3533,7 @@ function isIteratorResult(value) {
|
|
|
2608
3533
|
exports.AnthropicPromptCompressionStats = AnthropicPromptCompressionStats;
|
|
2609
3534
|
exports.withCompression = withCompression;
|
|
2610
3535
|
exports.withMetering = withMetering;
|
|
3536
|
+
exports.withSampling = withSampling;
|
|
2611
3537
|
exports.wrapAnthropic = wrapAnthropic;
|
|
2612
3538
|
//# sourceMappingURL=index.cjs.map
|
|
2613
3539
|
//# sourceMappingURL=index.cjs.map
|