@proveanything/smartlinks 1.7.6 → 1.7.8

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/docs/ai.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # SmartLinks AI
2
2
 
3
- Complete guide to using AI capabilities in the SmartLinks SDK, including chat completions, RAG (Retrieval-Augmented Generation), and voice integration.
3
+ Build AI-powered SmartLinks experiences with a practical SDK guide for responses, chat, product assistants, streaming, voice, and real-world integration patterns.
4
4
 
5
5
  ---
6
6
 
@@ -9,6 +9,7 @@ Complete guide to using AI capabilities in the SmartLinks SDK, including chat co
9
9
  - [Overview](#overview)
10
10
  - [Quick Start](#quick-start)
11
11
  - [Authentication](#authentication)
12
+ - [Responses API](#responses-api)
12
13
  - [Chat Completions](#chat-completions)
13
14
  - [RAG: Product Assistants](#rag-product-assistants)
14
15
  - [Voice Integration](#voice-integration)
@@ -25,12 +26,32 @@ Complete guide to using AI capabilities in the SmartLinks SDK, including chat co
25
26
 
26
27
  ## Overview
27
28
 
28
- SmartLinks AI provides four main capabilities:
29
+ This guide is written for SDK users building real products, not backend operators. It focuses on the public SDK surface, recommended starting points, and examples you can adapt directly.
29
30
 
30
- 1. **Chat Completions** - OpenAI-compatible text generation with streaming and tool calling
31
- 2. **RAG (Retrieval-Augmented Generation)** - Document-grounded Q&A for product assistants
32
- 3. **Voice Integration** - Voice-to-text and text-to-voice for hands-free interaction
33
- 4. **Podcast Generation** - NotebookLM-style multi-voice conversational podcasts from documents
31
+ ### Start with the path that matches your job
32
+
33
+ | If you want to... | Start here |
34
+ |---|---|
35
+ | Build a new AI workflow | Use [Responses API](#responses-api) |
36
+ | Add compatibility with existing chat clients | Use [Chat Completions](#chat-completions) |
37
+ | Build a product/manual assistant | Use [RAG: Product Assistants](#rag-product-assistants) |
38
+ | Add spoken input/output | Use [Voice Integration](#voice-integration) |
39
+ | Add progressive rendering | Use [Streaming Responses](#streaming-responses) or [Streaming Chat](#streaming-chat) |
40
+
41
+ ### Recommended starting points
42
+
43
+ - New AI features: start with `ai.chat.responses.create(...)`
44
+ - Product/manual assistants: start with `ai.public.chat(...)`
45
+ - Existing OpenAI-style clients: use `ai.chat.completions.create(...)`
46
+ - Real-time voice: use `ai.public.getToken(...)` and your provider's live client
47
+
48
+ SmartLinks AI provides five main capabilities:
49
+
50
+ 1. **Responses API** - Preferred API for agentic workflows, multimodal inputs, and tool-driven responses
51
+ 2. **Chat Completions** - OpenAI-compatible text generation with streaming and tool calling
52
+ 3. **RAG (Retrieval-Augmented Generation)** - Document-grounded Q&A for product assistants
53
+ 4. **Voice Integration** - Voice-to-text and text-to-voice for hands-free interaction
54
+ 5. **Podcast Generation** - NotebookLM-style multi-voice conversational podcasts from documents
34
55
 
35
56
  ### Key Features
36
57
 
@@ -40,6 +61,7 @@ SmartLinks AI provides four main capabilities:
40
61
  - ✅ Session management for conversations
41
62
  - ✅ Voice input/output helpers
42
63
  - ✅ Tool/function calling support
64
+ - ✅ OpenAI-style Responses API support
43
65
  - ✅ Document indexing and retrieval
44
66
  - ✅ Customizable assistant behavior
45
67
 
@@ -47,6 +69,10 @@ SmartLinks AI provides four main capabilities:
47
69
 
48
70
  ## Quick Start
49
71
 
72
+ If you're only reading one section, start here. The three snippets below cover the most common public SDK use cases.
73
+
74
+ ### 1. Generate a response
75
+
50
76
  ```typescript
51
77
  import { initializeApi, ai } from '@proveanything/smartlinks';
52
78
 
@@ -56,15 +82,44 @@ initializeApi({
56
82
  apiKey: process.env.SMARTLINKS_API_KEY // Required for admin endpoints
57
83
  });
58
84
 
59
- // Simple chat completion
60
- const response = await ai.chat.completions.create('my-collection', {
85
+ // Preferred: create a response
86
+ const response = await ai.chat.responses.create('my-collection', {
61
87
  model: 'google/gemini-2.5-flash',
62
- messages: [
63
- { role: 'user', content: 'Hello!' }
64
- ]
88
+ input: 'Summarize the key safety steps for descaling a coffee maker.'
65
89
  });
66
90
 
67
- console.log(response.choices[0].message.content);
91
+ console.log(response.output_text);
92
+ ```
93
+
94
+ ### 2. Build a product assistant
95
+
96
+ ```typescript
97
+ import { initializeApi, ai } from '@proveanything/smartlinks';
98
+
99
+ initializeApi({ baseURL: 'https://smartlinks.app/api/v1' });
100
+
101
+ const answer = await ai.public.chat('my-collection', {
102
+ productId: 'coffee-maker-deluxe',
103
+ userId: 'user-123',
104
+ message: 'How do I descale this machine?'
105
+ });
106
+
107
+ console.log(answer.message);
108
+ ```
109
+
110
+ ### 3. Stream output into your UI
111
+
112
+ ```typescript
113
+ const stream = await ai.chat.responses.create('my-collection', {
114
+ input: 'Write a launch checklist for a new product page.',
115
+ stream: true
116
+ });
117
+
118
+ for await (const event of stream) {
119
+ if (event.type === 'response.output_text.delta') {
120
+ updateUi(event.delta);
121
+ }
122
+ }
68
123
  ```
69
124
 
70
125
  ---
@@ -90,7 +145,7 @@ Public endpoints don't require an API key but are rate-limited by `userId`:
90
145
 
91
146
  ```typescript
92
147
  // No API key needed
93
- const response = await ai.publicApi.chat({
148
+ const response = await ai.public.chat('my-collection', {
94
149
  productId: 'coffee-maker',
95
150
  userId: 'user-123',
96
151
  message: 'How do I clean this?'
@@ -99,9 +154,108 @@ const response = await ai.publicApi.chat({
99
154
 
100
155
  ---
101
156
 
157
+ ## Responses API
158
+
159
+ The Responses API is the recommended starting point for new integrations. Use it when you want a single endpoint for structured input, tool use, and streaming output.
160
+
161
+ ### Basic Response
162
+
163
+ ```typescript
164
+ const response = await ai.chat.responses.create('my-collection', {
165
+ model: 'google/gemini-2.5-flash',
166
+ input: 'Write a friendly two-sentence welcome for a product assistant.'
167
+ });
168
+
169
+ console.log(response.output_text);
170
+ ```
171
+
172
+ ### Multimessage Input
173
+
174
+ ```typescript
175
+ const response = await ai.chat.responses.create('my-collection', {
176
+ model: 'google/gemini-2.5-flash',
177
+ input: [
178
+ {
179
+ role: 'system',
180
+ content: [
181
+ { type: 'input_text', text: 'You are a concise support assistant.' }
182
+ ]
183
+ },
184
+ {
185
+ role: 'user',
186
+ content: [
187
+ { type: 'input_text', text: 'Give me three troubleshooting steps for a grinder that will not start.' }
188
+ ]
189
+ }
190
+ ]
191
+ });
192
+
193
+ console.log(response.output_text);
194
+ ```
195
+
196
+ ### Streaming Responses
197
+
198
+ When you pass `stream: true`, the SDK returns an `AsyncIterable` of SSE events instead of a final JSON object. You do not need to parse raw SSE frames yourself — just iterate with `for await...of`.
199
+
200
+ ```typescript
201
+ const result = await ai.chat.responses.create('my-collection', {
202
+ input: 'Summarize the manual',
203
+ stream: true
204
+ });
205
+
206
+ for await (const event of result) {
207
+ if (event.type === 'response.output_text.delta') {
208
+ process.stdout.write(event.delta);
209
+ }
210
+ }
211
+ ```
212
+
213
+ If you omit `stream: true`, the same method returns the final `ResponsesResult` object instead.
214
+
215
+ ```typescript
216
+ const stream = await ai.chat.responses.create('my-collection', {
217
+ model: 'google/gemini-2.5-flash',
218
+ input: 'Explain how to descale an espresso machine step by step.',
219
+ stream: true
220
+ });
221
+
222
+ for await (const event of stream) {
223
+ if (event.type === 'response.output_text.delta') {
224
+ process.stdout.write(event.delta);
225
+ }
226
+ }
227
+ ```
228
+
229
+ ### Tool Calling
230
+
231
+ ```typescript
232
+ const response = await ai.chat.responses.create('my-collection', {
233
+ model: 'google/gemini-2.5-flash',
234
+ input: 'What is the weather in Paris?',
235
+ tools: [
236
+ {
237
+ type: 'function',
238
+ name: 'get_weather',
239
+ description: 'Get the current weather for a city',
240
+ parameters: {
241
+ type: 'object',
242
+ properties: {
243
+ location: { type: 'string' }
244
+ },
245
+ required: ['location']
246
+ }
247
+ }
248
+ ]
249
+ });
250
+
251
+ console.log(response.output);
252
+ ```
253
+
254
+ ---
255
+
102
256
  ## Chat Completions
103
257
 
104
- OpenAI-compatible chat completions with streaming and tool calling support.
258
+ OpenAI-compatible chat completions with streaming and tool calling support. Use this for compatibility with existing Chat Completions integrations; prefer the Responses API for new agentic features.
105
259
 
106
260
  ### Basic Chat
107
261
 
@@ -122,6 +276,12 @@ console.log(response.choices[0].message.content);
122
276
 
123
277
  Stream responses in real-time for better UX:
124
278
 
279
+ - Set `stream: true`
280
+ - The SDK returns an `AsyncIterable<ChatCompletionChunk>`
281
+ - Iterate over chunks with `for await...of`
282
+ - Read incremental text from `chunk.choices[0]?.delta?.content`
283
+ - If `stream` is omitted or `false`, the method returns the normal `ChatCompletionResponse`
284
+
125
285
  ```typescript
126
286
  const stream = await ai.chat.completions.create('my-collection', {
127
287
  model: 'google/gemini-2.5-flash',
@@ -188,6 +348,12 @@ if (toolCall) {
188
348
  // List all available models
189
349
  const models = await ai.models.list('my-collection');
190
350
 
351
+ // Or filter by provider / capability
352
+ const googleVisionModels = await ai.models.list('my-collection', {
353
+ provider: 'google',
354
+ capability: 'vision'
355
+ });
356
+
191
357
  models.data.forEach(model => {
192
358
  console.log(`${model.name}`);
193
359
  console.log(` Provider: ${model.provider}`);
@@ -268,7 +434,7 @@ Users can chat with the product assistant without authentication:
268
434
 
269
435
  ```typescript
270
436
  // First question
271
- const response = await ai.publicApiApi.chat('my-collection', {
437
+ const response = await ai.public.chat('my-collection', {
272
438
  productId: 'coffee-maker-deluxe',
273
439
  userId: 'user-123',
274
440
  message: 'How do I descale my coffee maker?'
@@ -328,8 +494,31 @@ console.log('Rate-limited users:', stats.rateLimitedUsers);
328
494
 
329
495
  Enable voice input and output for hands-free interaction.
330
496
 
497
+ ### Voice Patterns
498
+
499
+ The SDK supports three practical voice patterns:
500
+
501
+ | Pattern | Best For | SDK Building Blocks |
502
+ |---------|----------|---------------------|
503
+ | Voice → Text → AI → Text | Manual helper Q&A, troubleshooting steps | `ai.voice.listen()` + `ai.public.chat()` |
504
+ | Voice → Text → AI → Voice | Hands-free assistants, accessibility | `ai.voice.listen()` + `ai.public.chat()` + `ai.voice.speak()` or `ai.tts.generate()` |
505
+ | Real-time Voice | Low-latency spoken conversation | `ai.public.getToken()` + Gemini Live client |
506
+
507
+ ### Current SDK Support
508
+
509
+ - `ai.voice.listen()` and `ai.voice.speak()` are browser helpers built on the Web Speech APIs.
510
+ - `ai.public.getToken()` generates ephemeral tokens for Gemini Live sessions.
511
+ - `ai.tts.generate()` supports server-side text-to-speech generation.
512
+ - The SDK does not currently expose a first-class transcription endpoint like Whisper; if you need that flow, implement it as your own backend endpoint and feed the transcribed text into `ai.public.chat()` or `ai.chat.responses.create()`.
513
+
514
+ ### Recommended Approach
515
+
516
+ For product assistants and RAG-backed support, start with Voice → Text → AI → Text/Voice. It gives you the best control over retrieval, session history, and cost. Use Gemini Live when low-latency spoken conversation matters more than deep document grounding.
517
+
331
518
  ### Browser Voice Helpers
332
519
 
520
+ These helpers are browser-only and rely on native speech recognition / speech synthesis support.
521
+
333
522
  ```typescript
334
523
  // Check if voice is supported
335
524
  if (ai.voice.isSupported()) {
@@ -396,7 +585,7 @@ class ProductVoiceAssistant {
396
585
  }
397
586
 
398
587
  async getRemainingQuestions(): Promise<number> {
399
- const status = await ai.publicApiApi.getRateLimit(this.collectionId, this.userId);
588
+ const status = await ai.public.getRateLimit(this.collectionId, this.userId);
400
589
  return status.remaining;
401
590
  }
402
591
  }
@@ -417,9 +606,11 @@ console.log(`${remaining} questions remaining`);
417
606
 
418
607
  Generate ephemeral tokens for Gemini Live (multimodal voice):
419
608
 
609
+ Use this path for real-time voice sessions. The SDK only issues the short-lived token; the actual live connection is made with the provider client.
610
+
420
611
  ```typescript
421
612
  // Generate token for voice session
422
- const token = await ai.publicApi.getToken('my-collection', {
613
+ const token = await ai.public.getToken('my-collection', {
423
614
  settings: {
424
615
  ttl: 3600, // 1 hour
425
616
  voice: 'alloy',
@@ -434,6 +625,17 @@ console.log('Expires at:', new Date(token.expiresAt));
434
625
  // (See Google's Gemini documentation)
435
626
  ```
436
627
 
628
+ ### Voice + RAG Guidance
629
+
630
+ For document-grounded assistants, prefer this pattern:
631
+
632
+ 1. Capture voice with `ai.voice.listen()` or your own transcription flow.
633
+ 2. Send the transcribed text to `ai.public.chat()`.
634
+ 3. Render the text response for readability.
635
+ 4. Optionally speak the answer with `ai.voice.speak()` or `ai.tts.generate()`.
636
+
637
+ This is usually a better fit for manuals and procedural guidance than trying to use a live voice session as the primary retrieval layer.
638
+
437
639
  ---
438
640
 
439
641
  ## Podcast Generation
@@ -917,7 +1119,7 @@ const audioBlob = await ai.tts.generate('my-collection', {
917
1119
 
918
1120
  ### Public Endpoints
919
1121
 
920
- #### `ai.publicApi.chat(collectionId, request)`
1122
+ #### `ai.public.chat(collectionId, request)`
921
1123
 
922
1124
  Chat with product assistant (no auth required).
923
1125
 
@@ -930,7 +1132,7 @@ Chat with product assistant (no auth required).
930
1132
 
931
1133
  ---
932
1134
 
933
- #### `ai.publicApi.getSession(collectionId, sessionId)`
1135
+ #### `ai.public.getSession(collectionId, sessionId)`
934
1136
 
935
1137
  Get conversation history.
936
1138
 
@@ -938,7 +1140,7 @@ Get conversation history.
938
1140
 
939
1141
  ---
940
1142
 
941
- #### `ai.publicApi.clearSession(collectionId, sessionId)`
1143
+ #### `ai.public.clearSession(collectionId, sessionId)`
942
1144
 
943
1145
  Clear conversation history.
944
1146
 
@@ -946,7 +1148,7 @@ Clear conversation history.
946
1148
 
947
1149
  ---
948
1150
 
949
- #### `ai.publicApi.getRateLimit(collectionId, userId)`
1151
+ #### `ai.public.getRateLimit(collectionId, userId)`
950
1152
 
951
1153
  Check rate limit status.
952
1154
 
@@ -954,7 +1156,7 @@ Check rate limit status.
954
1156
 
955
1157
  ---
956
1158
 
957
- #### `ai.publicApi.getToken(collectionId, request)`
1159
+ #### `ai.public.getToken(collectionId, request)`
958
1160
 
959
1161
  Generate ephemeral token for Gemini Live.
960
1162
 
@@ -985,7 +1187,7 @@ async function createProductFAQ() {
985
1187
  });
986
1188
 
987
1189
  // 3. Answer user questions
988
- const answer = await ai.publicApi.chat(collectionId, {
1190
+ const answer = await ai.public.chat(collectionId, {
989
1191
  productId,
990
1192
  userId: 'user-123',
991
1193
  message: 'How do I make espresso?'
@@ -1032,7 +1234,7 @@ async function chatConversation() {
1032
1234
  const productId = 'coffee-maker-deluxe';
1033
1235
 
1034
1236
  // Question 1
1035
- const a1 = await ai.publicApi.chat(collectionId, {
1237
+ const a1 = await ai.public.chat(collectionId, {
1036
1238
  productId,
1037
1239
  userId,
1038
1240
  message: 'How do I clean the machine?',
@@ -1041,7 +1243,7 @@ async function chatConversation() {
1041
1243
  console.log('A1:', a1.message);
1042
1244
 
1043
1245
  // Question 2 (references previous context)
1044
- const a2 = await ai.publicApi.chat(collectionId, {
1246
+ const a2 = await ai.public.chat(collectionId, {
1045
1247
  productId,
1046
1248
  userId,
1047
1249
  message: 'How often should I do that?',
@@ -1050,7 +1252,7 @@ async function chatConversation() {
1050
1252
  console.log('A2:', a2.message);
1051
1253
 
1052
1254
  // Get full history
1053
- const session = await ai.publicApi.getSession(collectionId, sessionId);
1255
+ const session = await ai.public.getSession(collectionId, sessionId);
1054
1256
  console.log('Full conversation:', session.messages);
1055
1257
  }
1056
1258
  ```
@@ -1075,7 +1277,7 @@ export function useProductAssistant(
1075
1277
  setError(null);
1076
1278
 
1077
1279
  try {
1078
- const response = await ai.publicApi.chat(collectionId, {
1280
+ const response = await ai.public.chat(collectionId, {
1079
1281
  productId,
1080
1282
  userId,
1081
1283
  message
@@ -1251,7 +1453,7 @@ import { SmartLinksAIError } from '@proveanything/smartlinks';
1251
1453
 
1252
1454
  async function robustChat() {
1253
1455
  try {
1254
- const response = await ai.publicApi.chat('my-collection', {
1456
+ const response = await ai.public.chat('my-collection', {
1255
1457
  productId: 'coffee-maker',
1256
1458
  userId: 'user-123',
1257
1459
  message: 'Help!'
@@ -1295,7 +1497,7 @@ async function chatWithRetry(
1295
1497
 
1296
1498
  while (retries < maxRetries) {
1297
1499
  try {
1298
- return await ai.publicApi.chat('my-collection', request);
1500
+ return await ai.public.chat('my-collection', request);
1299
1501
  } catch (error) {
1300
1502
  if (error instanceof SmartLinksAIError && error.isRateLimitError()) {
1301
1503
  if (retries === maxRetries - 1) throw error;
@@ -1345,7 +1547,7 @@ X-RateLimit-Reset: 1707300000000
1345
1547
 
1346
1548
  ```typescript
1347
1549
  // Check rate limit status before making requests
1348
- const status = await ai.publicApi.getRateLimit('my-collection', 'user-123');
1550
+ const status = await ai.public.getRateLimit('my-collection', 'user-123');
1349
1551
 
1350
1552
  console.log('Used:', status.used);
1351
1553
  console.log('Remaining:', status.remaining);
@@ -1353,7 +1555,7 @@ console.log('Resets at:', new Date(status.resetAt));
1353
1555
 
1354
1556
  if (status.remaining > 0) {
1355
1557
  // Safe to make request
1356
- await ai.publicApi.chat(/* ... */);
1558
+ await ai.public.chat(/* ... */);
1357
1559
  } else {
1358
1560
  // Show user when they can ask again
1359
1561
  console.log('Rate limit reached. Try again at:', status.resetAt);
@@ -1443,7 +1645,7 @@ Show clear feedback to users:
1443
1645
 
1444
1646
  ```typescript
1445
1647
  try {
1446
- await ai.publicApi.chat('my-collection', {...});
1648
+ await ai.public.chat('my-collection', {...});
1447
1649
  } catch (error) {
1448
1650
  if (error instanceof SmartLinksAIError && error.isRateLimitError()) {
1449
1651
  const resetTime = new Date(error.resetAt!);
@@ -1649,7 +1851,7 @@ interface AIContentResponse {
1649
1851
 
1650
1852
  /**
1651
1853
  * Pre-built RAG configuration. When provided, the assistant can use
1652
- * SL.ai.public.chat() to ground answers in indexed product documents.
1854
+ * SL.ai.public.chat() to ground answers in indexed product documents.
1653
1855
  */
1654
1856
  ragHint?: {
1655
1857
  /** The product ID whose indexed documents should be queried */
@@ -1669,7 +1871,7 @@ interface AIContentResponse {
1669
1871
  /**
1670
1872
  * How the assistant should use this content:
1671
1873
  * - 'context' (default): inject text into the system prompt
1672
- * - 'rag': use ragHint to query indexed docs via SL.ai.public.chat()
1874
+ * - 'rag': use ragHint to query indexed docs via SL.ai.public.chat()
1673
1875
  * - 'hybrid': inject text as context AND ground answers via RAG
1674
1876
  */
1675
1877
  strategy?: 'context' | 'rag' | 'hybrid';