@mynitorai/sdk 0.1.6 → 0.1.8

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/index.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export interface MyNitorConfig {
6
6
  apiKey: string;
7
+ environment?: string;
7
8
  endpoint?: string;
8
9
  }
9
10
  export declare class MyNitor {
@@ -15,7 +16,7 @@ export declare class MyNitor {
15
16
  private setupAutoFlush;
16
17
  static init(config: MyNitorConfig): MyNitor;
17
18
  /**
18
- * Automatically detect and wrap AI libraries like OpenAI
19
+ * Automatically detect and wrap AI libraries: OpenAI, Anthropic, and Google Gemini
19
20
  */
20
21
  instrument(): void;
21
22
  /**
@@ -27,5 +28,7 @@ export declare class MyNitor {
27
28
  private getCallSite;
28
29
  private sendEvent;
29
30
  private wrapOpenAI;
31
+ private wrapAnthropic;
32
+ private wrapGemini;
30
33
  }
31
34
  export declare const init: typeof MyNitor.init;
package/dist/index.js CHANGED
@@ -5,11 +5,14 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.init = exports.MyNitor = void 0;
8
+ // Symbol to prevent double-patching (Idempotency)
9
+ const WRAPPED_MARKER = Symbol('mynitor_wrapped');
8
10
  class MyNitor {
9
11
  constructor(config) {
10
12
  this.isInstrumented = false;
11
13
  this.pendingPromises = new Set();
12
14
  this.config = {
15
+ environment: 'production',
13
16
  endpoint: 'https://app.mynitor.ai/api/v1/events',
14
17
  ...config
15
18
  };
@@ -21,7 +24,6 @@ class MyNitor {
21
24
  process.env.NETLIFY ||
22
25
  process.env.FUNCTIONS_WORKER_RUNTIME);
23
26
  if (!isServerless && typeof process !== 'undefined' && typeof process.on === 'function') {
24
- // Local script or long-running process
25
27
  process.on('beforeExit', async () => {
26
28
  await this.flush();
27
29
  });
@@ -37,14 +39,16 @@ class MyNitor {
37
39
  return MyNitor.instance;
38
40
  }
39
41
  /**
40
- * Automatically detect and wrap AI libraries like OpenAI
42
+ * Automatically detect and wrap AI libraries: OpenAI, Anthropic, and Google Gemini
41
43
  */
42
44
  instrument() {
43
45
  if (this.isInstrumented)
44
46
  return;
45
47
  this.wrapOpenAI();
48
+ this.wrapAnthropic();
49
+ this.wrapGemini();
46
50
  this.isInstrumented = true;
47
- console.log('🚀 MyNitor: Auto-instrumentation active.');
51
+ console.log('🚀 MyNitor: Universal Auto-instrumentation active.');
48
52
  }
49
53
  /**
50
54
  * Waits for all pending network requests to complete.
@@ -63,12 +67,8 @@ class MyNitor {
63
67
  try {
64
68
  const err = new Error();
65
69
  const stack = err.stack?.split('\n') || [];
66
- // Look for the frame that called the LLM method
67
- // Stack usually: Error -> getCallSite -> wrapOpenAI wrapper -> USER CODE
68
- // We iterate to find the first frame NOT in MyNitor SDK
69
70
  for (const line of stack) {
70
71
  if (!line.includes('mynitor') && !line.includes('Error') && line.includes('/')) {
71
- // Typical format: " at Object.myFunction (/path/to/file.ts:10:5)"
72
72
  const match = line.match(/at\s+(?:(.+?)\s+\()?(.*?):(\d+):(\d+)\)?/);
73
73
  if (match) {
74
74
  const func = match[1] || 'anonymous';
@@ -84,15 +84,11 @@ class MyNitor {
84
84
  }
85
85
  }
86
86
  }
87
- catch (e) {
88
- // fail safe
89
- }
87
+ catch (e) { }
90
88
  return { file: 'unknown', line: 0, functionName: 'unknown', workflowGuess: 'default-workflow' };
91
89
  }
92
90
  async sendEvent(payload) {
93
91
  try {
94
- // Fire and forget
95
- // Fire and forget (but track)
96
92
  const promise = fetch(this.config.endpoint, {
97
93
  method: 'POST',
98
94
  headers: {
@@ -101,6 +97,7 @@ class MyNitor {
101
97
  },
102
98
  body: JSON.stringify({
103
99
  ...payload,
100
+ environment: this.config.environment,
104
101
  event_version: '1.0',
105
102
  timestamp: new Date().toISOString()
106
103
  })
@@ -119,14 +116,17 @@ class MyNitor {
119
116
  const OpenAI = require('openai');
120
117
  if (!OpenAI || !OpenAI.OpenAI)
121
118
  return;
119
+ const target = OpenAI.OpenAI.Chat.Completions.prototype;
120
+ if (target[WRAPPED_MARKER])
121
+ return;
122
122
  const self = this;
123
- const originalChatCreate = OpenAI.OpenAI.Chat.Completions.prototype.create;
124
- OpenAI.OpenAI.Chat.Completions.prototype.create = async function (...args) {
123
+ const originalCreate = target.create;
124
+ target.create = async function (...args) {
125
125
  const start = Date.now();
126
126
  const body = args[0];
127
127
  const callsite = self.getCallSite();
128
128
  try {
129
- const result = await originalChatCreate.apply(this, args);
129
+ const result = await originalCreate.apply(this, args);
130
130
  const end = Date.now();
131
131
  self.sendEvent({
132
132
  request_id: result.id || `req_${Date.now()}`,
@@ -161,6 +161,115 @@ class MyNitor {
161
161
  throw error;
162
162
  }
163
163
  };
164
+ target[WRAPPED_MARKER] = true;
165
+ }
166
+ catch (e) { }
167
+ }
168
+ wrapAnthropic() {
169
+ try {
170
+ const Anthropic = require('@anthropic-ai/sdk');
171
+ if (!Anthropic || !Anthropic.Messages)
172
+ return;
173
+ const target = Anthropic.Messages.prototype;
174
+ if (target[WRAPPED_MARKER])
175
+ return;
176
+ const self = this;
177
+ const originalCreate = target.create;
178
+ target.create = async function (...args) {
179
+ const start = Date.now();
180
+ const body = args[0];
181
+ const callsite = self.getCallSite();
182
+ try {
183
+ const result = await originalCreate.apply(this, args);
184
+ const end = Date.now();
185
+ self.sendEvent({
186
+ request_id: result.id || `ant_${Date.now()}`,
187
+ model: result.model || body.model,
188
+ provider: 'anthropic',
189
+ agent: 'default-agent',
190
+ workflow: callsite.workflowGuess,
191
+ file: callsite.file,
192
+ function_name: callsite.functionName,
193
+ line_number: callsite.line,
194
+ input_tokens: result.usage?.input_tokens || 0,
195
+ output_tokens: result.usage?.output_tokens || 0,
196
+ latency_ms: end - start,
197
+ status: 'success'
198
+ });
199
+ return result;
200
+ }
201
+ catch (error) {
202
+ const end = Date.now();
203
+ self.sendEvent({
204
+ request_id: `err_ant_${Date.now()}`,
205
+ model: body?.model || 'unknown',
206
+ provider: 'anthropic',
207
+ agent: 'default-agent',
208
+ workflow: callsite.workflowGuess,
209
+ file: callsite.file,
210
+ function_name: callsite.functionName,
211
+ latency_ms: end - start,
212
+ status: 'error',
213
+ error_type: error?.constructor?.name || 'Error'
214
+ });
215
+ throw error;
216
+ }
217
+ };
218
+ target[WRAPPED_MARKER] = true;
219
+ }
220
+ catch (e) { }
221
+ }
222
+ wrapGemini() {
223
+ try {
224
+ const GoogleGenAI = require('@google/generative-ai');
225
+ if (!GoogleGenAI || !GoogleGenAI.GenerativeModel)
226
+ return;
227
+ const target = GoogleGenAI.GenerativeModel.prototype;
228
+ if (target[WRAPPED_MARKER])
229
+ return;
230
+ const self = this;
231
+ const originalGenerate = target.generateContent;
232
+ target.generateContent = async function (...args) {
233
+ const start = Date.now();
234
+ const callsite = self.getCallSite();
235
+ try {
236
+ const result = await originalGenerate.apply(this, args);
237
+ const end = Date.now();
238
+ const metadata = result.response?.usageMetadata;
239
+ self.sendEvent({
240
+ request_id: `gem_${Date.now()}`,
241
+ model: this.model || 'gemini',
242
+ provider: 'google',
243
+ agent: 'default-agent',
244
+ workflow: callsite.workflowGuess,
245
+ file: callsite.file,
246
+ function_name: callsite.functionName,
247
+ line_number: callsite.line,
248
+ input_tokens: metadata?.promptTokenCount || 0,
249
+ output_tokens: metadata?.candidatesTokenCount || 0,
250
+ latency_ms: end - start,
251
+ status: 'success'
252
+ });
253
+ return result;
254
+ }
255
+ catch (error) {
256
+ const end = Date.now();
257
+ self.sendEvent({
258
+ request_id: `err_gem_${Date.now()}`,
259
+ model: this.model || 'gemini',
260
+ provider: 'google',
261
+ agent: 'default-agent',
262
+ workflow: callsite.workflowGuess,
263
+ file: callsite.file,
264
+ function_name: callsite.functionName,
265
+ latency_ms: end - start,
266
+ status: 'error',
267
+ error_type: error?.constructor?.name || 'Error'
268
+ });
269
+ throw error;
270
+ }
271
+ };
272
+ target[WRAPPED_MARKER] = true;
164
273
  }
165
274
  catch (e) { }
166
275
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mynitorai/sdk",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Production safety and observability for AI systems.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,4 +30,4 @@
30
30
  "openai": "^4.0.0",
31
31
  "@types/node": "^20.0.0"
32
32
  }
33
- }
33
+ }