@alquimia-ai/tools 1.13.1 → 2.0.1

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.
Files changed (79) hide show
  1. package/dist/actions/index.d.mts +7 -17
  2. package/dist/actions/index.d.ts +7 -17
  3. package/dist/actions/index.js +45 -152
  4. package/dist/actions/index.js.map +1 -1
  5. package/dist/actions/index.mjs +45 -152
  6. package/dist/actions/index.mjs.map +1 -1
  7. package/dist/adapters/fetch.d.mts +12 -0
  8. package/dist/adapters/fetch.d.ts +12 -0
  9. package/dist/adapters/fetch.js +44 -0
  10. package/dist/adapters/fetch.js.map +1 -0
  11. package/dist/adapters/fetch.mjs +23 -0
  12. package/dist/adapters/fetch.mjs.map +1 -0
  13. package/dist/adapters/index.d.mts +11 -0
  14. package/dist/adapters/index.d.ts +11 -0
  15. package/dist/adapters/index.js +19 -0
  16. package/dist/adapters/index.js.map +1 -0
  17. package/dist/adapters/index.mjs +1 -0
  18. package/dist/adapters/index.mjs.map +1 -0
  19. package/dist/adapters/next.d.mts +10 -0
  20. package/dist/adapters/next.d.ts +10 -0
  21. package/dist/adapters/next.js +42 -0
  22. package/dist/adapters/next.js.map +1 -0
  23. package/dist/adapters/next.mjs +21 -0
  24. package/dist/adapters/next.mjs.map +1 -0
  25. package/dist/hooks/index.d.mts +22 -2
  26. package/dist/hooks/index.d.ts +22 -2
  27. package/dist/hooks/index.js +283 -66
  28. package/dist/hooks/index.js.map +1 -1
  29. package/dist/hooks/index.mjs +274 -67
  30. package/dist/hooks/index.mjs.map +1 -1
  31. package/dist/next/index.d.mts +49 -0
  32. package/dist/next/index.d.ts +49 -0
  33. package/dist/next/index.js +332 -0
  34. package/dist/next/index.js.map +1 -0
  35. package/dist/next/index.mjs +309 -0
  36. package/dist/next/index.mjs.map +1 -0
  37. package/dist/providers/index.d.mts +7 -3
  38. package/dist/providers/index.d.ts +7 -3
  39. package/dist/providers/index.js +15 -14
  40. package/dist/providers/index.js.map +1 -1
  41. package/dist/providers/index.mjs +15 -14
  42. package/dist/providers/index.mjs.map +1 -1
  43. package/dist/proxy.d.mts +15 -0
  44. package/dist/proxy.d.ts +15 -0
  45. package/dist/proxy.js +147 -0
  46. package/dist/proxy.js.map +1 -0
  47. package/dist/proxy.mjs +126 -0
  48. package/dist/proxy.mjs.map +1 -0
  49. package/dist/sdk/index.d.mts +8 -14
  50. package/dist/sdk/index.d.ts +8 -14
  51. package/dist/sdk/index.js +49 -49
  52. package/dist/sdk/index.js.map +1 -1
  53. package/dist/sdk/index.mjs +49 -49
  54. package/dist/sdk/index.mjs.map +1 -1
  55. package/dist/services/index.d.mts +25 -3
  56. package/dist/services/index.d.ts +25 -3
  57. package/dist/types/index.d.mts +2 -1
  58. package/dist/types/index.d.ts +2 -1
  59. package/dist/types/index.js.map +1 -1
  60. package/dist/types/index.mjs.map +1 -1
  61. package/dist/utils/index.d.mts +1 -2
  62. package/dist/utils/index.d.ts +1 -2
  63. package/dist/utils/index.js +0 -17
  64. package/dist/utils/index.js.map +1 -1
  65. package/dist/utils/index.mjs +0 -17
  66. package/dist/utils/index.mjs.map +1 -1
  67. package/package.json +35 -5
  68. package/dist/providers/elastic/index.d.mts +0 -23
  69. package/dist/providers/elastic/index.d.ts +0 -23
  70. package/dist/providers/elastic/index.js +0 -102
  71. package/dist/providers/elastic/index.js.map +0 -1
  72. package/dist/providers/elastic/index.mjs +0 -69
  73. package/dist/providers/elastic/index.mjs.map +0 -1
  74. package/dist/services/apm/index.d.mts +0 -26
  75. package/dist/services/apm/index.d.ts +0 -26
  76. package/dist/services/apm/index.js +0 -86
  77. package/dist/services/apm/index.js.map +0 -1
  78. package/dist/services/apm/index.mjs +0 -63
  79. package/dist/services/apm/index.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/next.ts"],"sourcesContent":["import type { AlquimiaAdapter } from './index';\n\nexport interface NextJsAdapterOptions {\n inferRoute?: string; // default: '/api/chat'\n streamRoute?: string; // default: '/api/stream'\n attachmentRoute?: string; // default: '/api/attachments'\n}\n\nexport function createNextJsAdapter(options?: NextJsAdapterOptions): AlquimiaAdapter {\n const inferRoute = options?.inferRoute ?? '/api/chat';\n const streamRoute = options?.streamRoute ?? '/api/stream';\n const attachmentRoute = options?.attachmentRoute ?? '/api/attachments';\n\n return {\n resolveInferUrl(assistantId: string): string {\n return `${inferRoute}/chat/${assistantId}`;\n },\n resolveStreamUrl(streamId: string): string {\n return `${streamRoute}/${streamId}`;\n },\n resolveAttachmentUrl(streamId: string, attachmentId: string): string {\n return `${attachmentRoute}/${streamId}/${attachmentId}`;\n },\n };\n}\n"],"mappings":";AAQO,SAAS,oBAAoB,SAAiD;AACnF,QAAM,aAAa,SAAS,cAAc;AAC1C,QAAM,cAAc,SAAS,eAAe;AAC5C,QAAM,kBAAkB,SAAS,mBAAmB;AAEpD,SAAO;AAAA,IACL,gBAAgB,aAA6B;AAC3C,aAAO,GAAG,UAAU,SAAS,WAAW;AAAA,IAC1C;AAAA,IACA,iBAAiB,UAA0B;AACzC,aAAO,GAAG,WAAW,IAAI,QAAQ;AAAA,IACnC;AAAA,IACA,qBAAqB,UAAkB,cAA8B;AACnE,aAAO,GAAG,eAAe,IAAI,QAAQ,IAAI,YAAY;AAAA,IACvD;AAAA,EACF;AACF;","names":[]}
@@ -1,12 +1,30 @@
1
1
  import * as react from 'react';
2
2
  import { AlquimiaSDK } from '../sdk/index.mjs';
3
+ import { AlquimiaAdapter } from '../adapters/index.mjs';
4
+ import { W as WhisperProvider, S as StableDiffusionProvider, C as CharacterizationProvider, R as RatingsProvider, L as LoggerProvider } from '../providers-0Gi78uNQ.mjs';
3
5
  import { AlquimiaMessage, ToolEvent, AIMessageChunk, RatingData } from '../types/index.mjs';
4
6
  import { createMessageId } from '../utils/index.mjs';
5
- import '../providers-0Gi78uNQ.mjs';
6
7
  import '@elastic/apm-rum';
7
8
  import 'ai';
8
9
 
9
- declare function useAlquimia(sdk: AlquimiaSDK): {
10
+ interface UseAlquimiaConfig {
11
+ assistantId: string;
12
+ adapter: AlquimiaAdapter;
13
+ providers?: {
14
+ whisper?: WhisperProvider;
15
+ stableDiffusion?: StableDiffusionProvider;
16
+ characterization?: CharacterizationProvider;
17
+ ratings?: RatingsProvider;
18
+ logger?: LoggerProvider;
19
+ };
20
+ options?: {
21
+ enforceCharacterization?: boolean;
22
+ userId?: string;
23
+ extraData?: any;
24
+ };
25
+ }
26
+ declare function useAlquimia(config: UseAlquimiaConfig): {
27
+ sdk: AlquimiaSDK;
10
28
  activeTool: any;
11
29
  cleanMessages: () => void;
12
30
  createMessageId: typeof createMessageId;
@@ -22,7 +40,9 @@ declare function useAlquimia(sdk: AlquimiaSDK): {
22
40
  handleLoadingCancel: () => void;
23
41
  input: string;
24
42
  isLoading: boolean;
43
+ isMessageLoading: boolean;
25
44
  isMessageStreaming: boolean;
45
+ isStreamingLoading: boolean;
26
46
  streamingMessageId: string | null;
27
47
  isAudioRecording: boolean;
28
48
  lastRequest: string | null;
@@ -1,12 +1,30 @@
1
1
  import * as react from 'react';
2
2
  import { AlquimiaSDK } from '../sdk/index.js';
3
+ import { AlquimiaAdapter } from '../adapters/index.js';
4
+ import { W as WhisperProvider, S as StableDiffusionProvider, C as CharacterizationProvider, R as RatingsProvider, L as LoggerProvider } from '../providers-BJTXCtI3.js';
3
5
  import { AlquimiaMessage, ToolEvent, AIMessageChunk, RatingData } from '../types/index.js';
4
6
  import { createMessageId } from '../utils/index.js';
5
- import '../providers-BJTXCtI3.js';
6
7
  import '@elastic/apm-rum';
7
8
  import 'ai';
8
9
 
9
- declare function useAlquimia(sdk: AlquimiaSDK): {
10
+ interface UseAlquimiaConfig {
11
+ assistantId: string;
12
+ adapter: AlquimiaAdapter;
13
+ providers?: {
14
+ whisper?: WhisperProvider;
15
+ stableDiffusion?: StableDiffusionProvider;
16
+ characterization?: CharacterizationProvider;
17
+ ratings?: RatingsProvider;
18
+ logger?: LoggerProvider;
19
+ };
20
+ options?: {
21
+ enforceCharacterization?: boolean;
22
+ userId?: string;
23
+ extraData?: any;
24
+ };
25
+ }
26
+ declare function useAlquimia(config: UseAlquimiaConfig): {
27
+ sdk: AlquimiaSDK;
10
28
  activeTool: any;
11
29
  cleanMessages: () => void;
12
30
  createMessageId: typeof createMessageId;
@@ -22,7 +40,9 @@ declare function useAlquimia(sdk: AlquimiaSDK): {
22
40
  handleLoadingCancel: () => void;
23
41
  input: string;
24
42
  isLoading: boolean;
43
+ isMessageLoading: boolean;
25
44
  isMessageStreaming: boolean;
45
+ isStreamingLoading: boolean;
26
46
  streamingMessageId: string | null;
27
47
  isAudioRecording: boolean;
28
48
  lastRequest: string | null;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/hooks/index.ts
@@ -28,6 +38,205 @@ module.exports = __toCommonJS(hooks_exports);
28
38
  // src/hooks/alquimia.hook.tsx
29
39
  var import_react = require("react");
30
40
 
41
+ // src/sdk/alquimia-sdk.ts
42
+ var import_axios = __toESM(require("axios"));
43
+ var AlquimiaSDK = class {
44
+ constructor(assistantId, adapter, options = {}) {
45
+ this.conversationId = null;
46
+ this.sessionId = null;
47
+ this.streamId = null;
48
+ this.evaluationStrategy = null;
49
+ this.tools = [];
50
+ this.extraData = null;
51
+ this.assistantConfig = null;
52
+ this.forceProfile = {};
53
+ this.userId = null;
54
+ this.attachments = [];
55
+ this.attachmentResponses = [];
56
+ if (!assistantId) {
57
+ throw new Error("AlquimiaSDK: assistantId is required");
58
+ }
59
+ if (!adapter) {
60
+ throw new Error(
61
+ "AlquimiaSDK: adapter is required. Use createNextJsAdapter() or createFetchAdapter()"
62
+ );
63
+ }
64
+ this.assistantId = assistantId;
65
+ this.adapter = adapter;
66
+ this.enforceCharacterization = options.enforceCharacterization ?? true;
67
+ this.axiosInstance = import_axios.default.create();
68
+ this.axiosInstance.interceptors.response.use(
69
+ (response) => response,
70
+ async (error) => {
71
+ if (error.response?.status && this.loggerProvider) {
72
+ await this.loggerProvider.logError("Server Error", error, {
73
+ url: error.config.url,
74
+ method: error.config.method,
75
+ data: error.config.data,
76
+ status: error.response.status,
77
+ responseData: error.response.data
78
+ });
79
+ }
80
+ return Promise.reject(error);
81
+ }
82
+ );
83
+ this.textToSpeech = this.textToSpeech.bind(this);
84
+ this.speechToText = this.speechToText.bind(this);
85
+ }
86
+ withConversationId(conversationId) {
87
+ this.conversationId = conversationId;
88
+ return this;
89
+ }
90
+ withAttachments(attachments) {
91
+ this.attachments = attachments;
92
+ return this;
93
+ }
94
+ withWhisperProvider(provider) {
95
+ this.whisperProvider = provider;
96
+ return this;
97
+ }
98
+ withStableDiffusionProvider(provider) {
99
+ this.stableDiffusionProvider = provider;
100
+ return this;
101
+ }
102
+ withAnalyzeCharacterizationProvider(provider) {
103
+ this.analyzeCharacterizationProvider = provider;
104
+ return this;
105
+ }
106
+ withRatingsProvider(provider) {
107
+ this.ratingsProvider = provider;
108
+ return this;
109
+ }
110
+ withLoggerProvider(provider) {
111
+ this.loggerProvider = provider;
112
+ return this;
113
+ }
114
+ withTools(tools) {
115
+ this.tools = tools;
116
+ return this;
117
+ }
118
+ withExtraData(extraData) {
119
+ this.extraData = extraData;
120
+ return this;
121
+ }
122
+ withForceProfile(forceProfile) {
123
+ this.forceProfile = forceProfile;
124
+ return this;
125
+ }
126
+ withAssistantConfig(assistantConfig) {
127
+ this.assistantConfig = assistantConfig;
128
+ return this;
129
+ }
130
+ withUserId(userId) {
131
+ this.userId = userId;
132
+ return this;
133
+ }
134
+ getEnforceCharacterization() {
135
+ return this.enforceCharacterization ?? true;
136
+ }
137
+ getEvaluationStrategy() {
138
+ return this.evaluationStrategy;
139
+ }
140
+ textToSpeech(text) {
141
+ if (!this.whisperProvider) {
142
+ throw new Error("Whisper provider not initialized");
143
+ }
144
+ return this.whisperProvider.textToSpeech(text);
145
+ }
146
+ speechToText(audio) {
147
+ if (!this.whisperProvider) {
148
+ throw new Error("Whisper provider not initialized");
149
+ }
150
+ return this.whisperProvider.speechToText(audio);
151
+ }
152
+ async sendMessage(query, traceParent) {
153
+ if (!this.conversationId) {
154
+ throw new Error(
155
+ "Conversation not initialized. Call withConversationId() before sendMessage()"
156
+ );
157
+ }
158
+ const inferUrl = this.adapter.resolveInferUrl(this.assistantId);
159
+ const adapterHeaders = this.adapter.getHeaders?.() ?? {};
160
+ const initMessage = {
161
+ query,
162
+ session_id: this.conversationId,
163
+ ...this.extraData && { extra_data: this.extraData },
164
+ force_profile: this.forceProfile,
165
+ ...this.assistantConfig && { config: this.assistantConfig },
166
+ tools: this.tools,
167
+ user_id: this.userId,
168
+ attachments: this.attachments
169
+ };
170
+ const result = (await this.axiosInstance.post(inferUrl, initMessage, {
171
+ headers: {
172
+ "Content-Type": "application/json",
173
+ "x-trace-parent": traceParent || "",
174
+ ...adapterHeaders
175
+ }
176
+ })).data;
177
+ this.evaluationStrategy = result?.config?.dante?.profile?.evaluation_strategy?.evaluation_strategy_id ?? null;
178
+ this.streamId = result.stream_id;
179
+ this.attachmentResponses = result.attachments ?? [];
180
+ this.attachments = [];
181
+ return this;
182
+ }
183
+ async generateImage(query) {
184
+ if (!this.stableDiffusionProvider) {
185
+ throw new Error("Stable Diffusion provider not initialized");
186
+ }
187
+ return this.stableDiffusionProvider.generateImage(query);
188
+ }
189
+ async analyzeCharacterization(text) {
190
+ if (!this.analyzeCharacterizationProvider) {
191
+ throw new Error("Analyze characterization provider not initialized");
192
+ }
193
+ return this.analyzeCharacterizationProvider.analyzeCharacterization(text);
194
+ }
195
+ async rate(data) {
196
+ if (!this.ratingsProvider) {
197
+ throw new Error("Ratings provider not initialized");
198
+ }
199
+ return this.ratingsProvider.rate(data);
200
+ }
201
+ async logInfo(message, data) {
202
+ if (!this.loggerProvider) {
203
+ throw new Error("Logger provider not initialized");
204
+ }
205
+ return this.loggerProvider.logInfo(message, data);
206
+ }
207
+ async logError(message, error, data) {
208
+ if (!this.loggerProvider) {
209
+ throw new Error("Logger provider not initialized");
210
+ }
211
+ return this.loggerProvider.logError(message, error, data);
212
+ }
213
+ getAttachmentResponses() {
214
+ return this.attachmentResponses;
215
+ }
216
+ async uploadAttachment(file, attachmentId) {
217
+ if (!this.streamId || !attachmentId) {
218
+ throw new Error("Stream or attachment ID not initialized");
219
+ }
220
+ const url = this.adapter.resolveAttachmentUrl(this.streamId, attachmentId);
221
+ const adapterHeaders = this.adapter.getHeaders?.() ?? {};
222
+ const formData = new FormData();
223
+ formData.append("file", file);
224
+ await this.axiosInstance.post(url, formData, {
225
+ headers: { "Content-Type": "multipart/form-data", ...adapterHeaders }
226
+ });
227
+ }
228
+ getUrlStream() {
229
+ if (!this.streamId) {
230
+ throw new Error("Stream ID not initialized. Call sendMessage() first");
231
+ }
232
+ return this.adapter.resolveStreamUrl(this.streamId);
233
+ }
234
+ getStreamId() {
235
+ return this.streamId;
236
+ }
237
+ };
238
+ var alquimia_sdk_default = AlquimiaSDK;
239
+
31
240
  // src/utils/utils.ts
32
241
  function getCookies(name) {
33
242
  if (typeof document === "undefined") return void 0;
@@ -65,22 +274,6 @@ function mergeThinkingsByControlId(existingThinkings = [], newThinkings = []) {
65
274
  }
66
275
  return Array.from(thinkingsMap.values());
67
276
  }
68
- function extractAnswerContent(content) {
69
- if (typeof content !== "string") return "";
70
- const trimmed = content.trim();
71
- if (trimmed.startsWith("{")) {
72
- try {
73
- const repaired = trimmed.replace(/(?<!\\)\n/g, "\\n");
74
- const parsed = JSON.parse(repaired);
75
- if (parsed && typeof parsed === "object" && "answer" in parsed) {
76
- return parsed.answer;
77
- }
78
- } catch (e) {
79
- return content;
80
- }
81
- }
82
- return content;
83
- }
84
277
  function mergeToolerByControlId(currentTooler, toolEvent) {
85
278
  const { event_class, control_id, data, status } = toolEvent;
86
279
  const existingTool = currentTooler.find((t) => t.control_id === control_id);
@@ -127,7 +320,20 @@ function mergeToolEvents(initial, toolEvents) {
127
320
  }
128
321
 
129
322
  // src/hooks/alquimia.hook.tsx
130
- function useAlquimia(sdk) {
323
+ function useAlquimia(config) {
324
+ const sdk = (0, import_react.useMemo)(() => {
325
+ const instance = new alquimia_sdk_default(config.assistantId, config.adapter, {
326
+ enforceCharacterization: config.options?.enforceCharacterization
327
+ });
328
+ if (config.providers?.whisper) instance.withWhisperProvider(config.providers.whisper);
329
+ if (config.providers?.stableDiffusion) instance.withStableDiffusionProvider(config.providers.stableDiffusion);
330
+ if (config.providers?.characterization) instance.withAnalyzeCharacterizationProvider(config.providers.characterization);
331
+ if (config.providers?.ratings) instance.withRatingsProvider(config.providers.ratings);
332
+ if (config.providers?.logger) instance.withLoggerProvider(config.providers.logger);
333
+ if (config.options?.userId) instance.withUserId(config.options.userId);
334
+ if (config.options?.extraData) instance.withExtraData(config.options.extraData);
335
+ return instance;
336
+ }, [config.assistantId, config.adapter]);
131
337
  const [input, setInput] = (0, import_react.useState)("");
132
338
  const [isMessageLoading, setIsMessageLoading] = (0, import_react.useState)(false);
133
339
  const [isStreamingLoading, setIsStreamingLoading] = (0, import_react.useState)(false);
@@ -162,9 +368,7 @@ function useAlquimia(sdk) {
162
368
  }
163
369
  function processMessageChunk(messageId, chunk, error_code, error_detail, stream_id, additionalInfo, loading, tooler, thinkings) {
164
370
  setMessages((currentMessages) => {
165
- const messageIndex = currentMessages.findIndex(
166
- (message) => message?.id === messageId
167
- );
371
+ const messageIndex = currentMessages.findIndex((message) => message?.id === messageId);
168
372
  if (messageIndex !== -1) {
169
373
  const updatedMessages = [...currentMessages];
170
374
  const updatedMessage = {
@@ -178,10 +382,7 @@ function useAlquimia(sdk) {
178
382
  additionalInfo,
179
383
  created_at: (/* @__PURE__ */ new Date()).getTime().toString(),
180
384
  loading,
181
- tooler: mergeToolEvents(
182
- updatedMessages[messageIndex]?.tooler || [],
183
- tooler
184
- ),
385
+ tooler: mergeToolEvents(updatedMessages[messageIndex]?.tooler || [], tooler),
185
386
  thinkings: mergeThinkingsByControlId(
186
387
  updatedMessages[messageIndex]?.thinkings || [],
187
388
  thinkings || []
@@ -236,11 +437,7 @@ function useAlquimia(sdk) {
236
437
  setIsMessageLoading(true);
237
438
  event.preventDefault();
238
439
  if (input) {
239
- await processAndSendMessage(input, {
240
- traceParentId,
241
- sessionId: sessionId2,
242
- additionalInfo
243
- });
440
+ await processAndSendMessage(input, { traceParentId, sessionId: sessionId2, additionalInfo });
244
441
  }
245
442
  setIsMessageLoading(false);
246
443
  }
@@ -287,7 +484,7 @@ function useAlquimia(sdk) {
287
484
  }
288
485
  function getCookie(name) {
289
486
  const cookies = document.cookie.split(";");
290
- for (let cookie of cookies) {
487
+ for (const cookie of cookies) {
291
488
  const [cookieName, cookieValue] = cookie.trim().split("=");
292
489
  if (cookieName === name) {
293
490
  return decodeURIComponent(cookieValue);
@@ -303,23 +500,22 @@ function useAlquimia(sdk) {
303
500
  }
304
501
  async function sendMessage(message, callBack, traceParentId, sessionId2) {
305
502
  setIsStreamingLoading(true);
306
- callBack({
307
- type: "loading",
308
- loading: true
309
- });
503
+ callBack({ type: "loading", loading: true });
310
504
  const conversationId = sessionId2 || getCookie("alquimia-session");
311
505
  setIsMessageStreaming(true);
312
506
  setActiveTool(null);
313
507
  setLastRequest(message);
314
508
  try {
315
509
  const hasAttachments = attachments.length > 0;
316
- const sdkCall = sdk.widthConversationId(conversationId || "");
510
+ const sdkCall = sdk.withConversationId(conversationId || "");
317
511
  if (hasAttachments) {
318
- sdkCall.withAttachments(attachments.map((f) => ({
319
- filename: f.name,
320
- content_type: f.type,
321
- content_size: f.size
322
- })));
512
+ sdkCall.withAttachments(
513
+ attachments.map((f) => ({
514
+ filename: f.name,
515
+ content_type: f.type,
516
+ content_size: f.size
517
+ }))
518
+ );
323
519
  }
324
520
  const response = await sdkCall.sendMessage(message, traceParentId);
325
521
  if (hasAttachments) {
@@ -327,9 +523,7 @@ function useAlquimia(sdk) {
327
523
  try {
328
524
  const attachmentResponses = response.getAttachmentResponses();
329
525
  await Promise.all(
330
- attachments.map(
331
- (file, i) => response.uploadAttachment(file, attachmentResponses[i])
332
- )
526
+ attachments.map((file, i) => response.uploadAttachment(file, attachmentResponses[i]))
333
527
  );
334
528
  } catch (uploadError) {
335
529
  setIsUploadingAttachments(false);
@@ -346,19 +540,47 @@ function useAlquimia(sdk) {
346
540
  setIsUploadingAttachments(false);
347
541
  clearAttachments();
348
542
  }
349
- const eventSource = new EventSource(response.getUrlStream());
543
+ const streamUrl = response.getUrlStream();
544
+ const streamId = response.getStreamId() || "";
545
+ const streamState = {
546
+ streamId,
547
+ connectedAt: Date.now(),
548
+ openedAt: null,
549
+ messagesReceived: 0,
550
+ lastMessageAt: null,
551
+ closedByClient: false
552
+ };
553
+ const eventSource = new EventSource(streamUrl);
350
554
  eventSourceRef.current = eventSource;
351
555
  const evaluationStrategy2 = response.getEvaluationStrategy();
352
556
  setEvaluationStrategy(evaluationStrategy2);
353
- eventSource.onmessage = (event) => handleMessageEvent(event, callBack, eventSource);
354
- eventSource.onerror = (event) => {
355
- console.error("Stream connection error", event);
557
+ eventSource.onopen = () => {
558
+ streamState.openedAt = Date.now();
559
+ };
560
+ eventSource.onmessage = (event) => {
561
+ streamState.messagesReceived++;
562
+ streamState.lastMessageAt = Date.now();
563
+ handleMessageEvent(event, callBack, eventSource, streamState);
564
+ };
565
+ eventSource.onerror = () => {
566
+ if (streamState.closedByClient) return;
567
+ streamState.closedByClient = true;
568
+ const elapsed = Date.now() - streamState.connectedAt;
569
+ const sinceLastMessage = streamState.lastMessageAt ? Date.now() - streamState.lastMessageAt : null;
356
570
  cleanup(false, eventSource);
571
+ let errorDetail;
572
+ if (!streamState.openedAt) {
573
+ errorDetail = `Stream failed to connect (after ${elapsed}ms). URL: ${streamUrl}`;
574
+ } else if (streamState.messagesReceived === 0) {
575
+ errorDetail = `Stream connected but received no messages before error (open for ${elapsed}ms).`;
576
+ } else {
577
+ errorDetail = `Stream dropped after ${streamState.messagesReceived} message(s), ${sinceLastMessage}ms since last message (total ${elapsed}ms). Possible timeout.`;
578
+ }
357
579
  callBack({
358
580
  type: "error",
359
581
  error_code: "STREAM_ERROR",
360
- error_detail: "Failed to establish stream connection. Stream event source error.",
361
- stream_id: response.getStreamId() || ""
582
+ error_detail: errorDetail,
583
+ stream_id: streamId
362
584
  });
363
585
  };
364
586
  } catch (error) {
@@ -374,46 +596,39 @@ function useAlquimia(sdk) {
374
596
  });
375
597
  }
376
598
  }
377
- function handleMessageEvent(event, callBack, eventSource) {
599
+ function handleMessageEvent(event, callBack, eventSource, streamState) {
378
600
  const data = JSON.parse(event.data);
379
601
  if (data.errors?.length) {
380
602
  for (const error of data.errors) {
381
- console.error("Stream error:", error);
603
+ const detail = error.error_detail || (error.data != null ? `${error.data} (controlId: ${error.control_id})` : "Unknown error");
382
604
  callBack({
383
605
  type: "error",
384
606
  error_code: error.event_class || error.status || "UNKNOWN_ERROR",
385
- error_detail: `${error.data}, controlId: ${error.control_id}` || error.error_detail || "Unknown error",
607
+ error_detail: detail,
386
608
  stream_id: data.stream_id || ""
387
609
  });
388
610
  }
611
+ if (streamState) streamState.closedByClient = true;
389
612
  cleanup(false, eventSource);
390
613
  return;
391
614
  }
392
615
  if (data.tools_output?.length) {
393
- callBack({
394
- type: "answer",
395
- answer: "",
396
- tooler: data.tools_output
397
- });
616
+ callBack({ type: "answer", answer: "", tooler: data.tools_output });
398
617
  }
399
618
  if (data.thinkings?.length) {
400
619
  setHasThinkings(true);
401
- callBack({
402
- type: "answer",
403
- thinkings: data.thinkings
404
- });
620
+ callBack({ type: "answer", thinkings: data.thinkings });
405
621
  }
406
622
  const response = data.response;
407
- const content = response?.event_class === "AssistantInferenceResponse" ? extractAnswerContent(response?.data?.content) : null;
623
+ const content = response?.event_class === "AssistantInferenceResponse" && typeof response.data === "string" ? response.data : null;
408
624
  if (content) {
409
- callBack({
410
- type: "answer",
411
- answer: content
412
- });
625
+ callBack({ type: "answer", answer: content });
626
+ if (streamState) streamState.closedByClient = true;
413
627
  cleanup(false, eventSource);
414
628
  }
415
629
  }
416
630
  return {
631
+ sdk,
417
632
  activeTool,
418
633
  cleanMessages,
419
634
  createMessageId,
@@ -424,7 +639,9 @@ function useAlquimia(sdk) {
424
639
  handleLoadingCancel,
425
640
  input,
426
641
  isLoading,
642
+ isMessageLoading,
427
643
  isMessageStreaming,
644
+ isStreamingLoading,
428
645
  streamingMessageId,
429
646
  isAudioRecording,
430
647
  lastRequest,