@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
|
@@ -240,6 +240,11 @@ async function compressMessagesWithUsageTap(options) {
|
|
|
240
240
|
aggressiveness,
|
|
241
241
|
"UsageTap prompt message compression"
|
|
242
242
|
);
|
|
243
|
+
if (options.latencyBudgetMs !== void 0 && (!Number.isFinite(options.latencyBudgetMs) || options.latencyBudgetMs < 0)) {
|
|
244
|
+
throw new Error(
|
|
245
|
+
"UsageTap prompt message compression latencyBudgetMs must be a non-negative number"
|
|
246
|
+
);
|
|
247
|
+
}
|
|
243
248
|
const original = stableStringifyInput(options.input);
|
|
244
249
|
const headers = {
|
|
245
250
|
"content-type": "application/json"
|
|
@@ -254,7 +259,15 @@ async function compressMessagesWithUsageTap(options) {
|
|
|
254
259
|
headers,
|
|
255
260
|
body: JSON.stringify({
|
|
256
261
|
...cloneInputRecord(options.input),
|
|
257
|
-
compression_settings: {
|
|
262
|
+
compression_settings: {
|
|
263
|
+
aggressiveness,
|
|
264
|
+
...options.mode === void 0 ? {} : { mode: options.mode },
|
|
265
|
+
...options.latencyBudgetMs === void 0 ? {} : { latency_budget_ms: options.latencyBudgetMs },
|
|
266
|
+
...options.compactEmptyUserMessages === void 0 ? {} : { compact_empty_user_messages: options.compactEmptyUserMessages },
|
|
267
|
+
...options.compactDuplicateUserTextParts === void 0 ? {} : {
|
|
268
|
+
compact_duplicate_user_text_parts: options.compactDuplicateUserTextParts
|
|
269
|
+
}
|
|
270
|
+
}
|
|
258
271
|
}),
|
|
259
272
|
signal: options.signal
|
|
260
273
|
}
|
|
@@ -750,10 +763,358 @@ function scalarToToon(value) {
|
|
|
750
763
|
return JSON.stringify(text);
|
|
751
764
|
}
|
|
752
765
|
|
|
766
|
+
// src/resources.ts
|
|
767
|
+
var CANONICAL_MEDIA_TYPE = "application/vnd.usagetap.v1+json";
|
|
768
|
+
var DEFAULT_GATEWAY_BASE_URL = "https://gateway.usagetap.com";
|
|
769
|
+
function normalizedBaseUrl(value) {
|
|
770
|
+
return `${value.replace(/\/+$/, "")}/`;
|
|
771
|
+
}
|
|
772
|
+
function errorMessage(payload, status) {
|
|
773
|
+
if (payload && typeof payload === "object") {
|
|
774
|
+
const record = payload;
|
|
775
|
+
const error = record.error;
|
|
776
|
+
if (error && typeof error === "object") {
|
|
777
|
+
const message2 = error.message;
|
|
778
|
+
if (typeof message2 === "string" && message2) return message2;
|
|
779
|
+
}
|
|
780
|
+
const message = record.message;
|
|
781
|
+
if (typeof message === "string" && message) return message;
|
|
782
|
+
}
|
|
783
|
+
return `UsageTap request failed with HTTP ${status}`;
|
|
784
|
+
}
|
|
785
|
+
function errorCode(status) {
|
|
786
|
+
if (status === 401 || status === 403) return "USAGETAP_AUTH_ERROR";
|
|
787
|
+
if (status === 429) return "USAGETAP_RATE_LIMITED";
|
|
788
|
+
if (status >= 500) return "USAGETAP_SERVER_ERROR";
|
|
789
|
+
return "USAGETAP_BAD_REQUEST";
|
|
790
|
+
}
|
|
791
|
+
var ResourceTransport = class {
|
|
792
|
+
baseUrl;
|
|
793
|
+
apiKey;
|
|
794
|
+
fetchImpl;
|
|
795
|
+
defaultHeaders;
|
|
796
|
+
sdkVersion;
|
|
797
|
+
constructor(baseUrl, config) {
|
|
798
|
+
this.baseUrl = normalizedBaseUrl(baseUrl);
|
|
799
|
+
this.apiKey = config.apiKey;
|
|
800
|
+
this.fetchImpl = config.fetchImpl;
|
|
801
|
+
this.defaultHeaders = config.headers ?? {};
|
|
802
|
+
this.sdkVersion = config.sdkVersion;
|
|
803
|
+
}
|
|
804
|
+
async request(request) {
|
|
805
|
+
const body = request.body === void 0 ? void 0 : JSON.stringify(request.body);
|
|
806
|
+
const headers = {
|
|
807
|
+
...this.defaultHeaders,
|
|
808
|
+
accept: request.response === "data" ? CANONICAL_MEDIA_TYPE : request.response === "ndjson" ? "application/x-ndjson" : "application/json",
|
|
809
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
810
|
+
"x-usage-sdk": `js/${this.sdkVersion}`,
|
|
811
|
+
...body ? { "content-type": "application/json" } : {},
|
|
812
|
+
...request.options?.idempotencyKey ? { "idempotency-key": request.options.idempotencyKey } : {},
|
|
813
|
+
...request.options?.headers
|
|
814
|
+
};
|
|
815
|
+
let response;
|
|
816
|
+
try {
|
|
817
|
+
response = await this.fetchImpl(
|
|
818
|
+
new URL(request.path.replace(/^\/+/, ""), this.baseUrl),
|
|
819
|
+
{
|
|
820
|
+
method: request.method,
|
|
821
|
+
headers,
|
|
822
|
+
body,
|
|
823
|
+
signal: request.options?.signal
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
} catch (error) {
|
|
827
|
+
throw new UsageTapError(
|
|
828
|
+
"USAGETAP_NETWORK_ERROR",
|
|
829
|
+
"Failed to reach UsageTap",
|
|
830
|
+
{ retryable: true, cause: error }
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
const requestId = response.headers.get("x-request-id") ?? response.headers.get("x-usage-correlation-id") ?? void 0;
|
|
834
|
+
const text = await response.text();
|
|
835
|
+
let payload;
|
|
836
|
+
if (text && request.response !== "ndjson") {
|
|
837
|
+
try {
|
|
838
|
+
payload = JSON.parse(text);
|
|
839
|
+
} catch (error) {
|
|
840
|
+
throw new UsageTapError(
|
|
841
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
842
|
+
"UsageTap returned invalid JSON",
|
|
843
|
+
{ status: response.status, correlationId: requestId, cause: error }
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (!response.ok) {
|
|
848
|
+
throw new UsageTapError(
|
|
849
|
+
errorCode(response.status),
|
|
850
|
+
errorMessage(payload, response.status),
|
|
851
|
+
{
|
|
852
|
+
status: response.status,
|
|
853
|
+
retryable: response.status === 429 || response.status >= 500,
|
|
854
|
+
correlationId: requestId,
|
|
855
|
+
details: payload && typeof payload === "object" ? payload : void 0
|
|
856
|
+
}
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
if (request.response === "ndjson") {
|
|
860
|
+
if (!text.trim()) return [];
|
|
861
|
+
try {
|
|
862
|
+
return text.trim().split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line));
|
|
863
|
+
} catch (error) {
|
|
864
|
+
throw new UsageTapError(
|
|
865
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
866
|
+
"UsageTap returned invalid NDJSON",
|
|
867
|
+
{ status: response.status, correlationId: requestId, cause: error }
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (request.response === "data") {
|
|
872
|
+
if (!payload || typeof payload !== "object" || !("data" in payload)) {
|
|
873
|
+
throw new UsageTapError(
|
|
874
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
875
|
+
"UsageTap response missing data",
|
|
876
|
+
{ status: response.status, correlationId: requestId }
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
return payload.data;
|
|
880
|
+
}
|
|
881
|
+
if (payload === void 0) {
|
|
882
|
+
throw new UsageTapError(
|
|
883
|
+
"USAGETAP_INVALID_RESPONSE",
|
|
884
|
+
"UsageTap response was empty",
|
|
885
|
+
{ status: response.status, correlationId: requestId }
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
return payload;
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
function resourceId(value, keys, label) {
|
|
892
|
+
const id = typeof value === "string" ? value : keys.map((key) => value[key]).find((candidate) => Boolean(candidate?.trim()));
|
|
893
|
+
if (!id?.trim()) {
|
|
894
|
+
throw new UsageTapError(
|
|
895
|
+
"USAGETAP_BAD_REQUEST",
|
|
896
|
+
`${label} requires a non-empty ID`
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
return id.trim();
|
|
900
|
+
}
|
|
901
|
+
function terminalSummary(status) {
|
|
902
|
+
return status === "COMPLETE" || status === "FAILED";
|
|
903
|
+
}
|
|
904
|
+
function terminalGatewayBatch(status) {
|
|
905
|
+
return ["completed", "failed", "expired", "cancelled"].includes(status);
|
|
906
|
+
}
|
|
907
|
+
function validateWaitOptions(options) {
|
|
908
|
+
const pollIntervalMs = options.pollIntervalMs ?? 1500;
|
|
909
|
+
const timeoutMs = options.timeoutMs ?? 3 * 6e4;
|
|
910
|
+
if (!Number.isFinite(pollIntervalMs) || pollIntervalMs < 0) {
|
|
911
|
+
throw new UsageTapError(
|
|
912
|
+
"USAGETAP_BAD_REQUEST",
|
|
913
|
+
"pollIntervalMs must be a non-negative number"
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs < 1) {
|
|
917
|
+
throw new UsageTapError(
|
|
918
|
+
"USAGETAP_BAD_REQUEST",
|
|
919
|
+
"timeoutMs must be a positive number"
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
return { pollIntervalMs, timeoutMs };
|
|
923
|
+
}
|
|
924
|
+
var SummarizationResource = class {
|
|
925
|
+
summaries;
|
|
926
|
+
batches;
|
|
927
|
+
profiles;
|
|
928
|
+
measurements;
|
|
929
|
+
transport;
|
|
930
|
+
constructor(config) {
|
|
931
|
+
this.transport = new ResourceTransport(config.apiBaseUrl, config);
|
|
932
|
+
this.summaries = {
|
|
933
|
+
create: (params, options) => this.transport.request({
|
|
934
|
+
method: "POST",
|
|
935
|
+
path: "/v1/compression/summaries",
|
|
936
|
+
body: params,
|
|
937
|
+
options,
|
|
938
|
+
response: "data"
|
|
939
|
+
}),
|
|
940
|
+
retrieve: (jobId, options) => this.transport.request({
|
|
941
|
+
method: "GET",
|
|
942
|
+
path: `/v1/compression/jobs/${encodeURIComponent(
|
|
943
|
+
resourceId(jobId, ["jobId"], "summaries.retrieve")
|
|
944
|
+
)}`,
|
|
945
|
+
options,
|
|
946
|
+
response: "data"
|
|
947
|
+
}),
|
|
948
|
+
wait: (job, options) => this.waitForSummary(job, options)
|
|
949
|
+
};
|
|
950
|
+
this.batches = {
|
|
951
|
+
create: (params, options) => this.transport.request({
|
|
952
|
+
method: "POST",
|
|
953
|
+
path: "/v1/compression/batches",
|
|
954
|
+
body: params,
|
|
955
|
+
options,
|
|
956
|
+
response: "data"
|
|
957
|
+
}),
|
|
958
|
+
retrieve: (batchId, options) => this.transport.request({
|
|
959
|
+
method: "GET",
|
|
960
|
+
path: `/v1/compression/batches/${encodeURIComponent(
|
|
961
|
+
resourceId(batchId, ["batchId"], "summarization.batches.retrieve")
|
|
962
|
+
)}`,
|
|
963
|
+
options,
|
|
964
|
+
response: "data"
|
|
965
|
+
}),
|
|
966
|
+
wait: (batch, options) => this.waitForBatch(batch, options)
|
|
967
|
+
};
|
|
968
|
+
this.profiles = {
|
|
969
|
+
retrieve: (profile, options) => this.transport.request({
|
|
970
|
+
method: "GET",
|
|
971
|
+
path: `/v1/compression/profiles/${encodeURIComponent(
|
|
972
|
+
resourceId(profile, [], "summarization.profiles.retrieve")
|
|
973
|
+
)}`,
|
|
974
|
+
options,
|
|
975
|
+
response: "data"
|
|
976
|
+
})
|
|
977
|
+
};
|
|
978
|
+
this.measurements = {
|
|
979
|
+
create: (params, options) => this.transport.request({
|
|
980
|
+
method: "POST",
|
|
981
|
+
path: "/v1/compression/measurements",
|
|
982
|
+
body: params,
|
|
983
|
+
options,
|
|
984
|
+
response: "data"
|
|
985
|
+
})
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
async waitForSummary(value, options = {}) {
|
|
989
|
+
const { pollIntervalMs, timeoutMs } = validateWaitOptions(options);
|
|
990
|
+
const deadline = Date.now() + timeoutMs;
|
|
991
|
+
let job = typeof value === "string" ? await this.summaries.retrieve(value, options) : value;
|
|
992
|
+
while (!terminalSummary(job.status)) {
|
|
993
|
+
if (Date.now() >= deadline) {
|
|
994
|
+
throw new UsageTapError(
|
|
995
|
+
"USAGETAP_RETRY_EXHAUSTED",
|
|
996
|
+
`Summarization job ${job.jobId} did not finish before timeout`,
|
|
997
|
+
{ retryable: true }
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
await sleep(pollIntervalMs, options.signal);
|
|
1001
|
+
job = await this.summaries.retrieve(job.jobId, options);
|
|
1002
|
+
}
|
|
1003
|
+
return job;
|
|
1004
|
+
}
|
|
1005
|
+
async waitForBatch(value, options = {}) {
|
|
1006
|
+
const { pollIntervalMs, timeoutMs } = validateWaitOptions(options);
|
|
1007
|
+
const deadline = Date.now() + timeoutMs;
|
|
1008
|
+
let batch = typeof value === "string" ? await this.batches.retrieve(value, options) : value;
|
|
1009
|
+
while (!terminalSummary(batch.status)) {
|
|
1010
|
+
if (Date.now() >= deadline) {
|
|
1011
|
+
throw new UsageTapError(
|
|
1012
|
+
"USAGETAP_RETRY_EXHAUSTED",
|
|
1013
|
+
`Summarization batch ${batch.batchId} did not finish before timeout`,
|
|
1014
|
+
{ retryable: true }
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
await sleep(pollIntervalMs, options.signal);
|
|
1018
|
+
batch = await this.batches.retrieve(batch.batchId, options);
|
|
1019
|
+
}
|
|
1020
|
+
return batch;
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
var GatewayResource = class {
|
|
1024
|
+
chat;
|
|
1025
|
+
models;
|
|
1026
|
+
batches;
|
|
1027
|
+
transport;
|
|
1028
|
+
idempotencyGenerator;
|
|
1029
|
+
constructor(config) {
|
|
1030
|
+
this.transport = new ResourceTransport(
|
|
1031
|
+
config.gatewayBaseUrl ?? DEFAULT_GATEWAY_BASE_URL,
|
|
1032
|
+
config
|
|
1033
|
+
);
|
|
1034
|
+
this.idempotencyGenerator = config.idempotencyGenerator ?? createIdempotencyKey;
|
|
1035
|
+
this.chat = {
|
|
1036
|
+
completions: {
|
|
1037
|
+
create: (params, options) => this.transport.request({
|
|
1038
|
+
method: "POST",
|
|
1039
|
+
path: "/v1/chat/completions",
|
|
1040
|
+
body: params,
|
|
1041
|
+
options,
|
|
1042
|
+
response: "json"
|
|
1043
|
+
})
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
this.models = {
|
|
1047
|
+
list: (options) => this.transport.request({
|
|
1048
|
+
method: "GET",
|
|
1049
|
+
path: "/v1/models",
|
|
1050
|
+
options,
|
|
1051
|
+
response: "json"
|
|
1052
|
+
})
|
|
1053
|
+
};
|
|
1054
|
+
this.batches = {
|
|
1055
|
+
create: (params, options = {}) => this.transport.request({
|
|
1056
|
+
method: "POST",
|
|
1057
|
+
path: "/v1/batches",
|
|
1058
|
+
body: params,
|
|
1059
|
+
options: {
|
|
1060
|
+
...options,
|
|
1061
|
+
idempotencyKey: options.idempotencyKey ?? this.idempotencyGenerator()
|
|
1062
|
+
},
|
|
1063
|
+
response: "json"
|
|
1064
|
+
}),
|
|
1065
|
+
retrieve: (batchId, options) => this.transport.request({
|
|
1066
|
+
method: "GET",
|
|
1067
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1068
|
+
resourceId(batchId, ["id"], "gateway.batches.retrieve")
|
|
1069
|
+
)}`,
|
|
1070
|
+
options,
|
|
1071
|
+
response: "json"
|
|
1072
|
+
}),
|
|
1073
|
+
wait: (batch, options) => this.waitForBatch(batch, options),
|
|
1074
|
+
cancel: (batchId, options) => this.transport.request({
|
|
1075
|
+
method: "POST",
|
|
1076
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1077
|
+
resourceId(batchId, ["id"], "gateway.batches.cancel")
|
|
1078
|
+
)}/cancel`,
|
|
1079
|
+
options,
|
|
1080
|
+
response: "json"
|
|
1081
|
+
}),
|
|
1082
|
+
results: (batchId, options) => this.transport.request({
|
|
1083
|
+
method: "GET",
|
|
1084
|
+
path: `/v1/batches/${encodeURIComponent(
|
|
1085
|
+
resourceId(batchId, ["id"], "gateway.batches.results")
|
|
1086
|
+
)}/results`,
|
|
1087
|
+
options,
|
|
1088
|
+
response: "ndjson"
|
|
1089
|
+
})
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
async waitForBatch(value, options = {}) {
|
|
1093
|
+
const { pollIntervalMs, timeoutMs } = validateWaitOptions(options);
|
|
1094
|
+
const deadline = Date.now() + timeoutMs;
|
|
1095
|
+
let batch = typeof value === "string" ? await this.batches.retrieve(value, options) : value;
|
|
1096
|
+
while (!terminalGatewayBatch(batch.status)) {
|
|
1097
|
+
if (Date.now() >= deadline) {
|
|
1098
|
+
throw new UsageTapError(
|
|
1099
|
+
"USAGETAP_RETRY_EXHAUSTED",
|
|
1100
|
+
`Gateway batch ${batch.id} did not finish before timeout`,
|
|
1101
|
+
{ retryable: true }
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
await sleep(pollIntervalMs, options.signal);
|
|
1105
|
+
batch = await this.batches.retrieve(batch.id, options);
|
|
1106
|
+
}
|
|
1107
|
+
return batch;
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
|
|
753
1111
|
// src/client.ts
|
|
754
1112
|
var CALL_BEGIN_PATH = "call_begin";
|
|
755
1113
|
var CALL_END_PATH = "call_end";
|
|
756
1114
|
var COMPRESS_PROMPT_PATH = "compress_prompt";
|
|
1115
|
+
var SAMPLES_PATH = "samples";
|
|
1116
|
+
var SAMPLING_SETTINGS_PATH = "sampling/settings";
|
|
1117
|
+
var SAMPLING_DECIDE_PATH = "sampling/decide";
|
|
757
1118
|
var CHECK_USAGE_PATH = "customers/{customerId}/usage";
|
|
758
1119
|
var CREATE_CUSTOMER_PATH = "customers";
|
|
759
1120
|
var CHANGE_PLAN_PATH = "customers/{customerId}/change_plan";
|
|
@@ -764,11 +1125,16 @@ var CORRELATION_HEADER = "x-usage-correlation-id";
|
|
|
764
1125
|
var IDEMPOTENCY_HEADER = "idempotency-key";
|
|
765
1126
|
var SDK_HEADER = "x-usage-sdk";
|
|
766
1127
|
var USER_AGENT = "UsageTapClient";
|
|
767
|
-
var
|
|
1128
|
+
var CANONICAL_MEDIA_TYPE2 = "application/vnd.usagetap.v1+json";
|
|
768
1129
|
var DEFAULT_BASE_URL = "https://api.usagetap.com";
|
|
769
|
-
var
|
|
1130
|
+
var DEFAULT_RUN_INACTIVITY_MS = 60 * 60 * 1e3;
|
|
1131
|
+
var SDK_VERSION = "1.4.0" ;
|
|
770
1132
|
var HAS_WINDOW = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
771
1133
|
var UsageTapClient = class {
|
|
1134
|
+
/** OpenAI-compatible chat, model, and native batch operations. */
|
|
1135
|
+
gateway;
|
|
1136
|
+
/** Published-profile context summarization operations. */
|
|
1137
|
+
summarization;
|
|
772
1138
|
apiKey;
|
|
773
1139
|
baseUrl;
|
|
774
1140
|
fetchImpl;
|
|
@@ -793,6 +1159,11 @@ var UsageTapClient = class {
|
|
|
793
1159
|
usageTapCompressionMessagesEndpoint;
|
|
794
1160
|
usageTapCompressionModel;
|
|
795
1161
|
usageTapCompressionAggressiveness;
|
|
1162
|
+
sampling;
|
|
1163
|
+
samplingSettingsCacheMs;
|
|
1164
|
+
circuitBreaker;
|
|
1165
|
+
circuitBreakerRuns = /* @__PURE__ */ new Map();
|
|
1166
|
+
samplingSettingsCache;
|
|
796
1167
|
constructor(options = {}) {
|
|
797
1168
|
const apiKey = options.apiKey?.trim() || readEnvironmentVariable("USAGETAP_API_KEY");
|
|
798
1169
|
const baseUrl = options.baseUrl?.trim() || readEnvironmentVariable("USAGETAP_BASE_URL") || DEFAULT_BASE_URL;
|
|
@@ -815,10 +1186,21 @@ var UsageTapClient = class {
|
|
|
815
1186
|
"A global fetch implementation was not found. Pass fetchImpl in UsageTapClientOptions."
|
|
816
1187
|
);
|
|
817
1188
|
}
|
|
818
|
-
const
|
|
819
|
-
this.baseUrl = new URL(
|
|
1189
|
+
const normalizedBaseUrl2 = normalizeBaseUrl(baseUrl);
|
|
1190
|
+
this.baseUrl = new URL(normalizedBaseUrl2);
|
|
820
1191
|
this.apiKey = apiKey;
|
|
821
1192
|
this.fetchImpl = wrapFetchImplementation(fetchCandidate, !options.fetchImpl);
|
|
1193
|
+
const resourceConfig = {
|
|
1194
|
+
apiKey,
|
|
1195
|
+
apiBaseUrl: normalizedBaseUrl2,
|
|
1196
|
+
gatewayBaseUrl: options.gatewayBaseUrl?.trim() || readEnvironmentVariable("USAGETAP_GATEWAY_URL"),
|
|
1197
|
+
fetchImpl: this.fetchImpl,
|
|
1198
|
+
headers: options.headers,
|
|
1199
|
+
sdkVersion: SDK_VERSION,
|
|
1200
|
+
idempotencyGenerator: options.idempotencyGenerator
|
|
1201
|
+
};
|
|
1202
|
+
this.gateway = new GatewayResource(resourceConfig);
|
|
1203
|
+
this.summarization = new SummarizationResource(resourceConfig);
|
|
822
1204
|
this.defaultFeature = options.defaultFeature;
|
|
823
1205
|
this.defaultTags = options.defaultTags?.length ? dedupeStrings(options.defaultTags) : void 0;
|
|
824
1206
|
this.defaultHeaders = options.headers ? normalizeHeaderDictionary(options.headers) : {};
|
|
@@ -840,11 +1222,122 @@ var UsageTapClient = class {
|
|
|
840
1222
|
this.usageTapCompressionMessagesEndpoint = options.usageTapCompressionMessagesEndpoint;
|
|
841
1223
|
this.usageTapCompressionModel = options.usageTapCompressionModel;
|
|
842
1224
|
this.usageTapCompressionAggressiveness = options.usageTapCompressionAggressiveness;
|
|
1225
|
+
this.sampling = options.sampling;
|
|
1226
|
+
this.samplingSettingsCacheMs = Number.isFinite(options.samplingSettingsCacheMs) ? Math.max(0, Number(options.samplingSettingsCacheMs)) : 5 * 60 * 1e3;
|
|
1227
|
+
if (options.circuitBreaker) {
|
|
1228
|
+
const maxCallsPerRun = options.circuitBreaker.maxCallsPerRun;
|
|
1229
|
+
if (!Number.isInteger(maxCallsPerRun) || maxCallsPerRun < 1) {
|
|
1230
|
+
throw new UsageTapError(
|
|
1231
|
+
"USAGETAP_BAD_REQUEST",
|
|
1232
|
+
"circuitBreaker.maxCallsPerRun must be a positive integer"
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
const runInactivityMs = options.circuitBreaker.runInactivityMs ?? DEFAULT_RUN_INACTIVITY_MS;
|
|
1236
|
+
if (!Number.isFinite(runInactivityMs) || runInactivityMs < 1) {
|
|
1237
|
+
throw new UsageTapError(
|
|
1238
|
+
"USAGETAP_BAD_REQUEST",
|
|
1239
|
+
"circuitBreaker.runInactivityMs must be a positive number"
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
this.circuitBreaker = {
|
|
1243
|
+
maxCallsPerRun,
|
|
1244
|
+
runInactivityMs
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
shouldSample(request, policy = this.sampling || void 0) {
|
|
1249
|
+
if (!policy) return false;
|
|
1250
|
+
const rate = Math.min(1, Math.max(0, Number(policy.rate) || 0));
|
|
1251
|
+
if (rate <= 0) return false;
|
|
1252
|
+
const customerId = request.customerId?.trim();
|
|
1253
|
+
if (customerId && policy.customers?.exclude?.includes(customerId)) return false;
|
|
1254
|
+
const feature = request.feature?.trim();
|
|
1255
|
+
if (feature && policy.features?.exclude?.includes(feature)) return false;
|
|
1256
|
+
const included = policy.features?.include?.filter(Boolean) ?? [];
|
|
1257
|
+
if (included.length > 0 && (!feature || !included.includes(feature))) return false;
|
|
1258
|
+
const minimum = Math.max(0, Math.round(policy.minInputTokens ?? 0));
|
|
1259
|
+
if (minimum > 0 && estimatePromptTokens(request.input) < minimum) return false;
|
|
1260
|
+
return (policy.random ?? Math.random)() < rate;
|
|
1261
|
+
}
|
|
1262
|
+
async getSamplingSettings(options = {}) {
|
|
1263
|
+
const now = Date.now();
|
|
1264
|
+
if (!options.forceRefresh && this.samplingSettingsCache && this.samplingSettingsCache.expiresAtMs > now) {
|
|
1265
|
+
return {
|
|
1266
|
+
result: { status: "ACCEPTED", code: "SAMPLING_SETTINGS_CACHED" },
|
|
1267
|
+
data: this.samplingSettingsCache.settings,
|
|
1268
|
+
correlationId: options.correlationId ?? "local-cache"
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
const response = await this.requestGet(
|
|
1272
|
+
SAMPLING_SETTINGS_PATH,
|
|
1273
|
+
{
|
|
1274
|
+
signal: options.signal,
|
|
1275
|
+
headers: options.headers,
|
|
1276
|
+
retries: options.retries,
|
|
1277
|
+
correlationId: options.correlationId
|
|
1278
|
+
}
|
|
1279
|
+
);
|
|
1280
|
+
const serverCacheMs = Math.max(0, Number(response.data.cacheSeconds) || 0) * 1e3;
|
|
1281
|
+
const cacheMs = Math.min(this.samplingSettingsCacheMs, serverCacheMs);
|
|
1282
|
+
this.samplingSettingsCache = {
|
|
1283
|
+
settings: response.data,
|
|
1284
|
+
expiresAtMs: now + cacheMs
|
|
1285
|
+
};
|
|
1286
|
+
return response;
|
|
1287
|
+
}
|
|
1288
|
+
async shouldSampleAsync(request, policy) {
|
|
1289
|
+
if (policy) return this.shouldSample(request, policy);
|
|
1290
|
+
if (this.sampling === false) return false;
|
|
1291
|
+
if (this.sampling) return this.shouldSample(request, this.sampling);
|
|
1292
|
+
try {
|
|
1293
|
+
const settings = await this.getSamplingSettings();
|
|
1294
|
+
return this.shouldSample(request, settings.data);
|
|
1295
|
+
} catch {
|
|
1296
|
+
return false;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
async decideSample(request, options = {}) {
|
|
1300
|
+
const hasTokens = Number.isFinite(request.inputTokens) && Number(request.inputTokens) >= 0;
|
|
1301
|
+
const hasCharacters = Number.isFinite(request.inputCharacters) && Number(request.inputCharacters) >= 0;
|
|
1302
|
+
if (!hasTokens && !hasCharacters) {
|
|
1303
|
+
throw new UsageTapError(
|
|
1304
|
+
"USAGETAP_BAD_REQUEST",
|
|
1305
|
+
"decideSample requires inputTokens or inputCharacters"
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
return this.request(
|
|
1309
|
+
SAMPLING_DECIDE_PATH,
|
|
1310
|
+
request,
|
|
1311
|
+
options
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
async captureSample(request, options = {}) {
|
|
1315
|
+
if (!request || request.input === void 0) {
|
|
1316
|
+
throw new UsageTapError(
|
|
1317
|
+
"USAGETAP_BAD_REQUEST",
|
|
1318
|
+
"captureSample requires input"
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
if (!request.provider?.trim()) {
|
|
1322
|
+
throw new UsageTapError(
|
|
1323
|
+
"USAGETAP_BAD_REQUEST",
|
|
1324
|
+
"captureSample requires provider"
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1327
|
+
const sampleId = request.sampleId?.trim() || this.idempotencyGenerator();
|
|
1328
|
+
return this.request(
|
|
1329
|
+
SAMPLES_PATH,
|
|
1330
|
+
{ ...request, sampleId },
|
|
1331
|
+
{ ...options, idempotencyKey: sampleId }
|
|
1332
|
+
);
|
|
843
1333
|
}
|
|
844
1334
|
async beginCall(request, options = {}) {
|
|
845
1335
|
const idempotencyKey = request.idempotencyKey ?? request.idempotency ?? (this.autoIdempotency ? this.idempotencyGenerator() : void 0);
|
|
1336
|
+
this.reserveRunCall(request, idempotencyKey);
|
|
1337
|
+
const apiRequest = { ...request };
|
|
1338
|
+
delete apiRequest.runId;
|
|
846
1339
|
const payload = {
|
|
847
|
-
...
|
|
1340
|
+
...apiRequest,
|
|
848
1341
|
feature: request.feature ?? this.defaultFeature,
|
|
849
1342
|
tags: this.mergeTags(request.tags)
|
|
850
1343
|
};
|
|
@@ -862,6 +1355,28 @@ var UsageTapClient = class {
|
|
|
862
1355
|
);
|
|
863
1356
|
return response;
|
|
864
1357
|
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Inspect a configured run circuit breaker without consuming another call.
|
|
1360
|
+
*/
|
|
1361
|
+
canRunContinue(request) {
|
|
1362
|
+
const identity = this.resolveRunIdentity(request);
|
|
1363
|
+
if (!identity || !this.circuitBreaker) {
|
|
1364
|
+
throw new UsageTapError(
|
|
1365
|
+
"USAGETAP_BAD_REQUEST",
|
|
1366
|
+
"canRunContinue requires circuitBreaker configuration and a non-empty runId"
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
this.expireInactiveRuns();
|
|
1370
|
+
const calls = this.circuitBreakerRuns.get(identity.key)?.calls ?? 0;
|
|
1371
|
+
return this.createCircuitBreakerDecision(identity.customerId, identity.runId, calls);
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Release local state after a workflow finishes. Returns true when state existed.
|
|
1375
|
+
*/
|
|
1376
|
+
resetRun(request) {
|
|
1377
|
+
const identity = this.resolveRunIdentity(request);
|
|
1378
|
+
return identity ? this.circuitBreakerRuns.delete(identity.key) : false;
|
|
1379
|
+
}
|
|
865
1380
|
async promptCompress(request, options = {}) {
|
|
866
1381
|
if (!request?.callId) {
|
|
867
1382
|
throw new UsageTapError(
|
|
@@ -959,6 +1474,10 @@ var UsageTapClient = class {
|
|
|
959
1474
|
usageTapCompressionMessagesEndpoint: this.usageTapCompressionMessagesEndpoint,
|
|
960
1475
|
aggressiveness: options.aggressiveness ?? this.aggressiveness,
|
|
961
1476
|
usageTapCompressionAggressiveness: options.usageTapCompressionAggressiveness ?? this.usageTapCompressionAggressiveness,
|
|
1477
|
+
mode: options.mode,
|
|
1478
|
+
latencyBudgetMs: options.latencyBudgetMs,
|
|
1479
|
+
compactEmptyUserMessages: options.compactEmptyUserMessages,
|
|
1480
|
+
compactDuplicateUserTextParts: options.compactDuplicateUserTextParts,
|
|
962
1481
|
fetchImpl: this.fetchImpl,
|
|
963
1482
|
signal: options.signal,
|
|
964
1483
|
failOpen: options.failOpen
|
|
@@ -1000,11 +1519,17 @@ var UsageTapClient = class {
|
|
|
1000
1519
|
callId: request.callId,
|
|
1001
1520
|
feature: feature ?? this.defaultFeature,
|
|
1002
1521
|
tags: tags ?? this.defaultTags,
|
|
1522
|
+
providerUsed: request.providerUsed,
|
|
1003
1523
|
modelUsed: request.modelUsed,
|
|
1524
|
+
reasoningEffort: request.reasoningEffort,
|
|
1525
|
+
reasoningEffortSource: request.reasoningEffortSource,
|
|
1526
|
+
reasoningMode: request.reasoningMode,
|
|
1527
|
+
reasoningBudgetTokens: request.reasoningBudgetTokens,
|
|
1004
1528
|
metrics: {
|
|
1005
1529
|
inputTokens: request.inputTokens,
|
|
1006
1530
|
responseTokens: request.responseTokens,
|
|
1007
1531
|
cachedInputTokens: request.cachedInputTokens,
|
|
1532
|
+
cacheWriteInputTokens: request.cacheWriteInputTokens,
|
|
1008
1533
|
reasoningTokens: request.reasoningTokens,
|
|
1009
1534
|
searches: request.searches,
|
|
1010
1535
|
audioSeconds: request.audioSeconds,
|
|
@@ -1112,6 +1637,15 @@ var UsageTapClient = class {
|
|
|
1112
1637
|
meterSlot: request.meterSlot,
|
|
1113
1638
|
amount: request.amount
|
|
1114
1639
|
};
|
|
1640
|
+
if (request.customerUserId) {
|
|
1641
|
+
payload.customerUserId = request.customerUserId;
|
|
1642
|
+
}
|
|
1643
|
+
if (request.customerUserName) {
|
|
1644
|
+
payload.customerUserName = request.customerUserName;
|
|
1645
|
+
}
|
|
1646
|
+
if (request.customerUserEmail) {
|
|
1647
|
+
payload.customerUserEmail = request.customerUserEmail;
|
|
1648
|
+
}
|
|
1115
1649
|
if (request.feature) {
|
|
1116
1650
|
payload.feature = request.feature;
|
|
1117
1651
|
}
|
|
@@ -1148,6 +1682,11 @@ var UsageTapClient = class {
|
|
|
1148
1682
|
const beginPayload = idempotencyKey ? { ...beginRequest, idempotencyKey, idempotency: idempotencyKey } : { ...beginRequest };
|
|
1149
1683
|
const beginResponse = await this.beginCall(beginPayload, options);
|
|
1150
1684
|
let usage = {};
|
|
1685
|
+
const pricingMode = beginResponse.data.pricingMode ?? beginRequest.pricingMode ?? (beginRequest.batch === true ? "batch" : beginRequest.batch === false ? "standard" : void 0);
|
|
1686
|
+
if (pricingMode) {
|
|
1687
|
+
usage.pricingMode = pricingMode;
|
|
1688
|
+
usage.batch = pricingMode === "batch";
|
|
1689
|
+
}
|
|
1151
1690
|
const initialStripeCustomerId = typeof beginResponse.data.stripeCustomerId === "string" ? beginResponse.data.stripeCustomerId : typeof beginRequest.stripeCustomerId === "string" ? beginRequest.stripeCustomerId : void 0;
|
|
1152
1691
|
if (initialStripeCustomerId) {
|
|
1153
1692
|
usage = { ...usage, stripeCustomerId: initialStripeCustomerId };
|
|
@@ -1215,17 +1754,86 @@ var UsageTapClient = class {
|
|
|
1215
1754
|
toPromptCompressionTelemetry(result) {
|
|
1216
1755
|
return {
|
|
1217
1756
|
provider: result.provider,
|
|
1218
|
-
originalCharacters: result.originalCharacters,
|
|
1219
|
-
compressedCharacters: result.compressedCharacters,
|
|
1220
|
-
savedCharacters: result.savedCharacters,
|
|
1221
1757
|
originalTokens: result.originalTokens,
|
|
1222
1758
|
compressedTokens: result.compressedTokens,
|
|
1223
1759
|
savedTokens: result.savedTokens,
|
|
1224
1760
|
tokenSavingsRatio: result.tokenSavingsRatio,
|
|
1225
|
-
savingsRatio: result.savingsRatio,
|
|
1226
1761
|
techniques: result.techniques
|
|
1227
1762
|
};
|
|
1228
1763
|
}
|
|
1764
|
+
reserveRunCall(request, idempotencyKey) {
|
|
1765
|
+
const identity = this.resolveRunIdentity(request);
|
|
1766
|
+
if (!identity || !this.circuitBreaker) return;
|
|
1767
|
+
this.expireInactiveRuns();
|
|
1768
|
+
const now = Date.now();
|
|
1769
|
+
const state = this.circuitBreakerRuns.get(identity.key) ?? {
|
|
1770
|
+
calls: 0,
|
|
1771
|
+
reservationKeys: /* @__PURE__ */ new Set(),
|
|
1772
|
+
lastSeenAtMs: now
|
|
1773
|
+
};
|
|
1774
|
+
const reservationKey = idempotencyKey ?? this.idempotencyGenerator();
|
|
1775
|
+
state.lastSeenAtMs = now;
|
|
1776
|
+
if (state.reservationKeys.has(reservationKey)) {
|
|
1777
|
+
this.circuitBreakerRuns.set(identity.key, state);
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
const decision = this.createCircuitBreakerDecision(
|
|
1781
|
+
identity.customerId,
|
|
1782
|
+
identity.runId,
|
|
1783
|
+
state.calls
|
|
1784
|
+
);
|
|
1785
|
+
if (!decision.allowed) {
|
|
1786
|
+
throw new UsageTapError(
|
|
1787
|
+
"USAGETAP_CIRCUIT_OPEN",
|
|
1788
|
+
`Run ${identity.runId} reached its ${decision.limit}-call circuit-breaker limit`,
|
|
1789
|
+
{
|
|
1790
|
+
details: {
|
|
1791
|
+
reason: decision.reason,
|
|
1792
|
+
customerId: identity.customerId,
|
|
1793
|
+
runId: identity.runId,
|
|
1794
|
+
calls: decision.calls,
|
|
1795
|
+
limit: decision.limit,
|
|
1796
|
+
remaining: decision.remaining
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
);
|
|
1800
|
+
}
|
|
1801
|
+
state.calls += 1;
|
|
1802
|
+
state.reservationKeys.add(reservationKey);
|
|
1803
|
+
this.circuitBreakerRuns.set(identity.key, state);
|
|
1804
|
+
}
|
|
1805
|
+
resolveRunIdentity(request) {
|
|
1806
|
+
const customerId = request.customerId?.trim();
|
|
1807
|
+
const runId = request.runId?.trim();
|
|
1808
|
+
if (!customerId || !runId) return void 0;
|
|
1809
|
+
return {
|
|
1810
|
+
key: `${customerId}\0${runId}`,
|
|
1811
|
+
customerId,
|
|
1812
|
+
runId
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
createCircuitBreakerDecision(customerId, runId, calls) {
|
|
1816
|
+
const limit = this.circuitBreaker?.maxCallsPerRun ?? 0;
|
|
1817
|
+
const allowed = calls < limit;
|
|
1818
|
+
return {
|
|
1819
|
+
allowed,
|
|
1820
|
+
...allowed ? {} : { reason: "max_calls_per_run" },
|
|
1821
|
+
customerId,
|
|
1822
|
+
runId,
|
|
1823
|
+
calls,
|
|
1824
|
+
limit,
|
|
1825
|
+
remaining: Math.max(0, limit - calls)
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
expireInactiveRuns() {
|
|
1829
|
+
if (!this.circuitBreaker || this.circuitBreakerRuns.size === 0) return;
|
|
1830
|
+
const expiredBefore = Date.now() - this.circuitBreaker.runInactivityMs;
|
|
1831
|
+
for (const [key, state] of this.circuitBreakerRuns) {
|
|
1832
|
+
if (state.lastSeenAtMs < expiredBefore) {
|
|
1833
|
+
this.circuitBreakerRuns.delete(key);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1229
1837
|
async request(path, payload, options) {
|
|
1230
1838
|
const url = new URL(path, this.baseUrl).toString();
|
|
1231
1839
|
const body = payload !== void 0 ? JSON.stringify(payload) : void 0;
|
|
@@ -1413,7 +2021,7 @@ var UsageTapClient = class {
|
|
|
1413
2021
|
...this.defaultHeaders,
|
|
1414
2022
|
[SDK_HEADER]: `js/${SDK_VERSION}`,
|
|
1415
2023
|
"content-type": "application/json",
|
|
1416
|
-
accept:
|
|
2024
|
+
accept: CANONICAL_MEDIA_TYPE2
|
|
1417
2025
|
};
|
|
1418
2026
|
if (!HAS_WINDOW) {
|
|
1419
2027
|
headers["user-agent"] = `${USER_AGENT}/${SDK_VERSION}`;
|
|
@@ -1614,6 +2222,82 @@ var AnthropicPromptCompressionStats = class {
|
|
|
1614
2222
|
}
|
|
1615
2223
|
};
|
|
1616
2224
|
var USAGETAP_CORRELATION_HEADER = "x-usage-correlation-id";
|
|
2225
|
+
function withSampling(client, options = {}) {
|
|
2226
|
+
if (!client?.messages || typeof client.messages.create !== "function" || !options) {
|
|
2227
|
+
throw new UsageTapError(
|
|
2228
|
+
"USAGETAP_BAD_REQUEST",
|
|
2229
|
+
"withSampling requires an Anthropic client and sampling options"
|
|
2230
|
+
);
|
|
2231
|
+
}
|
|
2232
|
+
const { apiKey, usageTapClient, ...policy } = options;
|
|
2233
|
+
const localPolicy = typeof policy.rate === "number" ? policy : void 0;
|
|
2234
|
+
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey, sampling: localPolicy });
|
|
2235
|
+
const originalCreate = client.messages.create.bind(client.messages);
|
|
2236
|
+
const create = async (params, requestOptions) => {
|
|
2237
|
+
const { usageTap: callContext = {}, ...providerOptions } = requestOptions ?? {};
|
|
2238
|
+
const decision = params.stream === true ? Promise.resolve(false) : usageTap.shouldSampleAsync({
|
|
2239
|
+
customerId: callContext.customerId,
|
|
2240
|
+
feature: callContext.feature,
|
|
2241
|
+
input: params
|
|
2242
|
+
}, localPolicy);
|
|
2243
|
+
const startedAt = Date.now();
|
|
2244
|
+
try {
|
|
2245
|
+
const response = await originalCreate(
|
|
2246
|
+
params,
|
|
2247
|
+
Object.keys(providerOptions).length ? providerOptions : void 0
|
|
2248
|
+
);
|
|
2249
|
+
const selected = await decision;
|
|
2250
|
+
if (selected) {
|
|
2251
|
+
const record = isObjectRecord(response) ? response : {};
|
|
2252
|
+
await usageTap.captureSample({
|
|
2253
|
+
customerId: callContext.customerId,
|
|
2254
|
+
feature: callContext.feature,
|
|
2255
|
+
environment: callContext.environment,
|
|
2256
|
+
tags: callContext.tags,
|
|
2257
|
+
provider: "anthropic",
|
|
2258
|
+
model: readString(record.model) ?? readString(params.model),
|
|
2259
|
+
input: params,
|
|
2260
|
+
output: response,
|
|
2261
|
+
usage: record.usage,
|
|
2262
|
+
latencyMs: Date.now() - startedAt
|
|
2263
|
+
}).catch(() => void 0);
|
|
2264
|
+
}
|
|
2265
|
+
return response;
|
|
2266
|
+
} catch (error) {
|
|
2267
|
+
const selected = await decision;
|
|
2268
|
+
if (selected) {
|
|
2269
|
+
await usageTap.captureSample({
|
|
2270
|
+
customerId: callContext.customerId,
|
|
2271
|
+
feature: callContext.feature,
|
|
2272
|
+
environment: callContext.environment,
|
|
2273
|
+
tags: callContext.tags,
|
|
2274
|
+
provider: "anthropic",
|
|
2275
|
+
model: readString(params.model),
|
|
2276
|
+
input: params,
|
|
2277
|
+
latencyMs: Date.now() - startedAt,
|
|
2278
|
+
error: error instanceof Error ? { name: error.name, message: error.message } : { message: String(error) }
|
|
2279
|
+
}).catch(() => void 0);
|
|
2280
|
+
}
|
|
2281
|
+
throw error;
|
|
2282
|
+
}
|
|
2283
|
+
};
|
|
2284
|
+
const messages = new Proxy(client.messages, {
|
|
2285
|
+
get(target, prop, receiver) {
|
|
2286
|
+
if (prop === "create") return create;
|
|
2287
|
+
return safeReflectGet(target, prop, receiver);
|
|
2288
|
+
}
|
|
2289
|
+
});
|
|
2290
|
+
return new Proxy(client, {
|
|
2291
|
+
get(target, prop, receiver) {
|
|
2292
|
+
if (prop === "messages") return messages;
|
|
2293
|
+
if (prop === "unwrap") return () => target;
|
|
2294
|
+
return safeReflectGet(target, prop, receiver);
|
|
2295
|
+
}
|
|
2296
|
+
});
|
|
2297
|
+
}
|
|
2298
|
+
function safeReflectGet(target, prop, receiver) {
|
|
2299
|
+
return Reflect.get(target, prop, receiver);
|
|
2300
|
+
}
|
|
1617
2301
|
function withCompression(client, options = {}) {
|
|
1618
2302
|
if (!client?.messages || typeof client.messages.create !== "function") {
|
|
1619
2303
|
throw new UsageTapError(
|
|
@@ -1624,11 +2308,13 @@ function withCompression(client, options = {}) {
|
|
|
1624
2308
|
const {
|
|
1625
2309
|
apiKey,
|
|
1626
2310
|
usageTapClient,
|
|
2311
|
+
sampling,
|
|
1627
2312
|
...compressionOverrides
|
|
1628
2313
|
} = options;
|
|
1629
2314
|
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey });
|
|
1630
2315
|
const compression = {
|
|
1631
2316
|
provider: "usagetap",
|
|
2317
|
+
roles: { user: { enabled: true, aggressiveness: 0.2 } },
|
|
1632
2318
|
minContextTokens: DEFAULT_PROMPT_COMPRESSION_MIN_CONTEXT_TOKENS,
|
|
1633
2319
|
...compressionOverrides
|
|
1634
2320
|
};
|
|
@@ -1642,13 +2328,18 @@ function withCompression(client, options = {}) {
|
|
|
1642
2328
|
return Reflect.get(target, prop, receiver);
|
|
1643
2329
|
}
|
|
1644
2330
|
});
|
|
1645
|
-
|
|
2331
|
+
const compressed = new Proxy(client, {
|
|
1646
2332
|
get(target, prop, receiver) {
|
|
1647
2333
|
if (prop === "messages") return proxiedMessages;
|
|
1648
2334
|
if (prop === "unwrap") return () => target;
|
|
1649
2335
|
return Reflect.get(target, prop, receiver);
|
|
1650
2336
|
}
|
|
1651
2337
|
});
|
|
2338
|
+
return sampling ? withSampling(compressed, {
|
|
2339
|
+
...sampling === true ? {} : sampling,
|
|
2340
|
+
apiKey,
|
|
2341
|
+
usageTapClient
|
|
2342
|
+
}) : compressed;
|
|
1652
2343
|
}
|
|
1653
2344
|
function withMetering(client, customer) {
|
|
1654
2345
|
const config = typeof customer === "string" ? { customerId: customer } : customer;
|
|
@@ -1663,6 +2354,7 @@ function withMetering(client, customer) {
|
|
|
1663
2354
|
usageTapClient,
|
|
1664
2355
|
applyVendorHints,
|
|
1665
2356
|
promptCompression,
|
|
2357
|
+
sampling,
|
|
1666
2358
|
...defaultContext
|
|
1667
2359
|
} = config;
|
|
1668
2360
|
const usageTap = usageTapClient ?? new UsageTapClient({ apiKey });
|
|
@@ -1670,7 +2362,8 @@ function withMetering(client, customer) {
|
|
|
1670
2362
|
return wrapAnthropic(client, usageTap, {
|
|
1671
2363
|
defaultContext,
|
|
1672
2364
|
applyVendorHints,
|
|
1673
|
-
promptCompression: normalizedCompression
|
|
2365
|
+
promptCompression: normalizedCompression,
|
|
2366
|
+
sampling
|
|
1674
2367
|
});
|
|
1675
2368
|
}
|
|
1676
2369
|
function wrapAnthropic(client, usageTap, options = {}) {
|
|
@@ -1683,6 +2376,7 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1683
2376
|
const defaultContext = options.defaultContext;
|
|
1684
2377
|
const applyVendorHints = options.applyVendorHints !== false;
|
|
1685
2378
|
const defaultPromptCompression = normalizePromptCompressionOptions(options.promptCompression);
|
|
2379
|
+
const defaultSampling = normalizeMeteredAnthropicSampling(options.sampling);
|
|
1686
2380
|
const promptCompressionStats = new AnthropicPromptCompressionStats();
|
|
1687
2381
|
const proxiedMessages = createMessagesProxy(
|
|
1688
2382
|
client.messages,
|
|
@@ -1690,7 +2384,8 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1690
2384
|
defaultContext,
|
|
1691
2385
|
applyVendorHints,
|
|
1692
2386
|
defaultPromptCompression,
|
|
1693
|
-
promptCompressionStats
|
|
2387
|
+
promptCompressionStats,
|
|
2388
|
+
defaultSampling
|
|
1694
2389
|
);
|
|
1695
2390
|
const handler = {
|
|
1696
2391
|
get(target, prop, receiver) {
|
|
@@ -1708,7 +2403,7 @@ function wrapAnthropic(client, usageTap, options = {}) {
|
|
|
1708
2403
|
};
|
|
1709
2404
|
return new Proxy(client, handler);
|
|
1710
2405
|
}
|
|
1711
|
-
function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
2406
|
+
function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling) {
|
|
1712
2407
|
if (typeof resource.create !== "function") {
|
|
1713
2408
|
throw new UsageTapError("USAGETAP_BAD_REQUEST", "wrapAnthropic requires client.messages.create");
|
|
1714
2409
|
}
|
|
@@ -1731,7 +2426,8 @@ function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHint
|
|
|
1731
2426
|
defaultContext,
|
|
1732
2427
|
applyVendorHints,
|
|
1733
2428
|
defaultPromptCompression,
|
|
1734
|
-
promptCompressionStats
|
|
2429
|
+
promptCompressionStats,
|
|
2430
|
+
defaultSampling
|
|
1735
2431
|
});
|
|
1736
2432
|
});
|
|
1737
2433
|
const handler = {
|
|
@@ -1745,13 +2441,26 @@ function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHint
|
|
|
1745
2441
|
return new Proxy(resource, handler);
|
|
1746
2442
|
}
|
|
1747
2443
|
async function invokeMessagesCreate(args) {
|
|
1748
|
-
const beginRequest =
|
|
2444
|
+
const beginRequest = withRuntimeCompressionContext(
|
|
2445
|
+
resolveBeginRequest(args.defaultContext, args.usageContext),
|
|
2446
|
+
"anthropic",
|
|
2447
|
+
String(args.params.model)
|
|
2448
|
+
);
|
|
1749
2449
|
const begin = await args.usageTap.beginCall(
|
|
1750
2450
|
beginRequest,
|
|
1751
2451
|
beginCallOptions(args.withUsage)
|
|
1752
2452
|
);
|
|
1753
2453
|
const state = createCallState(beginRequest, begin);
|
|
1754
2454
|
const ctx = createUsageContext(state);
|
|
2455
|
+
const sampleStartedAt = Date.now();
|
|
2456
|
+
const wantsStream = isStreamingRequest(args.params);
|
|
2457
|
+
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredAnthropicSampleDecision({
|
|
2458
|
+
usageTap: args.usageTap,
|
|
2459
|
+
sampling: args.defaultSampling,
|
|
2460
|
+
beginRequest,
|
|
2461
|
+
input: args.params
|
|
2462
|
+
});
|
|
2463
|
+
let providerCompleted = false;
|
|
1755
2464
|
try {
|
|
1756
2465
|
const hintedParams = args.applyVendorHints ? applyAnthropicVendorHints(args.params, ctx.begin.data.vendorHints) : args.params;
|
|
1757
2466
|
const finalParams = await compressAnthropicParamsForCall({
|
|
@@ -1764,8 +2473,10 @@ async function invokeMessagesCreate(args) {
|
|
|
1764
2473
|
withUsage: args.withUsage,
|
|
1765
2474
|
operation: "messages.create"
|
|
1766
2475
|
});
|
|
2476
|
+
ctx.setUsage(anthropicRequestExecutionMetadata(finalParams));
|
|
1767
2477
|
const request = attachCorrelationHeader(args.requestOptions, ctx.begin.correlationId);
|
|
1768
2478
|
const response = await args.originalCreate(finalParams, request);
|
|
2479
|
+
providerCompleted = true;
|
|
1769
2480
|
if (isStreamingRequest(finalParams)) {
|
|
1770
2481
|
ensureAsyncIterable(response, "messages.create");
|
|
1771
2482
|
return wrapAnthropicStreamForUsageTap(
|
|
@@ -1777,6 +2488,15 @@ async function invokeMessagesCreate(args) {
|
|
|
1777
2488
|
);
|
|
1778
2489
|
}
|
|
1779
2490
|
inferAnthropicUsage(response, readString(finalParams.model), ctx);
|
|
2491
|
+
await captureMeteredAnthropicSample({
|
|
2492
|
+
usageTap: args.usageTap,
|
|
2493
|
+
sampling: args.defaultSampling,
|
|
2494
|
+
decision: sampleDecision,
|
|
2495
|
+
state,
|
|
2496
|
+
input: args.params,
|
|
2497
|
+
response,
|
|
2498
|
+
startedAt: sampleStartedAt
|
|
2499
|
+
});
|
|
1780
2500
|
await finalizeCall(state, args.usageTap, args.withUsage);
|
|
1781
2501
|
return response;
|
|
1782
2502
|
} catch (error) {
|
|
@@ -1786,24 +2506,145 @@ async function invokeMessagesCreate(args) {
|
|
|
1786
2506
|
message: error instanceof Error ? error.message : String(error)
|
|
1787
2507
|
};
|
|
1788
2508
|
}
|
|
2509
|
+
if (!providerCompleted) {
|
|
2510
|
+
await captureMeteredAnthropicSample({
|
|
2511
|
+
usageTap: args.usageTap,
|
|
2512
|
+
sampling: args.defaultSampling,
|
|
2513
|
+
decision: sampleDecision,
|
|
2514
|
+
state,
|
|
2515
|
+
input: args.params,
|
|
2516
|
+
error,
|
|
2517
|
+
startedAt: sampleStartedAt
|
|
2518
|
+
});
|
|
2519
|
+
}
|
|
1789
2520
|
await finalizeCall(state, args.usageTap, args.withUsage);
|
|
1790
2521
|
throw error;
|
|
1791
2522
|
}
|
|
1792
2523
|
}
|
|
2524
|
+
function normalizeMeteredAnthropicSampling(options) {
|
|
2525
|
+
if (!options) return void 0;
|
|
2526
|
+
if (options === true) return {};
|
|
2527
|
+
return {
|
|
2528
|
+
policy: typeof options.rate === "number" ? options : void 0
|
|
2529
|
+
};
|
|
2530
|
+
}
|
|
2531
|
+
function startMeteredAnthropicSampleDecision({
|
|
2532
|
+
usageTap,
|
|
2533
|
+
sampling,
|
|
2534
|
+
beginRequest,
|
|
2535
|
+
input
|
|
2536
|
+
}) {
|
|
2537
|
+
if (!sampling) return Promise.resolve(false);
|
|
2538
|
+
return usageTap.shouldSampleAsync(
|
|
2539
|
+
{
|
|
2540
|
+
customerId: beginRequest.customerId,
|
|
2541
|
+
feature: beginRequest.feature,
|
|
2542
|
+
input
|
|
2543
|
+
},
|
|
2544
|
+
sampling.policy
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
async function captureMeteredAnthropicSample({
|
|
2548
|
+
usageTap,
|
|
2549
|
+
sampling,
|
|
2550
|
+
decision,
|
|
2551
|
+
state,
|
|
2552
|
+
input,
|
|
2553
|
+
response,
|
|
2554
|
+
error,
|
|
2555
|
+
startedAt
|
|
2556
|
+
}) {
|
|
2557
|
+
if (!sampling) return;
|
|
2558
|
+
let selected = false;
|
|
2559
|
+
try {
|
|
2560
|
+
selected = await decision;
|
|
2561
|
+
} catch {
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
if (!selected) return;
|
|
2565
|
+
const record = isObjectRecord(response) ? response : {};
|
|
2566
|
+
await usageTap.captureSample({
|
|
2567
|
+
sampleId: state.begin.data.callId,
|
|
2568
|
+
callId: state.begin.data.callId,
|
|
2569
|
+
customerId: state.beginRequest.customerId,
|
|
2570
|
+
feature: state.beginRequest.feature,
|
|
2571
|
+
tags: state.beginRequest.tags,
|
|
2572
|
+
provider: "anthropic",
|
|
2573
|
+
model: readString(record.model) ?? readString(input.model),
|
|
2574
|
+
input,
|
|
2575
|
+
...response === void 0 ? {} : { output: response },
|
|
2576
|
+
usage: record.usage,
|
|
2577
|
+
latencyMs: Date.now() - startedAt,
|
|
2578
|
+
...error === void 0 ? {} : {
|
|
2579
|
+
error: error instanceof Error ? { name: error.name, message: error.message } : { message: String(error) }
|
|
2580
|
+
}
|
|
2581
|
+
}).catch(() => void 0);
|
|
2582
|
+
}
|
|
1793
2583
|
async function compressAnthropicParamsForCall(args) {
|
|
1794
|
-
|
|
2584
|
+
let compression = resolveEffectivePromptCompressionOptions(
|
|
1795
2585
|
args.defaultPromptCompression,
|
|
1796
2586
|
args.callPromptCompression
|
|
1797
2587
|
);
|
|
2588
|
+
const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
|
|
2589
|
+
const policyDriven = !compression && Boolean(runtimePolicy);
|
|
2590
|
+
if (policyDriven && runtimePolicy) {
|
|
2591
|
+
if (!runtimePolicy.selected) {
|
|
2592
|
+
args.ctx.setUsage({
|
|
2593
|
+
runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
|
|
2594
|
+
});
|
|
2595
|
+
return args.params;
|
|
2596
|
+
}
|
|
2597
|
+
compression = anthropicPolicyCompressionOptions(runtimePolicy);
|
|
2598
|
+
if (runtimePolicy.mode === "SHADOW") {
|
|
2599
|
+
const startedAt2 = Date.now();
|
|
2600
|
+
void compressAnthropicParams(
|
|
2601
|
+
args.params,
|
|
2602
|
+
args.usageTap,
|
|
2603
|
+
compression,
|
|
2604
|
+
args.withUsage?.signal
|
|
2605
|
+
).then((outcome2) => {
|
|
2606
|
+
args.ctx.setUsage({
|
|
2607
|
+
runtimeCompression: anthropicRuntimeMeasurement({
|
|
2608
|
+
policy: runtimePolicy,
|
|
2609
|
+
telemetry: buildPromptCompressionTelemetry(outcome2.segments),
|
|
2610
|
+
latencyMs: Date.now() - startedAt2,
|
|
2611
|
+
shadow: true,
|
|
2612
|
+
originalPayload: args.params,
|
|
2613
|
+
resultPayload: outcome2.params
|
|
2614
|
+
})
|
|
2615
|
+
});
|
|
2616
|
+
}).catch(() => {
|
|
2617
|
+
args.ctx.setUsage({
|
|
2618
|
+
runtimeCompression: failedRuntimeMeasurement(
|
|
2619
|
+
runtimePolicy,
|
|
2620
|
+
Date.now() - startedAt2
|
|
2621
|
+
)
|
|
2622
|
+
});
|
|
2623
|
+
});
|
|
2624
|
+
return args.params;
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
1798
2627
|
if (!compression) {
|
|
1799
2628
|
return args.params;
|
|
1800
2629
|
}
|
|
2630
|
+
const startedAt = Date.now();
|
|
1801
2631
|
const outcome = await compressAnthropicParams(
|
|
1802
2632
|
args.params,
|
|
1803
2633
|
args.usageTap,
|
|
1804
2634
|
compression,
|
|
1805
2635
|
args.withUsage?.signal
|
|
1806
2636
|
);
|
|
2637
|
+
if (policyDriven && runtimePolicy) {
|
|
2638
|
+
const runtimeMeasurement = anthropicRuntimeMeasurement({
|
|
2639
|
+
policy: runtimePolicy,
|
|
2640
|
+
telemetry: buildPromptCompressionTelemetry(outcome.segments),
|
|
2641
|
+
latencyMs: Date.now() - startedAt,
|
|
2642
|
+
originalPayload: args.params,
|
|
2643
|
+
resultPayload: outcome.params
|
|
2644
|
+
});
|
|
2645
|
+
args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
|
|
2646
|
+
if (runtimeMeasurement.decision !== "compressed") return args.params;
|
|
2647
|
+
}
|
|
1807
2648
|
await recordCompressionOutcome({
|
|
1808
2649
|
outcome,
|
|
1809
2650
|
compression,
|
|
@@ -1815,6 +2656,76 @@ async function compressAnthropicParamsForCall(args) {
|
|
|
1815
2656
|
});
|
|
1816
2657
|
return outcome.params;
|
|
1817
2658
|
}
|
|
2659
|
+
function anthropicPolicyCompressionOptions(policy) {
|
|
2660
|
+
const roles = Object.fromEntries(
|
|
2661
|
+
["system", "user", "tool", "assistant"].filter((role) => policy.snapshot.scope.messageRoles.includes(role)).map((role) => [role, true])
|
|
2662
|
+
);
|
|
2663
|
+
return {
|
|
2664
|
+
provider: "usagetap",
|
|
2665
|
+
roles,
|
|
2666
|
+
minContextTokens: policy.snapshot.scope.minimumTokens,
|
|
2667
|
+
failOpen: true
|
|
2668
|
+
};
|
|
2669
|
+
}
|
|
2670
|
+
function bypassedRuntimeMeasurement(policy) {
|
|
2671
|
+
return {
|
|
2672
|
+
policyId: policy.policyId,
|
|
2673
|
+
policyVersionId: policy.policyVersionId,
|
|
2674
|
+
policyVersion: policy.version,
|
|
2675
|
+
rolloutMode: policy.mode,
|
|
2676
|
+
decision: "bypassed",
|
|
2677
|
+
reasonCode: "not_in_rollout_sample",
|
|
2678
|
+
methodsAttempted: [],
|
|
2679
|
+
methodsApplied: [],
|
|
2680
|
+
originalTokens: 0,
|
|
2681
|
+
resultTokens: 0,
|
|
2682
|
+
savedTokens: 0,
|
|
2683
|
+
reductionPercentage: 0,
|
|
2684
|
+
compressionLatencyMs: 0,
|
|
2685
|
+
compressionComputeCostUsd: 0,
|
|
2686
|
+
financialsEstimated: true
|
|
2687
|
+
};
|
|
2688
|
+
}
|
|
2689
|
+
function failedRuntimeMeasurement(policy, latencyMs) {
|
|
2690
|
+
return {
|
|
2691
|
+
...bypassedRuntimeMeasurement(policy),
|
|
2692
|
+
decision: "fallback",
|
|
2693
|
+
reasonCode: "transformation_failed",
|
|
2694
|
+
methodsAttempted: ["deterministic"],
|
|
2695
|
+
compressionLatencyMs: latencyMs
|
|
2696
|
+
};
|
|
2697
|
+
}
|
|
2698
|
+
function anthropicRuntimeMeasurement(input) {
|
|
2699
|
+
const telemetry = input.telemetry;
|
|
2700
|
+
const originalTokens = telemetry?.originalTokens ?? estimatePromptTokens(JSON.stringify(input.originalPayload));
|
|
2701
|
+
const resultTokens = telemetry?.compressedTokens ?? originalTokens;
|
|
2702
|
+
const savedTokens = Math.max(0, telemetry?.savedTokens ?? 0);
|
|
2703
|
+
const reductionPercentage = originalTokens > 0 ? savedTokens / originalTokens * 100 : 0;
|
|
2704
|
+
const belowMinimumSize = originalTokens < input.policy.snapshot.scope.minimumTokens;
|
|
2705
|
+
const latencyExceeded = input.latencyMs > input.policy.snapshot.gates.maximumLatencyMs;
|
|
2706
|
+
const belowSavings = savedTokens < input.policy.snapshot.gates.minimumTokensRemoved || reductionPercentage < input.policy.snapshot.gates.minimumReductionPercentage;
|
|
2707
|
+
const accepted = !belowMinimumSize && !latencyExceeded && !belowSavings;
|
|
2708
|
+
const deepText = input.policy.snapshot.methods.deepText.enabled && !input.policy.snapshot.rollout.deterministicOnly && (input.policy.mode !== "CANARY" || input.policy.snapshot.rollout.deepTextCanaryAllowed);
|
|
2709
|
+
const reasonCode = belowMinimumSize ? "below_minimum_size" : latencyExceeded ? "latency_budget_exceeded" : belowSavings ? "below_minimum_savings" : void 0;
|
|
2710
|
+
const measurement = {
|
|
2711
|
+
policyId: input.policy.policyId,
|
|
2712
|
+
policyVersionId: input.policy.policyVersionId,
|
|
2713
|
+
policyVersion: input.policy.version,
|
|
2714
|
+
rolloutMode: input.policy.mode,
|
|
2715
|
+
decision: accepted ? input.shadow ? "bypassed" : "compressed" : latencyExceeded ? "fallback" : "skipped",
|
|
2716
|
+
...reasonCode ? { reasonCode } : {},
|
|
2717
|
+
methodsAttempted: ["deterministic", ...deepText ? ["deep_text"] : []],
|
|
2718
|
+
methodsApplied: accepted ? ["deterministic"] : [],
|
|
2719
|
+
originalTokens,
|
|
2720
|
+
resultTokens: accepted ? resultTokens : originalTokens,
|
|
2721
|
+
savedTokens: accepted ? savedTokens : 0,
|
|
2722
|
+
reductionPercentage: accepted ? reductionPercentage : 0,
|
|
2723
|
+
compressionLatencyMs: input.latencyMs,
|
|
2724
|
+
compressionComputeCostUsd: 0,
|
|
2725
|
+
financialsEstimated: true
|
|
2726
|
+
};
|
|
2727
|
+
return measurement;
|
|
2728
|
+
}
|
|
1818
2729
|
async function recordCompressionOutcome(args) {
|
|
1819
2730
|
const telemetry = buildPromptCompressionTelemetry(args.outcome.segments);
|
|
1820
2731
|
if (!telemetry) {
|
|
@@ -1869,6 +2780,10 @@ async function compressAnthropicParams(params, usageTap, compression, signal) {
|
|
|
1869
2780
|
const result = await usageTap.compressPromptMessages(source, {
|
|
1870
2781
|
provider: "usagetap",
|
|
1871
2782
|
failOpen: compression.failOpen,
|
|
2783
|
+
mode: compression.mode,
|
|
2784
|
+
latencyBudgetMs: compression.latencyBudgetMs,
|
|
2785
|
+
compactEmptyUserMessages: compression.compactEmptyUserMessages,
|
|
2786
|
+
compactDuplicateUserTextParts: compression.compactDuplicateUserTextParts,
|
|
1872
2787
|
aggressiveness: resolveMessageEndpointAggressiveness(compression),
|
|
1873
2788
|
signal
|
|
1874
2789
|
});
|
|
@@ -2253,10 +3168,16 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2253
3168
|
}
|
|
2254
3169
|
const tags = mergeTags(base.tags, current.tags);
|
|
2255
3170
|
const begin = { customerId };
|
|
3171
|
+
const runtimeCompressionContext = current.runtimeCompressionContext ?? base.runtimeCompressionContext;
|
|
3172
|
+
if (runtimeCompressionContext) {
|
|
3173
|
+
begin.runtimeCompressionContext = runtimeCompressionContext;
|
|
3174
|
+
}
|
|
2256
3175
|
const requested = current.requested ?? base.requested;
|
|
2257
3176
|
if (requested) begin.requested = requested;
|
|
2258
3177
|
const feature = current.feature ?? base.feature;
|
|
2259
3178
|
if (feature) begin.feature = feature;
|
|
3179
|
+
const runId = current.runId ?? base.runId;
|
|
3180
|
+
if (runId) begin.runId = runId;
|
|
2260
3181
|
const idempotency = current.idempotency ?? base.idempotency;
|
|
2261
3182
|
if (idempotency) begin.idempotency = idempotency;
|
|
2262
3183
|
const idempotencyKey = current.idempotencyKey ?? base.idempotencyKey;
|
|
@@ -2265,6 +3186,12 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2265
3186
|
if (customerName) begin.customerName = customerName;
|
|
2266
3187
|
const customerEmail = current.customerEmail ?? base.customerEmail;
|
|
2267
3188
|
if (customerEmail) begin.customerEmail = customerEmail;
|
|
3189
|
+
const customerUserId = current.customerUserId ?? base.customerUserId;
|
|
3190
|
+
if (customerUserId) begin.customerUserId = customerUserId;
|
|
3191
|
+
const customerUserName = current.customerUserName ?? base.customerUserName;
|
|
3192
|
+
if (customerUserName) begin.customerUserName = customerUserName;
|
|
3193
|
+
const customerUserEmail = current.customerUserEmail ?? base.customerUserEmail;
|
|
3194
|
+
if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
|
|
2268
3195
|
const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
|
|
2269
3196
|
if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
|
|
2270
3197
|
const holdUsd = current.holdUsd ?? base.holdUsd;
|
|
@@ -2280,6 +3207,11 @@ function resolveBeginRequest(defaults, override) {
|
|
|
2280
3207
|
}
|
|
2281
3208
|
function createCallState(beginRequest, begin) {
|
|
2282
3209
|
const usage = {};
|
|
3210
|
+
const pricingMode = begin.data.pricingMode ?? beginRequest.pricingMode ?? (beginRequest.batch === true ? "batch" : beginRequest.batch === false ? "standard" : void 0);
|
|
3211
|
+
if (pricingMode) {
|
|
3212
|
+
usage.pricingMode = pricingMode;
|
|
3213
|
+
usage.batch = pricingMode === "batch";
|
|
3214
|
+
}
|
|
2283
3215
|
const stripeCustomerId = typeof begin.data.stripeCustomerId === "string" ? begin.data.stripeCustomerId : typeof beginRequest.stripeCustomerId === "string" ? beginRequest.stripeCustomerId : void 0;
|
|
2284
3216
|
if (stripeCustomerId) {
|
|
2285
3217
|
usage.stripeCustomerId = stripeCustomerId;
|
|
@@ -2343,10 +3275,12 @@ function inferAnthropicUsage(response, fallbackModel, ctx) {
|
|
|
2343
3275
|
}
|
|
2344
3276
|
function extractAnthropicUsage(payload, fallbackModel) {
|
|
2345
3277
|
if (!isObjectRecord(payload)) {
|
|
2346
|
-
return fallbackModel ? { modelUsed: fallbackModel } :
|
|
3278
|
+
return { providerUsed: "anthropic", ...fallbackModel ? { modelUsed: fallbackModel } : {} };
|
|
2347
3279
|
}
|
|
2348
3280
|
const usage = findAnthropicUsageRecord(payload);
|
|
2349
|
-
const result = {
|
|
3281
|
+
const result = {
|
|
3282
|
+
providerUsed: "anthropic"
|
|
3283
|
+
};
|
|
2350
3284
|
const model = readString(payload.model) ?? readString(payload.message?.model) ?? fallbackModel;
|
|
2351
3285
|
if (model) {
|
|
2352
3286
|
result.modelUsed = model;
|
|
@@ -2356,6 +3290,21 @@ function extractAnthropicUsage(payload, fallbackModel) {
|
|
|
2356
3290
|
}
|
|
2357
3291
|
return Object.keys(result).length ? result : void 0;
|
|
2358
3292
|
}
|
|
3293
|
+
function anthropicRequestExecutionMetadata(params) {
|
|
3294
|
+
const outputConfig = isObjectRecord(params.output_config) ? params.output_config : void 0;
|
|
3295
|
+
const thinking = isObjectRecord(params.thinking) ? params.thinking : void 0;
|
|
3296
|
+
const rawEffort = outputConfig?.effort;
|
|
3297
|
+
const effort = rawEffort === "none" || rawEffort === "minimal" || rawEffort === "low" || rawEffort === "medium" || rawEffort === "high" || rawEffort === "xhigh" || rawEffort === "max" ? rawEffort : void 0;
|
|
3298
|
+
const mode = readString(thinking?.type);
|
|
3299
|
+
const rawBudget = readNumber(thinking?.budget_tokens);
|
|
3300
|
+
const budget = rawBudget !== void 0 && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
|
|
3301
|
+
return {
|
|
3302
|
+
providerUsed: "anthropic",
|
|
3303
|
+
...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
|
|
3304
|
+
...mode ? { reasoningMode: mode } : {},
|
|
3305
|
+
...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
|
|
3306
|
+
};
|
|
3307
|
+
}
|
|
2359
3308
|
function findAnthropicUsageRecord(payload) {
|
|
2360
3309
|
if (isObjectRecord(payload.usage)) {
|
|
2361
3310
|
return payload.usage;
|
|
@@ -2366,10 +3315,6 @@ function findAnthropicUsageRecord(payload) {
|
|
|
2366
3315
|
return void 0;
|
|
2367
3316
|
}
|
|
2368
3317
|
function applyAnthropicUsageRecord(usage, usageRecord) {
|
|
2369
|
-
const inputTokens = readNumber(usageRecord.input_tokens ?? usageRecord.prompt_tokens);
|
|
2370
|
-
if (inputTokens !== void 0) {
|
|
2371
|
-
usage.inputTokens = inputTokens;
|
|
2372
|
-
}
|
|
2373
3318
|
const outputTokens = readNumber(usageRecord.output_tokens ?? usageRecord.completion_tokens);
|
|
2374
3319
|
if (outputTokens !== void 0) {
|
|
2375
3320
|
usage.responseTokens = outputTokens;
|
|
@@ -2380,6 +3325,26 @@ function applyAnthropicUsageRecord(usage, usageRecord) {
|
|
|
2380
3325
|
if (cachedInputTokens !== void 0) {
|
|
2381
3326
|
usage.cachedInputTokens = cachedInputTokens;
|
|
2382
3327
|
}
|
|
3328
|
+
const cacheWriteInputTokens = readNumber(
|
|
3329
|
+
usageRecord.cache_creation_input_tokens ?? usageRecord.prompt_cache_miss_tokens ?? usageRecord.cache_write_tokens
|
|
3330
|
+
);
|
|
3331
|
+
if (cacheWriteInputTokens !== void 0) {
|
|
3332
|
+
usage.cacheWriteInputTokens = cacheWriteInputTokens;
|
|
3333
|
+
}
|
|
3334
|
+
const baseInputTokens = readNumber(usageRecord.input_tokens ?? usageRecord.prompt_tokens);
|
|
3335
|
+
if (baseInputTokens !== void 0) {
|
|
3336
|
+
usage.inputTokens = baseInputTokens + (cachedInputTokens ?? 0) + (cacheWriteInputTokens ?? 0);
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
function withRuntimeCompressionContext(begin, provider, model) {
|
|
3340
|
+
return {
|
|
3341
|
+
...begin,
|
|
3342
|
+
runtimeCompressionContext: {
|
|
3343
|
+
...begin.runtimeCompressionContext ?? {},
|
|
3344
|
+
provider,
|
|
3345
|
+
model
|
|
3346
|
+
}
|
|
3347
|
+
};
|
|
2383
3348
|
}
|
|
2384
3349
|
function accumulateAnthropicStreamUsage(chunk, fallbackModel, state) {
|
|
2385
3350
|
const usage = extractAnthropicUsage(chunk, fallbackModel);
|
|
@@ -2603,6 +3568,6 @@ function isIteratorResult(value) {
|
|
|
2603
3568
|
return isObjectRecord(value) && "done" in value;
|
|
2604
3569
|
}
|
|
2605
3570
|
|
|
2606
|
-
export { AnthropicPromptCompressionStats, withCompression, withMetering, wrapAnthropic };
|
|
3571
|
+
export { AnthropicPromptCompressionStats, withCompression, withMetering, withSampling, wrapAnthropic };
|
|
2607
3572
|
//# sourceMappingURL=anthropic.mjs.map
|
|
2608
3573
|
//# sourceMappingURL=anthropic.mjs.map
|