weflayr 0.22.2 → 0.24.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/dist/api/generated/facade.gen.d.ts +102 -0
- package/dist/api/generated/facade.gen.js +8 -1
- package/dist/api/generated/index.d.ts +2 -2
- package/dist/api/generated/index.js +8 -1
- package/dist/api/generated/sdk.gen.d.ts +29 -1
- package/dist/api/generated/sdk.gen.js +64 -1
- package/dist/api/generated/types.gen.d.ts +356 -3
- package/index.d.ts +5 -2
- package/package.json +1 -1
- package/src/api/generated/facade.gen.ts +42 -0
- package/src/api/generated/index.ts +2 -2
- package/src/api/generated/sdk.gen.ts +64 -1
- package/src/api/generated/types.gen.ts +400 -3
- package/src/auto-instrument.js +1 -0
- package/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +11 -0
- package/src/instrumentation/anthropic/get_cache_config_telemetry.js +90 -0
- package/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +6 -1
- package/src/instrumentation/anthropic/set_telemetry_attributes.js +2 -0
- package/src/instrumentation/bedrock/get_cache_config_telemetry.js +106 -0
- package/src/instrumentation/bedrock/get_cache_telemetry.js +11 -6
- package/src/instrumentation/bedrock/set_telemetry_attributes.js +6 -2
- package/src/instrumentation/google/get_cache_operations_telemetry.js +171 -0
- package/src/instrumentation/google/get_cache_telemetry.js +16 -2
- package/src/instrumentation/google/set_telemetry_attributes.js +8 -2
- package/src/instrumentation/openai/get_cache_config_telemetry.js +63 -0
- package/src/instrumentation/openai/set_telemetry_attributes.js +2 -0
- package/src/instrumentation/vercel/full_instrumentation.js +10 -2
- package/src/instrumentation/vercel/get_cache_creation_telemetry.js +70 -0
- package/src/otel/propagation.js +22 -9
- package/src/otel/span-filter.js +12 -12
- package/src/otel/span-processor.js +8 -0
- package/src/otel/vercel-ai-sdk-span-filter.js +1 -1
- package/tests/api/index.test.js +76 -0
- package/tests/index.test.js +67 -7
- package/tests/instrumentation/anthropic/get_cache_and_reasoning_telemetry.test.js +23 -0
- package/tests/instrumentation/anthropic/get_cache_config_telemetry.test.js +92 -0
- package/tests/instrumentation/anthropic/get_tool_definitions_telemetry.test.js +15 -0
- package/tests/instrumentation/bedrock/get_cache_config_telemetry.test.js +107 -0
- package/tests/instrumentation/bedrock/get_cache_telemetry.test.js +16 -0
- package/tests/instrumentation/google/get_cache_operations_telemetry.test.js +130 -0
- package/tests/instrumentation/openai/get_cache_config_telemetry.test.js +83 -0
- package/tests/instrumentation/vercel/get_cache_creation_telemetry.test.js +96 -0
|
@@ -36,6 +36,56 @@ export type ApiErrorOutput = {
|
|
|
36
36
|
error: string;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* CustomerMarginRow
|
|
41
|
+
*
|
|
42
|
+
* One customer's gross margin for a period.
|
|
43
|
+
*/
|
|
44
|
+
export type CustomerMarginRow = {
|
|
45
|
+
/**
|
|
46
|
+
* Customer Name
|
|
47
|
+
*/
|
|
48
|
+
customer_name: string;
|
|
49
|
+
/**
|
|
50
|
+
* Revenue
|
|
51
|
+
*/
|
|
52
|
+
revenue: number;
|
|
53
|
+
/**
|
|
54
|
+
* Cost
|
|
55
|
+
*/
|
|
56
|
+
cost: number;
|
|
57
|
+
/**
|
|
58
|
+
* Margin
|
|
59
|
+
*/
|
|
60
|
+
margin: number;
|
|
61
|
+
/**
|
|
62
|
+
* Margin Pct
|
|
63
|
+
*/
|
|
64
|
+
margin_pct: number | null;
|
|
65
|
+
/**
|
|
66
|
+
* Profitability Status
|
|
67
|
+
*/
|
|
68
|
+
profitability_status: 'healthy' | 'watch' | 'at_risk' | 'unprofitable' | 'unknown';
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* CustomerOutput
|
|
73
|
+
*
|
|
74
|
+
* One customer, with its tags when requested (an empty dict otherwise).
|
|
75
|
+
*/
|
|
76
|
+
export type CustomerOutput = {
|
|
77
|
+
/**
|
|
78
|
+
* Customer Name
|
|
79
|
+
*/
|
|
80
|
+
customer_name: string;
|
|
81
|
+
/**
|
|
82
|
+
* Customer Tags
|
|
83
|
+
*/
|
|
84
|
+
customer_tags: {
|
|
85
|
+
[key: string]: string;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
39
89
|
/**
|
|
40
90
|
* CustomerTags
|
|
41
91
|
*
|
|
@@ -72,6 +122,26 @@ export type CustomerTagsOutput = {
|
|
|
72
122
|
};
|
|
73
123
|
};
|
|
74
124
|
|
|
125
|
+
/**
|
|
126
|
+
* CustomersOutput
|
|
127
|
+
*
|
|
128
|
+
* The customers GET response: every known customer of the project.
|
|
129
|
+
*/
|
|
130
|
+
export type CustomersOutput = {
|
|
131
|
+
/**
|
|
132
|
+
* Customers
|
|
133
|
+
*/
|
|
134
|
+
customers: Array<CustomerOutput>;
|
|
135
|
+
/**
|
|
136
|
+
* Next Cursor
|
|
137
|
+
*/
|
|
138
|
+
next_cursor: string | null;
|
|
139
|
+
/**
|
|
140
|
+
* Has More
|
|
141
|
+
*/
|
|
142
|
+
has_more: boolean;
|
|
143
|
+
};
|
|
144
|
+
|
|
75
145
|
/**
|
|
76
146
|
* CustomersUpdatedOutput
|
|
77
147
|
*
|
|
@@ -88,6 +158,18 @@ export type CustomersUpdatedOutput = {
|
|
|
88
158
|
customers_updated: number;
|
|
89
159
|
};
|
|
90
160
|
|
|
161
|
+
/**
|
|
162
|
+
* FeaturesOutput
|
|
163
|
+
*
|
|
164
|
+
* A features GET response: every distinct feature tag seen for the project.
|
|
165
|
+
*/
|
|
166
|
+
export type FeaturesOutput = {
|
|
167
|
+
/**
|
|
168
|
+
* Features
|
|
169
|
+
*/
|
|
170
|
+
features: Array<string>;
|
|
171
|
+
};
|
|
172
|
+
|
|
91
173
|
/**
|
|
92
174
|
* InvalidFieldDetail
|
|
93
175
|
*
|
|
@@ -212,6 +294,79 @@ export type KeyMetricRowOutput = {
|
|
|
212
294
|
user_name: string;
|
|
213
295
|
};
|
|
214
296
|
|
|
297
|
+
/**
|
|
298
|
+
* MarginListOutput
|
|
299
|
+
*
|
|
300
|
+
* Margin GET response: one row per customer with booked revenue, sorted by
|
|
301
|
+
* customer_name ascending.
|
|
302
|
+
*/
|
|
303
|
+
export type MarginListOutput = {
|
|
304
|
+
/**
|
|
305
|
+
* Rows
|
|
306
|
+
*/
|
|
307
|
+
rows: Array<CustomerMarginRow>;
|
|
308
|
+
/**
|
|
309
|
+
* Next Cursor
|
|
310
|
+
*/
|
|
311
|
+
next_cursor: string | null;
|
|
312
|
+
/**
|
|
313
|
+
* Has More
|
|
314
|
+
*/
|
|
315
|
+
has_more: boolean;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* ModelsOutput
|
|
320
|
+
*
|
|
321
|
+
* A models GET response: every distinct model name seen for the project.
|
|
322
|
+
*/
|
|
323
|
+
export type ModelsOutput = {
|
|
324
|
+
/**
|
|
325
|
+
* Models
|
|
326
|
+
*/
|
|
327
|
+
models: Array<string>;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* ProjectOutput
|
|
332
|
+
*
|
|
333
|
+
* A project GET response: static identity of the project.
|
|
334
|
+
*/
|
|
335
|
+
export type ProjectOutput = {
|
|
336
|
+
/**
|
|
337
|
+
* Name
|
|
338
|
+
*/
|
|
339
|
+
name: string;
|
|
340
|
+
/**
|
|
341
|
+
* Project Type
|
|
342
|
+
*/
|
|
343
|
+
project_type: 'revenue' | 'internal';
|
|
344
|
+
/**
|
|
345
|
+
* Key Metric Names
|
|
346
|
+
*/
|
|
347
|
+
key_metric_names: Array<string>;
|
|
348
|
+
/**
|
|
349
|
+
* Environment
|
|
350
|
+
*/
|
|
351
|
+
environment: 'dev' | 'pre-prod' | 'staging' | 'prod';
|
|
352
|
+
/**
|
|
353
|
+
* Created At
|
|
354
|
+
*/
|
|
355
|
+
created_at: string;
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* ProvidersOutput
|
|
360
|
+
*
|
|
361
|
+
* A providers GET response: every distinct provider name seen for the project.
|
|
362
|
+
*/
|
|
363
|
+
export type ProvidersOutput = {
|
|
364
|
+
/**
|
|
365
|
+
* Providers
|
|
366
|
+
*/
|
|
367
|
+
providers: Array<string>;
|
|
368
|
+
};
|
|
369
|
+
|
|
215
370
|
/**
|
|
216
371
|
* RevenueOrKeyMetricDeletedRowOutput
|
|
217
372
|
*
|
|
@@ -407,6 +562,79 @@ export type TagsDeletedOutput = {
|
|
|
407
562
|
tags_deleted: number;
|
|
408
563
|
};
|
|
409
564
|
|
|
565
|
+
/**
|
|
566
|
+
* TagsOutput
|
|
567
|
+
*
|
|
568
|
+
* A tags GET response: facets of every tag dimension seen on the project.
|
|
569
|
+
*
|
|
570
|
+
* Either field is an empty dict when its include_* flag was false.
|
|
571
|
+
*/
|
|
572
|
+
export type TagsOutput = {
|
|
573
|
+
/**
|
|
574
|
+
* Run Tags
|
|
575
|
+
*/
|
|
576
|
+
run_tags: {
|
|
577
|
+
[key: string]: Array<string>;
|
|
578
|
+
};
|
|
579
|
+
/**
|
|
580
|
+
* Customer Tags
|
|
581
|
+
*/
|
|
582
|
+
customer_tags: {
|
|
583
|
+
[key: string]: Array<string>;
|
|
584
|
+
};
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
export type GetMarginData = {
|
|
588
|
+
body?: never;
|
|
589
|
+
path?: never;
|
|
590
|
+
query?: {
|
|
591
|
+
/**
|
|
592
|
+
* Return only these customers' margin. Omit to return every customer's.
|
|
593
|
+
*/
|
|
594
|
+
customer_names?: Array<string>;
|
|
595
|
+
/**
|
|
596
|
+
* Opaque cursor from a previous page's next_cursor.
|
|
597
|
+
*/
|
|
598
|
+
cursor?: string;
|
|
599
|
+
limit?: number;
|
|
600
|
+
/**
|
|
601
|
+
* Start of the period (inclusive), YYYY-MM-DD. Defaults to 30 days ago.
|
|
602
|
+
*/
|
|
603
|
+
date_from?: string;
|
|
604
|
+
/**
|
|
605
|
+
* End of the period (inclusive), YYYY-MM-DD. Defaults to today.
|
|
606
|
+
*/
|
|
607
|
+
date_to?: string;
|
|
608
|
+
/**
|
|
609
|
+
* Apply free-credit discounts to cost. Defaults to false.
|
|
610
|
+
*/
|
|
611
|
+
apply_credits?: boolean;
|
|
612
|
+
};
|
|
613
|
+
url: '/margin/';
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
export type GetMarginErrors = {
|
|
617
|
+
/**
|
|
618
|
+
* Invalid request for this project type.
|
|
619
|
+
*/
|
|
620
|
+
400: ApiErrorOutput;
|
|
621
|
+
/**
|
|
622
|
+
* Missing or invalid API key.
|
|
623
|
+
*/
|
|
624
|
+
401: ApiErrorOutput;
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
export type GetMarginError = GetMarginErrors[keyof GetMarginErrors];
|
|
628
|
+
|
|
629
|
+
export type GetMarginResponses = {
|
|
630
|
+
/**
|
|
631
|
+
* Success.
|
|
632
|
+
*/
|
|
633
|
+
200: MarginListOutput;
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
export type GetMarginResponse = GetMarginResponses[keyof GetMarginResponses];
|
|
637
|
+
|
|
410
638
|
export type DeleteRevenueData = {
|
|
411
639
|
body?: never;
|
|
412
640
|
path?: never;
|
|
@@ -454,7 +682,7 @@ export type GetRevenueData = {
|
|
|
454
682
|
path?: never;
|
|
455
683
|
query?: {
|
|
456
684
|
/**
|
|
457
|
-
* Return only these customers' revenue
|
|
685
|
+
* Return only these customers' revenue. Omit to return every customer's.
|
|
458
686
|
*/
|
|
459
687
|
customer_names?: Array<string>;
|
|
460
688
|
/**
|
|
@@ -560,7 +788,7 @@ export type GetKeyMetricsData = {
|
|
|
560
788
|
path?: never;
|
|
561
789
|
query?: {
|
|
562
790
|
/**
|
|
563
|
-
* Return only these users' key metrics
|
|
791
|
+
* Return only these users' key metrics. Omit to return every user's.
|
|
564
792
|
*/
|
|
565
793
|
user_names?: Array<string>;
|
|
566
794
|
/**
|
|
@@ -624,7 +852,7 @@ export type DeleteCustomersTagsData = {
|
|
|
624
852
|
path?: never;
|
|
625
853
|
query: {
|
|
626
854
|
/**
|
|
627
|
-
* Delete every tag of these customers
|
|
855
|
+
* Delete every tag of these customers.
|
|
628
856
|
*/
|
|
629
857
|
customer_names: Array<string>;
|
|
630
858
|
};
|
|
@@ -716,3 +944,172 @@ export type SetCustomersTagsResponses = {
|
|
|
716
944
|
};
|
|
717
945
|
|
|
718
946
|
export type SetCustomersTagsResponse = SetCustomersTagsResponses[keyof SetCustomersTagsResponses];
|
|
947
|
+
|
|
948
|
+
export type GetProjectData = {
|
|
949
|
+
body?: never;
|
|
950
|
+
path?: never;
|
|
951
|
+
query?: never;
|
|
952
|
+
url: '/project/';
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
export type GetProjectErrors = {
|
|
956
|
+
/**
|
|
957
|
+
* Missing or invalid API key.
|
|
958
|
+
*/
|
|
959
|
+
401: ApiErrorOutput;
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
export type GetProjectError = GetProjectErrors[keyof GetProjectErrors];
|
|
963
|
+
|
|
964
|
+
export type GetProjectResponses = {
|
|
965
|
+
/**
|
|
966
|
+
* Success.
|
|
967
|
+
*/
|
|
968
|
+
200: ProjectOutput;
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
export type GetProjectResponse = GetProjectResponses[keyof GetProjectResponses];
|
|
972
|
+
|
|
973
|
+
export type GetTagsData = {
|
|
974
|
+
body?: never;
|
|
975
|
+
path?: never;
|
|
976
|
+
query?: {
|
|
977
|
+
/**
|
|
978
|
+
* Include run-tag facets. Defaults to true.
|
|
979
|
+
*/
|
|
980
|
+
include_run_tags?: boolean;
|
|
981
|
+
/**
|
|
982
|
+
* Include customer-tag facets. Defaults to true.
|
|
983
|
+
*/
|
|
984
|
+
include_customer_tags?: boolean;
|
|
985
|
+
};
|
|
986
|
+
url: '/tags/';
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
export type GetTagsErrors = {
|
|
990
|
+
/**
|
|
991
|
+
* Missing or invalid API key.
|
|
992
|
+
*/
|
|
993
|
+
401: ApiErrorOutput;
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
export type GetTagsError = GetTagsErrors[keyof GetTagsErrors];
|
|
997
|
+
|
|
998
|
+
export type GetTagsResponses = {
|
|
999
|
+
/**
|
|
1000
|
+
* Success.
|
|
1001
|
+
*/
|
|
1002
|
+
200: TagsOutput;
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
export type GetTagsResponse = GetTagsResponses[keyof GetTagsResponses];
|
|
1006
|
+
|
|
1007
|
+
export type GetModelsData = {
|
|
1008
|
+
body?: never;
|
|
1009
|
+
path?: never;
|
|
1010
|
+
query?: never;
|
|
1011
|
+
url: '/models/';
|
|
1012
|
+
};
|
|
1013
|
+
|
|
1014
|
+
export type GetModelsErrors = {
|
|
1015
|
+
/**
|
|
1016
|
+
* Missing or invalid API key.
|
|
1017
|
+
*/
|
|
1018
|
+
401: ApiErrorOutput;
|
|
1019
|
+
};
|
|
1020
|
+
|
|
1021
|
+
export type GetModelsError = GetModelsErrors[keyof GetModelsErrors];
|
|
1022
|
+
|
|
1023
|
+
export type GetModelsResponses = {
|
|
1024
|
+
/**
|
|
1025
|
+
* Success.
|
|
1026
|
+
*/
|
|
1027
|
+
200: ModelsOutput;
|
|
1028
|
+
};
|
|
1029
|
+
|
|
1030
|
+
export type GetModelsResponse = GetModelsResponses[keyof GetModelsResponses];
|
|
1031
|
+
|
|
1032
|
+
export type GetProvidersData = {
|
|
1033
|
+
body?: never;
|
|
1034
|
+
path?: never;
|
|
1035
|
+
query?: never;
|
|
1036
|
+
url: '/providers/';
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
export type GetProvidersErrors = {
|
|
1040
|
+
/**
|
|
1041
|
+
* Missing or invalid API key.
|
|
1042
|
+
*/
|
|
1043
|
+
401: ApiErrorOutput;
|
|
1044
|
+
};
|
|
1045
|
+
|
|
1046
|
+
export type GetProvidersError = GetProvidersErrors[keyof GetProvidersErrors];
|
|
1047
|
+
|
|
1048
|
+
export type GetProvidersResponses = {
|
|
1049
|
+
/**
|
|
1050
|
+
* Success.
|
|
1051
|
+
*/
|
|
1052
|
+
200: ProvidersOutput;
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
export type GetProvidersResponse = GetProvidersResponses[keyof GetProvidersResponses];
|
|
1056
|
+
|
|
1057
|
+
export type GetCustomersData = {
|
|
1058
|
+
body?: never;
|
|
1059
|
+
path?: never;
|
|
1060
|
+
query?: {
|
|
1061
|
+
/**
|
|
1062
|
+
* Opaque cursor from a previous page's next_cursor.
|
|
1063
|
+
*/
|
|
1064
|
+
cursor?: string;
|
|
1065
|
+
limit?: number;
|
|
1066
|
+
/**
|
|
1067
|
+
* Include each customer's tags. Defaults to true.
|
|
1068
|
+
*/
|
|
1069
|
+
include_customer_tags?: boolean;
|
|
1070
|
+
};
|
|
1071
|
+
url: '/customers/';
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
export type GetCustomersErrors = {
|
|
1075
|
+
/**
|
|
1076
|
+
* Missing or invalid API key.
|
|
1077
|
+
*/
|
|
1078
|
+
401: ApiErrorOutput;
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
export type GetCustomersError = GetCustomersErrors[keyof GetCustomersErrors];
|
|
1082
|
+
|
|
1083
|
+
export type GetCustomersResponses = {
|
|
1084
|
+
/**
|
|
1085
|
+
* Success.
|
|
1086
|
+
*/
|
|
1087
|
+
200: CustomersOutput;
|
|
1088
|
+
};
|
|
1089
|
+
|
|
1090
|
+
export type GetCustomersResponse = GetCustomersResponses[keyof GetCustomersResponses];
|
|
1091
|
+
|
|
1092
|
+
export type GetFeaturesData = {
|
|
1093
|
+
body?: never;
|
|
1094
|
+
path?: never;
|
|
1095
|
+
query?: never;
|
|
1096
|
+
url: '/features/';
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
export type GetFeaturesErrors = {
|
|
1100
|
+
/**
|
|
1101
|
+
* Missing or invalid API key.
|
|
1102
|
+
*/
|
|
1103
|
+
401: ApiErrorOutput;
|
|
1104
|
+
};
|
|
1105
|
+
|
|
1106
|
+
export type GetFeaturesError = GetFeaturesErrors[keyof GetFeaturesErrors];
|
|
1107
|
+
|
|
1108
|
+
export type GetFeaturesResponses = {
|
|
1109
|
+
/**
|
|
1110
|
+
* Success.
|
|
1111
|
+
*/
|
|
1112
|
+
200: FeaturesOutput;
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1115
|
+
export type GetFeaturesResponse = GetFeaturesResponses[keyof GetFeaturesResponses];
|
package/src/auto-instrument.js
CHANGED
|
@@ -18,9 +18,19 @@
|
|
|
18
18
|
|
|
19
19
|
const CACHE_READ_INPUT_TOKENS_ATTR = 'gen_ai.usage.cache_read_input_tokens';
|
|
20
20
|
const CACHE_CREATION_INPUT_TOKENS_ATTR = 'gen_ai.usage.cache_creation_input_tokens';
|
|
21
|
+
const CACHE_CREATION_ATTR = 'weflayr.cache.creation';
|
|
21
22
|
const INPUT_TOKENS_ATTR = 'gen_ai.usage.input_tokens';
|
|
22
23
|
const THINKING_TOKENS_ATTR = 'gen_ai.usage.output_tokens_details.thinking_tokens';
|
|
23
24
|
|
|
25
|
+
// Stamp `usage.cache_creation` verbatim (the written tokens per TTL, billed at
|
|
26
|
+
// different rates); skipped when nothing was written so non-caching calls
|
|
27
|
+
// carry no attribute. The normalizer owns the mapping to TTL tiers.
|
|
28
|
+
function _setCacheCreation(span, cacheCreation) {
|
|
29
|
+
if (!cacheCreation) return;
|
|
30
|
+
if (!Object.values(cacheCreation).some(Boolean)) return;
|
|
31
|
+
span.setAttribute(CACHE_CREATION_ATTR, JSON.stringify(cacheCreation));
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
// Stamp cache counts (and the raw uncached input) from an Anthropic usage object.
|
|
25
35
|
// The instrumentor emits no `gen_ai.usage.cache_*` attribute; `input_tokens` in
|
|
26
36
|
// the stream events is the raw uncached count (cached tokens are reported
|
|
@@ -35,6 +45,7 @@ function _setUsage(span, usage) {
|
|
|
35
45
|
if (usage.cache_creation_input_tokens) {
|
|
36
46
|
span.setAttribute(CACHE_CREATION_INPUT_TOKENS_ATTR, usage.cache_creation_input_tokens);
|
|
37
47
|
}
|
|
48
|
+
_setCacheCreation(span, usage.cache_creation);
|
|
38
49
|
if (typeof usage.input_tokens === 'number') {
|
|
39
50
|
span.setAttribute(INPUT_TOKENS_ATTR, usage.input_tokens);
|
|
40
51
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Request-side prompt-caching config capture for Anthropic spans.
|
|
5
|
+
*
|
|
6
|
+
* Anthropic callers opt into caching by placing `cache_control` breakpoints on
|
|
7
|
+
* content blocks (`system` text blocks, message content blocks, tool
|
|
8
|
+
* definitions), each with an optional `ttl` ("5m" default, or "1h"). The
|
|
9
|
+
* traceloop instrumentor never records them, so cache-hit analysis cannot tell
|
|
10
|
+
* what the request asked to cache or for how long.
|
|
11
|
+
*
|
|
12
|
+
* Wrap `startSpan({ type, params })` (covering the streamed and non-streamed
|
|
13
|
+
* paths) to stamp every breakpoint found in the request as
|
|
14
|
+
* `weflayr.cache.breakpoints`: a JSON list of `{location, ttl}` where location
|
|
15
|
+
* is `system`, `messages[i]` or `tools[i]` and `ttl` is the raw cache_control
|
|
16
|
+
* string, omitted when the breakpoint sets none; the normalizer owns the TTL
|
|
17
|
+
* vocabulary.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const BREAKPOINTS_ATTR = 'weflayr.cache.breakpoints';
|
|
21
|
+
|
|
22
|
+
// A breakpoint as location + the raw cache_control `ttl` string, omitted when
|
|
23
|
+
// the request sets none.
|
|
24
|
+
function _breakpointEntry(location, cacheControl) {
|
|
25
|
+
const ttl = cacheControl && cacheControl.ttl;
|
|
26
|
+
return ttl ? { location, ttl: String(ttl) } : { location };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function _cacheControlsInBlocks(blocks) {
|
|
30
|
+
if (!Array.isArray(blocks)) return [];
|
|
31
|
+
return blocks.map((block) => block && block.cache_control).filter(Boolean);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Every `cache_control` breakpoint in the request, as location + the raw
|
|
35
|
+
// `ttl` string (omitted when the breakpoint sets none).
|
|
36
|
+
function extractCacheBreakpoints(params) {
|
|
37
|
+
const breakpoints = [];
|
|
38
|
+
for (const cacheControl of _cacheControlsInBlocks(params.system)) {
|
|
39
|
+
breakpoints.push(_breakpointEntry('system', cacheControl));
|
|
40
|
+
}
|
|
41
|
+
(params.messages || []).forEach((message, index) => {
|
|
42
|
+
for (const cacheControl of _cacheControlsInBlocks(message && message.content)) {
|
|
43
|
+
breakpoints.push(_breakpointEntry(`messages[${index}]`, cacheControl));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
(params.tools || []).forEach((tool, index) => {
|
|
47
|
+
if (tool && tool.cache_control) {
|
|
48
|
+
breakpoints.push(_breakpointEntry(`tools[${index}]`, tool.cache_control));
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return breakpoints;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
class AnthropicCacheConfigInstrumentor {
|
|
55
|
+
constructor() {
|
|
56
|
+
// [instrumentation, methodName, originalFunction] for uninstrument().
|
|
57
|
+
this._patched = [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// No-op (and never throws) if the instrumentor's internals have changed shape —
|
|
61
|
+
// the worst case is missing cache-config capture, never a crash.
|
|
62
|
+
instrument(instrumentation) {
|
|
63
|
+
if (!instrumentation) return;
|
|
64
|
+
const startSpanOriginal = instrumentation.startSpan;
|
|
65
|
+
if (typeof startSpanOriginal !== 'function') return;
|
|
66
|
+
|
|
67
|
+
instrumentation.startSpan = function startSpanWithCacheConfig(request) {
|
|
68
|
+
const span = startSpanOriginal.call(this, request);
|
|
69
|
+
try {
|
|
70
|
+
const breakpoints = extractCacheBreakpoints((request && request.params) || {});
|
|
71
|
+
if (breakpoints.length) {
|
|
72
|
+
span.setAttribute(BREAKPOINTS_ATTR, JSON.stringify(breakpoints));
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
// Never break span creation over an enrichment failure.
|
|
76
|
+
}
|
|
77
|
+
return span;
|
|
78
|
+
};
|
|
79
|
+
this._patched.push([instrumentation, 'startSpan', startSpanOriginal]);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
uninstrument() {
|
|
83
|
+
for (const [owner, methodName, original] of this._patched) {
|
|
84
|
+
owner[methodName] = original;
|
|
85
|
+
}
|
|
86
|
+
this._patched = [];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = { AnthropicCacheConfigInstrumentor, extractCacheBreakpoints };
|
|
@@ -29,7 +29,12 @@ class AnthropicToolDefinitionsInstrumentor {
|
|
|
29
29
|
try {
|
|
30
30
|
const tools = request && request.params && request.params.tools;
|
|
31
31
|
if (Array.isArray(tools) && tools.length) {
|
|
32
|
-
|
|
32
|
+
// cache_control is dropped from the stamped definitions — the
|
|
33
|
+
// breakpoint is captured separately in cache_config.
|
|
34
|
+
const definitions = tools.map(
|
|
35
|
+
({ cache_control: _cacheControl, ...definition }) => definition
|
|
36
|
+
);
|
|
37
|
+
span.setAttribute('gen_ai.tool.definitions', JSON.stringify(definitions));
|
|
33
38
|
}
|
|
34
39
|
} catch {
|
|
35
40
|
// Never break span creation over an enrichment failure.
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* leaving an empty finish_reason on the output message).
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
const { AnthropicCacheConfigInstrumentor } = require('./get_cache_config_telemetry');
|
|
13
14
|
const { AnthropicPromptCachingInstrumentor } = require('./get_cache_and_reasoning_telemetry');
|
|
14
15
|
const {
|
|
15
16
|
AnthropicStreamingStopReasonInstrumentor,
|
|
@@ -20,6 +21,7 @@ class AnthropicTelemetryInstrumentor {
|
|
|
20
21
|
constructor() {
|
|
21
22
|
this._subInstrumentors = [
|
|
22
23
|
new AnthropicPromptCachingInstrumentor(),
|
|
24
|
+
new AnthropicCacheConfigInstrumentor(),
|
|
23
25
|
new AnthropicStreamingStopReasonInstrumentor(),
|
|
24
26
|
new AnthropicToolDefinitionsInstrumentor(),
|
|
25
27
|
];
|