ironside 0.1.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.
package/LICENSE.md ADDED
@@ -0,0 +1,58 @@
1
+ # Ironside Sustainable Use License v1.0
2
+
3
+ ## Acceptance
4
+
5
+ By using the software, you agree to all of the terms and conditions below.
6
+
7
+ ## Copyright License
8
+
9
+ The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations below.
10
+
11
+ ## Limitations
12
+
13
+ **Use.** You may use, modify, and self-host the software — including for your own commercial internal business purposes — subject to the limitations below.
14
+
15
+ **Competing Use.** You may not use the software to create a product or service that competes with the software or any product or service offered by the licensor. You may not offer the software to third parties on a hosted or managed basis (i.e. running it as a service on your infrastructure for other organizations to use), or embed the software into a product or service you provide to third parties, without a separate commercial agreement with the licensor. Self-hosting the software for your own organization's own use is always permitted and is not a competing use.
16
+
17
+ **SDK Integration Exception.** Notwithstanding the Competing Use and Distribution limitations, you may use, copy, modify, embed, and distribute the `ironside` client package as part of a product or service you provide to third parties, including for commercial purposes, solely to instrument that product or service and send telemetry to an Ironside deployment. This exception does not permit you to offer Ironside itself as a hosted or managed service for third parties, distribute the SDK as a standalone paid product, or use the SDK to create a product or service that competes with Ironside.
18
+
19
+ **Distribution.** You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes, or with a separate commercial agreement with the licensor.
20
+
21
+ **Attribution.** You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor's trademarks is subject to applicable law.
22
+
23
+ ## Patent License
24
+
25
+ The licensor grants you a license, under any patent claims the licensor can license or becomes able to license, to make, have made, use, sell, offer for sale, import, and have imported the software, in each case subject to the limitations and conditions in this license. This patent license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to the infringement of any patent, your patent license for the software granted under these terms ends immediately.
26
+
27
+ ## Distribution Requirements
28
+
29
+ If you distribute copies or modifications of the software, you must:
30
+
31
+ 1. Give each recipient a copy of this license.
32
+ 2. Include a prominent notice in any modified version stating that you have modified the software.
33
+
34
+ ## Fair Use
35
+
36
+ This license is not intended to limit any rights you have under applicable copyright doctrines of fair use, fair dealing, or other equivalents.
37
+
38
+ ## Termination
39
+
40
+ If you use the software in violation of this license, such use is not licensed, and your license will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your license will be reinstated. If you violate these license terms after such reinstatement, any further violation of these terms will cause your license to terminate automatically and permanently.
41
+
42
+ ## No Liability
43
+
44
+ As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.
45
+
46
+ ## Definitions
47
+
48
+ The **licensor** is the entity offering these terms — Luka Živković.
49
+
50
+ The **software** is the software the licensor makes available under these terms, including any portion of it.
51
+
52
+ **You** refers to the individual or entity agreeing to these terms.
53
+
54
+ **Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. Control means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
55
+
56
+ ---
57
+
58
+ Copyright 2026 Luka Živković. All rights reserved.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # `ironside`
2
+
3
+ The official Node.js/TypeScript client for sending LLM traces directly to Ironside. It provides automatic OpenAI and Anthropic instrumentation, manual trace/span/generation handles, media uploads, and score recording.
4
+
5
+ Requires Node.js 20 or newer. The package is ESM-only.
6
+
7
+ ## Install
8
+
9
+ Install the SDK together with whichever provider client your application uses:
10
+
11
+ ```sh
12
+ npm install ironside openai
13
+ # or: npm install ironside @anthropic-ai/sdk
14
+ ```
15
+
16
+ Provider packages are not runtime dependencies of `ironside`; the wrappers accept the client instance already used by your application.
17
+
18
+ ## Provider wrappers
19
+
20
+ ```ts
21
+ import OpenAI from "openai";
22
+ import { init, wrapOpenAI } from "ironside";
23
+
24
+ const ironside = init({
25
+ apiKey: process.env.IRONSIDE_API_KEY!,
26
+ host: process.env.IRONSIDE_HOST ?? "http://localhost:8788",
27
+ onError(error) {
28
+ console.error("Ironside ingest failed", error);
29
+ }
30
+ });
31
+
32
+ const openai = wrapOpenAI(
33
+ new OpenAI({ apiKey: process.env.OPENAI_API_KEY }),
34
+ ironside
35
+ );
36
+
37
+ await openai.chat.completions.create({
38
+ model: "gpt-4o",
39
+ messages: [{ role: "user", content: "Hello" }]
40
+ });
41
+
42
+ await ironside.shutdown();
43
+ ```
44
+
45
+ `wrapAnthropic(client, ironside)` instruments `messages.create()` in the same way. Both wrappers mutate and return the same provider client, preserve streaming behavior, and record input/output, model, token usage, and common sampling parameters. For OpenAI streaming token usage, request `stream_options: { include_usage: true }`.
46
+
47
+ ## Manual instrumentation
48
+
49
+ ```ts
50
+ import { init } from "ironside";
51
+
52
+ const ironside = init({
53
+ apiKey: process.env.IRONSIDE_API_KEY!,
54
+ host: "https://ironside.example.com"
55
+ });
56
+
57
+ const trace = ironside.trace({
58
+ name: "answer-question",
59
+ userId: "user-123",
60
+ input: { question: "Why is the sky blue?" }
61
+ });
62
+
63
+ const generation = trace.generation({
64
+ name: "generate-answer",
65
+ model: "example-model",
66
+ input: { prompt: "Why is the sky blue?" }
67
+ });
68
+
69
+ generation.end({
70
+ output: { answer: "Rayleigh scattering." },
71
+ usageDetails: { input_tokens: 8, output_tokens: 4 },
72
+ costDetails: { total_usd: 0.00012 }
73
+ });
74
+
75
+ trace.score({ name: "correctness", value: 1, source: "eval" });
76
+ trace.update({ output: { answer: "Rayleigh scattering." } });
77
+
78
+ await ironside.shutdown();
79
+ ```
80
+
81
+ Instrumentation calls buffer events and do not block application requests. Call `flush()` at a lifecycle boundary when needed, and always call `shutdown()` during graceful process termination so buffered and in-flight events finish sending. Failed background batches are reported through `onError`; they are not thrown into the instrumented request path.
82
+
83
+ `recordGenerateTextResult()` is available for results returned by the Vercel AI SDK. `uploadMedia()` stores binary content separately and returns an `ironside://media/...` reference suitable for trace input or output.
84
+
85
+ ## Choosing an integration
86
+
87
+ This package is the ergonomic Node.js integration and supports Ironside-specific cost and score fields. Third-party frameworks and non-Node runtimes should prefer Ironside's canonical OTLP/HTTP endpoint with OpenTelemetry `gen_ai.*` attributes. Low-level integrations can send the native JSON envelope directly at `POST /api/v1/ingest`.
88
+
89
+ ## License
90
+
91
+ [Ironside Sustainable Use License v1.0](./LICENSE.md), including its SDK Integration Exception for embedding and distributing this package as part of commercial applications that send telemetry to Ironside.
@@ -0,0 +1,51 @@
1
+ import type { IngestRequestEvent } from "./types.js";
2
+ export interface BatcherOptions {
3
+ apiKey: string;
4
+ host: string;
5
+ /** Flush automatically once this many events are buffered. */
6
+ maxBatchSize?: number;
7
+ /** Flush automatically after this many ms, even if under maxBatchSize. */
8
+ flushIntervalMs?: number;
9
+ /** Injectable for tests; defaults to global fetch. */
10
+ fetchImpl?: typeof fetch;
11
+ /** Called with a failed batch's events + the error; default: console.error. Never throws back into the caller's hot path. */
12
+ onError?: (error: unknown, events: IngestRequestEvent[]) => void;
13
+ }
14
+ /**
15
+ * Buffers ingest events in memory and flushes them to POST /api/v1/ingest
16
+ * in the background — instrumentation calls (trace/span/generation) never
17
+ * block on network I/O. Flushes are fire-and-forget from the caller's
18
+ * perspective; failures are reported via onError, not thrown, since a
19
+ * trace SDK must never be the reason an application request fails.
20
+ */
21
+ export declare class EventBatcher {
22
+ private readonly apiKey;
23
+ private readonly host;
24
+ private readonly maxBatchSize;
25
+ private readonly flushIntervalMs;
26
+ private readonly fetchImpl;
27
+ private readonly onError;
28
+ private buffer;
29
+ private timer;
30
+ private inFlight;
31
+ private closed;
32
+ constructor(options: BatcherOptions);
33
+ enqueue(event: IngestRequestEvent): void;
34
+ /** Sends whatever is currently buffered. Safe to call concurrently — flushes serialize via inFlight. */
35
+ flush(): Promise<void>;
36
+ private send;
37
+ /**
38
+ * Stops the background timer and flushes any remaining buffered events.
39
+ * Call on process shutdown.
40
+ *
41
+ * Must wait on `inFlight` explicitly, not just call `flush()` — if a
42
+ * different caller (the interval timer, or an enqueue() that just hit
43
+ * maxBatchSize) already claimed the buffer into its own in-progress
44
+ * send() moments earlier, flush() here sees an empty buffer and returns
45
+ * immediately without waiting for that still-pending request. Without
46
+ * this, shutdown() could resolve while a real network request is still
47
+ * in flight, and an immediately-following process.exit() would silently
48
+ * drop that batch.
49
+ */
50
+ close(): Promise<void>;
51
+ }
@@ -0,0 +1,90 @@
1
+ const DEFAULT_MAX_BATCH_SIZE = 50;
2
+ const DEFAULT_FLUSH_INTERVAL_MS = 5000;
3
+ /**
4
+ * Buffers ingest events in memory and flushes them to POST /api/v1/ingest
5
+ * in the background — instrumentation calls (trace/span/generation) never
6
+ * block on network I/O. Flushes are fire-and-forget from the caller's
7
+ * perspective; failures are reported via onError, not thrown, since a
8
+ * trace SDK must never be the reason an application request fails.
9
+ */
10
+ export class EventBatcher {
11
+ apiKey;
12
+ host;
13
+ maxBatchSize;
14
+ flushIntervalMs;
15
+ fetchImpl;
16
+ onError;
17
+ buffer = [];
18
+ timer = null;
19
+ inFlight = Promise.resolve();
20
+ closed = false;
21
+ constructor(options) {
22
+ this.apiKey = options.apiKey;
23
+ this.host = options.host.replace(/\/$/, "");
24
+ this.maxBatchSize = options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;
25
+ this.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
26
+ this.fetchImpl = options.fetchImpl ?? fetch;
27
+ this.onError =
28
+ options.onError ??
29
+ ((error) => console.error("[ironside] failed to send trace events:", error));
30
+ this.timer = setInterval(() => void this.flush(), this.flushIntervalMs);
31
+ // Don't let the flush timer keep the process alive on its own.
32
+ this.timer.unref?.();
33
+ }
34
+ enqueue(event) {
35
+ if (this.closed)
36
+ return;
37
+ this.buffer.push(event);
38
+ if (this.buffer.length >= this.maxBatchSize) {
39
+ void this.flush();
40
+ }
41
+ }
42
+ /** Sends whatever is currently buffered. Safe to call concurrently — flushes serialize via inFlight. */
43
+ async flush() {
44
+ if (this.buffer.length === 0)
45
+ return;
46
+ const events = this.buffer;
47
+ this.buffer = [];
48
+ this.inFlight = this.inFlight.then(() => this.send(events));
49
+ await this.inFlight;
50
+ }
51
+ async send(events) {
52
+ try {
53
+ const res = await this.fetchImpl(`${this.host}/api/v1/ingest`, {
54
+ method: "POST",
55
+ headers: {
56
+ "content-type": "application/json",
57
+ authorization: `Bearer ${this.apiKey}`
58
+ },
59
+ body: JSON.stringify({ events })
60
+ });
61
+ if (!res.ok) {
62
+ this.onError(new Error(`ingest request failed: HTTP ${res.status}`), events);
63
+ }
64
+ }
65
+ catch (error) {
66
+ this.onError(error, events);
67
+ }
68
+ }
69
+ /**
70
+ * Stops the background timer and flushes any remaining buffered events.
71
+ * Call on process shutdown.
72
+ *
73
+ * Must wait on `inFlight` explicitly, not just call `flush()` — if a
74
+ * different caller (the interval timer, or an enqueue() that just hit
75
+ * maxBatchSize) already claimed the buffer into its own in-progress
76
+ * send() moments earlier, flush() here sees an empty buffer and returns
77
+ * immediately without waiting for that still-pending request. Without
78
+ * this, shutdown() could resolve while a real network request is still
79
+ * in flight, and an immediately-following process.exit() would silently
80
+ * drop that batch.
81
+ */
82
+ async close() {
83
+ this.closed = true;
84
+ if (this.timer)
85
+ clearInterval(this.timer);
86
+ await this.flush();
87
+ await this.inFlight;
88
+ }
89
+ }
90
+ //# sourceMappingURL=batcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batcher.js","sourceRoot":"","sources":["../../src/batcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { type BatcherOptions } from "./batcher.js";
2
+ import type { EndObservationOptions, ScoreOptions, StartGenerationOptions, StartSpanOptions, StartTraceOptions, UpdateTraceOptions } from "./types.js";
3
+ export interface IronsideClientOptions {
4
+ apiKey: string;
5
+ /** Ironside API host, e.g. "https://ironside.example.com" or "http://localhost:8788". */
6
+ host: string;
7
+ maxBatchSize?: number;
8
+ flushIntervalMs?: number;
9
+ fetchImpl?: BatcherOptions["fetchImpl"];
10
+ onError?: BatcherOptions["onError"];
11
+ }
12
+ export interface ObservationHandle {
13
+ readonly id: string;
14
+ /** Starts a child span nested under this observation. */
15
+ span(options?: StartSpanOptions): ObservationHandle;
16
+ /** Starts a child generation nested under this observation. */
17
+ generation(options?: StartGenerationOptions): ObservationHandle;
18
+ /** Marks this observation complete. Safe to call at most once meaningfully; a second call overwrites endTime. */
19
+ end(options?: EndObservationOptions): void;
20
+ /** Records a score (user feedback, eval result, ...) attached to this observation. */
21
+ score(options: ScoreOptions): void;
22
+ }
23
+ export interface TraceHandle {
24
+ readonly id: string;
25
+ span(options?: StartSpanOptions): ObservationHandle;
26
+ generation(options?: StartGenerationOptions): ObservationHandle;
27
+ update(options: UpdateTraceOptions): void;
28
+ /** Records a score (user feedback, eval result, ...) attached to this trace. */
29
+ score(options: ScoreOptions): void;
30
+ }
31
+ export interface UploadMediaOptions {
32
+ /** The raw bytes to store. */
33
+ data: Uint8Array | ArrayBuffer;
34
+ /** Real content type of the bytes, e.g. "image/png". */
35
+ contentType: string;
36
+ }
37
+ export interface UploadedMedia {
38
+ id: string;
39
+ /** Compact ref string ("ironside://media/<id>") to embed in trace input/output/metadata. */
40
+ ref: string;
41
+ contentType: string;
42
+ sizeBytes: number;
43
+ sha256: string;
44
+ }
45
+ export interface IronsideClient {
46
+ trace(options?: StartTraceOptions): TraceHandle;
47
+ /**
48
+ * Uploads a media blob (image, audio, document, ...) and returns a
49
+ * compact ref string to embed in trace input/output instead of the
50
+ * bytes themselves — base64 payloads inside trace JSON bloat the
51
+ * columnar store. Content-addressed: uploading identical bytes twice
52
+ * returns the same asset. Unlike instrumentation calls this awaits the
53
+ * network (the ref doesn't exist until the server has the bytes).
54
+ */
55
+ uploadMedia(options: UploadMediaOptions): Promise<UploadedMedia>;
56
+ /** Sends buffered events immediately instead of waiting for the next automatic flush. */
57
+ flush(): Promise<void>;
58
+ /** Stops background flushing and sends any remaining buffered events. Call before process exit. */
59
+ shutdown(): Promise<void>;
60
+ }
61
+ /**
62
+ * Creates an Ironside client. Instrumentation calls (trace/span/generation)
63
+ * never block on network I/O — events are buffered and flushed in the
64
+ * background. Deliberately does NOT auto-register a process-exit handler:
65
+ * doing that inside a library is a footgun (can't be un-registered,
66
+ * surprising under multiple init() calls or in serverless runtimes with
67
+ * their own lifecycle hooks). Call `shutdown()` explicitly wherever your
68
+ * app already handles graceful shutdown.
69
+ */
70
+ export declare function init(options: IronsideClientOptions): IronsideClient;
@@ -0,0 +1,171 @@
1
+ import { ulid } from "ulid";
2
+ import { EventBatcher } from "./batcher.js";
3
+ /**
4
+ * Creates an Ironside client. Instrumentation calls (trace/span/generation)
5
+ * never block on network I/O — events are buffered and flushed in the
6
+ * background. Deliberately does NOT auto-register a process-exit handler:
7
+ * doing that inside a library is a footgun (can't be un-registered,
8
+ * surprising under multiple init() calls or in serverless runtimes with
9
+ * their own lifecycle hooks). Call `shutdown()` explicitly wherever your
10
+ * app already handles graceful shutdown.
11
+ */
12
+ export function init(options) {
13
+ const batcher = new EventBatcher({
14
+ apiKey: options.apiKey,
15
+ host: options.host,
16
+ ...(options.maxBatchSize !== undefined && { maxBatchSize: options.maxBatchSize }),
17
+ ...(options.flushIntervalMs !== undefined && { flushIntervalMs: options.flushIntervalMs }),
18
+ ...(options.fetchImpl !== undefined && { fetchImpl: options.fetchImpl }),
19
+ ...(options.onError !== undefined && { onError: options.onError })
20
+ });
21
+ function enqueueScore(traceId, observationId, scoreOptions) {
22
+ batcher.enqueue({
23
+ type: "score-upsert",
24
+ body: {
25
+ id: scoreOptions.id ?? ulid(),
26
+ traceId,
27
+ ...(observationId && { observationId }),
28
+ name: scoreOptions.name,
29
+ dataType: scoreOptions.value !== undefined ? "numeric" : "categorical",
30
+ source: scoreOptions.source ?? "api",
31
+ ...(scoreOptions.value !== undefined && { value: scoreOptions.value }),
32
+ ...(scoreOptions.stringValue !== undefined && { stringValue: scoreOptions.stringValue }),
33
+ ...(scoreOptions.comment && { comment: scoreOptions.comment }),
34
+ timestamp: new Date().toISOString(),
35
+ metadata: scoreOptions.metadata ?? {}
36
+ }
37
+ });
38
+ }
39
+ function makeObservationHandle(id, traceId, type, parentObservationId, startOptions) {
40
+ const startTime = new Date().toISOString();
41
+ const model = "model" in startOptions ? startOptions.model : undefined;
42
+ const modelParameters = "modelParameters" in startOptions ? startOptions.modelParameters : undefined;
43
+ batcher.enqueue({
44
+ type: "observation-upsert",
45
+ body: {
46
+ id,
47
+ traceId,
48
+ ...(parentObservationId && { parentObservationId }),
49
+ type,
50
+ ...(startOptions.name && { name: startOptions.name }),
51
+ startTime,
52
+ ...(model && { model }),
53
+ ...(modelParameters && { modelParameters }),
54
+ ...(startOptions.input !== undefined && { input: startOptions.input }),
55
+ metadata: startOptions.metadata ?? {}
56
+ }
57
+ });
58
+ function child(childType) {
59
+ return (options = {}) => makeObservationHandle(ulid(), traceId, childType, id, options);
60
+ }
61
+ return {
62
+ id,
63
+ span: child("span"),
64
+ generation: child("generation"),
65
+ end(endOptions = {}) {
66
+ batcher.enqueue({
67
+ type: "observation-upsert",
68
+ body: {
69
+ id,
70
+ traceId,
71
+ ...(parentObservationId && { parentObservationId }),
72
+ type,
73
+ ...(startOptions.name && { name: startOptions.name }),
74
+ startTime,
75
+ endTime: new Date().toISOString(),
76
+ level: endOptions.level ?? "default",
77
+ ...(model && { model }),
78
+ ...(modelParameters && { modelParameters }),
79
+ ...(startOptions.input !== undefined && { input: startOptions.input }),
80
+ ...(endOptions.output !== undefined && { output: endOptions.output }),
81
+ ...(endOptions.statusMessage && { statusMessage: endOptions.statusMessage }),
82
+ ...(endOptions.usageDetails && { usageDetails: endOptions.usageDetails }),
83
+ ...(endOptions.costDetails && { costDetails: endOptions.costDetails }),
84
+ metadata: { ...startOptions.metadata, ...endOptions.metadata }
85
+ }
86
+ });
87
+ },
88
+ score: (scoreOptions) => enqueueScore(traceId, id, scoreOptions)
89
+ };
90
+ }
91
+ const fetchImpl = options.fetchImpl ?? fetch;
92
+ const host = options.host.replace(/\/$/, "");
93
+ return {
94
+ async uploadMedia(uploadOptions) {
95
+ const body = uploadOptions.data instanceof ArrayBuffer
96
+ ? new Uint8Array(uploadOptions.data)
97
+ : uploadOptions.data;
98
+ const res = await fetchImpl(`${host}/api/v1/media`, {
99
+ method: "POST",
100
+ headers: {
101
+ authorization: `Bearer ${options.apiKey}`,
102
+ "content-type": uploadOptions.contentType
103
+ },
104
+ // DOM's BodyInit type isn't in the SDK's lib set; Uint8Array is a
105
+ // valid fetch body in Node 18+ and browsers alike.
106
+ body: body
107
+ });
108
+ if (!res.ok) {
109
+ const text = await res.text().catch(() => "");
110
+ throw new Error(`media upload failed: ${res.status}${text ? ` ${text}` : ""}`);
111
+ }
112
+ return (await res.json());
113
+ },
114
+ trace(startOptions = {}) {
115
+ const id = startOptions.id ?? ulid();
116
+ // Fixed at trace-start time and reused by update() below — the trace's
117
+ // timestamp must not drift to "whenever update() happened to be
118
+ // called" (e.g. re-stamping it at completion time would corrupt the
119
+ // recorded start time on every partial update).
120
+ const timestamp = new Date().toISOString();
121
+ batcher.enqueue({
122
+ type: "trace-upsert",
123
+ body: {
124
+ id,
125
+ timestamp,
126
+ ...(startOptions.name && { name: startOptions.name }),
127
+ ...(startOptions.userId && { userId: startOptions.userId }),
128
+ ...(startOptions.sessionId && { sessionId: startOptions.sessionId }),
129
+ ...(startOptions.environment && { environment: startOptions.environment }),
130
+ ...(startOptions.release && { release: startOptions.release }),
131
+ ...(startOptions.version && { version: startOptions.version }),
132
+ tags: startOptions.tags ?? [],
133
+ metadata: startOptions.metadata ?? {},
134
+ ...(startOptions.input !== undefined && { input: startOptions.input })
135
+ }
136
+ });
137
+ return {
138
+ id,
139
+ span: (options = {}) => makeObservationHandle(ulid(), id, "span", undefined, options),
140
+ generation: (options = {}) => makeObservationHandle(ulid(), id, "generation", undefined, options),
141
+ update(updateOptions) {
142
+ // A trace-upsert replaces the whole row (ClickHouse
143
+ // ReplacingMergeTree has no field-level merge) — every field set
144
+ // by trace() must be carried forward here too, or update() would
145
+ // silently wipe name/userId/sessionId/input back to absent.
146
+ batcher.enqueue({
147
+ type: "trace-upsert",
148
+ body: {
149
+ id,
150
+ timestamp,
151
+ ...(startOptions.name && { name: startOptions.name }),
152
+ ...(startOptions.userId && { userId: startOptions.userId }),
153
+ ...(startOptions.sessionId && { sessionId: startOptions.sessionId }),
154
+ ...(startOptions.environment && { environment: startOptions.environment }),
155
+ ...(startOptions.release && { release: startOptions.release }),
156
+ ...(startOptions.version && { version: startOptions.version }),
157
+ tags: startOptions.tags ?? [],
158
+ ...(startOptions.input !== undefined && { input: startOptions.input }),
159
+ ...(updateOptions.output !== undefined && { output: updateOptions.output }),
160
+ metadata: { ...startOptions.metadata, ...updateOptions.metadata }
161
+ }
162
+ });
163
+ },
164
+ score: (scoreOptions) => enqueueScore(id, undefined, scoreOptions)
165
+ };
166
+ },
167
+ flush: () => batcher.flush(),
168
+ shutdown: () => batcher.close()
169
+ };
170
+ }
171
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export { init } from "./client.js";
2
+ export type { IronsideClient, IronsideClientOptions, TraceHandle, ObservationHandle, UploadMediaOptions, UploadedMedia } from "./client.js";
3
+ export type { StartTraceOptions, UpdateTraceOptions, StartSpanOptions, StartGenerationOptions, EndObservationOptions, ScoreOptions } from "./types.js";
4
+ export { wrapOpenAI } from "./wrappers/openai.js";
5
+ export type { WrapOpenAIOptions } from "./wrappers/openai.js";
6
+ export { wrapAnthropic } from "./wrappers/anthropic.js";
7
+ export type { WrapAnthropicOptions } from "./wrappers/anthropic.js";
8
+ export { recordGenerateTextResult } from "./wrappers/vercel-ai.js";
9
+ export type { RecordGenerateTextOptions } from "./wrappers/vercel-ai.js";
@@ -0,0 +1,5 @@
1
+ export { init } from "./client.js";
2
+ export { wrapOpenAI } from "./wrappers/openai.js";
3
+ export { wrapAnthropic } from "./wrappers/anthropic.js";
4
+ export { recordGenerateTextResult } from "./wrappers/vercel-ai.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAkBnC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,64 @@
1
+ export interface StartTraceOptions {
2
+ id?: string;
3
+ name?: string;
4
+ userId?: string;
5
+ sessionId?: string;
6
+ environment?: string;
7
+ release?: string;
8
+ version?: string;
9
+ tags?: string[];
10
+ metadata?: Record<string, string>;
11
+ input?: unknown;
12
+ }
13
+ export interface UpdateTraceOptions {
14
+ output?: unknown;
15
+ metadata?: Record<string, string>;
16
+ }
17
+ interface ScoreOptionsBase {
18
+ id?: string;
19
+ /** Attaches the score to a specific observation instead of the trace as a whole. */
20
+ observationId?: string;
21
+ name: string;
22
+ source?: "api" | "eval" | "annotation";
23
+ comment?: string;
24
+ metadata?: Record<string, string>;
25
+ }
26
+ /**
27
+ * Exactly one of value/stringValue — enforced at the type level (not just
28
+ * documented) so a caller can't produce a score with neither (dropped
29
+ * server-side against the domain schema's invariant, silently, since the
30
+ * SDK's ingest is fire-and-forget) or both (an internally inconsistent
31
+ * dataType/payload pairing).
32
+ */
33
+ export type ScoreOptions = (ScoreOptionsBase & {
34
+ value: number;
35
+ stringValue?: undefined;
36
+ }) | (ScoreOptionsBase & {
37
+ stringValue: string;
38
+ value?: undefined;
39
+ });
40
+ export interface StartSpanOptions {
41
+ id?: string;
42
+ name?: string;
43
+ input?: unknown;
44
+ metadata?: Record<string, string>;
45
+ }
46
+ export interface StartGenerationOptions extends StartSpanOptions {
47
+ model?: string;
48
+ modelParameters?: Record<string, string | number | boolean | null>;
49
+ }
50
+ export interface EndObservationOptions {
51
+ output?: unknown;
52
+ statusMessage?: string;
53
+ level?: "debug" | "default" | "warning" | "error";
54
+ usageDetails?: Record<string, number>;
55
+ costDetails?: Record<string, number>;
56
+ metadata?: Record<string, string>;
57
+ }
58
+ export type IngestEventType = "trace-upsert" | "observation-upsert" | "score-upsert";
59
+ export interface IngestRequestEvent {
60
+ id?: string;
61
+ type: IngestEventType;
62
+ body: unknown;
63
+ }
64
+ export {};
@@ -0,0 +1,6 @@
1
+ // Wire-contract types, deliberately duplicated from (not imported from)
2
+ // @ironside/shared: this package ships standalone to npm and cannot depend
3
+ // on a private workspace package. Kept intentionally minimal — only the
4
+ // fields the SDK itself constructs.
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,2EAA2E;AAC3E,wEAAwE;AACxE,oCAAoC"}