@usagetap/sdk 1.3.1 → 1.4.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 +1058 -863
- package/dist/adapters/anthropic.cjs +990 -24
- 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 +990 -25
- package/dist/adapters/anthropic.mjs.map +1 -1
- package/dist/adapters/openai.cjs +1164 -44
- 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 +1164 -45
- package/dist/adapters/openai.mjs.map +1 -1
- package/dist/adapters/openrouter.cjs +3899 -22
- 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 +3897 -23
- package/dist/adapters/openrouter.mjs.map +1 -1
- package/dist/anthropic/index.cjs +990 -24
- 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 +990 -25
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/client-CExQ8e1T.d.cts +1225 -0
- package/dist/client-CExQ8e1T.d.ts +1225 -0
- package/dist/express/index.cjs +421 -29
- 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 +421 -29
- package/dist/express/index.mjs.map +1 -1
- package/dist/index.cjs +680 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.mjs +680 -15
- package/dist/index.mjs.map +1 -1
- package/dist/openai/index.cjs +1165 -45
- 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 +1165 -46
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openrouter/index.cjs +1182 -47
- 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 +1180 -46
- 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 +84 -84
- package/dist/client-BD8O2J8Z.d.cts +0 -668
- package/dist/client-BD8O2J8Z.d.ts +0 -668
|
@@ -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,358 @@ 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
|
+
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.models = {
|
|
1049
|
+
list: (options) => this.transport.request({
|
|
1050
|
+
method: "GET",
|
|
1051
|
+
path: "/v1/models",
|
|
1052
|
+
options,
|
|
1053
|
+
response: "json"
|
|
1054
|
+
})
|
|
1055
|
+
};
|
|
1056
|
+
this.batches = {
|
|
1057
|
+
create: (params, options = {}) => this.transport.request({
|
|
1058
|
+
method: "POST",
|
|
1059
|
+
path: "/v1/batches",
|
|
1060
|
+
body: params,
|
|
1061
|
+
options: {
|
|
1062
|
+
...options,
|
|
1063
|
+
idempotencyKey: options.idempotencyKey ?? this.idempotencyGenerator()
|
|
1064
|
+
},
|
|
1065
|
+
response: "json"
|
|
1066
|
+
}),
|
|
1067
|
+
retrieve: (batchId, options) => this.transport.request({
|
|
1068
|
+
method: "GET",
|
|
1069
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1070
|
+
resourceId(batchId, ["id"], "gateway.batches.retrieve")
|
|
1071
|
+
)}`,
|
|
1072
|
+
options,
|
|
1073
|
+
response: "json"
|
|
1074
|
+
}),
|
|
1075
|
+
wait: (batch, options) => this.waitForBatch(batch, options),
|
|
1076
|
+
cancel: (batchId, options) => this.transport.request({
|
|
1077
|
+
method: "POST",
|
|
1078
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1079
|
+
resourceId(batchId, ["id"], "gateway.batches.cancel")
|
|
1080
|
+
)}/cancel`,
|
|
1081
|
+
options,
|
|
1082
|
+
response: "json"
|
|
1083
|
+
}),
|
|
1084
|
+
results: (batchId, options) => this.transport.request({
|
|
1085
|
+
method: "GET",
|
|
1086
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1087
|
+
resourceId(batchId, ["id"], "gateway.batches.results")
|
|
1088
|
+
)}/results`,
|
|
1089
|
+
options,
|
|
1090
|
+
response: "ndjson"
|
|
1091
|
+
})
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
async waitForBatch(value, options = {}) {
|
|
1095
|
+
const { pollIntervalMs, timeoutMs } = validateWaitOptions(options);
|
|
1096
|
+
const deadline = Date.now() + timeoutMs;
|
|
1097
|
+
let batch = typeof value === "string" ? await this.batches.retrieve(value, options) : value;
|
|
1098
|
+
while (!terminalGatewayBatch(batch.status)) {
|
|
1099
|
+
if (Date.now() >= deadline) {
|
|
1100
|
+
throw new UsageTapError(
|
|
1101
|
+
"USAGETAP_RETRY_EXHAUSTED",
|
|
1102
|
+
`Gateway batch ${batch.id} did not finish before timeout`,
|
|
1103
|
+
{ retryable: true }
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
await sleep(pollIntervalMs, options.signal);
|
|
1107
|
+
batch = await this.batches.retrieve(batch.id, options);
|
|
1108
|
+
}
|
|
1109
|
+
return batch;
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
|
|
755
1113
|
// src/client.ts
|
|
756
1114
|
var CALL_BEGIN_PATH = "call_begin";
|
|
757
1115
|
var CALL_END_PATH = "call_end";
|
|
758
1116
|
var COMPRESS_PROMPT_PATH = "compress_prompt";
|
|
1117
|
+
var SAMPLES_PATH = "samples";
|
|
1118
|
+
var SAMPLING_SETTINGS_PATH = "sampling/settings";
|
|
1119
|
+
var SAMPLING_DECIDE_PATH = "sampling/decide";
|
|
759
1120
|
var CHECK_USAGE_PATH = "customers/{customerId}/usage";
|
|
760
1121
|
var CREATE_CUSTOMER_PATH = "customers";
|
|
761
1122
|
var CHANGE_PLAN_PATH = "customers/{customerId}/change_plan";
|
|
@@ -766,11 +1127,16 @@ var CORRELATION_HEADER = "x-usage-correlation-id";
|
|
|
766
1127
|
var IDEMPOTENCY_HEADER = "idempotency-key";
|
|
767
1128
|
var SDK_HEADER = "x-usage-sdk";
|
|
768
1129
|
var USER_AGENT = "UsageTapClient";
|
|
769
|
-
var
|
|
1130
|
+
var CANONICAL_MEDIA_TYPE2 = "application/vnd.usagetap.v1+json";
|
|
770
1131
|
var DEFAULT_BASE_URL = "https://api.usagetap.com";
|
|
771
|
-
var
|
|
1132
|
+
var DEFAULT_RUN_INACTIVITY_MS = 60 * 60 * 1e3;
|
|
1133
|
+
var SDK_VERSION = "1.4.0" ;
|
|
772
1134
|
var HAS_WINDOW = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
773
1135
|
var UsageTapClient = class {
|
|
1136
|
+
/** OpenAI-compatible chat, model, and native batch operations. */
|
|
1137
|
+
gateway;
|
|
1138
|
+
/** Published-profile context summarization operations. */
|
|
1139
|
+
summarization;
|
|
774
1140
|
apiKey;
|
|
775
1141
|
baseUrl;
|
|
776
1142
|
fetchImpl;
|
|
@@ -795,6 +1161,11 @@ var UsageTapClient = class {
|
|
|
795
1161
|
usageTapCompressionMessagesEndpoint;
|
|
796
1162
|
usageTapCompressionModel;
|
|
797
1163
|
usageTapCompressionAggressiveness;
|
|
1164
|
+
sampling;
|
|
1165
|
+
samplingSettingsCacheMs;
|
|
1166
|
+
circuitBreaker;
|
|
1167
|
+
circuitBreakerRuns = /* @__PURE__ */ new Map();
|
|
1168
|
+
samplingSettingsCache;
|
|
798
1169
|
constructor(options = {}) {
|
|
799
1170
|
const apiKey = options.apiKey?.trim() || readEnvironmentVariable("USAGETAP_API_KEY");
|
|
800
1171
|
const baseUrl = options.baseUrl?.trim() || readEnvironmentVariable("USAGETAP_BASE_URL") || DEFAULT_BASE_URL;
|
|
@@ -817,10 +1188,21 @@ var UsageTapClient = class {
|
|
|
817
1188
|
"A global fetch implementation was not found. Pass fetchImpl in UsageTapClientOptions."
|
|
818
1189
|
);
|
|
819
1190
|
}
|
|
820
|
-
const
|
|
821
|
-
this.baseUrl = new URL(
|
|
1191
|
+
const normalizedBaseUrl2 = normalizeBaseUrl(baseUrl);
|
|
1192
|
+
this.baseUrl = new URL(normalizedBaseUrl2);
|
|
822
1193
|
this.apiKey = apiKey;
|
|
823
1194
|
this.fetchImpl = wrapFetchImplementation(fetchCandidate, !options.fetchImpl);
|
|
1195
|
+
const resourceConfig = {
|
|
1196
|
+
apiKey,
|
|
1197
|
+
apiBaseUrl: normalizedBaseUrl2,
|
|
1198
|
+
gatewayBaseUrl: options.gatewayBaseUrl?.trim() || readEnvironmentVariable("USAGETAP_GATEWAY_URL"),
|
|
1199
|
+
fetchImpl: this.fetchImpl,
|
|
1200
|
+
headers: options.headers,
|
|
1201
|
+
sdkVersion: SDK_VERSION,
|
|
1202
|
+
idempotencyGenerator: options.idempotencyGenerator
|
|
1203
|
+
};
|
|
1204
|
+
this.gateway = new GatewayResource(resourceConfig);
|
|
1205
|
+
this.summarization = new SummarizationResource(resourceConfig);
|
|
824
1206
|
this.defaultFeature = options.defaultFeature;
|
|
825
1207
|
this.defaultTags = options.defaultTags?.length ? dedupeStrings(options.defaultTags) : void 0;
|
|
826
1208
|
this.defaultHeaders = options.headers ? normalizeHeaderDictionary(options.headers) : {};
|
|
@@ -842,11 +1224,122 @@ var UsageTapClient = class {
|
|
|
842
1224
|
this.usageTapCompressionMessagesEndpoint = options.usageTapCompressionMessagesEndpoint;
|
|
843
1225
|
this.usageTapCompressionModel = options.usageTapCompressionModel;
|
|
844
1226
|
this.usageTapCompressionAggressiveness = options.usageTapCompressionAggressiveness;
|
|
1227
|
+
this.sampling = options.sampling;
|
|
1228
|
+
this.samplingSettingsCacheMs = Number.isFinite(options.samplingSettingsCacheMs) ? Math.max(0, Number(options.samplingSettingsCacheMs)) : 5 * 60 * 1e3;
|
|
1229
|
+
if (options.circuitBreaker) {
|
|
1230
|
+
const maxCallsPerRun = options.circuitBreaker.maxCallsPerRun;
|
|
1231
|
+
if (!Number.isInteger(maxCallsPerRun) || maxCallsPerRun < 1) {
|
|
1232
|
+
throw new UsageTapError(
|
|
1233
|
+
"USAGETAP_BAD_REQUEST",
|
|
1234
|
+
"circuitBreaker.maxCallsPerRun must be a positive integer"
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
const runInactivityMs = options.circuitBreaker.runInactivityMs ?? DEFAULT_RUN_INACTIVITY_MS;
|
|
1238
|
+
if (!Number.isFinite(runInactivityMs) || runInactivityMs < 1) {
|
|
1239
|
+
throw new UsageTapError(
|
|
1240
|
+
"USAGETAP_BAD_REQUEST",
|
|
1241
|
+
"circuitBreaker.runInactivityMs must be a positive number"
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
this.circuitBreaker = {
|
|
1245
|
+
maxCallsPerRun,
|
|
1246
|
+
runInactivityMs
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
shouldSample(request, policy = this.sampling || void 0) {
|
|
1251
|
+
if (!policy) return false;
|
|
1252
|
+
const rate = Math.min(1, Math.max(0, Number(policy.rate) || 0));
|
|
1253
|
+
if (rate <= 0) return false;
|
|
1254
|
+
const customerId = request.customerId?.trim();
|
|
1255
|
+
if (customerId && policy.customers?.exclude?.includes(customerId)) return false;
|
|
1256
|
+
const feature = request.feature?.trim();
|
|
1257
|
+
if (feature && policy.features?.exclude?.includes(feature)) return false;
|
|
1258
|
+
const included = policy.features?.include?.filter(Boolean) ?? [];
|
|
1259
|
+
if (included.length > 0 && (!feature || !included.includes(feature))) return false;
|
|
1260
|
+
const minimum = Math.max(0, Math.round(policy.minInputTokens ?? 0));
|
|
1261
|
+
if (minimum > 0 && estimatePromptTokens(request.input) < minimum) return false;
|
|
1262
|
+
return (policy.random ?? Math.random)() < rate;
|
|
1263
|
+
}
|
|
1264
|
+
async getSamplingSettings(options = {}) {
|
|
1265
|
+
const now = Date.now();
|
|
1266
|
+
if (!options.forceRefresh && this.samplingSettingsCache && this.samplingSettingsCache.expiresAtMs > now) {
|
|
1267
|
+
return {
|
|
1268
|
+
result: { status: "ACCEPTED", code: "SAMPLING_SETTINGS_CACHED" },
|
|
1269
|
+
data: this.samplingSettingsCache.settings,
|
|
1270
|
+
correlationId: options.correlationId ?? "local-cache"
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
const response = await this.requestGet(
|
|
1274
|
+
SAMPLING_SETTINGS_PATH,
|
|
1275
|
+
{
|
|
1276
|
+
signal: options.signal,
|
|
1277
|
+
headers: options.headers,
|
|
1278
|
+
retries: options.retries,
|
|
1279
|
+
correlationId: options.correlationId
|
|
1280
|
+
}
|
|
1281
|
+
);
|
|
1282
|
+
const serverCacheMs = Math.max(0, Number(response.data.cacheSeconds) || 0) * 1e3;
|
|
1283
|
+
const cacheMs = Math.min(this.samplingSettingsCacheMs, serverCacheMs);
|
|
1284
|
+
this.samplingSettingsCache = {
|
|
1285
|
+
settings: response.data,
|
|
1286
|
+
expiresAtMs: now + cacheMs
|
|
1287
|
+
};
|
|
1288
|
+
return response;
|
|
1289
|
+
}
|
|
1290
|
+
async shouldSampleAsync(request, policy) {
|
|
1291
|
+
if (policy) return this.shouldSample(request, policy);
|
|
1292
|
+
if (this.sampling === false) return false;
|
|
1293
|
+
if (this.sampling) return this.shouldSample(request, this.sampling);
|
|
1294
|
+
try {
|
|
1295
|
+
const settings = await this.getSamplingSettings();
|
|
1296
|
+
return this.shouldSample(request, settings.data);
|
|
1297
|
+
} catch {
|
|
1298
|
+
return false;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
async decideSample(request, options = {}) {
|
|
1302
|
+
const hasTokens = Number.isFinite(request.inputTokens) && Number(request.inputTokens) >= 0;
|
|
1303
|
+
const hasCharacters = Number.isFinite(request.inputCharacters) && Number(request.inputCharacters) >= 0;
|
|
1304
|
+
if (!hasTokens && !hasCharacters) {
|
|
1305
|
+
throw new UsageTapError(
|
|
1306
|
+
"USAGETAP_BAD_REQUEST",
|
|
1307
|
+
"decideSample requires inputTokens or inputCharacters"
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
return this.request(
|
|
1311
|
+
SAMPLING_DECIDE_PATH,
|
|
1312
|
+
request,
|
|
1313
|
+
options
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
async captureSample(request, options = {}) {
|
|
1317
|
+
if (!request || request.input === void 0) {
|
|
1318
|
+
throw new UsageTapError(
|
|
1319
|
+
"USAGETAP_BAD_REQUEST",
|
|
1320
|
+
"captureSample requires input"
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
if (!request.provider?.trim()) {
|
|
1324
|
+
throw new UsageTapError(
|
|
1325
|
+
"USAGETAP_BAD_REQUEST",
|
|
1326
|
+
"captureSample requires provider"
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
const sampleId = request.sampleId?.trim() || this.idempotencyGenerator();
|
|
1330
|
+
return this.request(
|
|
1331
|
+
SAMPLES_PATH,
|
|
1332
|
+
{ ...request, sampleId },
|
|
1333
|
+
{ ...options, idempotencyKey: sampleId }
|
|
1334
|
+
);
|
|
845
1335
|
}
|
|
846
1336
|
async beginCall(request, options = {}) {
|
|
847
1337
|
const idempotencyKey = request.idempotencyKey ?? request.idempotency ?? (this.autoIdempotency ? this.idempotencyGenerator() : void 0);
|
|
1338
|
+
this.reserveRunCall(request, idempotencyKey);
|
|
1339
|
+
const apiRequest = { ...request };
|
|
1340
|
+
delete apiRequest.runId;
|
|
848
1341
|
const payload = {
|
|
849
|
-
...
|
|
1342
|
+
...apiRequest,
|
|
850
1343
|
feature: request.feature ?? this.defaultFeature,
|
|
851
1344
|
tags: this.mergeTags(request.tags)
|
|
852
1345
|
};
|
|
@@ -864,6 +1357,28 @@ var UsageTapClient = class {
|
|
|
864
1357
|
);
|
|
865
1358
|
return response;
|
|
866
1359
|
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Inspect a configured run circuit breaker without consuming another call.
|
|
1362
|
+
*/
|
|
1363
|
+
canRunContinue(request) {
|
|
1364
|
+
const identity = this.resolveRunIdentity(request);
|
|
1365
|
+
if (!identity || !this.circuitBreaker) {
|
|
1366
|
+
throw new UsageTapError(
|
|
1367
|
+
"USAGETAP_BAD_REQUEST",
|
|
1368
|
+
"canRunContinue requires circuitBreaker configuration and a non-empty runId"
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1371
|
+
this.expireInactiveRuns();
|
|
1372
|
+
const calls = this.circuitBreakerRuns.get(identity.key)?.calls ?? 0;
|
|
1373
|
+
return this.createCircuitBreakerDecision(identity.customerId, identity.runId, calls);
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Release local state after a workflow finishes. Returns true when state existed.
|
|
1377
|
+
*/
|
|
1378
|
+
resetRun(request) {
|
|
1379
|
+
const identity = this.resolveRunIdentity(request);
|
|
1380
|
+
return identity ? this.circuitBreakerRuns.delete(identity.key) : false;
|
|
1381
|
+
}
|
|
867
1382
|
async promptCompress(request, options = {}) {
|
|
868
1383
|
if (!request?.callId) {
|
|
869
1384
|
throw new UsageTapError(
|
|
@@ -961,6 +1476,10 @@ var UsageTapClient = class {
|
|
|
961
1476
|
usageTapCompressionMessagesEndpoint: this.usageTapCompressionMessagesEndpoint,
|
|
962
1477
|
aggressiveness: options.aggressiveness ?? this.aggressiveness,
|
|
963
1478
|
usageTapCompressionAggressiveness: options.usageTapCompressionAggressiveness ?? this.usageTapCompressionAggressiveness,
|
|
1479
|
+
mode: options.mode,
|
|
1480
|
+
latencyBudgetMs: options.latencyBudgetMs,
|
|
1481
|
+
compactEmptyUserMessages: options.compactEmptyUserMessages,
|
|
1482
|
+
compactDuplicateUserTextParts: options.compactDuplicateUserTextParts,
|
|
964
1483
|
fetchImpl: this.fetchImpl,
|
|
965
1484
|
signal: options.signal,
|
|
966
1485
|
failOpen: options.failOpen
|
|
@@ -1002,11 +1521,17 @@ var UsageTapClient = class {
|
|
|
1002
1521
|
callId: request.callId,
|
|
1003
1522
|
feature: feature ?? this.defaultFeature,
|
|
1004
1523
|
tags: tags ?? this.defaultTags,
|
|
1524
|
+
providerUsed: request.providerUsed,
|
|
1005
1525
|
modelUsed: request.modelUsed,
|
|
1526
|
+
reasoningEffort: request.reasoningEffort,
|
|
1527
|
+
reasoningEffortSource: request.reasoningEffortSource,
|
|
1528
|
+
reasoningMode: request.reasoningMode,
|
|
1529
|
+
reasoningBudgetTokens: request.reasoningBudgetTokens,
|
|
1006
1530
|
metrics: {
|
|
1007
1531
|
inputTokens: request.inputTokens,
|
|
1008
1532
|
responseTokens: request.responseTokens,
|
|
1009
1533
|
cachedInputTokens: request.cachedInputTokens,
|
|
1534
|
+
cacheWriteInputTokens: request.cacheWriteInputTokens,
|
|
1010
1535
|
reasoningTokens: request.reasoningTokens,
|
|
1011
1536
|
searches: request.searches,
|
|
1012
1537
|
audioSeconds: request.audioSeconds,
|
|
@@ -1114,6 +1639,15 @@ var UsageTapClient = class {
|
|
|
1114
1639
|
meterSlot: request.meterSlot,
|
|
1115
1640
|
amount: request.amount
|
|
1116
1641
|
};
|
|
1642
|
+
if (request.customerUserId) {
|
|
1643
|
+
payload.customerUserId = request.customerUserId;
|
|
1644
|
+
}
|
|
1645
|
+
if (request.customerUserName) {
|
|
1646
|
+
payload.customerUserName = request.customerUserName;
|
|
1647
|
+
}
|
|
1648
|
+
if (request.customerUserEmail) {
|
|
1649
|
+
payload.customerUserEmail = request.customerUserEmail;
|
|
1650
|
+
}
|
|
1117
1651
|
if (request.feature) {
|
|
1118
1652
|
payload.feature = request.feature;
|
|
1119
1653
|
}
|
|
@@ -1150,6 +1684,11 @@ var UsageTapClient = class {
|
|
|
1150
1684
|
const beginPayload = idempotencyKey ? { ...beginRequest, idempotencyKey, idempotency: idempotencyKey } : { ...beginRequest };
|
|
1151
1685
|
const beginResponse = await this.beginCall(beginPayload, options);
|
|
1152
1686
|
let usage = {};
|
|
1687
|
+
const pricingMode = beginResponse.data.pricingMode ?? beginRequest.pricingMode ?? (beginRequest.batch === true ? "batch" : beginRequest.batch === false ? "standard" : void 0);
|
|
1688
|
+
if (pricingMode) {
|
|
1689
|
+
usage.pricingMode = pricingMode;
|
|
1690
|
+
usage.batch = pricingMode === "batch";
|
|
1691
|
+
}
|
|
1153
1692
|
const initialStripeCustomerId = typeof beginResponse.data.stripeCustomerId === "string" ? beginResponse.data.stripeCustomerId : typeof beginRequest.stripeCustomerId === "string" ? beginRequest.stripeCustomerId : void 0;
|
|
1154
1693
|
if (initialStripeCustomerId) {
|
|
1155
1694
|
usage = { ...usage, stripeCustomerId: initialStripeCustomerId };
|
|
@@ -1217,17 +1756,86 @@ var UsageTapClient = class {
|
|
|
1217
1756
|
toPromptCompressionTelemetry(result) {
|
|
1218
1757
|
return {
|
|
1219
1758
|
provider: result.provider,
|
|
1220
|
-
originalCharacters: result.originalCharacters,
|
|
1221
|
-
compressedCharacters: result.compressedCharacters,
|
|
1222
|
-
savedCharacters: result.savedCharacters,
|
|
1223
1759
|
originalTokens: result.originalTokens,
|
|
1224
1760
|
compressedTokens: result.compressedTokens,
|
|
1225
1761
|
savedTokens: result.savedTokens,
|
|
1226
1762
|
tokenSavingsRatio: result.tokenSavingsRatio,
|
|
1227
|
-
savingsRatio: result.savingsRatio,
|
|
1228
1763
|
techniques: result.techniques
|
|
1229
1764
|
};
|
|
1230
1765
|
}
|
|
1766
|
+
reserveRunCall(request, idempotencyKey) {
|
|
1767
|
+
const identity = this.resolveRunIdentity(request);
|
|
1768
|
+
if (!identity || !this.circuitBreaker) return;
|
|
1769
|
+
this.expireInactiveRuns();
|
|
1770
|
+
const now = Date.now();
|
|
1771
|
+
const state = this.circuitBreakerRuns.get(identity.key) ?? {
|
|
1772
|
+
calls: 0,
|
|
1773
|
+
reservationKeys: /* @__PURE__ */ new Set(),
|
|
1774
|
+
lastSeenAtMs: now
|
|
1775
|
+
};
|
|
1776
|
+
const reservationKey = idempotencyKey ?? this.idempotencyGenerator();
|
|
1777
|
+
state.lastSeenAtMs = now;
|
|
1778
|
+
if (state.reservationKeys.has(reservationKey)) {
|
|
1779
|
+
this.circuitBreakerRuns.set(identity.key, state);
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
const decision = this.createCircuitBreakerDecision(
|
|
1783
|
+
identity.customerId,
|
|
1784
|
+
identity.runId,
|
|
1785
|
+
state.calls
|
|
1786
|
+
);
|
|
1787
|
+
if (!decision.allowed) {
|
|
1788
|
+
throw new UsageTapError(
|
|
1789
|
+
"USAGETAP_CIRCUIT_OPEN",
|
|
1790
|
+
`Run ${identity.runId} reached its ${decision.limit}-call circuit-breaker limit`,
|
|
1791
|
+
{
|
|
1792
|
+
details: {
|
|
1793
|
+
reason: decision.reason,
|
|
1794
|
+
customerId: identity.customerId,
|
|
1795
|
+
runId: identity.runId,
|
|
1796
|
+
calls: decision.calls,
|
|
1797
|
+
limit: decision.limit,
|
|
1798
|
+
remaining: decision.remaining
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
);
|
|
1802
|
+
}
|
|
1803
|
+
state.calls += 1;
|
|
1804
|
+
state.reservationKeys.add(reservationKey);
|
|
1805
|
+
this.circuitBreakerRuns.set(identity.key, state);
|
|
1806
|
+
}
|
|
1807
|
+
resolveRunIdentity(request) {
|
|
1808
|
+
const customerId = request.customerId?.trim();
|
|
1809
|
+
const runId = request.runId?.trim();
|
|
1810
|
+
if (!customerId || !runId) return void 0;
|
|
1811
|
+
return {
|
|
1812
|
+
key: `${customerId}\0${runId}`,
|
|
1813
|
+
customerId,
|
|
1814
|
+
runId
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
createCircuitBreakerDecision(customerId, runId, calls) {
|
|
1818
|
+
const limit = this.circuitBreaker?.maxCallsPerRun ?? 0;
|
|
1819
|
+
const allowed = calls < limit;
|
|
1820
|
+
return {
|
|
1821
|
+
allowed,
|
|
1822
|
+
...allowed ? {} : { reason: "max_calls_per_run" },
|
|
1823
|
+
customerId,
|
|
1824
|
+
runId,
|
|
1825
|
+
calls,
|
|
1826
|
+
limit,
|
|
1827
|
+
remaining: Math.max(0, limit - calls)
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
expireInactiveRuns() {
|
|
1831
|
+
if (!this.circuitBreaker || this.circuitBreakerRuns.size === 0) return;
|
|
1832
|
+
const expiredBefore = Date.now() - this.circuitBreaker.runInactivityMs;
|
|
1833
|
+
for (const [key, state] of this.circuitBreakerRuns) {
|
|
1834
|
+
if (state.lastSeenAtMs < expiredBefore) {
|
|
1835
|
+
this.circuitBreakerRuns.delete(key);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1231
1839
|
async request(path, payload, options) {
|
|
1232
1840
|
const url = new URL(path, this.baseUrl).toString();
|
|
1233
1841
|
const body = payload !== void 0 ? JSON.stringify(payload) : void 0;
|
|
@@ -1415,7 +2023,7 @@ var UsageTapClient = class {
|
|
|
1415
2023
|
...this.defaultHeaders,
|
|
1416
2024
|
[SDK_HEADER]: `js/${SDK_VERSION}`,
|
|
1417
2025
|
"content-type": "application/json",
|
|
1418
|
-
accept:
|
|
2026
|
+
accept: CANONICAL_MEDIA_TYPE2
|
|
1419
2027
|
};
|
|
1420
2028
|
if (!HAS_WINDOW) {
|
|
1421
2029
|
headers["user-agent"] = `${USER_AGENT}/${SDK_VERSION}`;
|
|
@@ -1616,6 +2224,82 @@ var AnthropicPromptCompressionStats = class {
|
|
|
1616
2224
|
}
|
|
1617
2225
|
};
|
|
1618
2226
|
var USAGETAP_CORRELATION_HEADER = "x-usage-correlation-id";
|
|
2227
|
+
function withSampling(client, options = {}) {
|
|
2228
|
+
if (!client?.messages || typeof client.messages.create !== "function" || !options) {
|
|
2229
|
+
throw new UsageTapError(
|
|
2230
|
+
"USAGETAP_BAD_REQUEST",
|
|
2231
|
+
"withSampling requires an Anthropic client and sampling options"
|
|
2232
|
+
);
|
|
2233
|
+
}
|
|
2234
|
+
const { apiKey, usageTapClient, ...policy } = options;
|
|
2235
|
+
const localPolicy = typeof policy.rate === "number" ? policy : void 0;
|
|
2236
|
+
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey, sampling: localPolicy });
|
|
2237
|
+
const originalCreate = client.messages.create.bind(client.messages);
|
|
2238
|
+
const create = async (params, requestOptions) => {
|
|
2239
|
+
const { usageTap: callContext = {}, ...providerOptions } = requestOptions ?? {};
|
|
2240
|
+
const decision = params.stream === true ? Promise.resolve(false) : usageTap.shouldSampleAsync({
|
|
2241
|
+
customerId: callContext.customerId,
|
|
2242
|
+
feature: callContext.feature,
|
|
2243
|
+
input: params
|
|
2244
|
+
}, localPolicy);
|
|
2245
|
+
const startedAt = Date.now();
|
|
2246
|
+
try {
|
|
2247
|
+
const response = await originalCreate(
|
|
2248
|
+
params,
|
|
2249
|
+
Object.keys(providerOptions).length ? providerOptions : void 0
|
|
2250
|
+
);
|
|
2251
|
+
const selected = await decision;
|
|
2252
|
+
if (selected) {
|
|
2253
|
+
const record = isObjectRecord(response) ? response : {};
|
|
2254
|
+
await usageTap.captureSample({
|
|
2255
|
+
customerId: callContext.customerId,
|
|
2256
|
+
feature: callContext.feature,
|
|
2257
|
+
environment: callContext.environment,
|
|
2258
|
+
tags: callContext.tags,
|
|
2259
|
+
provider: "anthropic",
|
|
2260
|
+
model: readString(record.model) ?? readString(params.model),
|
|
2261
|
+
input: params,
|
|
2262
|
+
output: response,
|
|
2263
|
+
usage: record.usage,
|
|
2264
|
+
latencyMs: Date.now() - startedAt
|
|
2265
|
+
}).catch(() => void 0);
|
|
2266
|
+
}
|
|
2267
|
+
return response;
|
|
2268
|
+
} catch (error) {
|
|
2269
|
+
const selected = await decision;
|
|
2270
|
+
if (selected) {
|
|
2271
|
+
await usageTap.captureSample({
|
|
2272
|
+
customerId: callContext.customerId,
|
|
2273
|
+
feature: callContext.feature,
|
|
2274
|
+
environment: callContext.environment,
|
|
2275
|
+
tags: callContext.tags,
|
|
2276
|
+
provider: "anthropic",
|
|
2277
|
+
model: readString(params.model),
|
|
2278
|
+
input: params,
|
|
2279
|
+
latencyMs: Date.now() - startedAt,
|
|
2280
|
+
error: error instanceof Error ? { name: error.name, message: error.message } : { message: String(error) }
|
|
2281
|
+
}).catch(() => void 0);
|
|
2282
|
+
}
|
|
2283
|
+
throw error;
|
|
2284
|
+
}
|
|
2285
|
+
};
|
|
2286
|
+
const messages = new Proxy(client.messages, {
|
|
2287
|
+
get(target, prop, receiver) {
|
|
2288
|
+
if (prop === "create") return create;
|
|
2289
|
+
return safeReflectGet(target, prop, receiver);
|
|
2290
|
+
}
|
|
2291
|
+
});
|
|
2292
|
+
return new Proxy(client, {
|
|
2293
|
+
get(target, prop, receiver) {
|
|
2294
|
+
if (prop === "messages") return messages;
|
|
2295
|
+
if (prop === "unwrap") return () => target;
|
|
2296
|
+
return safeReflectGet(target, prop, receiver);
|
|
2297
|
+
}
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2300
|
+
function safeReflectGet(target, prop, receiver) {
|
|
2301
|
+
return Reflect.get(target, prop, receiver);
|
|
2302
|
+
}
|
|
1619
2303
|
function withCompression(client, options = {}) {
|
|
1620
2304
|
if (!client?.messages || typeof client.messages.create !== "function") {
|
|
1621
2305
|
throw new UsageTapError(
|
|
@@ -1626,11 +2310,13 @@ function withCompression(client, options = {}) {
|
|
|
1626
2310
|
const {
|
|
1627
2311
|
apiKey,
|
|
1628
2312
|
usageTapClient,
|
|
2313
|
+
sampling,
|
|
1629
2314
|
...compressionOverrides
|
|
1630
2315
|
} = options;
|
|
1631
2316
|
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey });
|
|
1632
2317
|
const compression = {
|
|
1633
2318
|
provider: "usagetap",
|
|
2319
|
+
roles: { user: { enabled: true, aggressiveness: 0.2 } },
|
|
1634
2320
|
minContextTokens: DEFAULT_PROMPT_COMPRESSION_MIN_CONTEXT_TOKENS,
|
|
1635
2321
|
...compressionOverrides
|
|
1636
2322
|
};
|
|
@@ -1644,13 +2330,18 @@ function withCompression(client, options = {}) {
|
|
|
1644
2330
|
return Reflect.get(target, prop, receiver);
|
|
1645
2331
|
}
|
|
1646
2332
|
});
|
|
1647
|
-
|
|
2333
|
+
const compressed = new Proxy(client, {
|
|
1648
2334
|
get(target, prop, receiver) {
|
|
1649
2335
|
if (prop === "messages") return proxiedMessages;
|
|
1650
2336
|
if (prop === "unwrap") return () => target;
|
|
1651
2337
|
return Reflect.get(target, prop, receiver);
|
|
1652
2338
|
}
|
|
1653
2339
|
});
|
|
2340
|
+
return sampling ? withSampling(compressed, {
|
|
2341
|
+
...sampling === true ? {} : sampling,
|
|
2342
|
+
apiKey,
|
|
2343
|
+
usageTapClient
|
|
2344
|
+
}) : compressed;
|
|
1654
2345
|
}
|
|
1655
2346
|
function withMetering(client, customer) {
|
|
1656
2347
|
const config = typeof customer === "string" ? { customerId: customer } : customer;
|
|
@@ -1665,6 +2356,7 @@ function withMetering(client, customer) {
|
|
|
1665
2356
|
usageTapClient,
|
|
1666
2357
|
applyVendorHints,
|
|
1667
2358
|
promptCompression,
|
|
2359
|
+
sampling,
|
|
1668
2360
|
...defaultContext
|
|
1669
2361
|
} = config;
|
|
1670
2362
|
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey });
|
|
@@ -1672,7 +2364,8 @@ function withMetering(client, customer) {
|
|
|
1672
2364
|
return wrapAnthropic(client, usageTap, {
|
|
1673
2365
|
defaultContext,
|
|
1674
2366
|
applyVendorHints,
|
|
1675
|
-
promptCompression: normalizedCompression
|
|
2367
|
+
promptCompression: normalizedCompression,
|
|
2368
|
+
sampling
|
|
1676
2369
|
});
|
|
1677
2370
|
}
|
|
1678
2371
|
function wrapAnthropic(client, usageTap, options = {}) {
|
|
@@ -1685,6 +2378,7 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1685
2378
|
const defaultContext = options.defaultContext;
|
|
1686
2379
|
const applyVendorHints = options.applyVendorHints !== false;
|
|
1687
2380
|
const defaultPromptCompression = normalizePromptCompressionOptions(options.promptCompression);
|
|
2381
|
+
const defaultSampling = normalizeMeteredAnthropicSampling(options.sampling);
|
|
1688
2382
|
const promptCompressionStats = new AnthropicPromptCompressionStats();
|
|
1689
2383
|
const proxiedMessages = createMessagesProxy(
|
|
1690
2384
|
client.messages,
|
|
@@ -1692,7 +2386,8 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1692
2386
|
defaultContext,
|
|
1693
2387
|
applyVendorHints,
|
|
1694
2388
|
defaultPromptCompression,
|
|
1695
|
-
promptCompressionStats
|
|
2389
|
+
promptCompressionStats,
|
|
2390
|
+
defaultSampling
|
|
1696
2391
|
);
|
|
1697
2392
|
const handler = {
|
|
1698
2393
|
get(target, prop, receiver) {
|
|
@@ -1710,7 +2405,7 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1710
2405
|
};
|
|
1711
2406
|
return new Proxy(client, handler);
|
|
1712
2407
|
}
|
|
1713
|
-
function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
2408
|
+
function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling) {
|
|
1714
2409
|
if (typeof resource.create !== "function") {
|
|
1715
2410
|
throw new UsageTapError("USAGETAP_BAD_REQUEST", "wrapAnthropic requires client.messages.create");
|
|
1716
2411
|
}
|
|
@@ -1733,7 +2428,8 @@ function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHint
|
|
|
1733
2428
|
defaultContext,
|
|
1734
2429
|
applyVendorHints,
|
|
1735
2430
|
defaultPromptCompression,
|
|
1736
|
-
promptCompressionStats
|
|
2431
|
+
promptCompressionStats,
|
|
2432
|
+
defaultSampling
|
|
1737
2433
|
});
|
|
1738
2434
|
});
|
|
1739
2435
|
const handler = {
|
|
@@ -1747,13 +2443,26 @@ function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHint
|
|
|
1747
2443
|
return new Proxy(resource, handler);
|
|
1748
2444
|
}
|
|
1749
2445
|
async function invokeMessagesCreate(args) {
|
|
1750
|
-
const beginRequest =
|
|
2446
|
+
const beginRequest = withRuntimeCompressionContext(
|
|
2447
|
+
resolveBeginRequest(args.defaultContext, args.usageContext),
|
|
2448
|
+
"anthropic",
|
|
2449
|
+
String(args.params.model)
|
|
2450
|
+
);
|
|
1751
2451
|
const begin = await args.usageTap.beginCall(
|
|
1752
2452
|
beginRequest,
|
|
1753
2453
|
beginCallOptions(args.withUsage)
|
|
1754
2454
|
);
|
|
1755
2455
|
const state = createCallState(beginRequest, begin);
|
|
1756
2456
|
const ctx = createUsageContext(state);
|
|
2457
|
+
const sampleStartedAt = Date.now();
|
|
2458
|
+
const wantsStream = isStreamingRequest(args.params);
|
|
2459
|
+
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredAnthropicSampleDecision({
|
|
2460
|
+
usageTap: args.usageTap,
|
|
2461
|
+
sampling: args.defaultSampling,
|
|
2462
|
+
beginRequest,
|
|
2463
|
+
input: args.params
|
|
2464
|
+
});
|
|
2465
|
+
let providerCompleted = false;
|
|
1757
2466
|
try {
|
|
1758
2467
|
const hintedParams = args.applyVendorHints ? applyAnthropicVendorHints(args.params, ctx.begin.data.vendorHints) : args.params;
|
|
1759
2468
|
const finalParams = await compressAnthropicParamsForCall({
|
|
@@ -1766,8 +2475,10 @@ async function invokeMessagesCreate(args) {
|
|
|
1766
2475
|
withUsage: args.withUsage,
|
|
1767
2476
|
operation: "messages.create"
|
|
1768
2477
|
});
|
|
2478
|
+
ctx.setUsage(anthropicRequestExecutionMetadata(finalParams));
|
|
1769
2479
|
const request = attachCorrelationHeader(args.requestOptions, ctx.begin.correlationId);
|
|
1770
2480
|
const response = await args.originalCreate(finalParams, request);
|
|
2481
|
+
providerCompleted = true;
|
|
1771
2482
|
if (isStreamingRequest(finalParams)) {
|
|
1772
2483
|
ensureAsyncIterable(response, "messages.create");
|
|
1773
2484
|
return wrapAnthropicStreamForUsageTap(
|
|
@@ -1779,6 +2490,15 @@ async function invokeMessagesCreate(args) {
|
|
|
1779
2490
|
);
|
|
1780
2491
|
}
|
|
1781
2492
|
inferAnthropicUsage(response, readString(finalParams.model), ctx);
|
|
2493
|
+
await captureMeteredAnthropicSample({
|
|
2494
|
+
usageTap: args.usageTap,
|
|
2495
|
+
sampling: args.defaultSampling,
|
|
2496
|
+
decision: sampleDecision,
|
|
2497
|
+
state,
|
|
2498
|
+
input: args.params,
|
|
2499
|
+
response,
|
|
2500
|
+
startedAt: sampleStartedAt
|
|
2501
|
+
});
|
|
1782
2502
|
await finalizeCall(state, args.usageTap, args.withUsage);
|
|
1783
2503
|
return response;
|
|
1784
2504
|
} catch (error) {
|
|
@@ -1788,24 +2508,145 @@ async function invokeMessagesCreate(args) {
|
|
|
1788
2508
|
message: error instanceof Error ? error.message : String(error)
|
|
1789
2509
|
};
|
|
1790
2510
|
}
|
|
2511
|
+
if (!providerCompleted) {
|
|
2512
|
+
await captureMeteredAnthropicSample({
|
|
2513
|
+
usageTap: args.usageTap,
|
|
2514
|
+
sampling: args.defaultSampling,
|
|
2515
|
+
decision: sampleDecision,
|
|
2516
|
+
state,
|
|
2517
|
+
input: args.params,
|
|
2518
|
+
error,
|
|
2519
|
+
startedAt: sampleStartedAt
|
|
2520
|
+
});
|
|
2521
|
+
}
|
|
1791
2522
|
await finalizeCall(state, args.usageTap, args.withUsage);
|
|
1792
2523
|
throw error;
|
|
1793
2524
|
}
|
|
1794
2525
|
}
|
|
2526
|
+
function normalizeMeteredAnthropicSampling(options) {
|
|
2527
|
+
if (!options) return void 0;
|
|
2528
|
+
if (options === true) return {};
|
|
2529
|
+
return {
|
|
2530
|
+
policy: typeof options.rate === "number" ? options : void 0
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
function startMeteredAnthropicSampleDecision({
|
|
2534
|
+
usageTap,
|
|
2535
|
+
sampling,
|
|
2536
|
+
beginRequest,
|
|
2537
|
+
input
|
|
2538
|
+
}) {
|
|
2539
|
+
if (!sampling) return Promise.resolve(false);
|
|
2540
|
+
return usageTap.shouldSampleAsync(
|
|
2541
|
+
{
|
|
2542
|
+
customerId: beginRequest.customerId,
|
|
2543
|
+
feature: beginRequest.feature,
|
|
2544
|
+
input
|
|
2545
|
+
},
|
|
2546
|
+
sampling.policy
|
|
2547
|
+
);
|
|
2548
|
+
}
|
|
2549
|
+
async function captureMeteredAnthropicSample({
|
|
2550
|
+
usageTap,
|
|
2551
|
+
sampling,
|
|
2552
|
+
decision,
|
|
2553
|
+
state,
|
|
2554
|
+
input,
|
|
2555
|
+
response,
|
|
2556
|
+
error,
|
|
2557
|
+
startedAt
|
|
2558
|
+
}) {
|
|
2559
|
+
if (!sampling) return;
|
|
2560
|
+
let selected = false;
|
|
2561
|
+
try {
|
|
2562
|
+
selected = await decision;
|
|
2563
|
+
} catch {
|
|
2564
|
+
return;
|
|
2565
|
+
}
|
|
2566
|
+
if (!selected) return;
|
|
2567
|
+
const record = isObjectRecord(response) ? response : {};
|
|
2568
|
+
await usageTap.captureSample({
|
|
2569
|
+
sampleId: state.begin.data.callId,
|
|
2570
|
+
callId: state.begin.data.callId,
|
|
2571
|
+
customerId: state.beginRequest.customerId,
|
|
2572
|
+
feature: state.beginRequest.feature,
|
|
2573
|
+
tags: state.beginRequest.tags,
|
|
2574
|
+
provider: "anthropic",
|
|
2575
|
+
model: readString(record.model) ?? readString(input.model),
|
|
2576
|
+
input,
|
|
2577
|
+
...response === void 0 ? {} : { output: response },
|
|
2578
|
+
usage: record.usage,
|
|
2579
|
+
latencyMs: Date.now() - startedAt,
|
|
2580
|
+
...error === void 0 ? {} : {
|
|
2581
|
+
error: error instanceof Error ? { name: error.name, message: error.message } : { message: String(error) }
|
|
2582
|
+
}
|
|
2583
|
+
}).catch(() => void 0);
|
|
2584
|
+
}
|
|
1795
2585
|
async function compressAnthropicParamsForCall(args) {
|
|
1796
|
-
|
|
2586
|
+
let compression = resolveEffectivePromptCompressionOptions(
|
|
1797
2587
|
args.defaultPromptCompression,
|
|
1798
2588
|
args.callPromptCompression
|
|
1799
2589
|
);
|
|
2590
|
+
const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
|
|
2591
|
+
const policyDriven = !compression && Boolean(runtimePolicy);
|
|
2592
|
+
if (policyDriven && runtimePolicy) {
|
|
2593
|
+
if (!runtimePolicy.selected) {
|
|
2594
|
+
args.ctx.setUsage({
|
|
2595
|
+
runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
|
|
2596
|
+
});
|
|
2597
|
+
return args.params;
|
|
2598
|
+
}
|
|
2599
|
+
compression = anthropicPolicyCompressionOptions(runtimePolicy);
|
|
2600
|
+
if (runtimePolicy.mode === "SHADOW") {
|
|
2601
|
+
const startedAt2 = Date.now();
|
|
2602
|
+
void compressAnthropicParams(
|
|
2603
|
+
args.params,
|
|
2604
|
+
args.usageTap,
|
|
2605
|
+
compression,
|
|
2606
|
+
args.withUsage?.signal
|
|
2607
|
+
).then((outcome2) => {
|
|
2608
|
+
args.ctx.setUsage({
|
|
2609
|
+
runtimeCompression: anthropicRuntimeMeasurement({
|
|
2610
|
+
policy: runtimePolicy,
|
|
2611
|
+
telemetry: buildPromptCompressionTelemetry(outcome2.segments),
|
|
2612
|
+
latencyMs: Date.now() - startedAt2,
|
|
2613
|
+
shadow: true,
|
|
2614
|
+
originalPayload: args.params,
|
|
2615
|
+
resultPayload: outcome2.params
|
|
2616
|
+
})
|
|
2617
|
+
});
|
|
2618
|
+
}).catch(() => {
|
|
2619
|
+
args.ctx.setUsage({
|
|
2620
|
+
runtimeCompression: failedRuntimeMeasurement(
|
|
2621
|
+
runtimePolicy,
|
|
2622
|
+
Date.now() - startedAt2
|
|
2623
|
+
)
|
|
2624
|
+
});
|
|
2625
|
+
});
|
|
2626
|
+
return args.params;
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
1800
2629
|
if (!compression) {
|
|
1801
2630
|
return args.params;
|
|
1802
2631
|
}
|
|
2632
|
+
const startedAt = Date.now();
|
|
1803
2633
|
const outcome = await compressAnthropicParams(
|
|
1804
2634
|
args.params,
|
|
1805
2635
|
args.usageTap,
|
|
1806
2636
|
compression,
|
|
1807
2637
|
args.withUsage?.signal
|
|
1808
2638
|
);
|
|
2639
|
+
if (policyDriven && runtimePolicy) {
|
|
2640
|
+
const runtimeMeasurement = anthropicRuntimeMeasurement({
|
|
2641
|
+
policy: runtimePolicy,
|
|
2642
|
+
telemetry: buildPromptCompressionTelemetry(outcome.segments),
|
|
2643
|
+
latencyMs: Date.now() - startedAt,
|
|
2644
|
+
originalPayload: args.params,
|
|
2645
|
+
resultPayload: outcome.params
|
|
2646
|
+
});
|
|
2647
|
+
args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
|
|
2648
|
+
if (runtimeMeasurement.decision !== "compressed") return args.params;
|
|
2649
|
+
}
|
|
1809
2650
|
await recordCompressionOutcome({
|
|
1810
2651
|
outcome,
|
|
1811
2652
|
compression,
|
|
@@ -1817,6 +2658,76 @@ async function compressAnthropicParamsForCall(args) {
|
|
|
1817
2658
|
});
|
|
1818
2659
|
return outcome.params;
|
|
1819
2660
|
}
|
|
2661
|
+
function anthropicPolicyCompressionOptions(policy) {
|
|
2662
|
+
const roles = Object.fromEntries(
|
|
2663
|
+
["system", "user", "tool", "assistant"].filter((role) => policy.snapshot.scope.messageRoles.includes(role)).map((role) => [role, true])
|
|
2664
|
+
);
|
|
2665
|
+
return {
|
|
2666
|
+
provider: "usagetap",
|
|
2667
|
+
roles,
|
|
2668
|
+
minContextTokens: policy.snapshot.scope.minimumTokens,
|
|
2669
|
+
failOpen: true
|
|
2670
|
+
};
|
|
2671
|
+
}
|
|
2672
|
+
function bypassedRuntimeMeasurement(policy) {
|
|
2673
|
+
return {
|
|
2674
|
+
policyId: policy.policyId,
|
|
2675
|
+
policyVersionId: policy.policyVersionId,
|
|
2676
|
+
policyVersion: policy.version,
|
|
2677
|
+
rolloutMode: policy.mode,
|
|
2678
|
+
decision: "bypassed",
|
|
2679
|
+
reasonCode: "not_in_rollout_sample",
|
|
2680
|
+
methodsAttempted: [],
|
|
2681
|
+
methodsApplied: [],
|
|
2682
|
+
originalTokens: 0,
|
|
2683
|
+
resultTokens: 0,
|
|
2684
|
+
savedTokens: 0,
|
|
2685
|
+
reductionPercentage: 0,
|
|
2686
|
+
compressionLatencyMs: 0,
|
|
2687
|
+
compressionComputeCostUsd: 0,
|
|
2688
|
+
financialsEstimated: true
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
function failedRuntimeMeasurement(policy, latencyMs) {
|
|
2692
|
+
return {
|
|
2693
|
+
...bypassedRuntimeMeasurement(policy),
|
|
2694
|
+
decision: "fallback",
|
|
2695
|
+
reasonCode: "transformation_failed",
|
|
2696
|
+
methodsAttempted: ["deterministic"],
|
|
2697
|
+
compressionLatencyMs: latencyMs
|
|
2698
|
+
};
|
|
2699
|
+
}
|
|
2700
|
+
function anthropicRuntimeMeasurement(input) {
|
|
2701
|
+
const telemetry = input.telemetry;
|
|
2702
|
+
const originalTokens = telemetry?.originalTokens ?? estimatePromptTokens(JSON.stringify(input.originalPayload));
|
|
2703
|
+
const resultTokens = telemetry?.compressedTokens ?? originalTokens;
|
|
2704
|
+
const savedTokens = Math.max(0, telemetry?.savedTokens ?? 0);
|
|
2705
|
+
const reductionPercentage = originalTokens > 0 ? savedTokens / originalTokens * 100 : 0;
|
|
2706
|
+
const belowMinimumSize = originalTokens < input.policy.snapshot.scope.minimumTokens;
|
|
2707
|
+
const latencyExceeded = input.latencyMs > input.policy.snapshot.gates.maximumLatencyMs;
|
|
2708
|
+
const belowSavings = savedTokens < input.policy.snapshot.gates.minimumTokensRemoved || reductionPercentage < input.policy.snapshot.gates.minimumReductionPercentage;
|
|
2709
|
+
const accepted = !belowMinimumSize && !latencyExceeded && !belowSavings;
|
|
2710
|
+
const deepText = input.policy.snapshot.methods.deepText.enabled && !input.policy.snapshot.rollout.deterministicOnly && (input.policy.mode !== "CANARY" || input.policy.snapshot.rollout.deepTextCanaryAllowed);
|
|
2711
|
+
const reasonCode = belowMinimumSize ? "below_minimum_size" : latencyExceeded ? "latency_budget_exceeded" : belowSavings ? "below_minimum_savings" : void 0;
|
|
2712
|
+
const measurement = {
|
|
2713
|
+
policyId: input.policy.policyId,
|
|
2714
|
+
policyVersionId: input.policy.policyVersionId,
|
|
2715
|
+
policyVersion: input.policy.version,
|
|
2716
|
+
rolloutMode: input.policy.mode,
|
|
2717
|
+
decision: accepted ? input.shadow ? "bypassed" : "compressed" : latencyExceeded ? "fallback" : "skipped",
|
|
2718
|
+
...reasonCode ? { reasonCode } : {},
|
|
2719
|
+
methodsAttempted: ["deterministic", ...deepText ? ["deep_text"] : []],
|
|
2720
|
+
methodsApplied: accepted ? ["deterministic"] : [],
|
|
2721
|
+
originalTokens,
|
|
2722
|
+
resultTokens: accepted ? resultTokens : originalTokens,
|
|
2723
|
+
savedTokens: accepted ? savedTokens : 0,
|
|
2724
|
+
reductionPercentage: accepted ? reductionPercentage : 0,
|
|
2725
|
+
compressionLatencyMs: input.latencyMs,
|
|
2726
|
+
compressionComputeCostUsd: 0,
|
|
2727
|
+
financialsEstimated: true
|
|
2728
|
+
};
|
|
2729
|
+
return measurement;
|
|
2730
|
+
}
|
|
1820
2731
|
async function recordCompressionOutcome(args) {
|
|
1821
2732
|
const telemetry = buildPromptCompressionTelemetry(args.outcome.segments);
|
|
1822
2733
|
if (!telemetry) {
|
|
@@ -1871,6 +2782,10 @@ async function compressAnthropicParams(params, usageTap, compression, signal) {
|
|
|
1871
2782
|
const result = await usageTap.compressPromptMessages(source, {
|
|
1872
2783
|
provider: "usagetap",
|
|
1873
2784
|
failOpen: compression.failOpen,
|
|
2785
|
+
mode: compression.mode,
|
|
2786
|
+
latencyBudgetMs: compression.latencyBudgetMs,
|
|
2787
|
+
compactEmptyUserMessages: compression.compactEmptyUserMessages,
|
|
2788
|
+
compactDuplicateUserTextParts: compression.compactDuplicateUserTextParts,
|
|
1874
2789
|
aggressiveness: resolveMessageEndpointAggressiveness(compression),
|
|
1875
2790
|
signal
|
|
1876
2791
|
});
|
|
@@ -2255,10 +3170,16 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2255
3170
|
}
|
|
2256
3171
|
const tags = mergeTags(base.tags, current.tags);
|
|
2257
3172
|
const begin = { customerId };
|
|
3173
|
+
const runtimeCompressionContext = current.runtimeCompressionContext ?? base.runtimeCompressionContext;
|
|
3174
|
+
if (runtimeCompressionContext) {
|
|
3175
|
+
begin.runtimeCompressionContext = runtimeCompressionContext;
|
|
3176
|
+
}
|
|
2258
3177
|
const requested = current.requested ?? base.requested;
|
|
2259
3178
|
if (requested) begin.requested = requested;
|
|
2260
3179
|
const feature = current.feature ?? base.feature;
|
|
2261
3180
|
if (feature) begin.feature = feature;
|
|
3181
|
+
const runId = current.runId ?? base.runId;
|
|
3182
|
+
if (runId) begin.runId = runId;
|
|
2262
3183
|
const idempotency = current.idempotency ?? base.idempotency;
|
|
2263
3184
|
if (idempotency) begin.idempotency = idempotency;
|
|
2264
3185
|
const idempotencyKey = current.idempotencyKey ?? base.idempotencyKey;
|
|
@@ -2267,6 +3188,12 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2267
3188
|
if (customerName) begin.customerName = customerName;
|
|
2268
3189
|
const customerEmail = current.customerEmail ?? base.customerEmail;
|
|
2269
3190
|
if (customerEmail) begin.customerEmail = customerEmail;
|
|
3191
|
+
const customerUserId = current.customerUserId ?? base.customerUserId;
|
|
3192
|
+
if (customerUserId) begin.customerUserId = customerUserId;
|
|
3193
|
+
const customerUserName = current.customerUserName ?? base.customerUserName;
|
|
3194
|
+
if (customerUserName) begin.customerUserName = customerUserName;
|
|
3195
|
+
const customerUserEmail = current.customerUserEmail ?? base.customerUserEmail;
|
|
3196
|
+
if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
|
|
2270
3197
|
const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
|
|
2271
3198
|
if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
|
|
2272
3199
|
const holdUsd = current.holdUsd ?? base.holdUsd;
|
|
@@ -2282,6 +3209,11 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2282
3209
|
}
|
|
2283
3210
|
function createCallState(beginRequest, begin) {
|
|
2284
3211
|
const usage = {};
|
|
3212
|
+
const pricingMode = begin.data.pricingMode ?? beginRequest.pricingMode ?? (beginRequest.batch === true ? "batch" : beginRequest.batch === false ? "standard" : void 0);
|
|
3213
|
+
if (pricingMode) {
|
|
3214
|
+
usage.pricingMode = pricingMode;
|
|
3215
|
+
usage.batch = pricingMode === "batch";
|
|
3216
|
+
}
|
|
2285
3217
|
const stripeCustomerId = typeof begin.data.stripeCustomerId === "string" ? begin.data.stripeCustomerId : typeof beginRequest.stripeCustomerId === "string" ? beginRequest.stripeCustomerId : void 0;
|
|
2286
3218
|
if (stripeCustomerId) {
|
|
2287
3219
|
usage.stripeCustomerId = stripeCustomerId;
|
|
@@ -2345,10 +3277,12 @@ function inferAnthropicUsage(response, fallbackModel, ctx) {
|
|
|
2345
3277
|
}
|
|
2346
3278
|
function extractAnthropicUsage(payload, fallbackModel) {
|
|
2347
3279
|
if (!isObjectRecord(payload)) {
|
|
2348
|
-
return fallbackModel ? { modelUsed: fallbackModel } :
|
|
3280
|
+
return { providerUsed: "anthropic", ...fallbackModel ? { modelUsed: fallbackModel } : {} };
|
|
2349
3281
|
}
|
|
2350
3282
|
const usage = findAnthropicUsageRecord(payload);
|
|
2351
|
-
const result = {
|
|
3283
|
+
const result = {
|
|
3284
|
+
providerUsed: "anthropic"
|
|
3285
|
+
};
|
|
2352
3286
|
const model = readString(payload.model) ?? readString(payload.message?.model) ?? fallbackModel;
|
|
2353
3287
|
if (model) {
|
|
2354
3288
|
result.modelUsed = model;
|
|
@@ -2358,6 +3292,21 @@ function extractAnthropicUsage(payload, fallbackModel) {
|
|
|
2358
3292
|
}
|
|
2359
3293
|
return Object.keys(result).length ? result : void 0;
|
|
2360
3294
|
}
|
|
3295
|
+
function anthropicRequestExecutionMetadata(params) {
|
|
3296
|
+
const outputConfig = isObjectRecord(params.output_config) ? params.output_config : void 0;
|
|
3297
|
+
const thinking = isObjectRecord(params.thinking) ? params.thinking : void 0;
|
|
3298
|
+
const rawEffort = outputConfig?.effort;
|
|
3299
|
+
const effort = rawEffort === "none" || rawEffort === "minimal" || rawEffort === "low" || rawEffort === "medium" || rawEffort === "high" || rawEffort === "xhigh" || rawEffort === "max" ? rawEffort : void 0;
|
|
3300
|
+
const mode = readString(thinking?.type);
|
|
3301
|
+
const rawBudget = readNumber(thinking?.budget_tokens);
|
|
3302
|
+
const budget = rawBudget !== void 0 && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
|
|
3303
|
+
return {
|
|
3304
|
+
providerUsed: "anthropic",
|
|
3305
|
+
...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
|
|
3306
|
+
...mode ? { reasoningMode: mode } : {},
|
|
3307
|
+
...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
|
|
3308
|
+
};
|
|
3309
|
+
}
|
|
2361
3310
|
function findAnthropicUsageRecord(payload) {
|
|
2362
3311
|
if (isObjectRecord(payload.usage)) {
|
|
2363
3312
|
return payload.usage;
|
|
@@ -2368,10 +3317,6 @@ function findAnthropicUsageRecord(payload) {
|
|
|
2368
3317
|
return void 0;
|
|
2369
3318
|
}
|
|
2370
3319
|
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
3320
|
const outputTokens = readNumber(usageRecord.output_tokens ?? usageRecord.completion_tokens);
|
|
2376
3321
|
if (outputTokens !== void 0) {
|
|
2377
3322
|
usage.responseTokens = outputTokens;
|
|
@@ -2382,6 +3327,26 @@ function applyAnthropicUsageRecord(usage, usageRecord) {
|
|
|
2382
3327
|
if (cachedInputTokens !== void 0) {
|
|
2383
3328
|
usage.cachedInputTokens = cachedInputTokens;
|
|
2384
3329
|
}
|
|
3330
|
+
const cacheWriteInputTokens = readNumber(
|
|
3331
|
+
usageRecord.cache_creation_input_tokens ?? usageRecord.prompt_cache_miss_tokens ?? usageRecord.cache_write_tokens
|
|
3332
|
+
);
|
|
3333
|
+
if (cacheWriteInputTokens !== void 0) {
|
|
3334
|
+
usage.cacheWriteInputTokens = cacheWriteInputTokens;
|
|
3335
|
+
}
|
|
3336
|
+
const baseInputTokens = readNumber(usageRecord.input_tokens ?? usageRecord.prompt_tokens);
|
|
3337
|
+
if (baseInputTokens !== void 0) {
|
|
3338
|
+
usage.inputTokens = baseInputTokens + (cachedInputTokens ?? 0) + (cacheWriteInputTokens ?? 0);
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
function withRuntimeCompressionContext(begin, provider, model) {
|
|
3342
|
+
return {
|
|
3343
|
+
...begin,
|
|
3344
|
+
runtimeCompressionContext: {
|
|
3345
|
+
...begin.runtimeCompressionContext ?? {},
|
|
3346
|
+
provider,
|
|
3347
|
+
model
|
|
3348
|
+
}
|
|
3349
|
+
};
|
|
2385
3350
|
}
|
|
2386
3351
|
function accumulateAnthropicStreamUsage(chunk, fallbackModel, state) {
|
|
2387
3352
|
const usage = extractAnthropicUsage(chunk, fallbackModel);
|
|
@@ -2608,6 +3573,7 @@ function isIteratorResult(value) {
|
|
|
2608
3573
|
exports.AnthropicPromptCompressionStats = AnthropicPromptCompressionStats;
|
|
2609
3574
|
exports.withCompression = withCompression;
|
|
2610
3575
|
exports.withMetering = withMetering;
|
|
3576
|
+
exports.withSampling = withSampling;
|
|
2611
3577
|
exports.wrapAnthropic = wrapAnthropic;
|
|
2612
3578
|
//# sourceMappingURL=anthropic.cjs.map
|
|
2613
3579
|
//# sourceMappingURL=anthropic.cjs.map
|