neatlogs 1.0.6 → 1.0.9
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/azure-openai.cjs +344 -0
- package/dist/azure-openai.cjs.map +1 -0
- package/dist/azure-openai.d.ts +30 -0
- package/dist/azure-openai.mjs +318 -0
- package/dist/azure-openai.mjs.map +1 -0
- package/dist/bedrock.cjs +540 -0
- package/dist/bedrock.cjs.map +1 -0
- package/dist/bedrock.d.ts +29 -0
- package/dist/bedrock.mjs +514 -0
- package/dist/bedrock.mjs.map +1 -0
- package/dist/browser.cjs +121 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.ts +143 -0
- package/dist/browser.mjs +96 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/claude-agent-sdk.cjs +406 -0
- package/dist/claude-agent-sdk.cjs.map +1 -0
- package/dist/claude-agent-sdk.d.ts +49 -0
- package/dist/claude-agent-sdk.mjs +381 -0
- package/dist/claude-agent-sdk.mjs.map +1 -0
- package/dist/index.cjs +3642 -1126
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.mjs +3637 -1132
- package/dist/index.mjs.map +1 -1
- package/dist/langchain.cjs +9 -9
- package/dist/langchain.cjs.map +1 -1
- package/dist/langchain.mjs +9 -9
- package/dist/langchain.mjs.map +1 -1
- package/dist/mastra-wrap.cjs +25 -25
- package/dist/mastra-wrap.cjs.map +1 -1
- package/dist/mastra-wrap.mjs +25 -25
- package/dist/mastra-wrap.mjs.map +1 -1
- package/dist/openai-agents.cjs +6 -6
- package/dist/openai-agents.cjs.map +1 -1
- package/dist/openai-agents.mjs +6 -6
- package/dist/openai-agents.mjs.map +1 -1
- package/dist/opencode-plugin.cjs +682 -0
- package/dist/opencode-plugin.cjs.map +1 -0
- package/dist/opencode-plugin.d.ts +39 -0
- package/dist/opencode-plugin.mjs +644 -0
- package/dist/opencode-plugin.mjs.map +1 -0
- package/dist/openrouter-agent.cjs +273 -0
- package/dist/openrouter-agent.cjs.map +1 -0
- package/dist/openrouter-agent.d.ts +34 -0
- package/dist/openrouter-agent.mjs +247 -0
- package/dist/openrouter-agent.mjs.map +1 -0
- package/dist/pi-agent.cjs +10 -10
- package/dist/pi-agent.cjs.map +1 -1
- package/dist/pi-agent.mjs +10 -10
- package/dist/pi-agent.mjs.map +1 -1
- package/dist/vertex-ai.cjs +424 -0
- package/dist/vertex-ai.cjs.map +1 -0
- package/dist/vertex-ai.d.ts +39 -0
- package/dist/vertex-ai.mjs +397 -0
- package/dist/vertex-ai.mjs.map +1 -0
- package/package.json +78 -2
package/dist/browser.cjs
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/browser.ts
|
|
21
|
+
var browser_exports = {};
|
|
22
|
+
__export(browser_exports, {
|
|
23
|
+
Neatlogs: () => Neatlogs,
|
|
24
|
+
default: () => browser_default
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(browser_exports);
|
|
27
|
+
var DEFAULT_ENDPOINT = "https://staging-cloud.neatlogs.com";
|
|
28
|
+
var Neatlogs = class {
|
|
29
|
+
apiKey;
|
|
30
|
+
project;
|
|
31
|
+
baseUrl;
|
|
32
|
+
enabled;
|
|
33
|
+
onError;
|
|
34
|
+
constructor(opts) {
|
|
35
|
+
if (!opts || !opts.apiKey) {
|
|
36
|
+
throw new Error("Neatlogs: apiKey is required");
|
|
37
|
+
}
|
|
38
|
+
this.apiKey = opts.apiKey;
|
|
39
|
+
this.project = opts.project;
|
|
40
|
+
const endpoint = opts.endpoint || DEFAULT_ENDPOINT;
|
|
41
|
+
this.baseUrl = safeOrigin(endpoint) || DEFAULT_ENDPOINT;
|
|
42
|
+
this.enabled = opts.enabled !== false;
|
|
43
|
+
this.onError = opts.onError || ((err) => {
|
|
44
|
+
if (typeof console !== "undefined") console.warn("[neatlogs] send failed:", err);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** Send a full (optionally nested) trace. Returns the backend's result. */
|
|
48
|
+
async trace(trace) {
|
|
49
|
+
return this.post(trace);
|
|
50
|
+
}
|
|
51
|
+
/** Send a single AI interaction as a one-span trace (kind defaults to LLM). */
|
|
52
|
+
async trackAI(ai) {
|
|
53
|
+
const { name, kind, ...rest } = ai;
|
|
54
|
+
return this.post({ name, kind: kind ?? "LLM", ...rest });
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Begin a trace you'll complete later (e.g. streaming). Buffers the partial
|
|
58
|
+
* input; call `.finish()` with the final output/tokens to send it. Nothing is
|
|
59
|
+
* sent until `finish()`.
|
|
60
|
+
*/
|
|
61
|
+
startTrace(initial) {
|
|
62
|
+
const startedAt = nowIso();
|
|
63
|
+
return {
|
|
64
|
+
finish: (final) => {
|
|
65
|
+
const merged = { ...initial, ...final ?? {} };
|
|
66
|
+
const { name, kind, ...rest } = merged;
|
|
67
|
+
return this.post({
|
|
68
|
+
name,
|
|
69
|
+
kind: kind ?? "LLM",
|
|
70
|
+
start: startedAt,
|
|
71
|
+
end: nowIso(),
|
|
72
|
+
...rest
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** POST the trace JSON to the backend's /v1/trace endpoint. */
|
|
78
|
+
async post(body) {
|
|
79
|
+
if (!this.enabled) return { ok: true };
|
|
80
|
+
const payload = this.project && body.project === void 0 ? { ...body, project: this.project } : body;
|
|
81
|
+
try {
|
|
82
|
+
const res = await fetch(`${this.baseUrl}/v1/trace`, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
headers: {
|
|
85
|
+
"Content-Type": "application/json",
|
|
86
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify(payload),
|
|
89
|
+
// keepalive lets the request survive a page unload (e.g. on navigation).
|
|
90
|
+
keepalive: true
|
|
91
|
+
});
|
|
92
|
+
if (!res.ok) {
|
|
93
|
+
const text = await res.text().catch(() => "");
|
|
94
|
+
const result = { ok: false, error: `HTTP ${res.status}${text ? `: ${text}` : ""}` };
|
|
95
|
+
this.onError(new Error(result.error));
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
const data = await res.json().catch(() => ({}));
|
|
99
|
+
return { ok: true, trace_id: data.trace_id, spans: data.spans };
|
|
100
|
+
} catch (err) {
|
|
101
|
+
this.onError(err);
|
|
102
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
function safeOrigin(endpoint) {
|
|
107
|
+
try {
|
|
108
|
+
return new URL(endpoint).origin;
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function nowIso() {
|
|
114
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
115
|
+
}
|
|
116
|
+
var browser_default = Neatlogs;
|
|
117
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
118
|
+
0 && (module.exports = {
|
|
119
|
+
Neatlogs
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=browser.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/browser.ts"],"sourcesContent":["/**\n * Neatlogs Browser SDK — `neatlogs/browser`\n *\n * A minimal, browser-safe client for sending traces to Neatlogs from web apps.\n * It has ZERO dependencies (no OpenTelemetry, no Node APIs) — only `fetch` — so\n * it bundles cleanly into front-end apps. It POSTs plain JSON to the backend's\n * simple trace endpoint (`/v1/trace`); the backend generates trace/span ids,\n * builds the hierarchy from nesting, infers cost from model+tokens, and pushes\n * through the normal pipeline. Nothing here streams OTLP.\n *\n * Usage:\n * import { Neatlogs } from 'neatlogs/browser';\n * const nl = new Neatlogs({ apiKey: 'nl_...' });\n *\n * // one-shot AI interaction\n * await nl.trackAI({ name: 'chat', model: 'gpt-4o', input, output,\n * tokens: { prompt: 10, completion: 5 } });\n *\n * // a full nested trace (same shape the backend's POST /v1/trace accepts)\n * await nl.trace({ name: 'support-chat', children: [\n * { name: 'retrieve', query, documents },\n * { name: 'answer', model: 'gpt-4o', input, output },\n * ]});\n *\n * // streaming: open, accumulate, finish\n * const t = nl.startTrace({ name: 'chat', model: 'gpt-4o', input });\n * t.finish({ output: full, tokens: { prompt, completion } });\n */\n\nconst DEFAULT_ENDPOINT = \"https://staging-cloud.neatlogs.com\";\n\n// --- the simple trace shape the backend (/v1/trace) accepts --------------------\n// Mirrors the server-side SimpleSpan; kept local so this file has no imports.\n\nexport interface NeatlogsLog {\n level?: string;\n message: string;\n timestamp?: string;\n}\n\n/**\n * Span kinds the backend accepts (the canonical set). `kind` is optional — when\n * omitted the backend infers it from the fields present.\n */\nexport type NeatlogsKind =\n | \"WORKFLOW\" | \"AGENT\" | \"CHAIN\" | \"TOOL\" | \"RETRIEVER\" | \"RERANKER\"\n | \"EMBEDDING\" | \"LLM\" | \"GUARDRAIL\" | \"MCP_TOOL\" | \"TASK\"\n | \"VECTOR_STORE\" | \"EVALUATOR\" | \"HTTP\";\n\nexport interface NeatlogsSpan {\n name: string;\n /** Optional — the backend infers the kind from fields when omitted. */\n kind?: NeatlogsKind | string;\n input?: unknown;\n output?: unknown;\n model?: string;\n tokens?: { prompt?: number; completion?: number; total?: number };\n query?: unknown;\n documents?: unknown;\n tool_name?: string;\n passed?: boolean;\n score?: number;\n metadata?: Record<string, unknown>;\n status?: string;\n error?: string;\n start?: string;\n end?: string;\n /** Simplest way to record latency — the backend derives end from start + this. */\n duration_ms?: number;\n /**\n * Full canonical-attribute escape hatch. Send ANY neatlogs.* attribute the SDK\n * supports — e.g. { \"neatlogs.llm.temperature\": 0.7, \"neatlogs.agent.role\":\n * \"researcher\", \"neatlogs.tool.parameters\": {...} }. Non-canonical keys are\n * dropped server-side. The fields above (model/tokens/query/...) are shortcuts\n * for the common ones; explicit `attributes` win on conflict.\n */\n attributes?: Record<string, unknown>;\n children?: NeatlogsSpan[];\n logs?: NeatlogsLog[];\n}\n\n/** The root of a trace = a span node (its `name` becomes the workflow name). */\nexport type NeatlogsTrace = NeatlogsSpan;\n\nexport interface NeatlogsOptions {\n /**\n * Your Neatlogs WRITE key (`nlw_…`) — an ingest-only credential safe to embed in\n * browser code. (A full project key also works but should not be exposed client-side,\n * since it can read data.)\n */\n apiKey: string;\n /**\n * Project NAME to ingest into. REQUIRED when using a write key (the key identifies\n * you, not a project). Sent as the root `project` field on every trace. Ignored for\n * a full project key (already project-scoped).\n */\n project?: string;\n /** Backend base URL. Defaults to the same host the SDKs use. */\n endpoint?: string;\n /** Set false to validate calls without sending (default true). */\n enabled?: boolean;\n /** Called on transport errors instead of throwing (default: console.warn). */\n onError?: (err: unknown) => void;\n}\n\nexport interface TrackResult {\n ok: boolean;\n trace_id?: string;\n spans?: number;\n error?: string;\n}\n\n/** Shorthand for a single AI interaction → a one-span trace. */\nexport interface TrackAIInput {\n name: string;\n input?: unknown;\n output?: unknown;\n model?: string;\n tokens?: { prompt?: number; completion?: number; total?: number };\n metadata?: Record<string, unknown>;\n duration_ms?: number;\n /** Any canonical neatlogs.* attributes (see NeatlogsSpan.attributes). */\n attributes?: Record<string, unknown>;\n /** Override the inferred kind (defaults to LLM for trackAI). */\n kind?: NeatlogsKind | string;\n}\n\nexport class Neatlogs {\n private readonly apiKey: string;\n private readonly project?: string;\n private readonly baseUrl: string;\n private readonly enabled: boolean;\n private readonly onError: (err: unknown) => void;\n\n constructor(opts: NeatlogsOptions) {\n if (!opts || !opts.apiKey) {\n throw new Error(\"Neatlogs: apiKey is required\");\n }\n this.apiKey = opts.apiKey;\n this.project = opts.project;\n // Use the origin of the configured endpoint (same convention as the Node SDK),\n // so passing a full /v1/traces URL or a bare host both work.\n const endpoint = opts.endpoint || DEFAULT_ENDPOINT;\n this.baseUrl = safeOrigin(endpoint) || DEFAULT_ENDPOINT;\n this.enabled = opts.enabled !== false;\n this.onError =\n opts.onError ||\n ((err) => {\n // eslint-disable-next-line no-console\n if (typeof console !== \"undefined\") console.warn(\"[neatlogs] send failed:\", err);\n });\n }\n\n /** Send a full (optionally nested) trace. Returns the backend's result. */\n async trace(trace: NeatlogsTrace): Promise<TrackResult> {\n return this.post(trace);\n }\n\n /** Send a single AI interaction as a one-span trace (kind defaults to LLM). */\n async trackAI(ai: TrackAIInput): Promise<TrackResult> {\n const { name, kind, ...rest } = ai;\n return this.post({ name, kind: kind ?? \"LLM\", ...rest });\n }\n\n /**\n * Begin a trace you'll complete later (e.g. streaming). Buffers the partial\n * input; call `.finish()` with the final output/tokens to send it. Nothing is\n * sent until `finish()`.\n */\n startTrace(initial: TrackAIInput): {\n finish: (final?: Partial<TrackAIInput>) => Promise<TrackResult>;\n } {\n const startedAt = nowIso();\n return {\n finish: (final?: Partial<TrackAIInput>) => {\n const merged: TrackAIInput = { ...initial, ...(final ?? {}) };\n const { name, kind, ...rest } = merged;\n return this.post({\n name,\n kind: kind ?? \"LLM\",\n start: startedAt,\n end: nowIso(),\n ...rest,\n });\n },\n };\n }\n\n /** POST the trace JSON to the backend's /v1/trace endpoint. */\n private async post(body: NeatlogsTrace): Promise<TrackResult> {\n if (!this.enabled) return { ok: true };\n // Inject the configured project name into the root (required for write keys;\n // ignored server-side for full project keys). A `project` already on the body wins.\n const payload =\n this.project && (body as any).project === undefined\n ? { ...body, project: this.project }\n : body;\n try {\n const res = await fetch(`${this.baseUrl}/v1/trace`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(payload),\n // keepalive lets the request survive a page unload (e.g. on navigation).\n keepalive: true,\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n const result = { ok: false, error: `HTTP ${res.status}${text ? `: ${text}` : \"\"}` };\n this.onError(new Error(result.error));\n return result;\n }\n const data = (await res.json().catch(() => ({}))) as Partial<TrackResult>;\n return { ok: true, trace_id: data.trace_id, spans: data.spans };\n } catch (err) {\n // Never throw into the host app over telemetry.\n this.onError(err);\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n }\n}\n\n// --- helpers (kept local, no imports) -----------------------------------------\n\nfunction safeOrigin(endpoint: string): string | null {\n try {\n return new URL(endpoint).origin;\n } catch {\n return null;\n }\n}\n\nfunction nowIso(): string {\n return new Date().toISOString();\n}\n\nexport default Neatlogs;\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Neatlogs Browser SDK — `neatlogs/browser`
|
|
3
|
+
*
|
|
4
|
+
* A minimal, browser-safe client for sending traces to Neatlogs from web apps.
|
|
5
|
+
* It has ZERO dependencies (no OpenTelemetry, no Node APIs) — only `fetch` — so
|
|
6
|
+
* it bundles cleanly into front-end apps. It POSTs plain JSON to the backend's
|
|
7
|
+
* simple trace endpoint (`/v1/trace`); the backend generates trace/span ids,
|
|
8
|
+
* builds the hierarchy from nesting, infers cost from model+tokens, and pushes
|
|
9
|
+
* through the normal pipeline. Nothing here streams OTLP.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* import { Neatlogs } from 'neatlogs/browser';
|
|
13
|
+
* const nl = new Neatlogs({ apiKey: 'nl_...' });
|
|
14
|
+
*
|
|
15
|
+
* // one-shot AI interaction
|
|
16
|
+
* await nl.trackAI({ name: 'chat', model: 'gpt-4o', input, output,
|
|
17
|
+
* tokens: { prompt: 10, completion: 5 } });
|
|
18
|
+
*
|
|
19
|
+
* // a full nested trace (same shape the backend's POST /v1/trace accepts)
|
|
20
|
+
* await nl.trace({ name: 'support-chat', children: [
|
|
21
|
+
* { name: 'retrieve', query, documents },
|
|
22
|
+
* { name: 'answer', model: 'gpt-4o', input, output },
|
|
23
|
+
* ]});
|
|
24
|
+
*
|
|
25
|
+
* // streaming: open, accumulate, finish
|
|
26
|
+
* const t = nl.startTrace({ name: 'chat', model: 'gpt-4o', input });
|
|
27
|
+
* t.finish({ output: full, tokens: { prompt, completion } });
|
|
28
|
+
*/
|
|
29
|
+
interface NeatlogsLog {
|
|
30
|
+
level?: string;
|
|
31
|
+
message: string;
|
|
32
|
+
timestamp?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Span kinds the backend accepts (the canonical set). `kind` is optional — when
|
|
36
|
+
* omitted the backend infers it from the fields present.
|
|
37
|
+
*/
|
|
38
|
+
type NeatlogsKind = "WORKFLOW" | "AGENT" | "CHAIN" | "TOOL" | "RETRIEVER" | "RERANKER" | "EMBEDDING" | "LLM" | "GUARDRAIL" | "MCP_TOOL" | "TASK" | "VECTOR_STORE" | "EVALUATOR" | "HTTP";
|
|
39
|
+
interface NeatlogsSpan {
|
|
40
|
+
name: string;
|
|
41
|
+
/** Optional — the backend infers the kind from fields when omitted. */
|
|
42
|
+
kind?: NeatlogsKind | string;
|
|
43
|
+
input?: unknown;
|
|
44
|
+
output?: unknown;
|
|
45
|
+
model?: string;
|
|
46
|
+
tokens?: {
|
|
47
|
+
prompt?: number;
|
|
48
|
+
completion?: number;
|
|
49
|
+
total?: number;
|
|
50
|
+
};
|
|
51
|
+
query?: unknown;
|
|
52
|
+
documents?: unknown;
|
|
53
|
+
tool_name?: string;
|
|
54
|
+
passed?: boolean;
|
|
55
|
+
score?: number;
|
|
56
|
+
metadata?: Record<string, unknown>;
|
|
57
|
+
status?: string;
|
|
58
|
+
error?: string;
|
|
59
|
+
start?: string;
|
|
60
|
+
end?: string;
|
|
61
|
+
/** Simplest way to record latency — the backend derives end from start + this. */
|
|
62
|
+
duration_ms?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Full canonical-attribute escape hatch. Send ANY neatlogs.* attribute the SDK
|
|
65
|
+
* supports — e.g. { "neatlogs.llm.temperature": 0.7, "neatlogs.agent.role":
|
|
66
|
+
* "researcher", "neatlogs.tool.parameters": {...} }. Non-canonical keys are
|
|
67
|
+
* dropped server-side. The fields above (model/tokens/query/...) are shortcuts
|
|
68
|
+
* for the common ones; explicit `attributes` win on conflict.
|
|
69
|
+
*/
|
|
70
|
+
attributes?: Record<string, unknown>;
|
|
71
|
+
children?: NeatlogsSpan[];
|
|
72
|
+
logs?: NeatlogsLog[];
|
|
73
|
+
}
|
|
74
|
+
/** The root of a trace = a span node (its `name` becomes the workflow name). */
|
|
75
|
+
type NeatlogsTrace = NeatlogsSpan;
|
|
76
|
+
interface NeatlogsOptions {
|
|
77
|
+
/**
|
|
78
|
+
* Your Neatlogs WRITE key (`nlw_…`) — an ingest-only credential safe to embed in
|
|
79
|
+
* browser code. (A full project key also works but should not be exposed client-side,
|
|
80
|
+
* since it can read data.)
|
|
81
|
+
*/
|
|
82
|
+
apiKey: string;
|
|
83
|
+
/**
|
|
84
|
+
* Project NAME to ingest into. REQUIRED when using a write key (the key identifies
|
|
85
|
+
* you, not a project). Sent as the root `project` field on every trace. Ignored for
|
|
86
|
+
* a full project key (already project-scoped).
|
|
87
|
+
*/
|
|
88
|
+
project?: string;
|
|
89
|
+
/** Backend base URL. Defaults to the same host the SDKs use. */
|
|
90
|
+
endpoint?: string;
|
|
91
|
+
/** Set false to validate calls without sending (default true). */
|
|
92
|
+
enabled?: boolean;
|
|
93
|
+
/** Called on transport errors instead of throwing (default: console.warn). */
|
|
94
|
+
onError?: (err: unknown) => void;
|
|
95
|
+
}
|
|
96
|
+
interface TrackResult {
|
|
97
|
+
ok: boolean;
|
|
98
|
+
trace_id?: string;
|
|
99
|
+
spans?: number;
|
|
100
|
+
error?: string;
|
|
101
|
+
}
|
|
102
|
+
/** Shorthand for a single AI interaction → a one-span trace. */
|
|
103
|
+
interface TrackAIInput {
|
|
104
|
+
name: string;
|
|
105
|
+
input?: unknown;
|
|
106
|
+
output?: unknown;
|
|
107
|
+
model?: string;
|
|
108
|
+
tokens?: {
|
|
109
|
+
prompt?: number;
|
|
110
|
+
completion?: number;
|
|
111
|
+
total?: number;
|
|
112
|
+
};
|
|
113
|
+
metadata?: Record<string, unknown>;
|
|
114
|
+
duration_ms?: number;
|
|
115
|
+
/** Any canonical neatlogs.* attributes (see NeatlogsSpan.attributes). */
|
|
116
|
+
attributes?: Record<string, unknown>;
|
|
117
|
+
/** Override the inferred kind (defaults to LLM for trackAI). */
|
|
118
|
+
kind?: NeatlogsKind | string;
|
|
119
|
+
}
|
|
120
|
+
declare class Neatlogs {
|
|
121
|
+
private readonly apiKey;
|
|
122
|
+
private readonly project?;
|
|
123
|
+
private readonly baseUrl;
|
|
124
|
+
private readonly enabled;
|
|
125
|
+
private readonly onError;
|
|
126
|
+
constructor(opts: NeatlogsOptions);
|
|
127
|
+
/** Send a full (optionally nested) trace. Returns the backend's result. */
|
|
128
|
+
trace(trace: NeatlogsTrace): Promise<TrackResult>;
|
|
129
|
+
/** Send a single AI interaction as a one-span trace (kind defaults to LLM). */
|
|
130
|
+
trackAI(ai: TrackAIInput): Promise<TrackResult>;
|
|
131
|
+
/**
|
|
132
|
+
* Begin a trace you'll complete later (e.g. streaming). Buffers the partial
|
|
133
|
+
* input; call `.finish()` with the final output/tokens to send it. Nothing is
|
|
134
|
+
* sent until `finish()`.
|
|
135
|
+
*/
|
|
136
|
+
startTrace(initial: TrackAIInput): {
|
|
137
|
+
finish: (final?: Partial<TrackAIInput>) => Promise<TrackResult>;
|
|
138
|
+
};
|
|
139
|
+
/** POST the trace JSON to the backend's /v1/trace endpoint. */
|
|
140
|
+
private post;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export { Neatlogs, type NeatlogsKind, type NeatlogsLog, type NeatlogsOptions, type NeatlogsSpan, type NeatlogsTrace, type TrackAIInput, type TrackResult, Neatlogs as default };
|
package/dist/browser.mjs
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// src/browser.ts
|
|
2
|
+
var DEFAULT_ENDPOINT = "https://staging-cloud.neatlogs.com";
|
|
3
|
+
var Neatlogs = class {
|
|
4
|
+
apiKey;
|
|
5
|
+
project;
|
|
6
|
+
baseUrl;
|
|
7
|
+
enabled;
|
|
8
|
+
onError;
|
|
9
|
+
constructor(opts) {
|
|
10
|
+
if (!opts || !opts.apiKey) {
|
|
11
|
+
throw new Error("Neatlogs: apiKey is required");
|
|
12
|
+
}
|
|
13
|
+
this.apiKey = opts.apiKey;
|
|
14
|
+
this.project = opts.project;
|
|
15
|
+
const endpoint = opts.endpoint || DEFAULT_ENDPOINT;
|
|
16
|
+
this.baseUrl = safeOrigin(endpoint) || DEFAULT_ENDPOINT;
|
|
17
|
+
this.enabled = opts.enabled !== false;
|
|
18
|
+
this.onError = opts.onError || ((err) => {
|
|
19
|
+
if (typeof console !== "undefined") console.warn("[neatlogs] send failed:", err);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** Send a full (optionally nested) trace. Returns the backend's result. */
|
|
23
|
+
async trace(trace) {
|
|
24
|
+
return this.post(trace);
|
|
25
|
+
}
|
|
26
|
+
/** Send a single AI interaction as a one-span trace (kind defaults to LLM). */
|
|
27
|
+
async trackAI(ai) {
|
|
28
|
+
const { name, kind, ...rest } = ai;
|
|
29
|
+
return this.post({ name, kind: kind ?? "LLM", ...rest });
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Begin a trace you'll complete later (e.g. streaming). Buffers the partial
|
|
33
|
+
* input; call `.finish()` with the final output/tokens to send it. Nothing is
|
|
34
|
+
* sent until `finish()`.
|
|
35
|
+
*/
|
|
36
|
+
startTrace(initial) {
|
|
37
|
+
const startedAt = nowIso();
|
|
38
|
+
return {
|
|
39
|
+
finish: (final) => {
|
|
40
|
+
const merged = { ...initial, ...final ?? {} };
|
|
41
|
+
const { name, kind, ...rest } = merged;
|
|
42
|
+
return this.post({
|
|
43
|
+
name,
|
|
44
|
+
kind: kind ?? "LLM",
|
|
45
|
+
start: startedAt,
|
|
46
|
+
end: nowIso(),
|
|
47
|
+
...rest
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** POST the trace JSON to the backend's /v1/trace endpoint. */
|
|
53
|
+
async post(body) {
|
|
54
|
+
if (!this.enabled) return { ok: true };
|
|
55
|
+
const payload = this.project && body.project === void 0 ? { ...body, project: this.project } : body;
|
|
56
|
+
try {
|
|
57
|
+
const res = await fetch(`${this.baseUrl}/v1/trace`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: {
|
|
60
|
+
"Content-Type": "application/json",
|
|
61
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
62
|
+
},
|
|
63
|
+
body: JSON.stringify(payload),
|
|
64
|
+
// keepalive lets the request survive a page unload (e.g. on navigation).
|
|
65
|
+
keepalive: true
|
|
66
|
+
});
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
const text = await res.text().catch(() => "");
|
|
69
|
+
const result = { ok: false, error: `HTTP ${res.status}${text ? `: ${text}` : ""}` };
|
|
70
|
+
this.onError(new Error(result.error));
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
const data = await res.json().catch(() => ({}));
|
|
74
|
+
return { ok: true, trace_id: data.trace_id, spans: data.spans };
|
|
75
|
+
} catch (err) {
|
|
76
|
+
this.onError(err);
|
|
77
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
function safeOrigin(endpoint) {
|
|
82
|
+
try {
|
|
83
|
+
return new URL(endpoint).origin;
|
|
84
|
+
} catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function nowIso() {
|
|
89
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
90
|
+
}
|
|
91
|
+
var browser_default = Neatlogs;
|
|
92
|
+
export {
|
|
93
|
+
Neatlogs,
|
|
94
|
+
browser_default as default
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=browser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/browser.ts"],"sourcesContent":["/**\n * Neatlogs Browser SDK — `neatlogs/browser`\n *\n * A minimal, browser-safe client for sending traces to Neatlogs from web apps.\n * It has ZERO dependencies (no OpenTelemetry, no Node APIs) — only `fetch` — so\n * it bundles cleanly into front-end apps. It POSTs plain JSON to the backend's\n * simple trace endpoint (`/v1/trace`); the backend generates trace/span ids,\n * builds the hierarchy from nesting, infers cost from model+tokens, and pushes\n * through the normal pipeline. Nothing here streams OTLP.\n *\n * Usage:\n * import { Neatlogs } from 'neatlogs/browser';\n * const nl = new Neatlogs({ apiKey: 'nl_...' });\n *\n * // one-shot AI interaction\n * await nl.trackAI({ name: 'chat', model: 'gpt-4o', input, output,\n * tokens: { prompt: 10, completion: 5 } });\n *\n * // a full nested trace (same shape the backend's POST /v1/trace accepts)\n * await nl.trace({ name: 'support-chat', children: [\n * { name: 'retrieve', query, documents },\n * { name: 'answer', model: 'gpt-4o', input, output },\n * ]});\n *\n * // streaming: open, accumulate, finish\n * const t = nl.startTrace({ name: 'chat', model: 'gpt-4o', input });\n * t.finish({ output: full, tokens: { prompt, completion } });\n */\n\nconst DEFAULT_ENDPOINT = \"https://staging-cloud.neatlogs.com\";\n\n// --- the simple trace shape the backend (/v1/trace) accepts --------------------\n// Mirrors the server-side SimpleSpan; kept local so this file has no imports.\n\nexport interface NeatlogsLog {\n level?: string;\n message: string;\n timestamp?: string;\n}\n\n/**\n * Span kinds the backend accepts (the canonical set). `kind` is optional — when\n * omitted the backend infers it from the fields present.\n */\nexport type NeatlogsKind =\n | \"WORKFLOW\" | \"AGENT\" | \"CHAIN\" | \"TOOL\" | \"RETRIEVER\" | \"RERANKER\"\n | \"EMBEDDING\" | \"LLM\" | \"GUARDRAIL\" | \"MCP_TOOL\" | \"TASK\"\n | \"VECTOR_STORE\" | \"EVALUATOR\" | \"HTTP\";\n\nexport interface NeatlogsSpan {\n name: string;\n /** Optional — the backend infers the kind from fields when omitted. */\n kind?: NeatlogsKind | string;\n input?: unknown;\n output?: unknown;\n model?: string;\n tokens?: { prompt?: number; completion?: number; total?: number };\n query?: unknown;\n documents?: unknown;\n tool_name?: string;\n passed?: boolean;\n score?: number;\n metadata?: Record<string, unknown>;\n status?: string;\n error?: string;\n start?: string;\n end?: string;\n /** Simplest way to record latency — the backend derives end from start + this. */\n duration_ms?: number;\n /**\n * Full canonical-attribute escape hatch. Send ANY neatlogs.* attribute the SDK\n * supports — e.g. { \"neatlogs.llm.temperature\": 0.7, \"neatlogs.agent.role\":\n * \"researcher\", \"neatlogs.tool.parameters\": {...} }. Non-canonical keys are\n * dropped server-side. The fields above (model/tokens/query/...) are shortcuts\n * for the common ones; explicit `attributes` win on conflict.\n */\n attributes?: Record<string, unknown>;\n children?: NeatlogsSpan[];\n logs?: NeatlogsLog[];\n}\n\n/** The root of a trace = a span node (its `name` becomes the workflow name). */\nexport type NeatlogsTrace = NeatlogsSpan;\n\nexport interface NeatlogsOptions {\n /**\n * Your Neatlogs WRITE key (`nlw_…`) — an ingest-only credential safe to embed in\n * browser code. (A full project key also works but should not be exposed client-side,\n * since it can read data.)\n */\n apiKey: string;\n /**\n * Project NAME to ingest into. REQUIRED when using a write key (the key identifies\n * you, not a project). Sent as the root `project` field on every trace. Ignored for\n * a full project key (already project-scoped).\n */\n project?: string;\n /** Backend base URL. Defaults to the same host the SDKs use. */\n endpoint?: string;\n /** Set false to validate calls without sending (default true). */\n enabled?: boolean;\n /** Called on transport errors instead of throwing (default: console.warn). */\n onError?: (err: unknown) => void;\n}\n\nexport interface TrackResult {\n ok: boolean;\n trace_id?: string;\n spans?: number;\n error?: string;\n}\n\n/** Shorthand for a single AI interaction → a one-span trace. */\nexport interface TrackAIInput {\n name: string;\n input?: unknown;\n output?: unknown;\n model?: string;\n tokens?: { prompt?: number; completion?: number; total?: number };\n metadata?: Record<string, unknown>;\n duration_ms?: number;\n /** Any canonical neatlogs.* attributes (see NeatlogsSpan.attributes). */\n attributes?: Record<string, unknown>;\n /** Override the inferred kind (defaults to LLM for trackAI). */\n kind?: NeatlogsKind | string;\n}\n\nexport class Neatlogs {\n private readonly apiKey: string;\n private readonly project?: string;\n private readonly baseUrl: string;\n private readonly enabled: boolean;\n private readonly onError: (err: unknown) => void;\n\n constructor(opts: NeatlogsOptions) {\n if (!opts || !opts.apiKey) {\n throw new Error(\"Neatlogs: apiKey is required\");\n }\n this.apiKey = opts.apiKey;\n this.project = opts.project;\n // Use the origin of the configured endpoint (same convention as the Node SDK),\n // so passing a full /v1/traces URL or a bare host both work.\n const endpoint = opts.endpoint || DEFAULT_ENDPOINT;\n this.baseUrl = safeOrigin(endpoint) || DEFAULT_ENDPOINT;\n this.enabled = opts.enabled !== false;\n this.onError =\n opts.onError ||\n ((err) => {\n // eslint-disable-next-line no-console\n if (typeof console !== \"undefined\") console.warn(\"[neatlogs] send failed:\", err);\n });\n }\n\n /** Send a full (optionally nested) trace. Returns the backend's result. */\n async trace(trace: NeatlogsTrace): Promise<TrackResult> {\n return this.post(trace);\n }\n\n /** Send a single AI interaction as a one-span trace (kind defaults to LLM). */\n async trackAI(ai: TrackAIInput): Promise<TrackResult> {\n const { name, kind, ...rest } = ai;\n return this.post({ name, kind: kind ?? \"LLM\", ...rest });\n }\n\n /**\n * Begin a trace you'll complete later (e.g. streaming). Buffers the partial\n * input; call `.finish()` with the final output/tokens to send it. Nothing is\n * sent until `finish()`.\n */\n startTrace(initial: TrackAIInput): {\n finish: (final?: Partial<TrackAIInput>) => Promise<TrackResult>;\n } {\n const startedAt = nowIso();\n return {\n finish: (final?: Partial<TrackAIInput>) => {\n const merged: TrackAIInput = { ...initial, ...(final ?? {}) };\n const { name, kind, ...rest } = merged;\n return this.post({\n name,\n kind: kind ?? \"LLM\",\n start: startedAt,\n end: nowIso(),\n ...rest,\n });\n },\n };\n }\n\n /** POST the trace JSON to the backend's /v1/trace endpoint. */\n private async post(body: NeatlogsTrace): Promise<TrackResult> {\n if (!this.enabled) return { ok: true };\n // Inject the configured project name into the root (required for write keys;\n // ignored server-side for full project keys). A `project` already on the body wins.\n const payload =\n this.project && (body as any).project === undefined\n ? { ...body, project: this.project }\n : body;\n try {\n const res = await fetch(`${this.baseUrl}/v1/trace`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(payload),\n // keepalive lets the request survive a page unload (e.g. on navigation).\n keepalive: true,\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n const result = { ok: false, error: `HTTP ${res.status}${text ? `: ${text}` : \"\"}` };\n this.onError(new Error(result.error));\n return result;\n }\n const data = (await res.json().catch(() => ({}))) as Partial<TrackResult>;\n return { ok: true, trace_id: data.trace_id, spans: data.spans };\n } catch (err) {\n // Never throw into the host app over telemetry.\n this.onError(err);\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n }\n}\n\n// --- helpers (kept local, no imports) -----------------------------------------\n\nfunction safeOrigin(endpoint: string): string | null {\n try {\n return new URL(endpoint).origin;\n } catch {\n return null;\n }\n}\n\nfunction nowIso(): string {\n return new Date().toISOString();\n}\n\nexport default Neatlogs;\n"],"mappings":";AA6BA,IAAM,mBAAmB;AAkGlB,IAAM,WAAN,MAAe;AAAA,EACH;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,MAAuB;AACjC,QAAI,CAAC,QAAQ,CAAC,KAAK,QAAQ;AACzB,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AACA,SAAK,SAAS,KAAK;AACnB,SAAK,UAAU,KAAK;AAGpB,UAAM,WAAW,KAAK,YAAY;AAClC,SAAK,UAAU,WAAW,QAAQ,KAAK;AACvC,SAAK,UAAU,KAAK,YAAY;AAChC,SAAK,UACH,KAAK,YACJ,CAAC,QAAQ;AAER,UAAI,OAAO,YAAY,YAAa,SAAQ,KAAK,2BAA2B,GAAG;AAAA,IACjF;AAAA,EACJ;AAAA;AAAA,EAGA,MAAM,MAAM,OAA4C;AACtD,WAAO,KAAK,KAAK,KAAK;AAAA,EACxB;AAAA;AAAA,EAGA,MAAM,QAAQ,IAAwC;AACpD,UAAM,EAAE,MAAM,MAAM,GAAG,KAAK,IAAI;AAChC,WAAO,KAAK,KAAK,EAAE,MAAM,MAAM,QAAQ,OAAO,GAAG,KAAK,CAAC;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAW,SAET;AACA,UAAM,YAAY,OAAO;AACzB,WAAO;AAAA,MACL,QAAQ,CAAC,UAAkC;AACzC,cAAM,SAAuB,EAAE,GAAG,SAAS,GAAI,SAAS,CAAC,EAAG;AAC5D,cAAM,EAAE,MAAM,MAAM,GAAG,KAAK,IAAI;AAChC,eAAO,KAAK,KAAK;AAAA,UACf;AAAA,UACA,MAAM,QAAQ;AAAA,UACd,OAAO;AAAA,UACP,KAAK,OAAO;AAAA,UACZ,GAAG;AAAA,QACL,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,KAAK,MAA2C;AAC5D,QAAI,CAAC,KAAK,QAAS,QAAO,EAAE,IAAI,KAAK;AAGrC,UAAM,UACJ,KAAK,WAAY,KAAa,YAAY,SACtC,EAAE,GAAG,MAAM,SAAS,KAAK,QAAQ,IACjC;AACN,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,aAAa;AAAA,QAClD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,UAAU,KAAK,MAAM;AAAA,QACtC;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA;AAAA,QAE5B,WAAW;AAAA,MACb,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,cAAM,SAAS,EAAE,IAAI,OAAO,OAAO,QAAQ,IAAI,MAAM,GAAG,OAAO,KAAK,IAAI,KAAK,EAAE,GAAG;AAClF,aAAK,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpC,eAAO;AAAA,MACT;AACA,YAAM,OAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC/C,aAAO,EAAE,IAAI,MAAM,UAAU,KAAK,UAAU,OAAO,KAAK,MAAM;AAAA,IAChE,SAAS,KAAK;AAEZ,WAAK,QAAQ,GAAG;AAChB,aAAO,EAAE,IAAI,OAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,IAC9E;AAAA,EACF;AACF;AAIA,SAAS,WAAW,UAAiC;AACnD,MAAI;AACF,WAAO,IAAI,IAAI,QAAQ,EAAE;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,SAAiB;AACxB,UAAO,oBAAI,KAAK,GAAE,YAAY;AAChC;AAEA,IAAO,kBAAQ;","names":[]}
|