neatlogs 1.1.2 → 1.1.5
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/browser.cjs +11 -11
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +15 -15
- package/dist/browser.mjs +11 -11
- package/dist/browser.mjs.map +1 -1
- package/dist/google-genai.cjs +640 -0
- package/dist/google-genai.cjs.map +1 -0
- package/dist/google-genai.d.ts +42 -0
- package/dist/google-genai.mjs +622 -0
- package/dist/google-genai.mjs.map +1 -0
- package/dist/index.cjs +746 -346
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.mjs +745 -348
- package/dist/index.mjs.map +1 -1
- package/dist/opencode-plugin.cjs +1 -1
- package/dist/opencode-plugin.cjs.map +1 -1
- package/dist/opencode-plugin.mjs +1 -1
- package/dist/opencode-plugin.mjs.map +1 -1
- package/package.json +12 -1
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
// src/google-genai.ts
|
|
2
|
+
import { trace as trace2, context as otelContext4, SpanStatusCode as SpanStatusCode3 } from "@opentelemetry/api";
|
|
3
|
+
|
|
4
|
+
// src/core/auto-root.ts
|
|
5
|
+
import {
|
|
6
|
+
trace as otelTrace3,
|
|
7
|
+
context as otelContext3
|
|
8
|
+
} from "@opentelemetry/api";
|
|
9
|
+
|
|
10
|
+
// src/core/context.ts
|
|
11
|
+
import {
|
|
12
|
+
trace as otelTrace2,
|
|
13
|
+
context as otelContext2,
|
|
14
|
+
createContextKey,
|
|
15
|
+
ROOT_CONTEXT,
|
|
16
|
+
SpanStatusCode as SpanStatusCode2
|
|
17
|
+
} from "@opentelemetry/api";
|
|
18
|
+
|
|
19
|
+
// src/prompt/template.ts
|
|
20
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
21
|
+
var _promptStorage = new AsyncLocalStorage();
|
|
22
|
+
var _userPromptStorage = new AsyncLocalStorage();
|
|
23
|
+
|
|
24
|
+
// src/core/logger.ts
|
|
25
|
+
var LOG_LEVELS = {
|
|
26
|
+
DEBUG: 0,
|
|
27
|
+
INFO: 1,
|
|
28
|
+
WARN: 2,
|
|
29
|
+
ERROR: 3,
|
|
30
|
+
NONE: 4
|
|
31
|
+
};
|
|
32
|
+
var _logLevel = "INFO";
|
|
33
|
+
var _disabled = false;
|
|
34
|
+
function _getConfiguredLevel() {
|
|
35
|
+
const envLevel = process.env.NEATLOGS_LOG_LEVEL?.toUpperCase();
|
|
36
|
+
if (envLevel && envLevel in LOG_LEVELS) {
|
|
37
|
+
return envLevel;
|
|
38
|
+
}
|
|
39
|
+
return "INFO";
|
|
40
|
+
}
|
|
41
|
+
_logLevel = _getConfiguredLevel();
|
|
42
|
+
function getLogger() {
|
|
43
|
+
return {
|
|
44
|
+
debug(message, ...args) {
|
|
45
|
+
if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.DEBUG) {
|
|
46
|
+
console.debug(`[neatlogs] ${message}`, ...args);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
info(message, ...args) {
|
|
50
|
+
if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.INFO) {
|
|
51
|
+
console.info(`[neatlogs] ${message}`, ...args);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
warn(message, ...args) {
|
|
55
|
+
if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.WARN) {
|
|
56
|
+
console.warn(`[neatlogs] ${message}`, ...args);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
error(message, ...args) {
|
|
60
|
+
if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.ERROR) {
|
|
61
|
+
console.error(`[neatlogs] ${message}`, ...args);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/core/mask.ts
|
|
68
|
+
var logger = getLogger();
|
|
69
|
+
|
|
70
|
+
// src/core/end-user.ts
|
|
71
|
+
import { trace as otelTrace, context as otelContext } from "@opentelemetry/api";
|
|
72
|
+
|
|
73
|
+
// src/core/identity.ts
|
|
74
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
75
|
+
var _GLOBAL_KEY = /* @__PURE__ */ Symbol.for("neatlogs.identity.storage");
|
|
76
|
+
var _g = globalThis;
|
|
77
|
+
var _storage = _g[_GLOBAL_KEY] ?? (_g[_GLOBAL_KEY] = new AsyncLocalStorage2());
|
|
78
|
+
function currentSessionId() {
|
|
79
|
+
return _storage.getStore()?.sessionId;
|
|
80
|
+
}
|
|
81
|
+
function currentEndUserId() {
|
|
82
|
+
return _storage.getStore()?.endUserId;
|
|
83
|
+
}
|
|
84
|
+
function currentEndUserMetadata() {
|
|
85
|
+
return _storage.getStore()?.endUserMetadata;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/core/end-user.ts
|
|
89
|
+
var logger2 = getLogger();
|
|
90
|
+
var END_USER_ID_KEY = "neatlogs.end_user.id";
|
|
91
|
+
var END_USER_METADATA_KEY = "neatlogs.end_user.metadata";
|
|
92
|
+
function normalizeEndUserMetadata(metadata) {
|
|
93
|
+
if (metadata === void 0 || metadata === null) return void 0;
|
|
94
|
+
if (typeof metadata === "string") return metadata || void 0;
|
|
95
|
+
try {
|
|
96
|
+
return JSON.stringify(metadata);
|
|
97
|
+
} catch {
|
|
98
|
+
return JSON.stringify(String(metadata));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function applyEndUserAttributes(span, endUserId, endUserMetadata, isRoot = true) {
|
|
102
|
+
const resolvedId = isRoot ? endUserId ?? currentEndUserId() : endUserId;
|
|
103
|
+
const resolvedMeta = isRoot ? endUserMetadata ?? currentEndUserMetadata() : endUserMetadata;
|
|
104
|
+
if (!resolvedId && !resolvedMeta) return;
|
|
105
|
+
if (!isRoot) {
|
|
106
|
+
logger2.debug(
|
|
107
|
+
"[end_user] Ignoring endUserId/endUserMetadata on a non-root span \u2014 declare it on the trace root (top-level trace()/span()) or identify()."
|
|
108
|
+
);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (resolvedId) {
|
|
112
|
+
span.setAttribute(END_USER_ID_KEY, String(resolvedId));
|
|
113
|
+
}
|
|
114
|
+
const metaJson = normalizeEndUserMetadata(resolvedMeta);
|
|
115
|
+
if (metaJson) {
|
|
116
|
+
span.setAttribute(END_USER_METADATA_KEY, metaJson);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/core/session.ts
|
|
121
|
+
var logger3 = getLogger();
|
|
122
|
+
var SESSION_ID_KEY = "neatlogs.session.id";
|
|
123
|
+
function applySessionAttributes(span, sessionId, isRoot = true) {
|
|
124
|
+
const resolved = isRoot ? sessionId ?? currentSessionId() : sessionId;
|
|
125
|
+
if (!resolved) return;
|
|
126
|
+
if (!isRoot) {
|
|
127
|
+
logger3.debug(
|
|
128
|
+
"[session] Ignoring sessionId on a non-root span \u2014 declare it on the trace root (top-level trace()/span()) or identify()."
|
|
129
|
+
);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
span.setAttribute(SESSION_ID_KEY, String(resolved));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/decorators/base.ts
|
|
136
|
+
import { trace, SpanStatusCode } from "@opentelemetry/api";
|
|
137
|
+
var logger4 = getLogger();
|
|
138
|
+
|
|
139
|
+
// src/core/context.ts
|
|
140
|
+
var logger5 = getLogger();
|
|
141
|
+
var _sessionConfig = {};
|
|
142
|
+
function getSessionConfig() {
|
|
143
|
+
return { ..._sessionConfig };
|
|
144
|
+
}
|
|
145
|
+
var PROMPT_VARIABLES_KEY = createContextKey("neatlogs.prompt_variables");
|
|
146
|
+
var PROMPT_TEMPLATE_KEY = createContextKey("neatlogs.prompt_template");
|
|
147
|
+
var PROMPT_VERSION_KEY = createContextKey("neatlogs.prompt_version");
|
|
148
|
+
var USER_PROMPT_TEMPLATE_KEY = createContextKey("neatlogs.user_prompt_template");
|
|
149
|
+
var USER_PROMPT_VARIABLES_KEY = createContextKey("neatlogs.user_prompt_variables");
|
|
150
|
+
|
|
151
|
+
// src/core/auto-root.ts
|
|
152
|
+
var ROOT_KINDS = /* @__PURE__ */ new Set(["workflow", "chain", "agent", "mcp_tool"]);
|
|
153
|
+
function autoRootEnabled() {
|
|
154
|
+
const val = (process.env.NEATLOGS_AUTO_ROOT ?? "").trim().toLowerCase();
|
|
155
|
+
return !["false", "0", "no", "off"].includes(val);
|
|
156
|
+
}
|
|
157
|
+
function resolveRootWorkflowName() {
|
|
158
|
+
try {
|
|
159
|
+
const name = getSessionConfig()?.workflowName;
|
|
160
|
+
if (typeof name === "string" && name.trim()) return name;
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
return "workflow";
|
|
164
|
+
}
|
|
165
|
+
function stampAutoRootIdentity(root) {
|
|
166
|
+
applySessionAttributes(root, void 0, true);
|
|
167
|
+
applyEndUserAttributes(root, void 0, void 0, true);
|
|
168
|
+
}
|
|
169
|
+
function wrapSpanWithRoot(child, root) {
|
|
170
|
+
let ended = false;
|
|
171
|
+
return new Proxy(child, {
|
|
172
|
+
get(target, prop, _receiver) {
|
|
173
|
+
if (prop === "end") {
|
|
174
|
+
return (...args) => {
|
|
175
|
+
if (ended) return;
|
|
176
|
+
ended = true;
|
|
177
|
+
try {
|
|
178
|
+
target.end(...args);
|
|
179
|
+
} finally {
|
|
180
|
+
try {
|
|
181
|
+
root.end();
|
|
182
|
+
} catch {
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const value = Reflect.get(target, prop, target);
|
|
188
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
var AutoRootTracer = class {
|
|
193
|
+
constructor(_tracer) {
|
|
194
|
+
this._tracer = _tracer;
|
|
195
|
+
}
|
|
196
|
+
_tracer;
|
|
197
|
+
startSpan(name, options, context) {
|
|
198
|
+
const kind = String(
|
|
199
|
+
options?.attributes?.["neatlogs.span.kind"] ?? ""
|
|
200
|
+
).toLowerCase();
|
|
201
|
+
const ctx = context ?? otelContext3.active();
|
|
202
|
+
const parent = otelTrace3.getSpan(ctx);
|
|
203
|
+
const needsRoot = autoRootEnabled() && !ROOT_KINDS.has(kind) && !(parent !== void 0 && parent.isRecording());
|
|
204
|
+
if (!needsRoot) {
|
|
205
|
+
return this._tracer.startSpan(name, options, context);
|
|
206
|
+
}
|
|
207
|
+
const root = this._tracer.startSpan(
|
|
208
|
+
resolveRootWorkflowName(),
|
|
209
|
+
{ attributes: { "neatlogs.span.kind": "workflow", "neatlogs.auto_root": true } },
|
|
210
|
+
ctx
|
|
211
|
+
);
|
|
212
|
+
stampAutoRootIdentity(root);
|
|
213
|
+
const childCtx = otelTrace3.setSpan(ctx, root);
|
|
214
|
+
const child = this._tracer.startSpan(name, options, childCtx);
|
|
215
|
+
return wrapSpanWithRoot(child, root);
|
|
216
|
+
}
|
|
217
|
+
// startActiveSpan is used by traceTool (callback-scoped, always under an
|
|
218
|
+
// active span in practice) — pass straight through, no auto-root.
|
|
219
|
+
startActiveSpan = ((...args) => this._tracer.startActiveSpan(
|
|
220
|
+
...args
|
|
221
|
+
));
|
|
222
|
+
};
|
|
223
|
+
function getProviderTracer(name) {
|
|
224
|
+
return new AutoRootTracer(otelTrace3.getTracer(name));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/google-genai.ts
|
|
228
|
+
var TRACER_NAME = "neatlogs.google_genai";
|
|
229
|
+
var PROVIDER = "google";
|
|
230
|
+
var SYSTEM = "google_genai";
|
|
231
|
+
function wrapGoogleGenAI(client) {
|
|
232
|
+
return wrapNamespace(client, []);
|
|
233
|
+
}
|
|
234
|
+
function traceTool(name, fn) {
|
|
235
|
+
return async function tracedTool(args) {
|
|
236
|
+
const tracer = getProviderTracer(TRACER_NAME);
|
|
237
|
+
return tracer.startActiveSpan(
|
|
238
|
+
`tool.${name}`,
|
|
239
|
+
{
|
|
240
|
+
attributes: {
|
|
241
|
+
"neatlogs.span.kind": "TOOL",
|
|
242
|
+
"neatlogs.tool.name": name,
|
|
243
|
+
"input.value": safeStringify(args)
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
otelContext4.active(),
|
|
247
|
+
async (span) => {
|
|
248
|
+
try {
|
|
249
|
+
const result = await fn(args);
|
|
250
|
+
span.setAttribute("output.value", safeStringify(result));
|
|
251
|
+
span.setStatus({ code: SpanStatusCode3.OK });
|
|
252
|
+
return result;
|
|
253
|
+
} catch (err) {
|
|
254
|
+
recordError(span, err);
|
|
255
|
+
throw err;
|
|
256
|
+
} finally {
|
|
257
|
+
span.end();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function wrapNamespace(target, path) {
|
|
264
|
+
return new Proxy(target, {
|
|
265
|
+
get(obj, prop, receiver) {
|
|
266
|
+
const value = Reflect.get(obj, prop, receiver);
|
|
267
|
+
if (typeof prop === "symbol" || String(prop).startsWith("_")) return value;
|
|
268
|
+
const currentPath = [...path, String(prop)];
|
|
269
|
+
const pathStr = currentPath.join(".");
|
|
270
|
+
if (pathStr === "models.generateContent" && typeof value === "function") {
|
|
271
|
+
return tracedGenerateContent(value.bind(obj), false);
|
|
272
|
+
}
|
|
273
|
+
if (pathStr === "models.generateContentStream" && typeof value === "function") {
|
|
274
|
+
return tracedGenerateContent(value.bind(obj), true);
|
|
275
|
+
}
|
|
276
|
+
if (pathStr === "models.embedContent" && typeof value === "function") {
|
|
277
|
+
return tracedEmbedContent(value.bind(obj));
|
|
278
|
+
}
|
|
279
|
+
if (pathStr === "models.countTokens" && typeof value === "function") {
|
|
280
|
+
return tracedCountTokens(value.bind(obj));
|
|
281
|
+
}
|
|
282
|
+
if (value && typeof value === "object" && !Array.isArray(value) && isNamespace(currentPath)) {
|
|
283
|
+
return wrapNamespace(value, currentPath);
|
|
284
|
+
}
|
|
285
|
+
return value;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function isNamespace(path) {
|
|
290
|
+
if (path.length > 2) return false;
|
|
291
|
+
return ["models", "chats"].includes(path[path.length - 1]);
|
|
292
|
+
}
|
|
293
|
+
function wrapGoogleGenAIChat(chat) {
|
|
294
|
+
const c = chat;
|
|
295
|
+
if (!c || c._neatlogsGoogleGenAIPatched) return chat;
|
|
296
|
+
if (typeof c.sendMessage === "function") {
|
|
297
|
+
const orig = c.sendMessage.bind(c);
|
|
298
|
+
c.sendMessage = (params, ...rest) => tracedChatSend(orig, c, params, rest, false);
|
|
299
|
+
}
|
|
300
|
+
if (typeof c.sendMessageStream === "function") {
|
|
301
|
+
const orig = c.sendMessageStream.bind(c);
|
|
302
|
+
c.sendMessageStream = (params, ...rest) => tracedChatSend(orig, c, params, rest, true);
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
Object.defineProperty(c, "_neatlogsGoogleGenAIPatched", { value: true, enumerable: false, configurable: true });
|
|
306
|
+
} catch {
|
|
307
|
+
c._neatlogsGoogleGenAIPatched = true;
|
|
308
|
+
}
|
|
309
|
+
return chat;
|
|
310
|
+
}
|
|
311
|
+
function tracedChatSend(original, chat, params, rest, isStream) {
|
|
312
|
+
const tracer = getProviderTracer(TRACER_NAME);
|
|
313
|
+
const model = chat?.model ?? chat?.modelVersion ?? "";
|
|
314
|
+
const span = tracer.startSpan("google_genai.chat.send_message", {
|
|
315
|
+
attributes: {
|
|
316
|
+
"neatlogs.span.kind": "LLM",
|
|
317
|
+
"neatlogs.llm.provider": PROVIDER,
|
|
318
|
+
"neatlogs.llm.system": SYSTEM,
|
|
319
|
+
"neatlogs.llm.model_name": model,
|
|
320
|
+
"neatlogs.llm.is_streaming": isStream
|
|
321
|
+
}
|
|
322
|
+
}, otelContext4.active());
|
|
323
|
+
const message = params?.message ?? params;
|
|
324
|
+
span.setAttribute("neatlogs.llm.input_messages.0.role", "user");
|
|
325
|
+
span.setAttribute(
|
|
326
|
+
"neatlogs.llm.input_messages.0.content",
|
|
327
|
+
typeof message === "string" ? message : safeStringify(message)
|
|
328
|
+
);
|
|
329
|
+
const ctx = trace2.setSpan(otelContext4.active(), span);
|
|
330
|
+
const result = otelContext4.with(ctx, () => original(params, ...rest));
|
|
331
|
+
return Promise.resolve(result).then(
|
|
332
|
+
(response) => {
|
|
333
|
+
if (isStream) return wrapStream(response, span);
|
|
334
|
+
finalizeResponse(span, response);
|
|
335
|
+
return response;
|
|
336
|
+
},
|
|
337
|
+
(err) => {
|
|
338
|
+
recordError(span, err);
|
|
339
|
+
throw err;
|
|
340
|
+
}
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
function tracedEmbedContent(original) {
|
|
344
|
+
return function(opts, ...rest) {
|
|
345
|
+
const tracer = getProviderTracer(TRACER_NAME);
|
|
346
|
+
const span = tracer.startSpan("google_genai.models.embed_content", {
|
|
347
|
+
attributes: {
|
|
348
|
+
"neatlogs.span.kind": "EMBEDDING",
|
|
349
|
+
"neatlogs.llm.provider": PROVIDER,
|
|
350
|
+
"neatlogs.embedding.model_name": opts?.model ?? "",
|
|
351
|
+
"neatlogs.embedding.text": safeStringify(opts?.contents ?? "")
|
|
352
|
+
}
|
|
353
|
+
}, otelContext4.active());
|
|
354
|
+
const ctx = trace2.setSpan(otelContext4.active(), span);
|
|
355
|
+
const result = otelContext4.with(ctx, () => original(opts, ...rest));
|
|
356
|
+
return Promise.resolve(result).then(
|
|
357
|
+
(response) => {
|
|
358
|
+
const embeddings = response?.embeddings;
|
|
359
|
+
if (Array.isArray(embeddings)) {
|
|
360
|
+
span.setAttribute("neatlogs.embedding.count", embeddings.length);
|
|
361
|
+
const vals = embeddings[0]?.values;
|
|
362
|
+
if (Array.isArray(vals)) span.setAttribute("neatlogs.embedding.dimensions", vals.length);
|
|
363
|
+
}
|
|
364
|
+
span.setStatus({ code: SpanStatusCode3.OK });
|
|
365
|
+
span.end();
|
|
366
|
+
return response;
|
|
367
|
+
},
|
|
368
|
+
(err) => {
|
|
369
|
+
recordError(span, err);
|
|
370
|
+
throw err;
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
function tracedCountTokens(original) {
|
|
376
|
+
return function(opts, ...rest) {
|
|
377
|
+
const tracer = getProviderTracer(TRACER_NAME);
|
|
378
|
+
const span = tracer.startSpan("google_genai.models.count_tokens", {
|
|
379
|
+
attributes: {
|
|
380
|
+
"neatlogs.span.kind": "LLM",
|
|
381
|
+
"neatlogs.llm.provider": PROVIDER,
|
|
382
|
+
"neatlogs.llm.task": "count_tokens",
|
|
383
|
+
"neatlogs.llm.model_name": opts?.model ?? ""
|
|
384
|
+
}
|
|
385
|
+
}, otelContext4.active());
|
|
386
|
+
const ctx = trace2.setSpan(otelContext4.active(), span);
|
|
387
|
+
const result = otelContext4.with(ctx, () => original(opts, ...rest));
|
|
388
|
+
return Promise.resolve(result).then(
|
|
389
|
+
(response) => {
|
|
390
|
+
if (response?.totalTokens != null) span.setAttribute("neatlogs.llm.token_count.prompt", response.totalTokens);
|
|
391
|
+
span.setStatus({ code: SpanStatusCode3.OK });
|
|
392
|
+
span.end();
|
|
393
|
+
return response;
|
|
394
|
+
},
|
|
395
|
+
(err) => {
|
|
396
|
+
recordError(span, err);
|
|
397
|
+
throw err;
|
|
398
|
+
}
|
|
399
|
+
);
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
function tracedGenerateContent(original, isStream) {
|
|
403
|
+
return function(opts, ...rest) {
|
|
404
|
+
const tracer = getProviderTracer(TRACER_NAME);
|
|
405
|
+
const model = opts?.model ?? "";
|
|
406
|
+
const span = tracer.startSpan("google_genai.models.generate_content", {
|
|
407
|
+
attributes: {
|
|
408
|
+
"neatlogs.span.kind": "LLM",
|
|
409
|
+
"neatlogs.llm.provider": PROVIDER,
|
|
410
|
+
"neatlogs.llm.system": SYSTEM,
|
|
411
|
+
"neatlogs.llm.model_name": model,
|
|
412
|
+
"neatlogs.llm.is_streaming": isStream
|
|
413
|
+
}
|
|
414
|
+
}, otelContext4.active());
|
|
415
|
+
setInputAttributes(span, opts);
|
|
416
|
+
const ctx = trace2.setSpan(otelContext4.active(), span);
|
|
417
|
+
const result = otelContext4.with(ctx, () => original(opts, ...rest));
|
|
418
|
+
return Promise.resolve(result).then(
|
|
419
|
+
(response) => {
|
|
420
|
+
if (isStream) {
|
|
421
|
+
return wrapStream(response, span);
|
|
422
|
+
}
|
|
423
|
+
finalizeResponse(span, response);
|
|
424
|
+
return response;
|
|
425
|
+
},
|
|
426
|
+
(err) => {
|
|
427
|
+
recordError(span, err);
|
|
428
|
+
throw err;
|
|
429
|
+
}
|
|
430
|
+
);
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function setInputAttributes(span, opts) {
|
|
434
|
+
let idx = 0;
|
|
435
|
+
const config = opts?.config;
|
|
436
|
+
const systemInstruction = config?.systemInstruction ?? config?.system_instruction;
|
|
437
|
+
if (systemInstruction) {
|
|
438
|
+
span.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "system");
|
|
439
|
+
span.setAttribute(
|
|
440
|
+
`neatlogs.llm.input_messages.${idx}.content`,
|
|
441
|
+
typeof systemInstruction === "string" ? systemInstruction : safeStringify(systemInstruction)
|
|
442
|
+
);
|
|
443
|
+
idx++;
|
|
444
|
+
}
|
|
445
|
+
const contents = opts?.contents;
|
|
446
|
+
if (typeof contents === "string") {
|
|
447
|
+
span.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "user");
|
|
448
|
+
span.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, contents);
|
|
449
|
+
} else if (Array.isArray(contents)) {
|
|
450
|
+
for (const item of contents) {
|
|
451
|
+
if (typeof item === "string") {
|
|
452
|
+
span.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "user");
|
|
453
|
+
span.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, item);
|
|
454
|
+
idx++;
|
|
455
|
+
} else if (item && typeof item === "object") {
|
|
456
|
+
const role = item.role ?? "user";
|
|
457
|
+
span.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, role);
|
|
458
|
+
const parts = item.parts ?? [];
|
|
459
|
+
const textParts = [];
|
|
460
|
+
for (const part of parts) {
|
|
461
|
+
if (typeof part === "string") textParts.push(part);
|
|
462
|
+
else if (part?.text) textParts.push(part.text);
|
|
463
|
+
}
|
|
464
|
+
span.setAttribute(
|
|
465
|
+
`neatlogs.llm.input_messages.${idx}.content`,
|
|
466
|
+
textParts.length ? textParts.join("\n") : safeStringify(parts)
|
|
467
|
+
);
|
|
468
|
+
idx++;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
} else if (contents) {
|
|
472
|
+
span.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "user");
|
|
473
|
+
span.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, safeStringify(contents));
|
|
474
|
+
}
|
|
475
|
+
const tools = config?.tools;
|
|
476
|
+
if (Array.isArray(tools)) {
|
|
477
|
+
let t = 0;
|
|
478
|
+
for (const tool of tools) {
|
|
479
|
+
const decls = tool?.functionDeclarations ?? tool?.function_declarations ?? [];
|
|
480
|
+
for (const fn of decls) {
|
|
481
|
+
span.setAttribute(`neatlogs.llm.tools.${t}.name`, fn?.name ?? "");
|
|
482
|
+
if (fn?.description) span.setAttribute(`neatlogs.llm.tools.${t}.description`, fn.description);
|
|
483
|
+
if (fn?.parameters) span.setAttribute(`neatlogs.llm.tools.${t}.input_schema`, safeStringify(fn.parameters));
|
|
484
|
+
t++;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (config) {
|
|
489
|
+
if (config.temperature != null) span.setAttribute("neatlogs.llm.temperature", config.temperature);
|
|
490
|
+
if (config.topP != null) span.setAttribute("neatlogs.llm.top_p", config.topP);
|
|
491
|
+
if (config.topK != null) span.setAttribute("neatlogs.llm.top_k", config.topK);
|
|
492
|
+
const maxTokens = config.maxOutputTokens ?? config.max_output_tokens;
|
|
493
|
+
if (maxTokens != null) span.setAttribute("neatlogs.llm.max_tokens", maxTokens);
|
|
494
|
+
const params = {};
|
|
495
|
+
if (config.temperature != null) params.temperature = config.temperature;
|
|
496
|
+
if (config.topP != null) params.top_p = config.topP;
|
|
497
|
+
if (config.topK != null) params.top_k = config.topK;
|
|
498
|
+
if (maxTokens != null) params.max_tokens = maxTokens;
|
|
499
|
+
if (config.frequencyPenalty != null) params.frequency_penalty = config.frequencyPenalty;
|
|
500
|
+
if (config.presencePenalty != null) params.presence_penalty = config.presencePenalty;
|
|
501
|
+
if (Object.keys(params).length > 0) {
|
|
502
|
+
span.setAttribute("neatlogs.llm.invocation_parameters", JSON.stringify(params));
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function wrapStream(stream, span) {
|
|
507
|
+
const chunks = [];
|
|
508
|
+
const originalAsyncIterator = stream?.[Symbol.asyncIterator]?.bind(stream);
|
|
509
|
+
if (!originalAsyncIterator) {
|
|
510
|
+
finalizeStreamChunks(span, chunks);
|
|
511
|
+
return stream;
|
|
512
|
+
}
|
|
513
|
+
const wrapped = Object.create(Object.getPrototypeOf(stream));
|
|
514
|
+
Object.assign(wrapped, stream);
|
|
515
|
+
wrapped[Symbol.asyncIterator] = function() {
|
|
516
|
+
const iterator = originalAsyncIterator();
|
|
517
|
+
return {
|
|
518
|
+
async next() {
|
|
519
|
+
try {
|
|
520
|
+
const result = await iterator.next();
|
|
521
|
+
if (result.done) {
|
|
522
|
+
finalizeStreamChunks(span, chunks);
|
|
523
|
+
return result;
|
|
524
|
+
}
|
|
525
|
+
chunks.push(result.value);
|
|
526
|
+
return result;
|
|
527
|
+
} catch (err) {
|
|
528
|
+
recordError(span, err);
|
|
529
|
+
throw err;
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
async return(value) {
|
|
533
|
+
finalizeStreamChunks(span, chunks);
|
|
534
|
+
return iterator.return?.(value) ?? { done: true, value: void 0 };
|
|
535
|
+
},
|
|
536
|
+
async throw(err) {
|
|
537
|
+
recordError(span, err);
|
|
538
|
+
return iterator.throw?.(err) ?? { done: true, value: void 0 };
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
};
|
|
542
|
+
return wrapped;
|
|
543
|
+
}
|
|
544
|
+
function finalizeStreamChunks(span, chunks) {
|
|
545
|
+
const textParts = [];
|
|
546
|
+
let finishReason = "";
|
|
547
|
+
let usage = null;
|
|
548
|
+
for (const chunk of chunks) {
|
|
549
|
+
for (const candidate of chunk?.candidates ?? []) {
|
|
550
|
+
for (const part of candidate?.content?.parts ?? []) {
|
|
551
|
+
if (part?.text && !part?.thought) textParts.push(part.text);
|
|
552
|
+
}
|
|
553
|
+
if (candidate?.finishReason) finishReason = candidate.finishReason;
|
|
554
|
+
}
|
|
555
|
+
if (chunk?.usageMetadata) usage = chunk.usageMetadata;
|
|
556
|
+
}
|
|
557
|
+
const fullText = textParts.join("");
|
|
558
|
+
if (fullText) {
|
|
559
|
+
span.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
|
|
560
|
+
span.setAttribute("neatlogs.llm.output_messages.0.content", fullText);
|
|
561
|
+
}
|
|
562
|
+
if (finishReason) span.setAttribute("neatlogs.llm.finish_reason", String(finishReason));
|
|
563
|
+
setUsage(span, usage);
|
|
564
|
+
span.setStatus({ code: SpanStatusCode3.OK });
|
|
565
|
+
span.end();
|
|
566
|
+
}
|
|
567
|
+
function finalizeResponse(span, response) {
|
|
568
|
+
const textParts = [];
|
|
569
|
+
let toolIdx = 0;
|
|
570
|
+
for (const candidate of response?.candidates ?? []) {
|
|
571
|
+
for (const part of candidate?.content?.parts ?? []) {
|
|
572
|
+
if (part?.text && !part?.thought) {
|
|
573
|
+
textParts.push(part.text);
|
|
574
|
+
} else if (part?.thought && part?.text) {
|
|
575
|
+
span.setAttribute("neatlogs.llm.output_messages.0.thinking", part.text);
|
|
576
|
+
} else if (part?.functionCall) {
|
|
577
|
+
const fc = part.functionCall;
|
|
578
|
+
span.setAttribute(`neatlogs.llm.tool_calls.${toolIdx}.name`, fc?.name ?? "");
|
|
579
|
+
span.setAttribute(`neatlogs.llm.tool_calls.${toolIdx}.arguments`, safeStringify(fc?.args ?? {}));
|
|
580
|
+
toolIdx++;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
if (candidate?.finishReason) span.setAttribute("neatlogs.llm.finish_reason", String(candidate.finishReason));
|
|
584
|
+
}
|
|
585
|
+
if (textParts.length) {
|
|
586
|
+
span.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
|
|
587
|
+
span.setAttribute("neatlogs.llm.output_messages.0.content", textParts.join(""));
|
|
588
|
+
}
|
|
589
|
+
setUsage(span, response?.usageMetadata);
|
|
590
|
+
span.setStatus({ code: SpanStatusCode3.OK });
|
|
591
|
+
span.end();
|
|
592
|
+
}
|
|
593
|
+
function setUsage(span, usage) {
|
|
594
|
+
if (!usage) return;
|
|
595
|
+
if (usage.promptTokenCount != null) span.setAttribute("neatlogs.llm.token_count.prompt", usage.promptTokenCount);
|
|
596
|
+
if (usage.candidatesTokenCount != null) span.setAttribute("neatlogs.llm.token_count.completion", usage.candidatesTokenCount);
|
|
597
|
+
if (usage.totalTokenCount != null) span.setAttribute("neatlogs.llm.token_count.total", usage.totalTokenCount);
|
|
598
|
+
if (usage.cachedContentTokenCount != null) span.setAttribute("neatlogs.llm.token_count.cache_read", usage.cachedContentTokenCount);
|
|
599
|
+
if (usage.thoughtsTokenCount != null) span.setAttribute("neatlogs.llm.token_count.reasoning", usage.thoughtsTokenCount);
|
|
600
|
+
}
|
|
601
|
+
function safeStringify(value) {
|
|
602
|
+
try {
|
|
603
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
604
|
+
} catch {
|
|
605
|
+
return "";
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
function recordError(span, err) {
|
|
609
|
+
if (err instanceof Error) {
|
|
610
|
+
span.setStatus({ code: SpanStatusCode3.ERROR, message: err.message });
|
|
611
|
+
span.recordException(err);
|
|
612
|
+
} else {
|
|
613
|
+
span.setStatus({ code: SpanStatusCode3.ERROR, message: String(err) });
|
|
614
|
+
}
|
|
615
|
+
span.end();
|
|
616
|
+
}
|
|
617
|
+
export {
|
|
618
|
+
traceTool,
|
|
619
|
+
wrapGoogleGenAI,
|
|
620
|
+
wrapGoogleGenAIChat
|
|
621
|
+
};
|
|
622
|
+
//# sourceMappingURL=google-genai.mjs.map
|