@synova-cloud/sdk 1.6.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 +253 -9
- package/dist/index.cjs +692 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +655 -79
- package/dist/index.d.ts +655 -79
- package/dist/index.js +671 -109
- package/dist/index.js.map +1 -1
- package/package.json +16 -1
package/README.md
CHANGED
|
@@ -174,6 +174,205 @@ if (response.type === 'image') {
|
|
|
174
174
|
}
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
+
#### Structured Output (Typed Responses)
|
|
178
|
+
|
|
179
|
+
Get typed and validated responses from LLMs using JSON Schema.
|
|
180
|
+
|
|
181
|
+
First, install optional peer dependencies:
|
|
182
|
+
```bash
|
|
183
|
+
npm install class-validator class-transformer
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Define a response class with decorators:
|
|
187
|
+
```typescript
|
|
188
|
+
import { IsString, IsArray, IsNumber, Min, Max } from 'class-validator';
|
|
189
|
+
import { Description, Example, ArrayItems } from '@synova-cloud/sdk';
|
|
190
|
+
|
|
191
|
+
class TopicDto {
|
|
192
|
+
@IsString()
|
|
193
|
+
@Description('Article title for SEO')
|
|
194
|
+
@Example('10 Ways to Improve SQL Performance')
|
|
195
|
+
title: string;
|
|
196
|
+
|
|
197
|
+
@IsString()
|
|
198
|
+
@Description('Short description')
|
|
199
|
+
description: string;
|
|
200
|
+
|
|
201
|
+
@IsArray()
|
|
202
|
+
@ArrayItems(String)
|
|
203
|
+
@Description('SEO keywords')
|
|
204
|
+
keywords: string[];
|
|
205
|
+
|
|
206
|
+
@IsNumber()
|
|
207
|
+
@Min(1)
|
|
208
|
+
@Max(10)
|
|
209
|
+
@Description('Priority from 1 to 10')
|
|
210
|
+
priority: number;
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Execute with `responseClass` to get typed response:
|
|
215
|
+
```typescript
|
|
216
|
+
const topic = await client.prompts.execute('prm_abc123', {
|
|
217
|
+
provider: 'openai',
|
|
218
|
+
model: 'gpt-4o',
|
|
219
|
+
responseClass: TopicDto,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// topic is typed as TopicDto
|
|
223
|
+
console.log(topic.title); // string
|
|
224
|
+
console.log(topic.keywords); // string[]
|
|
225
|
+
console.log(topic.priority); // number
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Works with `executeByTag` and `executeByVersion` too:
|
|
229
|
+
```typescript
|
|
230
|
+
const topic = await client.prompts.executeByTag('prm_abc123', 'production', {
|
|
231
|
+
provider: 'openai',
|
|
232
|
+
model: 'gpt-4o',
|
|
233
|
+
responseClass: TopicDto,
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Disable validation if needed:
|
|
238
|
+
```typescript
|
|
239
|
+
const topic = await client.prompts.execute('prm_abc123', {
|
|
240
|
+
provider: 'openai',
|
|
241
|
+
model: 'gpt-4o',
|
|
242
|
+
responseClass: TopicDto,
|
|
243
|
+
validate: false, // Skip class-validator validation
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Available Schema Decorators:**
|
|
248
|
+
|
|
249
|
+
| Decorator | Description |
|
|
250
|
+
|-----------|-------------|
|
|
251
|
+
| `@Description(text)` | Adds description to help LLM |
|
|
252
|
+
| `@Example(...values)` | Adds example values |
|
|
253
|
+
| `@Default(value)` | Sets default value |
|
|
254
|
+
| `@ArrayItems(Type)` | Sets array item type |
|
|
255
|
+
| `@Format(format)` | Sets string format (email, uri, uuid, date-time) |
|
|
256
|
+
| `@Nullable()` | Marks as nullable |
|
|
257
|
+
| `@SchemaMin(n)` | Minimum number value |
|
|
258
|
+
| `@SchemaMax(n)` | Maximum number value |
|
|
259
|
+
| `@SchemaMinLength(n)` | Minimum string length |
|
|
260
|
+
| `@SchemaMaxLength(n)` | Maximum string length |
|
|
261
|
+
| `@SchemaPattern(regex)` | Regex pattern for string |
|
|
262
|
+
| `@SchemaMinItems(n)` | Minimum array length |
|
|
263
|
+
| `@SchemaMaxItems(n)` | Maximum array length |
|
|
264
|
+
| `@SchemaEnum(values)` | Allowed enum values |
|
|
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
|
+
|
|
177
376
|
### Models
|
|
178
377
|
|
|
179
378
|
#### List All Models
|
|
@@ -268,28 +467,48 @@ console.log(response.content);
|
|
|
268
467
|
|
|
269
468
|
## Error Handling
|
|
270
469
|
|
|
271
|
-
The SDK provides typed errors for different failure scenarios
|
|
470
|
+
The SDK provides typed errors for different failure scenarios:
|
|
272
471
|
|
|
273
472
|
```typescript
|
|
274
473
|
import {
|
|
275
474
|
SynovaCloudSdk,
|
|
276
|
-
|
|
277
|
-
|
|
475
|
+
ExecutionSynovaError,
|
|
476
|
+
ValidationSynovaError,
|
|
278
477
|
AuthSynovaError,
|
|
279
478
|
NotFoundSynovaError,
|
|
280
479
|
RateLimitSynovaError,
|
|
281
480
|
ServerSynovaError,
|
|
282
481
|
TimeoutSynovaError,
|
|
283
482
|
NetworkSynovaError,
|
|
483
|
+
ApiSynovaError,
|
|
284
484
|
} from '@synova-cloud/sdk';
|
|
285
485
|
|
|
286
486
|
try {
|
|
287
487
|
const response = await client.prompts.execute('prm_abc123', {
|
|
288
488
|
provider: 'openai',
|
|
289
489
|
model: 'gpt-4o',
|
|
290
|
-
|
|
490
|
+
responseClass: TopicDto,
|
|
291
491
|
});
|
|
292
492
|
} catch (error) {
|
|
493
|
+
// LLM execution error (rate limit, invalid key, context too long, etc.)
|
|
494
|
+
if (error instanceof ExecutionSynovaError) {
|
|
495
|
+
console.error(`LLM error [${error.code}]: ${error.message}`);
|
|
496
|
+
console.error(`Provider: ${error.provider}`);
|
|
497
|
+
console.error(`Retryable: ${error.retryable}`);
|
|
498
|
+
if (error.retryAfterMs) {
|
|
499
|
+
console.error(`Retry after: ${error.retryAfterMs}ms`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Validation error (response doesn't match class-validator constraints)
|
|
504
|
+
if (error instanceof ValidationSynovaError) {
|
|
505
|
+
console.error('Validation failed:');
|
|
506
|
+
for (const v of error.violations) {
|
|
507
|
+
console.error(` ${v.property}: ${Object.values(v.constraints).join(', ')}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// API errors
|
|
293
512
|
if (error instanceof AuthSynovaError) {
|
|
294
513
|
console.error('Invalid API key');
|
|
295
514
|
} else if (error instanceof NotFoundSynovaError) {
|
|
@@ -303,10 +522,8 @@ try {
|
|
|
303
522
|
} else if (error instanceof NetworkSynovaError) {
|
|
304
523
|
console.error(`Network error: ${error.message}`);
|
|
305
524
|
} else if (error instanceof ApiSynovaError) {
|
|
306
|
-
// All API errors have these properties:
|
|
307
525
|
console.error(`API error [${error.code}]: ${error.message}`);
|
|
308
526
|
console.error(`Request ID: ${error.requestId}`);
|
|
309
|
-
console.error(`Details:`, error.details);
|
|
310
527
|
}
|
|
311
528
|
}
|
|
312
529
|
```
|
|
@@ -412,14 +629,25 @@ import type {
|
|
|
412
629
|
ISynovaPromptVariable,
|
|
413
630
|
ISynovaGetPromptOptions,
|
|
414
631
|
// Execution
|
|
415
|
-
ISynovaExecuteOptions,
|
|
416
|
-
|
|
417
|
-
|
|
632
|
+
ISynovaExecuteOptions, // includes sessionId
|
|
633
|
+
ISynovaExecuteTypedOptions,
|
|
634
|
+
ISynovaExecuteResponse, // includes spanDataId, traceId, spanId
|
|
635
|
+
ISynovaExecutionUsage,
|
|
418
636
|
ISynovaExecutionError,
|
|
419
637
|
// Messages
|
|
420
638
|
ISynovaMessage,
|
|
421
639
|
TSynovaMessageRole,
|
|
422
640
|
TSynovaResponseType,
|
|
641
|
+
// Spans
|
|
642
|
+
ISynovaSpan,
|
|
643
|
+
ISynovaSpanData,
|
|
644
|
+
ISynovaCreateSpanOptions,
|
|
645
|
+
ISynovaEndSpanOptions,
|
|
646
|
+
ISynovaWrapOptions,
|
|
647
|
+
ISynovaWrapToolOptions,
|
|
648
|
+
TSynovaSpanType,
|
|
649
|
+
TSynovaSpanStatus,
|
|
650
|
+
TSynovaSpanLevel,
|
|
423
651
|
// Files
|
|
424
652
|
ISynovaFileAttachment,
|
|
425
653
|
ISynovaFileThumbnails,
|
|
@@ -435,6 +663,13 @@ import type {
|
|
|
435
663
|
ISynovaModelsResponse,
|
|
436
664
|
ISynovaListModelsOptions,
|
|
437
665
|
TSynovaModelType,
|
|
666
|
+
// Schema
|
|
667
|
+
IJsonSchema,
|
|
668
|
+
TJsonSchemaType,
|
|
669
|
+
TJsonSchemaFormat,
|
|
670
|
+
TClassConstructor,
|
|
671
|
+
// Errors
|
|
672
|
+
IValidationViolation,
|
|
438
673
|
} from '@synova-cloud/sdk';
|
|
439
674
|
```
|
|
440
675
|
|
|
@@ -452,6 +687,15 @@ const client = new SynovaCloudSdk('your-api-key');
|
|
|
452
687
|
|
|
453
688
|
- Node.js 18+ (uses native `fetch`)
|
|
454
689
|
|
|
690
|
+
### Optional Peer Dependencies
|
|
691
|
+
|
|
692
|
+
For structured output with typed responses:
|
|
693
|
+
```bash
|
|
694
|
+
npm install class-validator class-transformer
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
These are optional - the SDK works without them, but `responseClass` feature requires them.
|
|
698
|
+
|
|
455
699
|
## License
|
|
456
700
|
|
|
457
701
|
MIT
|