@qelos/sdk 3.11.3 → 3.11.4
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/ai/chat.d.ts +29 -0
- package/dist/ai/chat.js +146 -0
- package/dist/ai/chat.js.map +1 -0
- package/dist/ai/index.d.ts +4 -0
- package/dist/ai/index.js +5 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/rag.d.ts +33 -0
- package/dist/ai/rag.js +38 -0
- package/dist/ai/rag.js.map +1 -0
- package/dist/ai/threads.d.ts +33 -0
- package/dist/ai/threads.js +39 -0
- package/dist/ai/threads.js.map +1 -0
- package/dist/ai/types.d.ts +91 -0
- package/dist/ai/types.js +2 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/ai.d.ts +62 -76
- package/dist/ai.js +74 -134
- package/dist/ai.js.map +1 -1
- package/examples/ai-sdk-usage.ts +170 -0
- package/package.json +1 -1
- package/src/ai/chat.ts +187 -0
- package/src/ai/index.ts +4 -0
- package/src/ai/rag.ts +72 -0
- package/src/ai/threads.ts +56 -0
- package/src/ai/types.ts +101 -0
- package/src/ai-sdk-migration-guide.md +172 -0
- package/src/ai.ts +107 -223
package/dist/ai.js
CHANGED
|
@@ -1,175 +1,115 @@
|
|
|
1
1
|
import BaseSDK from './base-sdk';
|
|
2
|
+
import ThreadsSDK from './ai/threads';
|
|
3
|
+
import ChatSDK from './ai/chat';
|
|
4
|
+
import RAGSDK from './ai/rag';
|
|
5
|
+
/**
|
|
6
|
+
* Qelos AI SDK - Main SDK class with three sub-SDKs
|
|
7
|
+
*
|
|
8
|
+
* This class provides access to AI functionality through three sub-SDKs:
|
|
9
|
+
* - `threads`: Thread CRUD operations for managing conversation threads
|
|
10
|
+
* - `chat`: Chat completion operations (streaming and non-streaming)
|
|
11
|
+
* - `rag`: Vector storage management for Retrieval-Augmented Generation
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const sdk = new QelosSDK({
|
|
16
|
+
* appUrl: 'https://your-qelos-instance.com',
|
|
17
|
+
* fetch: globalThis.fetch,
|
|
18
|
+
* accessToken: 'your-token'
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Create a thread
|
|
22
|
+
* const thread = await sdk.ai.threads.create({ integration: 'id' });
|
|
23
|
+
*
|
|
24
|
+
* // Stream chat
|
|
25
|
+
* const stream = await sdk.ai.chat.stream('id', { messages: [...] });
|
|
26
|
+
* for await (const chunk of sdk.ai.chat.parseSSEStream(stream)) {
|
|
27
|
+
* console.log(chunk);
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* // Upload RAG content
|
|
31
|
+
* await sdk.ai.rag.uploadContent('sourceId', { content: '...' });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
// Main AI SDK with sub-SDKs
|
|
2
35
|
export default class QlAI extends BaseSDK {
|
|
3
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Threads sub-SDK for managing conversation threads
|
|
38
|
+
*/
|
|
39
|
+
threads;
|
|
40
|
+
/**
|
|
41
|
+
* Chat sub-SDK for chat completion operations
|
|
42
|
+
*/
|
|
43
|
+
chat;
|
|
44
|
+
/**
|
|
45
|
+
* RAG sub-SDK for vector storage management
|
|
46
|
+
*/
|
|
47
|
+
rag;
|
|
48
|
+
/**
|
|
49
|
+
* Initialize the AI SDK with sub-SDKs
|
|
50
|
+
*
|
|
51
|
+
* @param options - SDK configuration options
|
|
52
|
+
*/
|
|
4
53
|
constructor(options) {
|
|
5
54
|
super(options);
|
|
55
|
+
this.threads = new ThreadsSDK(options);
|
|
56
|
+
this.chat = new ChatSDK(options);
|
|
57
|
+
this.rag = new RAGSDK(options);
|
|
6
58
|
}
|
|
7
|
-
//
|
|
59
|
+
// Legacy methods for backward compatibility
|
|
8
60
|
/**
|
|
9
|
-
*
|
|
61
|
+
* @deprecated Use sdk.threads.create() instead
|
|
10
62
|
*/
|
|
11
63
|
createThread(data) {
|
|
12
|
-
return this.
|
|
13
|
-
method: 'POST',
|
|
14
|
-
headers: { 'content-type': 'application/json' },
|
|
15
|
-
body: JSON.stringify(data)
|
|
16
|
-
});
|
|
64
|
+
return this.threads.create(data);
|
|
17
65
|
}
|
|
18
66
|
/**
|
|
19
|
-
*
|
|
67
|
+
* @deprecated Use sdk.threads.getOne() instead
|
|
20
68
|
*/
|
|
21
69
|
getThread(threadId) {
|
|
22
|
-
return this.
|
|
70
|
+
return this.threads.getOne(threadId);
|
|
23
71
|
}
|
|
24
72
|
/**
|
|
25
|
-
*
|
|
73
|
+
* @deprecated Use sdk.threads.list() instead
|
|
26
74
|
*/
|
|
27
75
|
listThreads(options) {
|
|
28
|
-
|
|
29
|
-
return this.callJsonApi(`${this.relativePath}/threads${queryParams}`);
|
|
76
|
+
return this.threads.list(options);
|
|
30
77
|
}
|
|
31
78
|
/**
|
|
32
|
-
*
|
|
79
|
+
* @deprecated Use sdk.threads.delete() instead
|
|
33
80
|
*/
|
|
34
81
|
deleteThread(threadId) {
|
|
35
|
-
return this.
|
|
36
|
-
method: 'DELETE'
|
|
37
|
-
});
|
|
82
|
+
return this.threads.delete(threadId);
|
|
38
83
|
}
|
|
39
|
-
// Chat Completion Operations
|
|
40
84
|
/**
|
|
41
|
-
*
|
|
85
|
+
* @deprecated Use sdk.chat.chat() instead
|
|
42
86
|
*/
|
|
43
|
-
|
|
44
|
-
return this.
|
|
45
|
-
method: 'POST',
|
|
46
|
-
headers: { 'content-type': 'application/json' },
|
|
47
|
-
body: JSON.stringify(options)
|
|
48
|
-
});
|
|
87
|
+
chatCompletion(integrationId, options) {
|
|
88
|
+
return this.chat.chat(integrationId, options);
|
|
49
89
|
}
|
|
50
90
|
/**
|
|
51
|
-
*
|
|
91
|
+
* @deprecated Use sdk.chat.chatInThread() instead
|
|
52
92
|
*/
|
|
53
|
-
|
|
54
|
-
return this.
|
|
55
|
-
method: 'POST',
|
|
56
|
-
headers: { 'content-type': 'application/json' },
|
|
57
|
-
body: JSON.stringify(options)
|
|
58
|
-
});
|
|
93
|
+
chatCompletionInThread(integrationId, threadId, options) {
|
|
94
|
+
return this.chat.chatInThread(integrationId, threadId, options);
|
|
59
95
|
}
|
|
60
96
|
/**
|
|
61
|
-
*
|
|
62
|
-
* Returns a ReadableStream for Server-Sent Events
|
|
97
|
+
* @deprecated Use sdk.chat.stream() instead
|
|
63
98
|
*/
|
|
64
99
|
async streamChat(integrationId, options) {
|
|
65
|
-
|
|
66
|
-
method: 'POST',
|
|
67
|
-
headers: {
|
|
68
|
-
'content-type': 'application/json',
|
|
69
|
-
'accept': 'text/event-stream'
|
|
70
|
-
},
|
|
71
|
-
body: JSON.stringify({ ...options, stream: true })
|
|
72
|
-
});
|
|
73
|
-
if (!response.ok) {
|
|
74
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
75
|
-
}
|
|
76
|
-
return response.body;
|
|
100
|
+
return this.chat.stream(integrationId, options);
|
|
77
101
|
}
|
|
78
102
|
/**
|
|
79
|
-
*
|
|
80
|
-
* Returns a ReadableStream for Server-Sent Events
|
|
103
|
+
* @deprecated Use sdk.chat.streamInThread() instead
|
|
81
104
|
*/
|
|
82
105
|
async streamChatInThread(integrationId, threadId, options) {
|
|
83
|
-
|
|
84
|
-
method: 'POST',
|
|
85
|
-
headers: {
|
|
86
|
-
'content-type': 'application/json',
|
|
87
|
-
'accept': 'text/event-stream'
|
|
88
|
-
},
|
|
89
|
-
body: JSON.stringify({ ...options, stream: true })
|
|
90
|
-
});
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
93
|
-
}
|
|
94
|
-
return response.body;
|
|
106
|
+
return this.chat.streamInThread(integrationId, threadId, options);
|
|
95
107
|
}
|
|
96
108
|
/**
|
|
97
|
-
*
|
|
109
|
+
* @deprecated Use sdk.chat.parseSSEStream() instead
|
|
98
110
|
*/
|
|
99
111
|
parseSSEStream(stream) {
|
|
100
|
-
|
|
101
|
-
const reader = stream.getReader();
|
|
102
|
-
const decoder = new TextDecoder();
|
|
103
|
-
let buffer = "";
|
|
104
|
-
try {
|
|
105
|
-
while (true) {
|
|
106
|
-
const { done, value } = await reader.read();
|
|
107
|
-
if (done)
|
|
108
|
-
break;
|
|
109
|
-
buffer += decoder.decode(value, { stream: true });
|
|
110
|
-
let lines = buffer.split(/\r?\n/);
|
|
111
|
-
buffer = lines.pop() || ""; // Keep incomplete line
|
|
112
|
-
for (const line of lines) {
|
|
113
|
-
if (line.startsWith('data: ')) {
|
|
114
|
-
const data = line.slice(6);
|
|
115
|
-
if (data === '[DONE]')
|
|
116
|
-
return;
|
|
117
|
-
try {
|
|
118
|
-
const parsed = JSON.parse(data);
|
|
119
|
-
const shouldContinue = onData(parsed);
|
|
120
|
-
if (shouldContinue === false) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
catch (e) {
|
|
125
|
-
// Skip invalid JSON
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
finally {
|
|
132
|
-
reader.releaseLock();
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
const processor = {
|
|
136
|
-
async *[Symbol.asyncIterator]() {
|
|
137
|
-
const reader = stream.getReader();
|
|
138
|
-
const decoder = new TextDecoder();
|
|
139
|
-
let buffer = "";
|
|
140
|
-
try {
|
|
141
|
-
while (true) {
|
|
142
|
-
const { done, value } = await reader.read();
|
|
143
|
-
if (done) {
|
|
144
|
-
break;
|
|
145
|
-
}
|
|
146
|
-
buffer += decoder.decode(value, { stream: true });
|
|
147
|
-
let lines = buffer.split(/\r?\n/);
|
|
148
|
-
buffer = lines.pop() || ""; // Keep incomplete line
|
|
149
|
-
for (const line of lines) {
|
|
150
|
-
if (line.startsWith('data: ')) {
|
|
151
|
-
const data = line.slice(6);
|
|
152
|
-
if (data === '[DONE]') {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
try {
|
|
156
|
-
const parsed = JSON.parse(data);
|
|
157
|
-
yield parsed;
|
|
158
|
-
}
|
|
159
|
-
catch (e) {
|
|
160
|
-
//
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
finally {
|
|
167
|
-
reader.releaseLock();
|
|
168
|
-
}
|
|
169
|
-
},
|
|
170
|
-
processManually: processStream
|
|
171
|
-
};
|
|
172
|
-
return processor;
|
|
112
|
+
return this.chat.parseSSEStream(stream);
|
|
173
113
|
}
|
|
174
114
|
}
|
|
175
115
|
//# sourceMappingURL=ai.js.map
|
package/dist/ai.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.js","sourceRoot":"","sources":["../src/ai.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"ai.js","sourceRoot":"","sources":["../src/ai.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,YAAY,CAAC;AACjC,OAAO,UAAU,MAAM,cAAc,CAAC;AACtC,OAAO,OAAO,MAAM,WAAW,CAAC;AAChC,OAAO,MAAM,MAAM,UAAU,CAAC;AA8B9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,4BAA4B;AAC5B,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,OAAO;IACvC;;OAEG;IACI,OAAO,CAAa;IAE3B;;OAEG;IACI,IAAI,CAAU;IAErB;;OAEG;IACI,GAAG,CAAS;IAEnB;;;;OAIG;IACH,YAAY,OAAwB;QAClC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,4CAA4C;IAE5C;;OAEG;IACH,YAAY,CAAC,IAA0B;QACrC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,QAAgB;QACxB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,OAQX;QACC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,QAAgB;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACH,cAAc,CACZ,aAAqB,EACrB,OAA+B;QAE/B,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,sBAAsB,CACpB,aAAqB,EACrB,QAAgB,EAChB,OAA+B;QAE/B,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CACd,aAAqB,EACrB,OAA+B;QAE/B,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,kBAAkB,CACtB,aAAqB,EACrB,QAAgB,EAChB,OAA+B;QAE/B,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,aAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,MAAkC;QAC/C,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAC1C,CAAC;CACF"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Example usage of the new Qelos AI SDK with sub-SDKs
|
|
2
|
+
|
|
3
|
+
import QelosSDK from '../src';
|
|
4
|
+
// Alternatively, you can import specific sub-SDKs:
|
|
5
|
+
// import { ThreadsSDK, ChatSDK, RAGSDK } from '../src/ai';
|
|
6
|
+
|
|
7
|
+
// Initialize the SDK
|
|
8
|
+
const sdk = new QelosSDK({
|
|
9
|
+
appUrl: 'https://your-qelos-instance.com',
|
|
10
|
+
fetch: globalThis.fetch || require('node-fetch'),
|
|
11
|
+
accessToken: 'your-access-token'
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// ===== THREADS SUB-SDK =====
|
|
15
|
+
async function threadExamples() {
|
|
16
|
+
// Create a new thread
|
|
17
|
+
const thread = await sdk.ai.threads.create({
|
|
18
|
+
integration: 'integration-id',
|
|
19
|
+
title: 'Customer Support Chat'
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Get a specific thread
|
|
23
|
+
const retrievedThread = await sdk.ai.threads.getOne(thread._id!);
|
|
24
|
+
|
|
25
|
+
// List threads with filters
|
|
26
|
+
const threads = await sdk.ai.threads.list({
|
|
27
|
+
integration: 'integration-id',
|
|
28
|
+
limit: 10,
|
|
29
|
+
page: 1
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Delete a thread
|
|
33
|
+
await sdk.ai.threads.delete(thread._id!);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ===== CHAT SUB-SDK =====
|
|
37
|
+
async function chatExamples() {
|
|
38
|
+
const integrationId = 'your-integration-id';
|
|
39
|
+
const messages = [
|
|
40
|
+
{ role: 'user', content: 'Hello, how can you help me?' }
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Non-streaming chat
|
|
44
|
+
const response = await sdk.ai.chat.chat(integrationId, {
|
|
45
|
+
messages,
|
|
46
|
+
model: 'gpt-4',
|
|
47
|
+
temperature: 0.7,
|
|
48
|
+
queryParams: { bypassAdmin: true } // Optional query params
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
console.log(response.choices[0].message.content);
|
|
52
|
+
|
|
53
|
+
// Streaming chat
|
|
54
|
+
const stream = await sdk.ai.chat.stream(integrationId, {
|
|
55
|
+
messages,
|
|
56
|
+
stream: true
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Parse the SSE stream
|
|
60
|
+
const processor = sdk.ai.chat.parseSSEStream(stream);
|
|
61
|
+
for await (const chunk of processor) {
|
|
62
|
+
if (chunk.choices?.[0]?.delta?.content) {
|
|
63
|
+
console.log(chunk.choices[0].delta.content);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Chat in a thread
|
|
68
|
+
const threadId = 'thread-id';
|
|
69
|
+
const threadResponse = await sdk.ai.chat.chatInThread(integrationId, threadId, {
|
|
70
|
+
messages
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Streaming chat in a thread
|
|
74
|
+
const threadStream = await sdk.ai.chat.streamInThread(integrationId, threadId, {
|
|
75
|
+
messages,
|
|
76
|
+
stream: true
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
for await (const chunk of sdk.ai.chat.parseSSEStream(threadStream)) {
|
|
80
|
+
console.log(chunk);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ===== RAG SUB-SDK =====
|
|
85
|
+
async function ragExamples() {
|
|
86
|
+
const sourceId = 'your-ai-source-id';
|
|
87
|
+
|
|
88
|
+
// Create vector storage
|
|
89
|
+
const storage = await sdk.ai.rag.createStorage(sourceId, {
|
|
90
|
+
integrationId: 'integration-id',
|
|
91
|
+
scope: 'thread', // 'thread' | 'user' | 'workspace' | 'tenant'
|
|
92
|
+
subjectId: 'thread-id', // Required for non-tenant scopes
|
|
93
|
+
expirationAfterDays: 30
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Upload content to storage
|
|
97
|
+
const uploadResult = await sdk.ai.rag.uploadContent(sourceId, {
|
|
98
|
+
integrationId: 'integration-id',
|
|
99
|
+
content: 'This is the content to be indexed for RAG.',
|
|
100
|
+
fileName: 'document.txt',
|
|
101
|
+
metadata: { type: 'documentation', category: 'help' }
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Upload JSON content
|
|
105
|
+
const jsonUpload = await sdk.ai.rag.uploadContent(sourceId, {
|
|
106
|
+
integrationId: 'integration-id',
|
|
107
|
+
content: { title: 'Product Info', description: '...' },
|
|
108
|
+
fileName: 'product-info.json'
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Clear specific files
|
|
112
|
+
const clearResult = await sdk.ai.rag.clearStorage(sourceId, {
|
|
113
|
+
integrationId: 'integration-id',
|
|
114
|
+
fileIds: [uploadResult.fileId]
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Clear all files (if no fileIds provided)
|
|
118
|
+
await sdk.ai.rag.clearStorage(sourceId, {
|
|
119
|
+
integrationId: 'integrationId'
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Get vector stores (internal API - server-side only)
|
|
123
|
+
const stores = await sdk.ai.rag.getVectorStores({
|
|
124
|
+
scope: 'thread',
|
|
125
|
+
subjectId: 'thread-id'
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ===== COMBINED EXAMPLE =====
|
|
130
|
+
async function combinedExample() {
|
|
131
|
+
// 1. Create a thread
|
|
132
|
+
const thread = await sdk.ai.threads.create({
|
|
133
|
+
integration: 'integration-id',
|
|
134
|
+
title: 'RAG-enabled Chat'
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// 2. Upload knowledge base content
|
|
138
|
+
await sdk.ai.rag.uploadContent('source-id', {
|
|
139
|
+
integrationId: 'integration-id',
|
|
140
|
+
content: 'Your knowledge base content here...',
|
|
141
|
+
fileName: 'knowledge-base.txt'
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// 3. Chat with RAG
|
|
145
|
+
const stream = await sdk.ai.chat.streamInThread(
|
|
146
|
+
'integration-id',
|
|
147
|
+
thread._id!,
|
|
148
|
+
{
|
|
149
|
+
messages: [
|
|
150
|
+
{ role: 'user', content: 'What do you know about the topic?' }
|
|
151
|
+
],
|
|
152
|
+
stream: true
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// 4. Process the response
|
|
157
|
+
for await (const chunk of sdk.ai.chat.parseSSEStream(stream)) {
|
|
158
|
+
if (chunk.choices?.[0]?.delta?.content) {
|
|
159
|
+
console.log(chunk.choices[0].delta.content);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Export for reference
|
|
165
|
+
export {
|
|
166
|
+
threadExamples,
|
|
167
|
+
chatExamples,
|
|
168
|
+
ragExamples,
|
|
169
|
+
combinedExample
|
|
170
|
+
};
|
package/package.json
CHANGED
package/src/ai/chat.ts
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import BaseSDK from '../base-sdk';
|
|
2
|
+
import { QelosSDKOptions } from '../types';
|
|
3
|
+
import {
|
|
4
|
+
IChatCompletionOptions,
|
|
5
|
+
IChatCompletionResponse,
|
|
6
|
+
ISSEStreamProcessor
|
|
7
|
+
} from '../ai';
|
|
8
|
+
|
|
9
|
+
export default class ChatSDK extends BaseSDK {
|
|
10
|
+
private relativePath = '/api/ai';
|
|
11
|
+
|
|
12
|
+
constructor(options: QelosSDKOptions) {
|
|
13
|
+
super(options);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a chat completion without a thread
|
|
18
|
+
*/
|
|
19
|
+
chat(
|
|
20
|
+
integrationId: string,
|
|
21
|
+
options: IChatCompletionOptions
|
|
22
|
+
): Promise<IChatCompletionResponse> {
|
|
23
|
+
const queryParams = this.getQueryParams(options.queryParams || {});
|
|
24
|
+
return this.callJsonApi<IChatCompletionResponse>(`${this.relativePath}/${integrationId}/chat-completion${queryParams}`, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { 'content-type': 'application/json' },
|
|
27
|
+
body: JSON.stringify(options)
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a chat completion with a thread
|
|
33
|
+
*/
|
|
34
|
+
chatInThread(
|
|
35
|
+
integrationId: string,
|
|
36
|
+
threadId: string,
|
|
37
|
+
options: IChatCompletionOptions
|
|
38
|
+
): Promise<IChatCompletionResponse> {
|
|
39
|
+
const queryParams = this.getQueryParams(options.queryParams || {});
|
|
40
|
+
return this.callJsonApi<IChatCompletionResponse>(
|
|
41
|
+
`${this.relativePath}/${integrationId}/chat-completion/${threadId}${queryParams}`,
|
|
42
|
+
{
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: { 'content-type': 'application/json' },
|
|
45
|
+
body: JSON.stringify(options)
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create a streaming chat completion without a thread
|
|
52
|
+
* Returns a ReadableStream for Server-Sent Events
|
|
53
|
+
*/
|
|
54
|
+
async stream(
|
|
55
|
+
integrationId: string,
|
|
56
|
+
options: IChatCompletionOptions
|
|
57
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
58
|
+
const queryParams = this.getQueryParams({ ...options.queryParams, stream: true });
|
|
59
|
+
const response = await this.callApi(`${this.relativePath}/${integrationId}/chat-completion${queryParams}`, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: {
|
|
62
|
+
'content-type': 'application/json',
|
|
63
|
+
'accept': 'text/event-stream'
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify({ ...options, stream: true })
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return response.body!;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Create a streaming chat completion with a thread
|
|
77
|
+
* Returns a ReadableStream for Server-Sent Events
|
|
78
|
+
*/
|
|
79
|
+
async streamInThread(
|
|
80
|
+
integrationId: string,
|
|
81
|
+
threadId: string,
|
|
82
|
+
options: IChatCompletionOptions
|
|
83
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
84
|
+
const queryParams = this.getQueryParams({ ...options.queryParams, stream: true });
|
|
85
|
+
const response = await this.callApi(
|
|
86
|
+
`${this.relativePath}/${integrationId}/chat-completion/${threadId}${queryParams}`,
|
|
87
|
+
{
|
|
88
|
+
method: 'POST',
|
|
89
|
+
headers: {
|
|
90
|
+
'content-type': 'application/json',
|
|
91
|
+
'accept': 'text/event-stream'
|
|
92
|
+
},
|
|
93
|
+
body: JSON.stringify({ ...options, stream: true })
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return response.body!;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Helper method to parse Server-Sent Events stream
|
|
106
|
+
*/
|
|
107
|
+
parseSSEStream(stream: ReadableStream<Uint8Array>): ISSEStreamProcessor {
|
|
108
|
+
const processStream = async (onData: (data: any) => void | boolean): Promise<void> => {
|
|
109
|
+
const reader = stream.getReader();
|
|
110
|
+
const decoder = new TextDecoder();
|
|
111
|
+
let buffer = "";
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
while (true) {
|
|
115
|
+
const { done, value } = await reader.read();
|
|
116
|
+
if (done) break;
|
|
117
|
+
|
|
118
|
+
buffer += decoder.decode(value, { stream: true });
|
|
119
|
+
let lines = buffer.split(/\r?\n/);
|
|
120
|
+
buffer = lines.pop() || ""; // Keep incomplete line
|
|
121
|
+
|
|
122
|
+
for (const line of lines) {
|
|
123
|
+
if (line.startsWith('data: ')) {
|
|
124
|
+
const data = line.slice(6);
|
|
125
|
+
if (data === '[DONE]') return;
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const parsed = JSON.parse(data);
|
|
129
|
+
const shouldContinue = onData(parsed);
|
|
130
|
+
if (shouldContinue === false) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
} catch (e) {
|
|
134
|
+
// Skip invalid JSON
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} finally {
|
|
140
|
+
reader.releaseLock();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const processor = {
|
|
145
|
+
async *[Symbol.asyncIterator]() {
|
|
146
|
+
const reader = stream.getReader();
|
|
147
|
+
const decoder = new TextDecoder();
|
|
148
|
+
let buffer = "";
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
while (true) {
|
|
152
|
+
const { done, value } = await reader.read();
|
|
153
|
+
if (done) {
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
buffer += decoder.decode(value, { stream: true });
|
|
158
|
+
let lines = buffer.split(/\r?\n/);
|
|
159
|
+
buffer = lines.pop() || ""; // Keep incomplete line
|
|
160
|
+
|
|
161
|
+
for (const line of lines) {
|
|
162
|
+
if (line.startsWith('data: ')) {
|
|
163
|
+
const data = line.slice(6);
|
|
164
|
+
if (data === '[DONE]') {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const parsed = JSON.parse(data);
|
|
170
|
+
yield parsed;
|
|
171
|
+
} catch (e) {
|
|
172
|
+
//
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} finally {
|
|
178
|
+
reader.releaseLock();
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
processManually: processStream
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
return processor as ISSEStreamProcessor;
|
|
186
|
+
}
|
|
187
|
+
}
|
package/src/ai/index.ts
ADDED