@yh-ui/ai-sdk 1.0.51 → 1.0.53

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.
@@ -159,7 +159,17 @@ ${reflections2.join("\n")}
159
159
  return `Attempt ${steps.value.length}: ${score < 5 ? "Need to try different approach" : "Making progress"}`;
160
160
  }
161
161
  async function evaluateOutput(output) {
162
- return output.length > 10 ? 5 + Math.random() * 5 : Math.random() * 5;
162
+ if (config.scorer) {
163
+ return await config.scorer(output);
164
+ }
165
+ let score = 0;
166
+ if (output.length > 10) score += 2;
167
+ if (output.length > 50) score += 1;
168
+ if (output.includes("Thought:")) score += 2;
169
+ if (output.includes("Action:")) score += 1;
170
+ if (output.includes("Action Input:")) score += 1;
171
+ if (output.includes("Observation:")) score += 1;
172
+ return score;
163
173
  }
164
174
  function checkStop(iterationOutput, bestOutput) {
165
175
  for (const cond of stopConditions) {
@@ -65,6 +65,8 @@ export interface ReflexionConfig extends Omit<EnhancedAgentConfig, 'mode'> {
65
65
  mode: 'reflexion';
66
66
  /** 记忆上下文窗口大小 */
67
67
  memoryWindow?: number;
68
+ /** 可注入的评估函数 (scorer) */
69
+ scorer?: (output: string) => number | Promise<number>;
68
70
  }
69
71
  /**
70
72
  * 创建 Reflexion Agent
@@ -146,7 +146,17 @@ ${reflections2.join("\n")}
146
146
  return `Attempt ${steps.value.length}: ${score < 5 ? "Need to try different approach" : "Making progress"}`;
147
147
  }
148
148
  async function evaluateOutput(output) {
149
- return output.length > 10 ? 5 + Math.random() * 5 : Math.random() * 5;
149
+ if (config.scorer) {
150
+ return await config.scorer(output);
151
+ }
152
+ let score = 0;
153
+ if (output.length > 10) score += 2;
154
+ if (output.length > 50) score += 1;
155
+ if (output.includes("Thought:")) score += 2;
156
+ if (output.includes("Action:")) score += 1;
157
+ if (output.includes("Action Input:")) score += 1;
158
+ if (output.includes("Observation:")) score += 1;
159
+ return score;
150
160
  }
151
161
  function checkStop(iterationOutput, bestOutput) {
152
162
  for (const cond of stopConditions) {
@@ -36,6 +36,7 @@ function createMemoryCache() {
36
36
  function createLocalStorageCache(prefix = "yh-ai-cache-") {
37
37
  return {
38
38
  get(key) {
39
+ if (typeof localStorage === "undefined") return null;
39
40
  try {
40
41
  const raw = localStorage.getItem(prefix + key);
41
42
  if (!raw) return null;
@@ -50,6 +51,7 @@ function createLocalStorageCache(prefix = "yh-ai-cache-") {
50
51
  }
51
52
  },
52
53
  set(key, value, ttlMs) {
54
+ if (typeof localStorage === "undefined") return;
53
55
  try {
54
56
  const item = {
55
57
  data: value,
@@ -59,6 +61,7 @@ function createLocalStorageCache(prefix = "yh-ai-cache-") {
59
61
  } catch {}
60
62
  },
61
63
  delete(key) {
64
+ if (typeof localStorage === "undefined") return;
62
65
  try {
63
66
  localStorage.removeItem(prefix + key);
64
67
  } catch {}
@@ -68,6 +71,7 @@ function createLocalStorageCache(prefix = "yh-ai-cache-") {
68
71
  function createSessionStorageCache(prefix = "yh-ai-cache-") {
69
72
  return {
70
73
  get(key) {
74
+ if (typeof sessionStorage === "undefined") return null;
71
75
  try {
72
76
  const raw = sessionStorage.getItem(prefix + key);
73
77
  if (!raw) return null;
@@ -82,6 +86,7 @@ function createSessionStorageCache(prefix = "yh-ai-cache-") {
82
86
  }
83
87
  },
84
88
  set(key, value, ttlMs) {
89
+ if (typeof sessionStorage === "undefined") return;
85
90
  try {
86
91
  const item = {
87
92
  data: value,
@@ -91,6 +96,7 @@ function createSessionStorageCache(prefix = "yh-ai-cache-") {
91
96
  } catch {}
92
97
  },
93
98
  delete(key) {
99
+ if (typeof sessionStorage === "undefined") return;
94
100
  try {
95
101
  sessionStorage.removeItem(prefix + key);
96
102
  } catch {}
@@ -28,6 +28,7 @@ export function createMemoryCache() {
28
28
  export function createLocalStorageCache(prefix = "yh-ai-cache-") {
29
29
  return {
30
30
  get(key) {
31
+ if (typeof localStorage === "undefined") return null;
31
32
  try {
32
33
  const raw = localStorage.getItem(prefix + key);
33
34
  if (!raw) return null;
@@ -42,6 +43,7 @@ export function createLocalStorageCache(prefix = "yh-ai-cache-") {
42
43
  }
43
44
  },
44
45
  set(key, value, ttlMs) {
46
+ if (typeof localStorage === "undefined") return;
45
47
  try {
46
48
  const item = {
47
49
  data: value,
@@ -52,6 +54,7 @@ export function createLocalStorageCache(prefix = "yh-ai-cache-") {
52
54
  }
53
55
  },
54
56
  delete(key) {
57
+ if (typeof localStorage === "undefined") return;
55
58
  try {
56
59
  localStorage.removeItem(prefix + key);
57
60
  } catch {
@@ -62,6 +65,7 @@ export function createLocalStorageCache(prefix = "yh-ai-cache-") {
62
65
  export function createSessionStorageCache(prefix = "yh-ai-cache-") {
63
66
  return {
64
67
  get(key) {
68
+ if (typeof sessionStorage === "undefined") return null;
65
69
  try {
66
70
  const raw = sessionStorage.getItem(prefix + key);
67
71
  if (!raw) return null;
@@ -76,6 +80,7 @@ export function createSessionStorageCache(prefix = "yh-ai-cache-") {
76
80
  }
77
81
  },
78
82
  set(key, value, ttlMs) {
83
+ if (typeof sessionStorage === "undefined") return;
79
84
  try {
80
85
  const item = {
81
86
  data: value,
@@ -86,6 +91,7 @@ export function createSessionStorageCache(prefix = "yh-ai-cache-") {
86
91
  }
87
92
  },
88
93
  delete(key) {
94
+ if (typeof sessionStorage === "undefined") return;
89
95
  try {
90
96
  sessionStorage.removeItem(prefix + key);
91
97
  } catch {
package/dist/future.cjs CHANGED
@@ -310,13 +310,55 @@ function createRAGSystem(config) {
310
310
  strategy = "similarity"
311
311
  } = config;
312
312
  const vectorStore = /* @__PURE__ */new Map();
313
+ const calculateSimilarity = (q, t) => {
314
+ const tokenize = str => {
315
+ const tokens = [];
316
+ const words = str.toLowerCase().split(/[\s,.<>?/;:'"[{}]|`~!@#$%^&*()_\-+=,。!?;:‘’“”【】『』]+/g);
317
+ for (const w of words) {
318
+ if (!w) continue;
319
+ if (/[\u4e00-\u9fa5]/.test(w)) {
320
+ for (const char of w) {
321
+ tokens.push(char);
322
+ }
323
+ } else {
324
+ tokens.push(w);
325
+ }
326
+ }
327
+ return tokens;
328
+ };
329
+ const qTokens = tokenize(q);
330
+ const tTokens = tokenize(t);
331
+ if (qTokens.length === 0 || tTokens.length === 0) return 0;
332
+ const qMap = /* @__PURE__ */new Map();
333
+ const tMap = /* @__PURE__ */new Map();
334
+ const allTokens = /* @__PURE__ */new Set();
335
+ for (const tok of qTokens) {
336
+ qMap.set(tok, (qMap.get(tok) || 0) + 1);
337
+ allTokens.add(tok);
338
+ }
339
+ for (const tok of tTokens) {
340
+ tMap.set(tok, (tMap.get(tok) || 0) + 1);
341
+ allTokens.add(tok);
342
+ }
343
+ let dotProduct = 0;
344
+ let qNorm = 0;
345
+ let tNorm = 0;
346
+ for (const tok of allTokens) {
347
+ const qVal = qMap.get(tok) || 0;
348
+ const tVal = tMap.get(tok) || 0;
349
+ dotProduct += qVal * tVal;
350
+ qNorm += qVal * qVal;
351
+ tNorm += tVal * tVal;
352
+ }
353
+ if (qNorm === 0 || tNorm === 0) return 0;
354
+ return dotProduct / (Math.sqrt(qNorm) * Math.sqrt(tNorm));
355
+ };
313
356
  const addDocuments = async documents => {
314
357
  const chunks = documents.map((doc, i) => ({
315
358
  id: `chunk-${Date.now()}-${i}`,
316
359
  content: doc.content,
317
360
  metadata: doc.metadata || {},
318
- // 简化:实际应该调用 embedding API
319
- score: Math.random()
361
+ score: 1
320
362
  }));
321
363
  if (knowledgeBaseId) {
322
364
  const existing = vectorStore.get(knowledgeBaseId) || [];
@@ -327,8 +369,12 @@ function createRAGSystem(config) {
327
369
  const retrieve = async (query2, k = topK) => {
328
370
  if (!knowledgeBaseId) return [];
329
371
  const chunks = vectorStore.get(knowledgeBaseId) || [];
330
- const sorted = [...chunks].sort((a, b) => (b.score || 0) - (a.score || 0)).slice(0, k);
331
- return sorted.filter(c => (c.score || 0) >= similarityThreshold);
372
+ const scoredChunks = chunks.map(chunk => ({
373
+ ...chunk,
374
+ score: calculateSimilarity(query2, chunk.content)
375
+ }));
376
+ const sorted = [...scoredChunks].sort((a, b) => b.score - a.score).slice(0, k);
377
+ return sorted.filter(c => c.score >= similarityThreshold);
332
378
  };
333
379
  const query = async (question, llm) => {
334
380
  const relevantDocs = await retrieve(question);
package/dist/future.mjs CHANGED
@@ -275,13 +275,55 @@ export function createRAGSystem(config) {
275
275
  strategy = "similarity"
276
276
  } = config;
277
277
  const vectorStore = /* @__PURE__ */ new Map();
278
+ const calculateSimilarity = (q, t) => {
279
+ const tokenize = (str) => {
280
+ const tokens = [];
281
+ const words = str.toLowerCase().split(/[\s,.<>?/;:'"[{}]|`~!@#$%^&*()_\-+=,。!?;:‘’“”【】『』]+/g);
282
+ for (const w of words) {
283
+ if (!w) continue;
284
+ if (/[\u4e00-\u9fa5]/.test(w)) {
285
+ for (const char of w) {
286
+ tokens.push(char);
287
+ }
288
+ } else {
289
+ tokens.push(w);
290
+ }
291
+ }
292
+ return tokens;
293
+ };
294
+ const qTokens = tokenize(q);
295
+ const tTokens = tokenize(t);
296
+ if (qTokens.length === 0 || tTokens.length === 0) return 0;
297
+ const qMap = /* @__PURE__ */ new Map();
298
+ const tMap = /* @__PURE__ */ new Map();
299
+ const allTokens = /* @__PURE__ */ new Set();
300
+ for (const tok of qTokens) {
301
+ qMap.set(tok, (qMap.get(tok) || 0) + 1);
302
+ allTokens.add(tok);
303
+ }
304
+ for (const tok of tTokens) {
305
+ tMap.set(tok, (tMap.get(tok) || 0) + 1);
306
+ allTokens.add(tok);
307
+ }
308
+ let dotProduct = 0;
309
+ let qNorm = 0;
310
+ let tNorm = 0;
311
+ for (const tok of allTokens) {
312
+ const qVal = qMap.get(tok) || 0;
313
+ const tVal = tMap.get(tok) || 0;
314
+ dotProduct += qVal * tVal;
315
+ qNorm += qVal * qVal;
316
+ tNorm += tVal * tVal;
317
+ }
318
+ if (qNorm === 0 || tNorm === 0) return 0;
319
+ return dotProduct / (Math.sqrt(qNorm) * Math.sqrt(tNorm));
320
+ };
278
321
  const addDocuments = async (documents) => {
279
322
  const chunks = documents.map((doc, i) => ({
280
323
  id: `chunk-${Date.now()}-${i}`,
281
324
  content: doc.content,
282
325
  metadata: doc.metadata || {},
283
- // 简化:实际应该调用 embedding API
284
- score: Math.random()
326
+ score: 1
285
327
  }));
286
328
  if (knowledgeBaseId) {
287
329
  const existing = vectorStore.get(knowledgeBaseId) || [];
@@ -292,8 +334,12 @@ export function createRAGSystem(config) {
292
334
  const retrieve = async (query2, k = topK) => {
293
335
  if (!knowledgeBaseId) return [];
294
336
  const chunks = vectorStore.get(knowledgeBaseId) || [];
295
- const sorted = [...chunks].sort((a, b) => (b.score || 0) - (a.score || 0)).slice(0, k);
296
- return sorted.filter((c) => (c.score || 0) >= similarityThreshold);
337
+ const scoredChunks = chunks.map((chunk) => ({
338
+ ...chunk,
339
+ score: calculateSimilarity(query2, chunk.content)
340
+ }));
341
+ const sorted = [...scoredChunks].sort((a, b) => b.score - a.score).slice(0, k);
342
+ return sorted.filter((c) => c.score >= similarityThreshold);
297
343
  };
298
344
  const query = async (question, llm) => {
299
345
  const relevantDocs = await retrieve(question);
@@ -91,6 +91,8 @@ const langChainRuntime = exports.langChainRuntime = {
91
91
  const stream = await modelWithTools.stream(messages);
92
92
  let fullContent = "";
93
93
  const toolCalls = [];
94
+ const toolCallArgumentsMap = /* @__PURE__ */new Map();
95
+ const toolCallInfoMap = /* @__PURE__ */new Map();
94
96
  for await (const chunk of stream) {
95
97
  const content = String(chunk?.content || "");
96
98
  if (content) {
@@ -101,10 +103,25 @@ const langChainRuntime = exports.langChainRuntime = {
101
103
  if (additionalKwargs?.tool_calls) {
102
104
  for (const tc of additionalKwargs.tool_calls) {
103
105
  toolCalls.push(tc);
104
- options?.onToolCall?.({
105
- name: tc.function?.name || "",
106
- args: tc.function?.arguments ? JSON.parse(tc.function.arguments) : {}
107
- });
106
+ const idx = tc.index ?? 0;
107
+ if (tc.id) {
108
+ toolCallInfoMap.set(idx, {
109
+ id: tc.id,
110
+ name: tc.function?.name
111
+ });
112
+ toolCallArgumentsMap.set(idx, tc.function?.arguments || "");
113
+ } else {
114
+ const currentArgs = toolCallArgumentsMap.get(idx) || "";
115
+ toolCallArgumentsMap.set(idx, currentArgs + (tc.function?.arguments || ""));
116
+ }
117
+ const rawArgs = toolCallArgumentsMap.get(idx) || "";
118
+ try {
119
+ const parsedArgs = JSON.parse(rawArgs);
120
+ options?.onToolCall?.({
121
+ name: toolCallInfoMap.get(idx)?.name || tc.function?.name || "",
122
+ args: parsedArgs
123
+ });
124
+ } catch {}
108
125
  }
109
126
  }
110
127
  }
@@ -244,6 +261,7 @@ function useLangChainStream(options) {
244
261
  error.value = null;
245
262
  isStreaming.value = true;
246
263
  content.value = "";
264
+ abortController = new AbortController();
247
265
  try {
248
266
  const messages = [];
249
267
  if (systemMessage) {
@@ -263,8 +281,13 @@ function useLangChainStream(options) {
263
281
  }
264
282
  }
265
283
  messages.push(new _messages.HumanMessage(prompt));
266
- const stream = await model.stream(messages);
284
+ const stream = await model.stream(messages, {
285
+ signal: abortController.signal
286
+ });
267
287
  for await (const chunk of stream) {
288
+ if (abortController?.signal.aborted) {
289
+ break;
290
+ }
268
291
  const chunkContent = String(chunk?.content || "");
269
292
  content.value += chunkContent;
270
293
  }
@@ -275,6 +298,7 @@ function useLangChainStream(options) {
275
298
  }
276
299
  } finally {
277
300
  isStreaming.value = false;
301
+ abortController = null;
278
302
  }
279
303
  };
280
304
  const stop = () => {
@@ -340,7 +364,12 @@ function createLangChainChain(model, config) {
340
364
  if (toolCalls && toolCalls.length > 0 && toolHandler) {
341
365
  for (const tc of toolCalls) {
342
366
  const toolName = tc.function?.name || "";
343
- const args = tc.function?.arguments ? JSON.parse(tc.function.arguments) : {};
367
+ let args = {};
368
+ try {
369
+ args = tc.function?.arguments ? JSON.parse(tc.function.arguments) : {};
370
+ } catch {
371
+ args = {};
372
+ }
344
373
  const toolResult = await toolHandler(toolName, args);
345
374
  messages.push(response);
346
375
  messages.push(new _messages.ToolMessage({
@@ -56,6 +56,8 @@ export const langChainRuntime = {
56
56
  const stream = await modelWithTools.stream(messages);
57
57
  let fullContent = "";
58
58
  const toolCalls = [];
59
+ const toolCallArgumentsMap = /* @__PURE__ */ new Map();
60
+ const toolCallInfoMap = /* @__PURE__ */ new Map();
59
61
  for await (const chunk of stream) {
60
62
  const content = String(chunk?.content || "");
61
63
  if (content) {
@@ -66,10 +68,23 @@ export const langChainRuntime = {
66
68
  if (additionalKwargs?.tool_calls) {
67
69
  for (const tc of additionalKwargs.tool_calls) {
68
70
  toolCalls.push(tc);
69
- options?.onToolCall?.({
70
- name: tc.function?.name || "",
71
- args: tc.function?.arguments ? JSON.parse(tc.function.arguments) : {}
72
- });
71
+ const idx = tc.index ?? 0;
72
+ if (tc.id) {
73
+ toolCallInfoMap.set(idx, { id: tc.id, name: tc.function?.name });
74
+ toolCallArgumentsMap.set(idx, tc.function?.arguments || "");
75
+ } else {
76
+ const currentArgs = toolCallArgumentsMap.get(idx) || "";
77
+ toolCallArgumentsMap.set(idx, currentArgs + (tc.function?.arguments || ""));
78
+ }
79
+ const rawArgs = toolCallArgumentsMap.get(idx) || "";
80
+ try {
81
+ const parsedArgs = JSON.parse(rawArgs);
82
+ options?.onToolCall?.({
83
+ name: toolCallInfoMap.get(idx)?.name || tc.function?.name || "",
84
+ args: parsedArgs
85
+ });
86
+ } catch {
87
+ }
73
88
  }
74
89
  }
75
90
  }
@@ -199,6 +214,7 @@ export function useLangChainStream(options) {
199
214
  error.value = null;
200
215
  isStreaming.value = true;
201
216
  content.value = "";
217
+ abortController = new AbortController();
202
218
  try {
203
219
  const messages = [];
204
220
  if (systemMessage) {
@@ -218,8 +234,11 @@ export function useLangChainStream(options) {
218
234
  }
219
235
  }
220
236
  messages.push(new HumanMessage(prompt));
221
- const stream = await model.stream(messages);
237
+ const stream = await model.stream(messages, { signal: abortController.signal });
222
238
  for await (const chunk of stream) {
239
+ if (abortController?.signal.aborted) {
240
+ break;
241
+ }
223
242
  const chunkContent = String(chunk?.content || "");
224
243
  content.value += chunkContent;
225
244
  }
@@ -230,6 +249,7 @@ export function useLangChainStream(options) {
230
249
  }
231
250
  } finally {
232
251
  isStreaming.value = false;
252
+ abortController = null;
233
253
  }
234
254
  };
235
255
  const stop = () => {
@@ -293,7 +313,12 @@ export function createLangChainChain(model, config) {
293
313
  if (toolCalls && toolCalls.length > 0 && toolHandler) {
294
314
  for (const tc of toolCalls) {
295
315
  const toolName = tc.function?.name || "";
296
- const args = tc.function?.arguments ? JSON.parse(tc.function.arguments) : {};
316
+ let args = {};
317
+ try {
318
+ args = tc.function?.arguments ? JSON.parse(tc.function.arguments) : {};
319
+ } catch {
320
+ args = {};
321
+ }
297
322
  const toolResult = await toolHandler(toolName, args);
298
323
  messages.push(response);
299
324
  messages.push(
package/dist/mcp.cjs CHANGED
@@ -57,11 +57,11 @@ class MCPStdioTransport {
57
57
  this.initialized = this.init();
58
58
  }
59
59
  init() {
60
- return new Promise((resolve, reject) => {
60
+ return new Promise(async (resolve, reject) => {
61
61
  try {
62
62
  const {
63
63
  spawn
64
- } = require("child_process");
64
+ } = await Promise.resolve().then(() => require("child_process"));
65
65
  this.process = spawn(this.command, this.args, {
66
66
  env: {
67
67
  ...process.env,
@@ -313,7 +313,8 @@ function useMCPTools(options) {
313
313
  return client.state.value;
314
314
  },
315
315
  connect: client.connect,
316
- disconnect: client.disconnect
316
+ disconnect: client.disconnect,
317
+ callTool: client.callTool
317
318
  });
318
319
  }
319
320
  function updateAllTools() {
@@ -347,12 +348,8 @@ function useMCPTools(options) {
347
348
  const tool = server.tools.find(t => t.name === name);
348
349
  if (tool) {
349
350
  const clientIndex = serverStates.value.indexOf(server);
350
- const _client = clients[clientIndex];
351
- const mcpClient = useMCPClient({
352
- config: server.config,
353
- autoConnect: false
354
- });
355
- return mcpClient.callTool(name, args);
351
+ const client = clients[clientIndex];
352
+ return client.callTool(name, args);
356
353
  }
357
354
  }
358
355
  throw new Error(`Tool not found: ${name}`);
package/dist/mcp.mjs CHANGED
@@ -50,9 +50,9 @@ class MCPStdioTransport {
50
50
  this.initialized = this.init();
51
51
  }
52
52
  init() {
53
- return new Promise((resolve, reject) => {
53
+ return new Promise(async (resolve, reject) => {
54
54
  try {
55
- const { spawn } = require("child_process");
55
+ const { spawn } = await import("child_process");
56
56
  this.process = spawn(this.command, this.args, {
57
57
  env: { ...process.env, ...this.env },
58
58
  stdio: ["pipe", "pipe", "pipe"]
@@ -300,7 +300,8 @@ export function useMCPTools(options) {
300
300
  return client.state.value;
301
301
  },
302
302
  connect: client.connect,
303
- disconnect: client.disconnect
303
+ disconnect: client.disconnect,
304
+ callTool: client.callTool
304
305
  });
305
306
  }
306
307
  function updateAllTools() {
@@ -334,9 +335,8 @@ export function useMCPTools(options) {
334
335
  const tool = server.tools.find((t) => t.name === name);
335
336
  if (tool) {
336
337
  const clientIndex = serverStates.value.indexOf(server);
337
- const _client = clients[clientIndex];
338
- const mcpClient = useMCPClient({ config: server.config, autoConnect: false });
339
- return mcpClient.callTool(name, args);
338
+ const client = clients[clientIndex];
339
+ return client.callTool(name, args);
340
340
  }
341
341
  }
342
342
  throw new Error(`Tool not found: ${name}`);
@@ -136,7 +136,7 @@ async function XRequest(config, callbacks, options) {
136
136
  const retryDelay = retry.retryDelay || 1e3;
137
137
  const retryCondition = retry.retryCondition || (error => {
138
138
  const msg = error.message.toLowerCase();
139
- return msg.includes("fetch") || msg.includes("network");
139
+ return msg.includes("fetch") || msg.includes("network") || /http error: 5\d\d/.test(msg);
140
140
  });
141
141
  let lastError = null;
142
142
  let attempt = 0;
@@ -153,6 +153,9 @@ async function XRequest(config, callbacks, options) {
153
153
  signal: finalConfig.timeout ? AbortSignal.timeout(finalConfig.timeout) : void 0
154
154
  });
155
155
  mergedCallbacks.onResponse?.(response);
156
+ if (!response.ok) {
157
+ throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
158
+ }
156
159
  if (finalConfig.stream && response.body) {
157
160
  const reader = response.body.getReader();
158
161
  const decoder = new TextDecoder();
@@ -234,7 +237,7 @@ function useConversation(config = {}) {
234
237
  } = config;
235
238
  const messages = (0, _vue.ref)([]);
236
239
  const loadHistory = () => {
237
- if (persist) {
240
+ if (persist && typeof localStorage !== "undefined") {
238
241
  const stored = localStorage.getItem(storageKey);
239
242
  if (stored) {
240
243
  try {
@@ -246,7 +249,7 @@ function useConversation(config = {}) {
246
249
  }
247
250
  };
248
251
  const saveHistory = () => {
249
- if (persist) {
252
+ if (persist && typeof localStorage !== "undefined") {
250
253
  localStorage.setItem(storageKey, JSON.stringify(messages.value));
251
254
  }
252
255
  };
@@ -262,11 +265,13 @@ function useConversation(config = {}) {
262
265
  };
263
266
  const clearHistory = () => {
264
267
  messages.value = [];
265
- if (persist) {
268
+ if (persist && typeof localStorage !== "undefined") {
266
269
  localStorage.removeItem(storageKey);
267
270
  }
268
271
  };
269
- loadHistory();
272
+ if (typeof localStorage !== "undefined") {
273
+ loadHistory();
274
+ }
270
275
  return {
271
276
  messages,
272
277
  addMessage,
@@ -302,7 +307,7 @@ function useConversations(options = {}) {
302
307
  return currentConversation.value?.messages || [];
303
308
  });
304
309
  const loadConversations = () => {
305
- if (persist) {
310
+ if (persist && typeof localStorage !== "undefined") {
306
311
  try {
307
312
  const stored = localStorage.getItem(storageKey);
308
313
  if (stored) {
@@ -320,7 +325,7 @@ function useConversations(options = {}) {
320
325
  }
321
326
  };
322
327
  const saveConversations = () => {
323
- if (persist) {
328
+ if (persist && typeof localStorage !== "undefined") {
324
329
  localStorage.setItem(storageKey, JSON.stringify({
325
330
  conversations: conversations.value,
326
331
  currentId: currentId.value
@@ -393,9 +398,11 @@ function useConversations(options = {}) {
393
398
  saveConversations();
394
399
  }
395
400
  };
396
- loadConversations();
397
- if (conversations.value.length === 0) {
398
- create();
401
+ if (typeof localStorage !== "undefined") {
402
+ loadConversations();
403
+ if (conversations.value.length === 0) {
404
+ create();
405
+ }
399
406
  }
400
407
  return {
401
408
  conversations,
@@ -555,6 +562,7 @@ function useAIChat(options) {
555
562
  const decoder = new TextDecoder();
556
563
  let fullContent = "";
557
564
  let currentToolCalls = [];
565
+ const toolCallArguments = {};
558
566
  while (true) {
559
567
  const {
560
568
  done,
@@ -588,26 +596,38 @@ function useAIChat(options) {
588
596
  if (parsed.choices?.[0]?.delta?.tool_calls) {
589
597
  const toolCalls = parsed.choices[0].delta.tool_calls;
590
598
  for (const tc of toolCalls) {
591
- const existingIndex = currentToolCalls.findIndex(t => t.id === tc.id);
592
- if (existingIndex >= 0) {
593
- currentToolCalls[existingIndex] = {
594
- ...currentToolCalls[existingIndex],
595
- arguments: {
596
- ...currentToolCalls[existingIndex].arguments,
597
- ...(tc.function?.arguments && JSON.parse(tc.function.arguments))
598
- }
599
- };
600
- } else if (tc.id && tc.function?.name) {
601
- currentToolCalls.push({
599
+ const idx = tc.index ?? 0;
600
+ if (tc.id) {
601
+ currentToolCalls[idx] = {
602
602
  id: tc.id,
603
603
  type: "function",
604
- name: tc.function.name,
605
- arguments: tc.function.arguments ? JSON.parse(tc.function.arguments) : {}
606
- });
604
+ name: tc.function?.name || "",
605
+ arguments: {}
606
+ };
607
+ toolCallArguments[idx] = tc.function?.arguments || "";
608
+ } else {
609
+ if (!currentToolCalls[idx]) {
610
+ currentToolCalls[idx] = {
611
+ id: "",
612
+ type: "function",
613
+ name: "",
614
+ arguments: {}
615
+ };
616
+ }
617
+ if (tc.function?.arguments) {
618
+ toolCallArguments[idx] = (toolCallArguments[idx] || "") + tc.function.arguments;
619
+ }
607
620
  }
608
621
  }
622
+ for (let i = 0; i < currentToolCalls.length; i++) {
623
+ if (!currentToolCalls[i]) continue;
624
+ const rawArgs = toolCallArguments[i] || "";
625
+ try {
626
+ currentToolCalls[i].arguments = rawArgs ? JSON.parse(rawArgs) : {};
627
+ } catch {}
628
+ }
609
629
  updateLastMessage({
610
- toolCalls: [...currentToolCalls]
630
+ toolCalls: [...currentToolCalls].filter(Boolean)
611
631
  });
612
632
  }
613
633
  } catch {}
@@ -617,6 +637,16 @@ function useAIChat(options) {
617
637
  await new Promise(resolve => setTimeout(resolve, streamInterval));
618
638
  }
619
639
  }
640
+ for (let i = 0; i < currentToolCalls.length; i++) {
641
+ if (!currentToolCalls[i]) continue;
642
+ const rawArgs = toolCallArguments[i] || "";
643
+ try {
644
+ currentToolCalls[i].arguments = rawArgs ? JSON.parse(rawArgs) : {};
645
+ } catch {
646
+ currentToolCalls[i].arguments = {};
647
+ }
648
+ }
649
+ currentToolCalls = currentToolCalls.filter(Boolean);
620
650
  if (currentToolCalls.length > 0) {
621
651
  updateLastMessage({
622
652
  toolCalls: currentToolCalls
@@ -95,7 +95,7 @@ export async function XRequest(config, callbacks, options) {
95
95
  const retryDelay = retry.retryDelay || 1e3;
96
96
  const retryCondition = retry.retryCondition || ((error) => {
97
97
  const msg = error.message.toLowerCase();
98
- return msg.includes("fetch") || msg.includes("network");
98
+ return msg.includes("fetch") || msg.includes("network") || /http error: 5\d\d/.test(msg);
99
99
  });
100
100
  let lastError = null;
101
101
  let attempt = 0;
@@ -112,6 +112,9 @@ export async function XRequest(config, callbacks, options) {
112
112
  signal: finalConfig.timeout ? AbortSignal.timeout(finalConfig.timeout) : void 0
113
113
  });
114
114
  mergedCallbacks.onResponse?.(response);
115
+ if (!response.ok) {
116
+ throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
117
+ }
115
118
  if (finalConfig.stream && response.body) {
116
119
  const reader = response.body.getReader();
117
120
  const decoder = new TextDecoder();
@@ -177,7 +180,7 @@ export function useConversation(config = {}) {
177
180
  const { maxHistory = 50, persist = false, storageKey = "yh-ai-conversation" } = config;
178
181
  const messages = ref([]);
179
182
  const loadHistory = () => {
180
- if (persist) {
183
+ if (persist && typeof localStorage !== "undefined") {
181
184
  const stored = localStorage.getItem(storageKey);
182
185
  if (stored) {
183
186
  try {
@@ -189,7 +192,7 @@ export function useConversation(config = {}) {
189
192
  }
190
193
  };
191
194
  const saveHistory = () => {
192
- if (persist) {
195
+ if (persist && typeof localStorage !== "undefined") {
193
196
  localStorage.setItem(storageKey, JSON.stringify(messages.value));
194
197
  }
195
198
  };
@@ -205,11 +208,13 @@ export function useConversation(config = {}) {
205
208
  };
206
209
  const clearHistory = () => {
207
210
  messages.value = [];
208
- if (persist) {
211
+ if (persist && typeof localStorage !== "undefined") {
209
212
  localStorage.removeItem(storageKey);
210
213
  }
211
214
  };
212
- loadHistory();
215
+ if (typeof localStorage !== "undefined") {
216
+ loadHistory();
217
+ }
213
218
  return {
214
219
  messages,
215
220
  addMessage,
@@ -245,7 +250,7 @@ export function useConversations(options = {}) {
245
250
  return currentConversation.value?.messages || [];
246
251
  });
247
252
  const loadConversations = () => {
248
- if (persist) {
253
+ if (persist && typeof localStorage !== "undefined") {
249
254
  try {
250
255
  const stored = localStorage.getItem(storageKey);
251
256
  if (stored) {
@@ -263,7 +268,7 @@ export function useConversations(options = {}) {
263
268
  }
264
269
  };
265
270
  const saveConversations = () => {
266
- if (persist) {
271
+ if (persist && typeof localStorage !== "undefined") {
267
272
  localStorage.setItem(
268
273
  storageKey,
269
274
  JSON.stringify({
@@ -339,9 +344,11 @@ export function useConversations(options = {}) {
339
344
  saveConversations();
340
345
  }
341
346
  };
342
- loadConversations();
343
- if (conversations.value.length === 0) {
344
- create();
347
+ if (typeof localStorage !== "undefined") {
348
+ loadConversations();
349
+ if (conversations.value.length === 0) {
350
+ create();
351
+ }
345
352
  }
346
353
  return {
347
354
  conversations,
@@ -493,6 +500,7 @@ export function useAIChat(options) {
493
500
  const decoder = new TextDecoder();
494
501
  let fullContent = "";
495
502
  let currentToolCalls = [];
503
+ const toolCallArguments = {};
496
504
  while (true) {
497
505
  const { done, value } = await reader.read();
498
506
  if (done) break;
@@ -516,25 +524,38 @@ export function useAIChat(options) {
516
524
  if (parsed.choices?.[0]?.delta?.tool_calls) {
517
525
  const toolCalls = parsed.choices[0].delta.tool_calls;
518
526
  for (const tc of toolCalls) {
519
- const existingIndex = currentToolCalls.findIndex((t) => t.id === tc.id);
520
- if (existingIndex >= 0) {
521
- currentToolCalls[existingIndex] = {
522
- ...currentToolCalls[existingIndex],
523
- arguments: {
524
- ...currentToolCalls[existingIndex].arguments,
525
- ...tc.function?.arguments && JSON.parse(tc.function.arguments)
526
- }
527
- };
528
- } else if (tc.id && tc.function?.name) {
529
- currentToolCalls.push({
527
+ const idx = tc.index ?? 0;
528
+ if (tc.id) {
529
+ currentToolCalls[idx] = {
530
530
  id: tc.id,
531
531
  type: "function",
532
- name: tc.function.name,
533
- arguments: tc.function.arguments ? JSON.parse(tc.function.arguments) : {}
534
- });
532
+ name: tc.function?.name || "",
533
+ arguments: {}
534
+ };
535
+ toolCallArguments[idx] = tc.function?.arguments || "";
536
+ } else {
537
+ if (!currentToolCalls[idx]) {
538
+ currentToolCalls[idx] = {
539
+ id: "",
540
+ type: "function",
541
+ name: "",
542
+ arguments: {}
543
+ };
544
+ }
545
+ if (tc.function?.arguments) {
546
+ toolCallArguments[idx] = (toolCallArguments[idx] || "") + tc.function.arguments;
547
+ }
535
548
  }
536
549
  }
537
- updateLastMessage({ toolCalls: [...currentToolCalls] });
550
+ for (let i = 0; i < currentToolCalls.length; i++) {
551
+ if (!currentToolCalls[i]) continue;
552
+ const rawArgs = toolCallArguments[i] || "";
553
+ try {
554
+ currentToolCalls[i].arguments = rawArgs ? JSON.parse(rawArgs) : {};
555
+ } catch {
556
+ }
557
+ }
558
+ updateLastMessage({ toolCalls: [...currentToolCalls].filter(Boolean) });
538
559
  }
539
560
  } catch {
540
561
  }
@@ -544,6 +565,16 @@ export function useAIChat(options) {
544
565
  await new Promise((resolve) => setTimeout(resolve, streamInterval));
545
566
  }
546
567
  }
568
+ for (let i = 0; i < currentToolCalls.length; i++) {
569
+ if (!currentToolCalls[i]) continue;
570
+ const rawArgs = toolCallArguments[i] || "";
571
+ try {
572
+ currentToolCalls[i].arguments = rawArgs ? JSON.parse(rawArgs) : {};
573
+ } catch {
574
+ currentToolCalls[i].arguments = {};
575
+ }
576
+ }
577
+ currentToolCalls = currentToolCalls.filter(Boolean);
547
578
  if (currentToolCalls.length > 0) {
548
579
  updateLastMessage({ toolCalls: currentToolCalls });
549
580
  const toolResults = await executeTools(currentToolCalls);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yh-ui/ai-sdk",
3
- "version": "1.0.51",
3
+ "version": "1.0.53",
4
4
  "description": "YH-UI AI SDK integration for Vercel AI SDK and LangChain",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "peerDependencies": {
47
47
  "vue": "^3.5.35",
48
- "@yh-ui/components": "^1.0.51",
48
+ "@yh-ui/components": "^1.0.53",
49
49
  "@langchain/core": ">=0.3.0"
50
50
  },
51
51
  "peerDependenciesMeta": {
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "devDependencies": {
57
57
  "@langchain/core": ">=0.3.0",
58
- "@yh-ui/components": "^1.0.51",
58
+ "@yh-ui/components": "^1.0.53",
59
59
  "typescript": "^5.7.3",
60
60
  "unbuild": "^3.3.1",
61
61
  "vitest": "^4.0.18",