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.
Files changed (42) hide show
  1. package/dist/api/generated/facade.gen.d.ts +102 -0
  2. package/dist/api/generated/facade.gen.js +8 -1
  3. package/dist/api/generated/index.d.ts +2 -2
  4. package/dist/api/generated/index.js +8 -1
  5. package/dist/api/generated/sdk.gen.d.ts +29 -1
  6. package/dist/api/generated/sdk.gen.js +64 -1
  7. package/dist/api/generated/types.gen.d.ts +356 -3
  8. package/index.d.ts +5 -2
  9. package/package.json +1 -1
  10. package/src/api/generated/facade.gen.ts +42 -0
  11. package/src/api/generated/index.ts +2 -2
  12. package/src/api/generated/sdk.gen.ts +64 -1
  13. package/src/api/generated/types.gen.ts +400 -3
  14. package/src/auto-instrument.js +1 -0
  15. package/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +11 -0
  16. package/src/instrumentation/anthropic/get_cache_config_telemetry.js +90 -0
  17. package/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +6 -1
  18. package/src/instrumentation/anthropic/set_telemetry_attributes.js +2 -0
  19. package/src/instrumentation/bedrock/get_cache_config_telemetry.js +106 -0
  20. package/src/instrumentation/bedrock/get_cache_telemetry.js +11 -6
  21. package/src/instrumentation/bedrock/set_telemetry_attributes.js +6 -2
  22. package/src/instrumentation/google/get_cache_operations_telemetry.js +171 -0
  23. package/src/instrumentation/google/get_cache_telemetry.js +16 -2
  24. package/src/instrumentation/google/set_telemetry_attributes.js +8 -2
  25. package/src/instrumentation/openai/get_cache_config_telemetry.js +63 -0
  26. package/src/instrumentation/openai/set_telemetry_attributes.js +2 -0
  27. package/src/instrumentation/vercel/full_instrumentation.js +10 -2
  28. package/src/instrumentation/vercel/get_cache_creation_telemetry.js +70 -0
  29. package/src/otel/propagation.js +22 -9
  30. package/src/otel/span-filter.js +12 -12
  31. package/src/otel/span-processor.js +8 -0
  32. package/src/otel/vercel-ai-sdk-span-filter.js +1 -1
  33. package/tests/api/index.test.js +76 -0
  34. package/tests/index.test.js +67 -7
  35. package/tests/instrumentation/anthropic/get_cache_and_reasoning_telemetry.test.js +23 -0
  36. package/tests/instrumentation/anthropic/get_cache_config_telemetry.test.js +92 -0
  37. package/tests/instrumentation/anthropic/get_tool_definitions_telemetry.test.js +15 -0
  38. package/tests/instrumentation/bedrock/get_cache_config_telemetry.test.js +107 -0
  39. package/tests/instrumentation/bedrock/get_cache_telemetry.test.js +16 -0
  40. package/tests/instrumentation/google/get_cache_operations_telemetry.test.js +130 -0
  41. package/tests/instrumentation/openai/get_cache_config_telemetry.test.js +83 -0
  42. 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 (repeat the param for several). Omit to return every customer's.
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 (repeat the param for several). Omit to return every user's.
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 (repeat the param for several).
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];
@@ -22,6 +22,7 @@ const AiSdk = Object.freeze({
22
22
  // OpenAI SDK (OpenAI-compatible endpoint) but reported as itself.
23
23
  const AIProviderName = Object.freeze({
24
24
  OPENAI: 'openai',
25
+ AZURE_OPENAI: 'azure',
25
26
  ANTHROPIC: 'anthropic',
26
27
  BEDROCK: 'bedrock',
27
28
  COHERE: 'cohere',
@@ -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
- span.setAttribute('gen_ai.tool.definitions', JSON.stringify(tools));
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
  ];