@synova-cloud/sdk 1.7.0 → 1.8.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/README.md CHANGED
@@ -263,6 +263,116 @@ const topic = await client.prompts.execute('prm_abc123', {
263
263
  | `@SchemaMaxItems(n)` | Maximum array length |
264
264
  | `@SchemaEnum(values)` | Allowed enum values |
265
265
 
266
+ ### Observability
267
+
268
+ Track and group your LLM calls using traces and spans. Each execution creates a span, and multiple spans can be grouped into a trace using `sessionId`.
269
+
270
+ #### Session-Based Tracing
271
+
272
+ Use `sessionId` to group related calls (e.g., a conversation) into a single trace:
273
+
274
+ ```typescript
275
+ const sessionId = 'chat_user123_conv1';
276
+
277
+ // First message - creates new trace
278
+ const response1 = await client.prompts.execute('prm_abc123', {
279
+ provider: 'openai',
280
+ model: 'gpt-4o',
281
+ sessionId,
282
+ variables: { topic: 'TypeScript' },
283
+ });
284
+
285
+ console.log(response1.traceId); // trc_xxx
286
+ console.log(response1.spanId); // spn_xxx
287
+
288
+ // Follow-up - same sessionId = same trace, new span
289
+ const response2 = await client.prompts.execute('prm_abc123', {
290
+ provider: 'openai',
291
+ model: 'gpt-4o',
292
+ sessionId,
293
+ messages: [
294
+ { role: 'assistant', content: response1.content },
295
+ { role: 'user', content: 'Tell me more' },
296
+ ],
297
+ });
298
+
299
+ // response2.traceId === response1.traceId (same trace)
300
+ // response2.spanId !== response1.spanId (new span)
301
+ ```
302
+
303
+ #### Response Properties
304
+
305
+ Every execution returns observability IDs:
306
+
307
+ | Property | Type | Description |
308
+ |----------|------|-------------|
309
+ | `spanDataId` | `string` | Execution data ID (messages, response, usage) |
310
+ | `traceId` | `string` | Trace ID (groups related calls) |
311
+ | `spanId` | `string` | Span ID (this specific call) |
312
+
313
+ #### Custom Span Tracking
314
+
315
+ Track tool calls, retrieval operations, and custom logic as spans within a trace.
316
+
317
+ **Manual approach:**
318
+
319
+ ```typescript
320
+ // Create span
321
+ const span = await client.spans.create(traceId, {
322
+ type: 'tool',
323
+ toolName: 'fetch_weather',
324
+ toolArguments: { city: 'NYC' },
325
+ parentSpanId: generationSpanId,
326
+ });
327
+
328
+ // Execute
329
+ const weather = await fetchWeather('NYC');
330
+
331
+ // End span
332
+ await client.spans.end(span.id, {
333
+ status: 'completed',
334
+ toolResult: weather,
335
+ });
336
+ ```
337
+
338
+ **Wrapper approach:**
339
+
340
+ ```typescript
341
+ // wrapTool() - for tools
342
+ const weather = await client.spans.wrapTool(
343
+ { traceId, toolName: 'fetch_weather', parentSpanId },
344
+ { city: 'NYC' },
345
+ async (args) => fetchWeather(args.city),
346
+ );
347
+
348
+ // wrap() - for custom/retriever/embedding
349
+ const docs = await client.spans.wrap(
350
+ { traceId, type: 'retriever', name: 'vector_search' },
351
+ { query: 'how to...', topK: 5 },
352
+ async () => vectorDb.search(query),
353
+ );
354
+ ```
355
+
356
+ Wrappers automatically handle errors and set `status: 'error'` with message.
357
+
358
+ #### Span Types
359
+
360
+ | Type | Use Case |
361
+ |------|----------|
362
+ | `generation` | LLM calls (auto-created by `execute()`) |
363
+ | `tool` | Tool/function calls |
364
+ | `retriever` | RAG document retrieval |
365
+ | `embedding` | Embedding generation |
366
+ | `custom` | Any custom operation |
367
+
368
+ #### Viewing Traces
369
+
370
+ View your traces in the [Synova Cloud Dashboard](https://app.synova.cloud) under the Observability section. Each trace shows:
371
+ - All spans (LLM calls) in the session
372
+ - Input/output for each span
373
+ - Token usage and latency
374
+ - Error details if any
375
+
266
376
  ### Models
267
377
 
268
378
  #### List All Models
@@ -519,15 +629,25 @@ import type {
519
629
  ISynovaPromptVariable,
520
630
  ISynovaGetPromptOptions,
521
631
  // Execution
522
- ISynovaExecuteOptions,
632
+ ISynovaExecuteOptions, // includes sessionId
523
633
  ISynovaExecuteTypedOptions,
524
- ISynovaExecuteResponse,
634
+ ISynovaExecuteResponse, // includes spanDataId, traceId, spanId
525
635
  ISynovaExecutionUsage,
526
636
  ISynovaExecutionError,
527
637
  // Messages
528
638
  ISynovaMessage,
529
639
  TSynovaMessageRole,
530
640
  TSynovaResponseType,
641
+ // Spans
642
+ ISynovaSpan,
643
+ ISynovaSpanData,
644
+ ISynovaCreateSpanOptions,
645
+ ISynovaEndSpanOptions,
646
+ ISynovaWrapOptions,
647
+ ISynovaWrapToolOptions,
648
+ TSynovaSpanType,
649
+ TSynovaSpanStatus,
650
+ TSynovaSpanLevel,
531
651
  // Files
532
652
  ISynovaFileAttachment,
533
653
  ISynovaFileThumbnails,
package/dist/index.cjs CHANGED
@@ -107,7 +107,7 @@ var ValidationSynovaError = class extends SynovaError {
107
107
  };
108
108
 
109
109
  // src/version.ts
110
- var SDK_VERSION = "1.7.0" ;
110
+ var SDK_VERSION = "1.8.0" ;
111
111
 
112
112
  // src/utils/http.ts
113
113
  var HttpClient = class {
@@ -820,6 +820,7 @@ var PromptsResource = class {
820
820
  if (options.metadata !== void 0) body.metadata = options.metadata;
821
821
  if (options.parameters !== void 0) body.parameters = options.parameters;
822
822
  if (options.responseSchema !== void 0) body.responseSchema = options.responseSchema;
823
+ if (options.sessionId !== void 0) body.sessionId = options.sessionId;
823
824
  const response = await this.http.request({
824
825
  method: "POST",
825
826
  path: `/api/v1/prompts/${promptId}/run`,
@@ -1004,6 +1005,137 @@ var FilesResource = class {
1004
1005
  }
1005
1006
  };
1006
1007
 
1008
+ // src/resources/spans.ts
1009
+ var SpansResource = class {
1010
+ constructor(http) {
1011
+ this.http = http;
1012
+ }
1013
+ /**
1014
+ * Create a new span within a trace
1015
+ *
1016
+ * @param traceId - The trace ID to create span in
1017
+ * @param options - Span creation options
1018
+ * @returns Created span
1019
+ */
1020
+ async create(traceId, options) {
1021
+ const body = {
1022
+ type: options.type
1023
+ };
1024
+ if (options.parentSpanId !== void 0) body.parentSpanId = options.parentSpanId;
1025
+ if (options.name !== void 0) body.name = options.name;
1026
+ if (options.input !== void 0) body.input = options.input;
1027
+ if (options.toolName !== void 0) body.toolName = options.toolName;
1028
+ if (options.toolArguments !== void 0) body.toolArguments = options.toolArguments;
1029
+ if (options.metadata !== void 0) body.metadata = options.metadata;
1030
+ return this.http.request({
1031
+ method: "POST",
1032
+ path: `/api/v1/traces/${traceId}/spans`,
1033
+ body
1034
+ });
1035
+ }
1036
+ /**
1037
+ * End/complete a span
1038
+ *
1039
+ * @param spanId - The span ID to end
1040
+ * @param options - Span end options
1041
+ * @returns Updated span
1042
+ */
1043
+ async end(spanId, options) {
1044
+ const body = {};
1045
+ if (options?.status !== void 0) body.status = options.status;
1046
+ if (options?.level !== void 0) body.level = options.level;
1047
+ if (options?.statusMessage !== void 0) body.statusMessage = options.statusMessage;
1048
+ if (options?.output !== void 0) body.output = options.output;
1049
+ if (options?.toolResult !== void 0) body.toolResult = options.toolResult;
1050
+ if (options?.durationMs !== void 0) body.durationMs = options.durationMs;
1051
+ return this.http.request({
1052
+ method: "PATCH",
1053
+ path: `/api/v1/spans/${spanId}`,
1054
+ body
1055
+ });
1056
+ }
1057
+ /**
1058
+ * Wrap an async function with automatic span tracking
1059
+ *
1060
+ * @param options - Span options (traceId, type, name, etc.)
1061
+ * @param input - Input data to record in span
1062
+ * @param fn - Async function to execute
1063
+ * @returns Result of the function
1064
+ *
1065
+ * @example
1066
+ * ```ts
1067
+ * const result = await client.spans.wrap(
1068
+ * { traceId: 'trc_123', type: 'retriever', name: 'vector_search' },
1069
+ * { query: 'how to...', topK: 5 },
1070
+ * async () => vectorDb.search(query),
1071
+ * );
1072
+ * ```
1073
+ */
1074
+ async wrap(options, input, fn) {
1075
+ const span = await this.create(options.traceId, {
1076
+ type: options.type,
1077
+ name: options.name,
1078
+ parentSpanId: options.parentSpanId,
1079
+ input,
1080
+ metadata: options.metadata
1081
+ });
1082
+ try {
1083
+ const result = await fn();
1084
+ await this.end(span.id, {
1085
+ status: "completed",
1086
+ output: result
1087
+ });
1088
+ return result;
1089
+ } catch (error) {
1090
+ await this.end(span.id, {
1091
+ status: "error",
1092
+ statusMessage: error instanceof Error ? error.message : String(error)
1093
+ });
1094
+ throw error;
1095
+ }
1096
+ }
1097
+ /**
1098
+ * Wrap a tool function with automatic span tracking
1099
+ *
1100
+ * @param options - Tool span options (traceId, toolName, etc.)
1101
+ * @param args - Tool arguments to record
1102
+ * @param fn - Tool function to execute
1103
+ * @returns Result of the tool
1104
+ *
1105
+ * @example
1106
+ * ```ts
1107
+ * const weather = await client.spans.wrapTool(
1108
+ * { traceId: 'trc_123', toolName: 'fetch_weather', parentSpanId: 'spn_abc' },
1109
+ * { city: 'NYC' },
1110
+ * async (args) => fetchWeather(args.city),
1111
+ * );
1112
+ * ```
1113
+ */
1114
+ async wrapTool(options, args, fn) {
1115
+ const span = await this.create(options.traceId, {
1116
+ type: "tool",
1117
+ toolName: options.toolName,
1118
+ toolArguments: args,
1119
+ parentSpanId: options.parentSpanId,
1120
+ metadata: options.metadata
1121
+ });
1122
+ try {
1123
+ const result = await fn(args);
1124
+ await this.end(span.id, {
1125
+ status: "completed",
1126
+ toolResult: result
1127
+ });
1128
+ return result;
1129
+ } catch (error) {
1130
+ await this.end(span.id, {
1131
+ status: "error",
1132
+ statusMessage: error instanceof Error ? error.message : String(error)
1133
+ });
1134
+ throw error;
1135
+ }
1136
+ }
1137
+ };
1138
+
1007
1139
  // src/client.ts
1008
1140
  var DEFAULT_BASE_URL = "https://api.synova.cloud";
1009
1141
  var DEFAULT_TIMEOUT = 3e4;
@@ -1024,6 +1156,7 @@ var SynovaCloudSdk = class {
1024
1156
  prompts;
1025
1157
  models;
1026
1158
  files;
1159
+ spans;
1027
1160
  http;
1028
1161
  /**
1029
1162
  * Create a new Synova Cloud SDK client
@@ -1052,6 +1185,7 @@ var SynovaCloudSdk = class {
1052
1185
  this.prompts = new PromptsResource(this.http);
1053
1186
  this.models = new ModelsResource(this.http);
1054
1187
  this.files = new FilesResource(this.http);
1188
+ this.spans = new SpansResource(this.http);
1055
1189
  }
1056
1190
  };
1057
1191