@reverbia/sdk 1.0.0-next.20251208112742 → 1.0.0-next.20251208162906

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.
@@ -4,7 +4,136 @@ import { useCallback, useEffect, useRef, useState } from "react";
4
4
  // src/clientConfig.ts
5
5
  var BASE_URL = "https://ai-portal-dev.zetachain.com";
6
6
 
7
+ // src/lib/chat/useChat/utils.ts
8
+ var VALIDATION_ERROR_MESSAGES = {
9
+ messages_required: "messages are required to call sendMessage.",
10
+ model_required: "model is required to call sendMessage.",
11
+ token_getter_required: "Token getter function is required.",
12
+ token_unavailable: "No access token available."
13
+ };
14
+ function validateMessages(messages) {
15
+ if (!messages?.length) {
16
+ return {
17
+ valid: false,
18
+ error: "messages_required",
19
+ message: VALIDATION_ERROR_MESSAGES.messages_required
20
+ };
21
+ }
22
+ return { valid: true };
23
+ }
24
+ function validateModel(model) {
25
+ if (!model) {
26
+ return {
27
+ valid: false,
28
+ error: "model_required",
29
+ message: VALIDATION_ERROR_MESSAGES.model_required
30
+ };
31
+ }
32
+ return { valid: true };
33
+ }
34
+ function validateTokenGetter(getToken) {
35
+ if (!getToken) {
36
+ return {
37
+ valid: false,
38
+ error: "token_getter_required",
39
+ message: VALIDATION_ERROR_MESSAGES.token_getter_required
40
+ };
41
+ }
42
+ return { valid: true };
43
+ }
44
+ function validateToken(token) {
45
+ if (!token) {
46
+ return {
47
+ valid: false,
48
+ error: "token_unavailable",
49
+ message: VALIDATION_ERROR_MESSAGES.token_unavailable
50
+ };
51
+ }
52
+ return { valid: true };
53
+ }
54
+ function createStreamAccumulator() {
55
+ return {
56
+ content: "",
57
+ completionId: "",
58
+ completionModel: "",
59
+ usage: {},
60
+ finishReason: void 0
61
+ };
62
+ }
63
+ function buildCompletionResponse(accumulator) {
64
+ return {
65
+ id: accumulator.completionId,
66
+ model: accumulator.completionModel,
67
+ choices: [
68
+ {
69
+ index: 0,
70
+ message: {
71
+ role: "assistant",
72
+ content: [{ type: "text", text: accumulator.content }]
73
+ },
74
+ finish_reason: accumulator.finishReason
75
+ }
76
+ ],
77
+ usage: Object.keys(accumulator.usage).length > 0 ? accumulator.usage : void 0
78
+ };
79
+ }
80
+ function createErrorResult(message, onError) {
81
+ if (onError) {
82
+ onError(new Error(message));
83
+ }
84
+ return { data: null, error: message };
85
+ }
86
+ function handleError(err, onError) {
87
+ const errorMsg = err instanceof Error ? err.message : "Failed to send message.";
88
+ const errorObj = err instanceof Error ? err : new Error(errorMsg);
89
+ if (onError) {
90
+ onError(errorObj);
91
+ }
92
+ return { data: null, error: errorMsg };
93
+ }
94
+ function parseSSEDataLine(line) {
95
+ if (!line.startsWith("data: ")) {
96
+ return null;
97
+ }
98
+ const data = line.substring(6).trim();
99
+ if (data === "[DONE]") {
100
+ return null;
101
+ }
102
+ try {
103
+ return JSON.parse(data);
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
7
109
  // src/expo/useChat.ts
110
+ function processSSELines(lines, accumulator, onData, globalOnData) {
111
+ for (const line of lines) {
112
+ const chunk = parseSSEDataLine(line);
113
+ if (!chunk) continue;
114
+ if (chunk.id && !accumulator.completionId) {
115
+ accumulator.completionId = chunk.id;
116
+ }
117
+ if (chunk.model && !accumulator.completionModel) {
118
+ accumulator.completionModel = chunk.model;
119
+ }
120
+ if (chunk.usage) {
121
+ accumulator.usage = { ...accumulator.usage, ...chunk.usage };
122
+ }
123
+ if (chunk.choices?.[0]) {
124
+ const choice = chunk.choices[0];
125
+ if (choice.delta?.content) {
126
+ const content = choice.delta.content;
127
+ accumulator.content += content;
128
+ if (onData) onData(content);
129
+ if (globalOnData) globalOnData(content);
130
+ }
131
+ if (choice.finish_reason) {
132
+ accumulator.finishReason = choice.finish_reason;
133
+ }
134
+ }
135
+ }
136
+ }
8
137
  function useChat(options) {
9
138
  const {
10
139
  getToken,
@@ -35,20 +164,17 @@ function useChat(options) {
35
164
  model,
36
165
  onData
37
166
  }) => {
38
- if (!messages?.length) {
39
- const errorMsg = "messages are required to call sendMessage.";
40
- if (onError) onError(new Error(errorMsg));
41
- return { data: null, error: errorMsg };
167
+ const messagesValidation = validateMessages(messages);
168
+ if (!messagesValidation.valid) {
169
+ return createErrorResult(messagesValidation.message, onError);
42
170
  }
43
- if (!model) {
44
- const errorMsg = "model is required to call sendMessage.";
45
- if (onError) onError(new Error(errorMsg));
46
- return { data: null, error: errorMsg };
171
+ const modelValidation = validateModel(model);
172
+ if (!modelValidation.valid) {
173
+ return createErrorResult(modelValidation.message, onError);
47
174
  }
48
- if (!getToken) {
49
- const errorMsg = "Token getter function is required.";
50
- if (onError) onError(new Error(errorMsg));
51
- return { data: null, error: errorMsg };
175
+ const tokenGetterValidation = validateTokenGetter(getToken);
176
+ if (!tokenGetterValidation.valid) {
177
+ return createErrorResult(tokenGetterValidation.message, onError);
52
178
  }
53
179
  if (abortControllerRef.current) {
54
180
  abortControllerRef.current.abort();
@@ -58,20 +184,15 @@ function useChat(options) {
58
184
  setIsLoading(true);
59
185
  try {
60
186
  const token = await getToken();
61
- if (!token) {
62
- const errorMsg = "No access token available.";
187
+ const tokenValidation = validateToken(token);
188
+ if (!tokenValidation.valid) {
63
189
  setIsLoading(false);
64
- if (onError) onError(new Error(errorMsg));
65
- return { data: null, error: errorMsg };
190
+ return createErrorResult(tokenValidation.message, onError);
66
191
  }
67
192
  const result = await new Promise((resolve) => {
68
193
  const xhr = new XMLHttpRequest();
69
194
  const url = `${baseUrl}/api/v1/chat/completions`;
70
- let accumulatedContent = "";
71
- let completionId = "";
72
- let completionModel = "";
73
- let accumulatedUsage = {};
74
- let finishReason;
195
+ const accumulator = createStreamAccumulator();
75
196
  let lastProcessedIndex = 0;
76
197
  let incompleteLineBuffer = "";
77
198
  const abortHandler = () => {
@@ -91,93 +212,21 @@ function useChat(options) {
91
212
  if (!newData.endsWith("\n") && lines.length > 0) {
92
213
  incompleteLineBuffer = lines.pop() || "";
93
214
  }
94
- for (const line of lines) {
95
- if (line.startsWith("data: ")) {
96
- const data = line.substring(6).trim();
97
- if (data === "[DONE]") continue;
98
- try {
99
- const chunk = JSON.parse(data);
100
- if (chunk.id && !completionId) {
101
- completionId = chunk.id;
102
- }
103
- if (chunk.model && !completionModel) {
104
- completionModel = chunk.model;
105
- }
106
- if (chunk.usage) {
107
- accumulatedUsage = { ...accumulatedUsage, ...chunk.usage };
108
- }
109
- if (chunk.choices?.[0]) {
110
- const choice = chunk.choices[0];
111
- if (choice.delta?.content) {
112
- const content = choice.delta.content;
113
- accumulatedContent += content;
114
- if (onData) onData(content);
115
- if (globalOnData) globalOnData(content);
116
- }
117
- if (choice.finish_reason) {
118
- finishReason = choice.finish_reason;
119
- }
120
- }
121
- } catch {
122
- }
123
- }
124
- }
215
+ processSSELines(lines, accumulator, onData, globalOnData);
125
216
  };
126
217
  xhr.onload = () => {
127
218
  abortController.signal.removeEventListener("abort", abortHandler);
128
219
  if (incompleteLineBuffer) {
129
- const line = incompleteLineBuffer.trim();
130
- if (line.startsWith("data: ")) {
131
- const data = line.substring(6).trim();
132
- if (data !== "[DONE]") {
133
- try {
134
- const chunk = JSON.parse(data);
135
- if (chunk.id && !completionId) {
136
- completionId = chunk.id;
137
- }
138
- if (chunk.model && !completionModel) {
139
- completionModel = chunk.model;
140
- }
141
- if (chunk.usage) {
142
- accumulatedUsage = {
143
- ...accumulatedUsage,
144
- ...chunk.usage
145
- };
146
- }
147
- if (chunk.choices?.[0]) {
148
- const choice = chunk.choices[0];
149
- if (choice.delta?.content) {
150
- const content = choice.delta.content;
151
- accumulatedContent += content;
152
- if (onData) onData(content);
153
- if (globalOnData) globalOnData(content);
154
- }
155
- if (choice.finish_reason) {
156
- finishReason = choice.finish_reason;
157
- }
158
- }
159
- } catch {
160
- }
161
- }
162
- }
220
+ processSSELines(
221
+ [incompleteLineBuffer.trim()],
222
+ accumulator,
223
+ onData,
224
+ globalOnData
225
+ );
163
226
  incompleteLineBuffer = "";
164
227
  }
165
228
  if (xhr.status >= 200 && xhr.status < 300) {
166
- const completion = {
167
- id: completionId,
168
- model: completionModel,
169
- choices: [
170
- {
171
- index: 0,
172
- message: {
173
- role: "assistant",
174
- content: [{ type: "text", text: accumulatedContent }]
175
- },
176
- finish_reason: finishReason
177
- }
178
- ],
179
- usage: Object.keys(accumulatedUsage).length > 0 ? accumulatedUsage : void 0
180
- };
229
+ const completion = buildCompletionResponse(accumulator);
181
230
  setIsLoading(false);
182
231
  if (onFinish) onFinish(completion);
183
232
  resolve({ data: completion, error: null });
@@ -210,13 +259,8 @@ function useChat(options) {
210
259
  });
211
260
  return result;
212
261
  } catch (err) {
213
- const errorMsg = err instanceof Error ? err.message : "Failed to send message.";
214
- const errorObj = err instanceof Error ? err : new Error(errorMsg);
215
262
  setIsLoading(false);
216
- if (onError) {
217
- onError(errorObj);
218
- }
219
- return { data: null, error: errorMsg };
263
+ return handleError(err, onError);
220
264
  } finally {
221
265
  if (abortControllerRef.current === abortController) {
222
266
  abortControllerRef.current = null;