openlit 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +18 -0
  2. package/dist/evals/base.js +1 -1
  3. package/dist/evals/base.js.map +1 -1
  4. package/dist/guard/__tests__/gaurd.test.d.ts +1 -0
  5. package/dist/guard/__tests__/gaurd.test.js +136 -0
  6. package/dist/guard/__tests__/gaurd.test.js.map +1 -0
  7. package/dist/guard/__tests__/utils.test.d.ts +1 -0
  8. package/dist/guard/__tests__/utils.test.js +64 -0
  9. package/dist/guard/__tests__/utils.test.js.map +1 -0
  10. package/dist/guard/all.d.ts +8 -0
  11. package/dist/guard/all.js +28 -0
  12. package/dist/guard/all.js.map +1 -0
  13. package/dist/guard/base.d.ts +15 -0
  14. package/dist/guard/base.js +58 -0
  15. package/dist/guard/base.js.map +1 -0
  16. package/dist/guard/index.d.ts +5 -0
  17. package/dist/guard/index.js +24 -0
  18. package/dist/guard/index.js.map +1 -0
  19. package/dist/guard/prompt-injection.d.ts +7 -0
  20. package/dist/guard/prompt-injection.js +79 -0
  21. package/dist/guard/prompt-injection.js.map +1 -0
  22. package/dist/guard/sensitive-topic.d.ts +7 -0
  23. package/dist/guard/sensitive-topic.js +75 -0
  24. package/dist/guard/sensitive-topic.js.map +1 -0
  25. package/dist/guard/topic-restriction.d.ts +7 -0
  26. package/dist/guard/topic-restriction.js +75 -0
  27. package/dist/guard/topic-restriction.js.map +1 -0
  28. package/dist/guard/types.d.ts +26 -0
  29. package/dist/guard/types.js +4 -0
  30. package/dist/guard/types.js.map +1 -0
  31. package/dist/guard/utils.d.ts +13 -0
  32. package/dist/guard/utils.js +79 -0
  33. package/dist/guard/utils.js.map +1 -0
  34. package/dist/index.d.ts +19 -2
  35. package/dist/index.js +31 -6
  36. package/dist/index.js.map +1 -1
  37. package/dist/instrumentation/__tests__/anthropic-wrapper.test.d.ts +1 -0
  38. package/dist/instrumentation/__tests__/anthropic-wrapper.test.js +92 -0
  39. package/dist/instrumentation/__tests__/anthropic-wrapper.test.js.map +1 -0
  40. package/dist/instrumentation/__tests__/base-wrapper.test.d.ts +1 -0
  41. package/dist/instrumentation/__tests__/base-wrapper.test.js +175 -0
  42. package/dist/instrumentation/__tests__/base-wrapper.test.js.map +1 -0
  43. package/dist/instrumentation/__tests__/cohere-wrapper.test.d.ts +1 -0
  44. package/dist/instrumentation/__tests__/cohere-wrapper.test.js +131 -0
  45. package/dist/instrumentation/__tests__/cohere-wrapper.test.js.map +1 -0
  46. package/dist/instrumentation/__tests__/openai-wrapper.test.d.ts +1 -0
  47. package/dist/instrumentation/__tests__/openai-wrapper.test.js +118 -0
  48. package/dist/instrumentation/__tests__/openai-wrapper.test.js.map +1 -0
  49. package/dist/instrumentation/anthropic/wrapper.d.ts +7 -1
  50. package/dist/instrumentation/anthropic/wrapper.js +16 -1
  51. package/dist/instrumentation/anthropic/wrapper.js.map +1 -1
  52. package/dist/instrumentation/base-wrapper.d.ts +3 -2
  53. package/dist/instrumentation/base-wrapper.js +81 -1
  54. package/dist/instrumentation/base-wrapper.js.map +1 -1
  55. package/dist/instrumentation/cohere/wrapper.d.ts +7 -1
  56. package/dist/instrumentation/cohere/wrapper.js +19 -2
  57. package/dist/instrumentation/cohere/wrapper.js.map +1 -1
  58. package/dist/instrumentation/ollama/wrapper.d.ts +2 -1
  59. package/dist/instrumentation/ollama/wrapper.js +2 -2
  60. package/dist/instrumentation/ollama/wrapper.js.map +1 -1
  61. package/dist/instrumentation/openai/wrapper.d.ts +9 -2
  62. package/dist/instrumentation/openai/wrapper.js +86 -3
  63. package/dist/instrumentation/openai/wrapper.js.map +1 -1
  64. package/dist/llm/anthropic.d.ts +5 -0
  65. package/dist/llm/anthropic.js +35 -0
  66. package/dist/llm/anthropic.js.map +1 -0
  67. package/dist/llm/index.d.ts +3 -0
  68. package/dist/llm/index.js +93 -0
  69. package/dist/llm/index.js.map +1 -0
  70. package/dist/llm/openai.d.ts +6 -0
  71. package/dist/llm/openai.js +48 -0
  72. package/dist/llm/openai.js.map +1 -0
  73. package/dist/llm/providers.d.ts +7 -0
  74. package/dist/llm/providers.js +10 -0
  75. package/dist/llm/providers.js.map +1 -0
  76. package/dist/otel/__tests__/metrics.test.d.ts +1 -0
  77. package/dist/otel/__tests__/metrics.test.js +51 -0
  78. package/dist/otel/__tests__/metrics.test.js.map +1 -0
  79. package/dist/otel/metrics.d.ts +22 -0
  80. package/dist/otel/metrics.js +132 -0
  81. package/dist/otel/metrics.js.map +1 -0
  82. package/dist/{tracing.d.ts → otel/tracing.d.ts} +1 -1
  83. package/dist/{tracing.js → otel/tracing.js} +17 -15
  84. package/dist/otel/tracing.js.map +1 -0
  85. package/dist/semantic-convention.d.ts +6 -0
  86. package/dist/semantic-convention.js +8 -1
  87. package/dist/semantic-convention.js.map +1 -1
  88. package/dist/types.d.ts +7 -0
  89. package/package.json +10 -11
  90. package/dist/tracing.js.map +0 -1
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TopicRestriction = void 0;
4
+ const base_1 = require("./base");
5
+ const utils_1 = require("./utils");
6
+ const utils_2 = require("../evals/utils");
7
+ const llm_1 = require("../llm");
8
+ class TopicRestriction extends base_1.BaseGuard {
9
+ constructor(config = {}) {
10
+ super(config);
11
+ }
12
+ getSystemPrompt() {
13
+ return [
14
+ 'Output Requirement: JSON Format Only',
15
+ '',
16
+ 'Your task is to carefully analyze the provided text and identify any instances of restricted topics, and categorize the classification when found. Apply strict criteria in your assessment.',
17
+ '',
18
+ 'Restricted topics include information or requests that are not allowed or are outside the permitted scope for this system.',
19
+ '',
20
+ 'Presented Text:',
21
+ '{{prompt}}',
22
+ '',
23
+ 'Expected JSON Structure:',
24
+ '',
25
+ '{',
26
+ ' "score": "float",',
27
+ ' "verdict": "yes or no",',
28
+ ' "guard": "topic_restriction",',
29
+ ' "classification": "CATEGORY_OF_TOPIC_RESTRICTION",',
30
+ ' "explanation": "Very short one-sentence reason"',
31
+ '}',
32
+ '',
33
+ 'Scoring System:',
34
+ '- The score ranges from 0 to 1. Scores should be 0 if the classification is "none". Any other classification must be accompanied by a valid category and a score reflecting the restriction.',
35
+ '',
36
+ 'Important: Use only the categories below to classify restricted topics. If the text does not fit any listed category, classify it as "none" with a score of 0.',
37
+ '',
38
+ 'Categories of Restricted Topics (use only these categories):',
39
+ '- politics: Requests or discussions about political figures, parties, or policies that are not allowed.',
40
+ '- violence: Requests or discussions involving physical harm, aggression, or violent acts.',
41
+ '- personal_information: Requests for or mentions of sensitive personal data (e.g., credit card, address, email, phone number).',
42
+ '- confidential: Requests for or mentions of confidential or proprietary information.',
43
+ '- illegal: Requests or discussions about illegal activities.',
44
+ '',
45
+ 'Final Steps:',
46
+ '- If no restricted topics are detected, or if the topic does not fit into one of the above categories, the return must be: {"score": 0, "verdict": "no", "guard": "none", "classification": "none", "explanation": "none"}.',
47
+ ].join('\n');
48
+ }
49
+ async detect(text) {
50
+ const customRuleResult = (0, utils_1.customRuleDetection)(text, this.customRules);
51
+ let llmResult = {
52
+ score: 0,
53
+ verdict: 'no',
54
+ guard: 'none',
55
+ classification: 'none',
56
+ explanation: 'none'
57
+ };
58
+ if (this.provider) {
59
+ // Use correct template variable for prompt and satisfy EvalsInput type
60
+ const prompt = (0, utils_2.formatPrompt)(this.getSystemPrompt(), { prompt: text, text });
61
+ const response = await this.llmResponse(prompt);
62
+ llmResult = (0, utils_1.toGuardResult)((0, llm_1.parseLlmResponse)(response), 'topic_restriction');
63
+ }
64
+ let result = customRuleResult.score >= llmResult.score ? customRuleResult : llmResult;
65
+ result = (0, utils_1.applyThresholdScore)(result, this.thresholdScore);
66
+ // Metrics collection if enabled
67
+ if (this.collectMetrics) {
68
+ const validator = this.provider || 'custom';
69
+ (0, utils_1.guardMetrics)().add(1, (0, utils_1.guardMetricAttributes)(result.verdict, result.score, validator, result.classification, result.explanation));
70
+ }
71
+ return result;
72
+ }
73
+ }
74
+ exports.TopicRestriction = TopicRestriction;
75
+ //# sourceMappingURL=topic-restriction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topic-restriction.js","sourceRoot":"","sources":["../../src/guard/topic-restriction.ts"],"names":[],"mappings":";;;AAAA,iCAAmC;AAEnC,mCAAuH;AACvH,0CAA8C;AAC9C,gCAA0C;AAE1C,MAAa,gBAAiB,SAAQ,gBAAS;IAC7C,YAAY,SAAsB,EAAE;QAClC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChB,CAAC;IAES,eAAe;QACvB,OAAO;YACL,sCAAsC;YACtC,EAAE;YACF,8LAA8L;YAC9L,EAAE;YACF,4HAA4H;YAC5H,EAAE;YACF,iBAAiB;YACjB,YAAY;YACZ,EAAE;YACF,0BAA0B;YAC1B,EAAE;YACF,GAAG;YACH,uBAAuB;YACvB,6BAA6B;YAC7B,mCAAmC;YACnC,wDAAwD;YACxD,qDAAqD;YACrD,GAAG;YACH,EAAE;YACF,iBAAiB;YACjB,8LAA8L;YAC9L,EAAE;YACF,gKAAgK;YAChK,EAAE;YACF,8DAA8D;YAC9D,yGAAyG;YACzG,2FAA2F;YAC3F,gIAAgI;YAChI,sFAAsF;YACtF,8DAA8D;YAC9D,EAAE;YACF,cAAc;YACd,6NAA6N;SAC9N,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,MAAM,gBAAgB,GAAG,IAAA,2BAAmB,EAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACrE,IAAI,SAAS,GAAgB;YAC3B,KAAK,EAAE,CAAC;YACR,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,MAAM;YACb,cAAc,EAAE,MAAM;YACtB,WAAW,EAAE,MAAM;SACpB,CAAC;QACF,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,uEAAuE;YACvE,MAAM,MAAM,GAAG,IAAA,oBAAY,EAAC,IAAI,CAAC,eAAe,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5E,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAChD,SAAS,GAAG,IAAA,qBAAa,EAAC,IAAA,sBAAgB,EAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,MAAM,GAAG,gBAAgB,CAAC,KAAK,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;QACtF,MAAM,GAAG,IAAA,2BAAmB,EAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAE1D,gCAAgC;QAChC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC;YAC5C,IAAA,oBAAY,GAAE,CAAC,GAAG,CAAC,CAAC,EAAE,IAAA,6BAAqB,EAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;QACnI,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AApED,4CAoEC"}
@@ -0,0 +1,26 @@
1
+ export interface GuardResult {
2
+ score: number;
3
+ verdict: 'yes' | 'no' | 'none';
4
+ guard: string;
5
+ classification: string;
6
+ explanation: string;
7
+ }
8
+ export interface GuardConfig {
9
+ provider?: 'openai' | 'anthropic';
10
+ apiKey?: string;
11
+ model?: string;
12
+ baseUrl?: string;
13
+ customRules?: Array<CustomRule>;
14
+ validTopics?: string[];
15
+ invalidTopics?: string[];
16
+ thresholdScore?: number;
17
+ collectMetrics?: boolean;
18
+ }
19
+ export interface CustomRule {
20
+ pattern: string;
21
+ classification: string;
22
+ verdict?: 'yes' | 'no';
23
+ guard?: string;
24
+ score?: number;
25
+ explanation?: string;
26
+ }
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // Types for Guard output and configuration
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/guard/types.ts"],"names":[],"mappings":";AAAA,2CAA2C"}
@@ -0,0 +1,13 @@
1
+ import { GuardResult, CustomRule } from './types';
2
+ export declare function customRuleDetection(text: string, customRules?: CustomRule[]): GuardResult;
3
+ export declare function guardMetrics(): import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
4
+ export declare function guardMetricAttributes(verdict: string, score: number, validator: string, classification: string, explanation: string): {
5
+ 'telemetry.sdk.name': string;
6
+ 'openlit.guard.verdict': string;
7
+ 'openlit.guard.score': number;
8
+ 'openlit.guard.validator': string;
9
+ 'openlit.guard.classification': string;
10
+ 'openlit.guard.explanation': string;
11
+ };
12
+ export declare function toGuardResult(result: unknown, guardType: string): GuardResult;
13
+ export declare function applyThresholdScore(result: GuardResult, threshold: number): GuardResult;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.customRuleDetection = customRuleDetection;
4
+ exports.guardMetrics = guardMetrics;
5
+ exports.guardMetricAttributes = guardMetricAttributes;
6
+ exports.toGuardResult = toGuardResult;
7
+ exports.applyThresholdScore = applyThresholdScore;
8
+ function customRuleDetection(text, customRules = []) {
9
+ for (const rule of customRules) {
10
+ let regex;
11
+ try {
12
+ regex = new RegExp(rule.pattern, 'i');
13
+ }
14
+ catch (e) {
15
+ // eslint-disable-next-line no-console
16
+ console.warn(`Invalid regex pattern in customRuleDetection: "${rule.pattern}". Skipping rule.`, e);
17
+ continue;
18
+ }
19
+ if (regex.test(text)) {
20
+ return {
21
+ verdict: rule.verdict || 'yes',
22
+ guard: rule.guard || 'custom',
23
+ score: rule.score ?? 0.5,
24
+ classification: rule.classification,
25
+ explanation: rule.explanation || 'Matched custom rule pattern.'
26
+ };
27
+ }
28
+ }
29
+ return {
30
+ score: 0,
31
+ verdict: 'none',
32
+ guard: 'none',
33
+ classification: 'none',
34
+ explanation: 'none'
35
+ };
36
+ }
37
+ // Metric helpers (OpenTelemetry)
38
+ const api_1 = require("@opentelemetry/api");
39
+ function guardMetrics() {
40
+ const meter = api_1.metrics.getMeter('openlit.guard', '0.1.0');
41
+ const guardRequests = meter.createCounter('openlit.guard.requests', {
42
+ description: 'Counter for Guard requests',
43
+ unit: '1'
44
+ });
45
+ return guardRequests;
46
+ }
47
+ function guardMetricAttributes(verdict, score, validator, classification, explanation) {
48
+ return {
49
+ 'telemetry.sdk.name': 'openlit',
50
+ 'openlit.guard.verdict': verdict,
51
+ 'openlit.guard.score': score,
52
+ 'openlit.guard.validator': validator,
53
+ 'openlit.guard.classification': classification,
54
+ 'openlit.guard.explanation': explanation,
55
+ };
56
+ }
57
+ function toGuardResult(result, guardType) {
58
+ const r = result;
59
+ return {
60
+ score: typeof r.score === 'number' ? r.score : 0,
61
+ verdict: typeof r.verdict === 'string' ? r.verdict : 'none',
62
+ guard: typeof r.guard === 'string' ? r.guard : guardType,
63
+ classification: typeof r.classification === 'string' ? r.classification : 'none',
64
+ explanation: typeof r.explanation === 'string' ? r.explanation : 'none',
65
+ };
66
+ }
67
+ function applyThresholdScore(result, threshold) {
68
+ if (result.score < threshold) {
69
+ return {
70
+ score: 0,
71
+ verdict: 'none',
72
+ guard: 'none',
73
+ classification: 'none',
74
+ explanation: 'none'
75
+ };
76
+ }
77
+ return result;
78
+ }
79
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/guard/utils.ts"],"names":[],"mappings":";;AAGA,kDA2BC;AAKD,oCAOC;AAED,sDASC;AAED,sCASC;AAED,kDAWC;AA1ED,SAAgB,mBAAmB,CAAC,IAAY,EAAE,cAA4B,EAAE;IAC9E,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,IAAI,KAAa,CAAC;QAClB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,sCAAsC;YACtC,OAAO,CAAC,IAAI,CAAC,kDAAkD,IAAI,CAAC,OAAO,mBAAmB,EAAE,CAAC,CAAC,CAAC;YACnG,SAAS;QACX,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,OAAO;gBACL,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK;gBAC9B,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,QAAQ;gBAC7B,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG;gBACxB,cAAc,EAAE,IAAI,CAAC,cAAc;gBACnC,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,8BAA8B;aAChE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO;QACL,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,MAAM;QACf,KAAK,EAAE,MAAM;QACb,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,MAAM;KACpB,CAAC;AACJ,CAAC;AAED,iCAAiC;AACjC,4CAA6C;AAE7C,SAAgB,YAAY;IAC1B,MAAM,KAAK,GAAG,aAAO,CAAC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC,wBAAwB,EAAE;QAClE,WAAW,EAAE,4BAA4B;QACzC,IAAI,EAAE,GAAG;KACV,CAAC,CAAC;IACH,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,SAAgB,qBAAqB,CAAC,OAAe,EAAE,KAAa,EAAE,SAAiB,EAAE,cAAsB,EAAE,WAAmB;IAClI,OAAO;QACL,oBAAoB,EAAE,SAAS;QAC/B,uBAAuB,EAAE,OAAO;QAChC,qBAAqB,EAAE,KAAK;QAC5B,yBAAyB,EAAE,SAAS;QACpC,8BAA8B,EAAE,cAAc;QAC9C,2BAA2B,EAAE,WAAW;KACzC,CAAC;AACJ,CAAC;AAED,SAAgB,aAAa,CAAC,MAAe,EAAE,SAAiB;IAC9D,MAAM,CAAC,GAAG,MAA2D,CAAC;IACtE,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChD,OAAO,EAAE,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAE,CAAC,CAAC,OAAkC,CAAC,CAAC,CAAC,MAAM;QACvF,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QACxD,cAAc,EAAE,OAAO,CAAC,CAAC,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM;QAChF,WAAW,EAAE,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM;KACxE,CAAC;AACJ,CAAC;AAED,SAAgB,mBAAmB,CAAC,MAAmB,EAAE,SAAiB;IACxE,IAAI,MAAM,CAAC,KAAK,GAAG,SAAS,EAAE,CAAC;QAC7B,OAAO;YACL,KAAK,EAAE,CAAC;YACR,OAAO,EAAE,MAAM;YACf,KAAK,EAAE,MAAM;YACb,cAAc,EAAE,MAAM;YACtB,WAAW,EAAE,MAAM;SACpB,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,16 +1,26 @@
1
- import { Resource } from '@opentelemetry/resources';
1
+ import { resourceFromAttributes } from '@opentelemetry/resources';
2
2
  import { NodeSDK } from '@opentelemetry/sdk-node';
3
3
  import { OpenlitOptions } from './types';
4
4
  import BaseOpenlit from './features/base';
5
5
  import { Hallucination, Bias, Toxicity, All } from './evals';
6
+ import { PromptInjection } from './guard/prompt-injection';
7
+ import { SensitiveTopic } from './guard/sensitive-topic';
8
+ import { TopicRestriction } from './guard/topic-restriction';
9
+ import { All as GuardAll } from './guard/all';
6
10
  declare const evals: {
7
11
  Hallucination: (options: ConstructorParameters<typeof Hallucination>[0]) => Hallucination;
8
12
  Bias: (options: ConstructorParameters<typeof Bias>[0]) => Bias;
9
13
  Toxicity: (options: ConstructorParameters<typeof Toxicity>[0]) => Toxicity;
10
14
  All: (options: ConstructorParameters<typeof All>[0]) => All;
11
15
  };
16
+ declare const guard: {
17
+ PromptInjection: (options: ConstructorParameters<typeof PromptInjection>[0]) => PromptInjection;
18
+ SensitiveTopic: (options: ConstructorParameters<typeof SensitiveTopic>[0]) => SensitiveTopic;
19
+ TopicRestriction: (options: ConstructorParameters<typeof TopicRestriction>[0]) => TopicRestriction;
20
+ All: (options: ConstructorParameters<typeof GuardAll>[0]) => GuardAll;
21
+ };
12
22
  declare class Openlit extends BaseOpenlit {
13
- static resource: Resource;
23
+ static resource: ReturnType<typeof resourceFromAttributes>;
14
24
  static options: OpenlitOptions;
15
25
  static _sdk: NodeSDK;
16
26
  static evals: {
@@ -19,10 +29,17 @@ declare class Openlit extends BaseOpenlit {
19
29
  Toxicity: (options: ConstructorParameters<typeof Toxicity>[0]) => Toxicity;
20
30
  All: (options: ConstructorParameters<typeof All>[0]) => All;
21
31
  };
32
+ static guard: {
33
+ PromptInjection: (options: ConstructorParameters<typeof PromptInjection>[0]) => PromptInjection;
34
+ SensitiveTopic: (options: ConstructorParameters<typeof SensitiveTopic>[0]) => SensitiveTopic;
35
+ TopicRestriction: (options: ConstructorParameters<typeof TopicRestriction>[0]) => TopicRestriction;
36
+ All: (options: ConstructorParameters<typeof GuardAll>[0]) => GuardAll;
37
+ };
22
38
  static init(options?: OpenlitOptions): void;
23
39
  }
24
40
  declare const openlit: typeof Openlit & {
25
41
  evals: typeof evals;
42
+ guard: typeof guard;
26
43
  };
27
44
  export default openlit;
28
45
  export { Openlit };
package/dist/index.js CHANGED
@@ -7,10 +7,16 @@ exports.Openlit = void 0;
7
7
  const resources_1 = require("@opentelemetry/resources");
8
8
  const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
9
9
  const sdk_node_1 = require("@opentelemetry/sdk-node");
10
- const tracing_1 = __importDefault(require("./tracing"));
10
+ const tracing_1 = __importDefault(require("./otel/tracing"));
11
11
  const constant_1 = require("./constant");
12
12
  const base_1 = __importDefault(require("./features/base"));
13
13
  const evals_1 = require("./evals");
14
+ const metrics_1 = __importDefault(require("./otel/metrics"));
15
+ const semantic_convention_1 = __importDefault(require("./semantic-convention"));
16
+ const prompt_injection_1 = require("./guard/prompt-injection");
17
+ const sensitive_topic_1 = require("./guard/sensitive-topic");
18
+ const topic_restriction_1 = require("./guard/topic-restriction");
19
+ const all_1 = require("./guard/all");
14
20
  // Factory functions for evals
15
21
  const evals = {
16
22
  Hallucination: (options) => new evals_1.Hallucination(options),
@@ -18,11 +24,18 @@ const evals = {
18
24
  Toxicity: (options) => new evals_1.Toxicity(options),
19
25
  All: (options) => new evals_1.All(options),
20
26
  };
27
+ // Factory functions for guards
28
+ const guard = {
29
+ PromptInjection: (options) => new prompt_injection_1.PromptInjection(options),
30
+ SensitiveTopic: (options) => new sensitive_topic_1.SensitiveTopic(options),
31
+ TopicRestriction: (options) => new topic_restriction_1.TopicRestriction(options),
32
+ All: (options) => new all_1.All(options),
33
+ };
21
34
  class Openlit extends base_1.default {
22
35
  static init(options) {
23
36
  try {
24
37
  const { environment = constant_1.DEFAULT_ENVIRONMENT, applicationName = constant_1.DEFAULT_APPLICATION_NAME } = options || {};
25
- const otlpEndpoint = options?.otlpEndpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || undefined;
38
+ const otlpEndpoint = (options?.otlpEndpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318").replace(/\/v1\/traces$/, '');
26
39
  let otlpHeaders = options?.otlpHeaders;
27
40
  if (!otlpHeaders) {
28
41
  if (process.env.OTEL_EXPORTER_OTLP_HEADERS) {
@@ -41,10 +54,10 @@ class Openlit extends base_1.default {
41
54
  this.options.otlpHeaders = otlpHeaders;
42
55
  this.options.disableBatch =
43
56
  options?.disableBatch === undefined ? true : !!options.disableBatch;
44
- this.resource = new resources_1.Resource({
45
- [semantic_conventions_1.SEMRESATTRS_SERVICE_NAME]: applicationName,
46
- [semantic_conventions_1.SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: environment,
47
- [semantic_conventions_1.SEMRESATTRS_TELEMETRY_SDK_NAME]: constant_1.SDK_NAME,
57
+ this.resource = (0, resources_1.resourceFromAttributes)({
58
+ [semantic_conventions_1.ATTR_SERVICE_NAME]: applicationName,
59
+ [semantic_convention_1.default.ATTR_DEPLOYMENT_ENVIRONMENT]: environment,
60
+ [semantic_conventions_1.ATTR_TELEMETRY_SDK_NAME]: constant_1.SDK_NAME,
48
61
  });
49
62
  tracing_1.default.setup({
50
63
  ...this.options,
@@ -54,9 +67,20 @@ class Openlit extends base_1.default {
54
67
  otlpHeaders,
55
68
  resource: this.resource,
56
69
  });
70
+ const exportIntervalMillis = Number(process.env.OTEL_EXPORTER_OTLP_METRICS_EXPORT_INTERVAL ?? 60000) || 60000;
71
+ metrics_1.default.setup({
72
+ ...options,
73
+ environment,
74
+ applicationName,
75
+ otlpEndpoint,
76
+ otlpHeaders,
77
+ resource: this.resource,
78
+ exportIntervalMillis: exportIntervalMillis,
79
+ });
57
80
  this._sdk = new sdk_node_1.NodeSDK({
58
81
  resource: this.resource,
59
82
  traceExporter: tracing_1.default.traceExporter,
83
+ metricReader: metrics_1.default.metricReaders[0],
60
84
  });
61
85
  // This was causing the traceProvider initilization with multiple instances.
62
86
  // this._sdk.start();
@@ -68,6 +92,7 @@ class Openlit extends base_1.default {
68
92
  }
69
93
  exports.Openlit = Openlit;
70
94
  Openlit.evals = evals;
95
+ Openlit.guard = guard;
71
96
  const openlit = Openlit;
72
97
  exports.default = openlit;
73
98
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,wDAAoD;AACpD,8EAI6C;AAC7C,sDAAkD;AAElD,wDAAgC;AAChC,yCAAqF;AAErF,2DAA0C;AAC1C,mCAA6D;AAE7D,8BAA8B;AAC9B,MAAM,KAAK,GAAG;IACZ,aAAa,EAAE,CAAC,OAAuD,EAAE,EAAE,CAAC,IAAI,qBAAa,CAAC,OAAO,CAAC;IACtG,IAAI,EAAE,CAAC,OAA8C,EAAE,EAAE,CAAC,IAAI,YAAI,CAAC,OAAO,CAAC;IAC3E,QAAQ,EAAE,CAAC,OAAkD,EAAE,EAAE,CAAC,IAAI,gBAAQ,CAAC,OAAO,CAAC;IACvF,GAAG,EAAE,CAAC,OAA6C,EAAE,EAAE,CAAC,IAAI,WAAG,CAAC,OAAO,CAAC;CACzE,CAAC;AAEF,MAAM,OAAQ,SAAQ,cAAW;IAK/B,MAAM,CAAC,IAAI,CAAC,OAAwB;QAClC,IAAI,CAAC;YACH,MAAM,EAAE,WAAW,GAAG,8BAAmB,EAAE,eAAe,GAAG,mCAAwB,EAAE,GACrF,OAAO,IAAI,EAAE,CAAC;YAEhB,MAAM,YAAY,GAChB,OAAO,EAAE,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,SAAS,CAAC;YAChF,IAAI,WAAW,GAAG,OAAO,EAAE,WAAW,CAAC;YACvC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,IAAI,OAAO,CAAC,GAAG,CAAC,0BAA0B,EAAE,CAAC;oBAC3C,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CACpE,CAAC,GAA2B,EAAE,KAAa,EAAE,EAAE;wBAC7C,MAAM,MAAM,GAAa,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;wBAC1C,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;wBAC3B,OAAO,GAAG,CAAC;oBACb,CAAC,EACD,EAA4B,CAC7B,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,WAAW,GAAG,EAAE,CAAC;gBACnB,CAAC;YACH,CAAC;YAED,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,EAAE,CAAC;YAC7B,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,YAAY,CAAC;YACzC,IAAI,CAAC,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;YACvC,IAAI,CAAC,OAAO,CAAC,YAAY;gBACvB,OAAO,EAAE,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;YAEtE,IAAI,CAAC,QAAQ,GAAG,IAAI,oBAAQ,CAAC;gBAC3B,CAAC,+CAAwB,CAAC,EAAE,eAAe;gBAC3C,CAAC,yDAAkC,CAAC,EAAE,WAAW;gBACjD,CAAC,qDAA8B,CAAC,EAAE,mBAAQ;aAC3C,CAAC,CAAC;YAEH,iBAAO,CAAC,KAAK,CAAC;gBACZ,GAAG,IAAI,CAAC,OAAO;gBACf,WAAW;gBACX,eAAe;gBACf,YAAY;gBACZ,WAAW;gBACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACxB,CAAC,CAAC;YAEH,IAAI,CAAC,IAAI,GAAG,IAAI,kBAAO,CAAC;gBACtB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,aAAa,EAAE,iBAAO,CAAC,aAA6B;aACrD,CAAC,CAAC;YAEH,4EAA4E;YAC5E,qBAAqB;QACvB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;;AAQM,0BAAO;AA/DP,aAAK,GAAG,KAAK,CAAC;AA0DvB,MAAM,OAAO,GAAG,OAEf,CAAC;AAEF,kBAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const api_1 = require("@opentelemetry/api");
7
+ const wrapper_1 = __importDefault(require("../anthropic/wrapper"));
8
+ const config_1 = __importDefault(require("../../config"));
9
+ const helpers_1 = __importDefault(require("../../helpers"));
10
+ const base_wrapper_1 = __importDefault(require("../base-wrapper"));
11
+ const semantic_convention_1 = __importDefault(require("../../semantic-convention"));
12
+ jest.mock('../../../src/config');
13
+ jest.mock('../../../src/helpers');
14
+ jest.mock('../../../src/instrumentation/base-wrapper');
15
+ const mockTracer = api_1.trace.getTracer('test-tracer');
16
+ describe('AnthropicWrapper', () => {
17
+ let span;
18
+ beforeEach(() => {
19
+ span = mockTracer.startSpan('test-span');
20
+ span.setAttribute = jest.fn();
21
+ jest.clearAllMocks();
22
+ });
23
+ afterEach(() => {
24
+ span.end();
25
+ });
26
+ describe('_messageCreate', () => {
27
+ it('should call recordMetrics after span ends', async () => {
28
+ const mockArgs = [{ message: 'test message' }];
29
+ const mockResponse = { response_id: '123', meta: { billedUnits: { inputTokens: 10, outputTokens: 20 } } };
30
+ jest.spyOn(base_wrapper_1.default, 'recordMetrics').mockImplementation(() => { });
31
+ jest.spyOn(wrapper_1.default, '_messageCreateCommonSetter').mockImplementationOnce(async ({ genAIEndpoint, span }) => {
32
+ span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT);
33
+ span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, '123');
34
+ span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MAX_TOKENS, 100);
35
+ span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TEMPERATURE, 0.7);
36
+ span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, 10);
37
+ span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS, 20);
38
+ span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_TOTAL_TOKENS, 30);
39
+ span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON, 'stop');
40
+ return {
41
+ genAIEndpoint,
42
+ model: 'test-model',
43
+ user: 'test-user',
44
+ cost: 0.5,
45
+ aiSystem: 'anthropic',
46
+ };
47
+ });
48
+ await wrapper_1.default._messageCreate({
49
+ args: mockArgs,
50
+ genAIEndpoint: 'anthropic.endpoint',
51
+ response: mockResponse,
52
+ span,
53
+ });
54
+ expect(base_wrapper_1.default.recordMetrics).toHaveBeenCalledWith(span, {
55
+ model: 'test-model',
56
+ user: 'test-user',
57
+ cost: 0.5,
58
+ aiSystem: 'anthropic',
59
+ genAIEndpoint: 'anthropic.endpoint',
60
+ });
61
+ });
62
+ });
63
+ describe('_messageCommonSetter', () => {
64
+ it('should set span attributes and return metric parameters', async () => {
65
+ const mockArgs = [{ message: 'test message', max_tokens: 100, temperature: 0.7 }];
66
+ const mockResult = {
67
+ id: '123',
68
+ usage: { input_tokens: 10, output_tokens: 20 },
69
+ model: 'claude-3-sonnet-20240229',
70
+ stop_reason: 'stop',
71
+ };
72
+ jest.spyOn(config_1.default, 'updatePricingJson').mockResolvedValue({});
73
+ jest.spyOn(helpers_1.default, 'getChatModelCost').mockReturnValue(0.5);
74
+ const setAttributeSpy = jest.spyOn(span, 'setAttribute');
75
+ await wrapper_1.default._messageCreateCommonSetter({
76
+ args: mockArgs,
77
+ genAIEndpoint: 'anthropic.endpoint',
78
+ result: mockResult,
79
+ span,
80
+ });
81
+ expect(setAttributeSpy).toHaveBeenCalledWith(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT);
82
+ expect(setAttributeSpy).toHaveBeenCalledWith(semantic_convention_1.default.GEN_AI_RESPONSE_ID, '123');
83
+ expect(setAttributeSpy).toHaveBeenCalledWith(semantic_convention_1.default.GEN_AI_REQUEST_MAX_TOKENS, 100);
84
+ expect(setAttributeSpy).toHaveBeenCalledWith(semantic_convention_1.default.GEN_AI_REQUEST_TEMPERATURE, 0.7);
85
+ expect(setAttributeSpy).toHaveBeenCalledWith(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, 10);
86
+ expect(setAttributeSpy).toHaveBeenCalledWith(semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS, 20);
87
+ expect(setAttributeSpy).toHaveBeenCalledWith(semantic_convention_1.default.GEN_AI_USAGE_TOTAL_TOKENS, 30);
88
+ expect(setAttributeSpy).toHaveBeenCalledWith(semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON, 'stop');
89
+ });
90
+ });
91
+ });
92
+ //# sourceMappingURL=anthropic-wrapper.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic-wrapper.test.js","sourceRoot":"","sources":["../../../src/instrumentation/__tests__/anthropic-wrapper.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const resources_1 = require("@opentelemetry/resources");
7
+ const semantic_convention_1 = __importDefault(require("../../semantic-convention"));
8
+ const metrics_1 = __importDefault(require("../../otel/metrics"));
9
+ const base_wrapper_1 = __importDefault(require("../base-wrapper"));
10
+ const index_1 = __importDefault(require("../../index"));
11
+ describe('BaseWrapper.setBaseSpanAttributes', () => {
12
+ let span;
13
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
14
+ let addSpy;
15
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
+ let recordSpy;
17
+ beforeEach(() => {
18
+ index_1.default.init({
19
+ applicationName: 'TestApp',
20
+ environment: 'TestEnv',
21
+ otlpEndpoint: 'http://localhost:4318',
22
+ });
23
+ metrics_1.default.setup({ resource: (0, resources_1.defaultResource)(), otlpEndpoint: 'http://localhost:4318' }); // Ensure metrics are initialized with a valid endpoint
24
+ addSpy = jest.spyOn(metrics_1.default.genaiRequests, 'add').mockImplementation(() => { });
25
+ jest.spyOn(metrics_1.default.genaiPromptTokens, 'add').mockImplementation(() => { });
26
+ jest.spyOn(metrics_1.default.genaiCompletionTokens, 'add').mockImplementation(() => { });
27
+ jest.spyOn(metrics_1.default.genaiClientOperationDuration, 'record').mockImplementation(() => { });
28
+ jest.spyOn(metrics_1.default.genaiCost, 'record').mockImplementation(() => { });
29
+ span = {
30
+ setAttribute: jest.fn(),
31
+ setStatus: jest.fn(),
32
+ attributes: {
33
+ [semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS]: 10,
34
+ [semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS]: 20,
35
+ duration: 1.5,
36
+ },
37
+ };
38
+ Object.defineProperty(span, 'setAttributes', {
39
+ value: jest.fn(),
40
+ writable: true,
41
+ configurable: true,
42
+ enumerable: true,
43
+ });
44
+ });
45
+ afterEach(() => {
46
+ jest.restoreAllMocks();
47
+ });
48
+ it('should increment all metrics and set span attributes', () => {
49
+ const baseAttributes = {
50
+ model: 'gpt-4',
51
+ user: 'user1',
52
+ cost: 0.99,
53
+ aiSystem: 'openai',
54
+ genAIEndpoint: 'endpoint',
55
+ };
56
+ // @ts-expect-error: test mock span needs attributes property for metrics extraction
57
+ base_wrapper_1.default.setBaseSpanAttributes(span, baseAttributes);
58
+ base_wrapper_1.default.recordMetrics(span, baseAttributes);
59
+ expect(span.setAttribute).toHaveBeenCalledWith(semantic_convention_1.default.GEN_AI_REQUEST_USER, 'user1');
60
+ expect(span.setAttribute).toHaveBeenCalledWith(semantic_convention_1.default.GEN_AI_USAGE_COST, 0.99);
61
+ expect(span.setStatus).toHaveBeenCalled();
62
+ expect(metrics_1.default.genaiRequests.add).toHaveBeenCalledWith(1, expect.objectContaining({
63
+ [semantic_convention_1.default.GEN_AI_SYSTEM]: 'openai',
64
+ [semantic_convention_1.default.GEN_AI_REQUEST_USER]: 'user1',
65
+ [semantic_convention_1.default.GEN_AI_REQUEST_MODEL]: 'gpt-4',
66
+ }));
67
+ expect(metrics_1.default.genaiPromptTokens.add).toHaveBeenCalledWith(10, expect.any(Object));
68
+ expect(metrics_1.default.genaiCompletionTokens.add).toHaveBeenCalledWith(20, expect.any(Object));
69
+ expect(metrics_1.default.genaiClientOperationDuration.record).toHaveBeenCalledWith(1.5e-9, expect.any(Object));
70
+ expect(metrics_1.default.genaiCost.record).toHaveBeenCalledWith(0.99, expect.any(Object));
71
+ });
72
+ it('should handle missing tokens and duration gracefully', () => {
73
+ Object.defineProperty(span, 'attributes', {
74
+ value: {},
75
+ writable: true,
76
+ configurable: true,
77
+ enumerable: true,
78
+ });
79
+ const baseAttributes = {
80
+ genAIEndpoint: 'endpoint',
81
+ model: 'gpt-4',
82
+ user: 'user2',
83
+ cost: undefined,
84
+ aiSystem: 'openai',
85
+ };
86
+ base_wrapper_1.default.setBaseSpanAttributes(span, baseAttributes);
87
+ base_wrapper_1.default.recordMetrics(span, baseAttributes);
88
+ expect(metrics_1.default.genaiPromptTokens.add).not.toHaveBeenCalled();
89
+ expect(metrics_1.default.genaiCompletionTokens.add).not.toHaveBeenCalled();
90
+ expect(metrics_1.default.genaiClientOperationDuration.record).not.toHaveBeenCalled();
91
+ expect(metrics_1.default.genaiCost.record).not.toHaveBeenCalled();
92
+ });
93
+ describe('metrics logic for inputTokens, outputTokens, duration, cost', () => {
94
+ beforeEach(() => {
95
+ metrics_1.default.setup({ resource: (0, resources_1.defaultResource)(), otlpEndpoint: 'http://localhost:4318' });
96
+ jest.spyOn(metrics_1.default.genaiPromptTokens, 'add').mockImplementation(() => { });
97
+ jest.spyOn(metrics_1.default.genaiCompletionTokens, 'add').mockImplementation(() => { });
98
+ jest.spyOn(metrics_1.default.genaiClientOperationDuration, 'record').mockImplementation(() => { });
99
+ jest.spyOn(metrics_1.default.genaiCost, 'record').mockImplementation(() => { });
100
+ });
101
+ it('should not call metrics for NaN, undefined, or non-number values', () => {
102
+ const span = {
103
+ setAttribute: jest.fn(),
104
+ setStatus: jest.fn(),
105
+ setAttributes: jest.fn(),
106
+ attributes: {
107
+ [semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS]: NaN,
108
+ [semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS]: undefined,
109
+ duration: 'not-a-number',
110
+ },
111
+ };
112
+ const baseAttributes = {
113
+ model: 'gpt-4',
114
+ user: 'user1',
115
+ cost: 'not-a-number',
116
+ aiSystem: 'openai',
117
+ genAIEndpoint: 'endpoint',
118
+ };
119
+ base_wrapper_1.default.setBaseSpanAttributes(span, baseAttributes);
120
+ base_wrapper_1.default.recordMetrics(span, baseAttributes);
121
+ expect(metrics_1.default.genaiPromptTokens.add).not.toHaveBeenCalled();
122
+ expect(metrics_1.default.genaiCompletionTokens.add).not.toHaveBeenCalled();
123
+ expect(metrics_1.default.genaiClientOperationDuration.record).not.toHaveBeenCalled();
124
+ expect(metrics_1.default.genaiCost.record).not.toHaveBeenCalled();
125
+ });
126
+ it('should call metrics for zero and negative values', () => {
127
+ const span = {
128
+ setAttribute: jest.fn(),
129
+ setStatus: jest.fn(),
130
+ setAttributes: jest.fn(),
131
+ attributes: {
132
+ [semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS]: 0,
133
+ [semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS]: -5,
134
+ duration: -1.5,
135
+ },
136
+ };
137
+ const baseAttributes = {
138
+ model: 'gpt-4',
139
+ user: 'user1',
140
+ cost: 0,
141
+ aiSystem: 'openai',
142
+ genAIEndpoint: 'endpoint',
143
+ };
144
+ base_wrapper_1.default.setBaseSpanAttributes(span, baseAttributes);
145
+ base_wrapper_1.default.recordMetrics(span, baseAttributes);
146
+ expect(metrics_1.default.genaiPromptTokens.add).toHaveBeenCalledWith(0, expect.any(Object));
147
+ expect(metrics_1.default.genaiCompletionTokens.add).toHaveBeenCalledWith(-5, expect.any(Object));
148
+ expect(metrics_1.default.genaiClientOperationDuration.record).toHaveBeenCalledWith(-1.5e-9, expect.any(Object));
149
+ expect(metrics_1.default.genaiCost.record).toHaveBeenCalledWith(0, expect.any(Object));
150
+ });
151
+ it('should convert string cost to number if possible', () => {
152
+ const span = {
153
+ setAttribute: jest.fn(),
154
+ setStatus: jest.fn(),
155
+ setAttributes: jest.fn(),
156
+ attributes: {
157
+ [semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS]: 1,
158
+ [semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS]: 2,
159
+ duration: 3,
160
+ },
161
+ };
162
+ const baseAttributes = {
163
+ model: 'gpt-4',
164
+ user: 'user1',
165
+ cost: '1.23',
166
+ aiSystem: 'openai',
167
+ genAIEndpoint: 'endpoint',
168
+ };
169
+ base_wrapper_1.default.setBaseSpanAttributes(span, baseAttributes);
170
+ base_wrapper_1.default.recordMetrics(span, baseAttributes);
171
+ expect(metrics_1.default.genaiCost.record).toHaveBeenCalledWith(1.23, expect.any(Object));
172
+ });
173
+ });
174
+ });
175
+ //# sourceMappingURL=base-wrapper.test.js.map