@meistrari/chat-nuxt 3.16.0-rc.0 → 3.16.0-rc.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.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "3.16.0-rc.0",
4
+ "version": "3.16.0-rc.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -180,13 +180,12 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
180
180
 
181
181
  <template v-else>
182
182
  <!--
183
- A live stream normally owns the pending state, so this block stays
184
- out of its way. Stand-in steps are the exception: until the run
185
- emits anything the stream has nothing to show, and that silence is
186
- exactly what they cover.
183
+ Mounted for the whole pending turn so activity remains visible
184
+ while the current streamed chunk is empty between assistant
185
+ messages or tool phases.
187
186
  -->
188
187
  <ChatReasoningThought
189
- v-if="hasReasoning || preparingSteps?.length && !message.content || isPending && !streamActive"
188
+ v-if="hasReasoning || isPending"
190
189
  :reasoning-data="message.reasoningData ?? []"
191
190
  :completed="!isPending"
192
191
  :preparing-steps="preparingSteps"
@@ -207,12 +207,10 @@ export function useChat(options = {}) {
207
207
  );
208
208
  const reasoningCache = createReasoningCache();
209
209
  const processedMessages = computed(() => {
210
- const resolved = resolveReasoningDisplayMessages(messages.value, reasoningCache);
211
210
  const messageId = streamingMessageId.value;
212
211
  const content = directStream.streamingText.value;
213
- if (!messageId || !content)
214
- return resolved;
215
- return resolved.map((message) => message.id === messageId && (message.status === "pending" || pendingStreamReconciliationIds.value.has(message.id)) ? { ...message, content: resolveStreamingContent(message.content, content) } : message);
212
+ const overlaid = !messageId || content === null ? messages.value : messages.value.map((message) => message.id === messageId && (message.status === "pending" || pendingStreamReconciliationIds.value.has(message.id)) ? { ...message, content } : message);
213
+ return resolveReasoningDisplayMessages(overlaid, reasoningCache);
216
214
  });
217
215
  const stopCondition = computed(
218
216
  () => !hasPendingMessage.value && pendingStreamReconciliationIds.value.size === 0
@@ -529,9 +527,9 @@ export function useChat(options = {}) {
529
527
  agentSessionExecutable.value = false;
530
528
  agentSessionRewakeable.value = false;
531
529
  }
530
+ startPolling();
532
531
  };
533
532
  let response;
534
- let acceptedByStream = false;
535
533
  try {
536
534
  response = await directStream.connectMessage(
537
535
  chatApi.path(`/conversations/${currentConversation.value.id}/messages/stream`),
@@ -542,7 +540,6 @@ export function useChat(options = {}) {
542
540
  }),
543
541
  acceptMessagePair
544
542
  );
545
- acceptedByStream = true;
546
543
  } catch (streamError) {
547
544
  const statusCode = getChatApiErrorStatus(streamError);
548
545
  if (statusCode !== null && statusCode >= 400 && statusCode < 500)
@@ -565,8 +562,6 @@ export function useChat(options = {}) {
565
562
  "user"
566
563
  );
567
564
  }
568
- if (!acceptedByStream)
569
- startPolling();
570
565
  return response.userMessage;
571
566
  } catch (err) {
572
567
  const code = getChatApiErrorCode(err);
@@ -115,7 +115,7 @@ function reviveMessagePair(input) {
115
115
  }
116
116
  export function useDirectStream(options = {}) {
117
117
  const activity = ref("idle");
118
- const streamingText = ref("");
118
+ const streamingText = ref(null);
119
119
  const streamingThinking = ref("");
120
120
  const activeRunId = ref(null);
121
121
  let accumulator = createMessagePartsAccumulator();
@@ -123,7 +123,7 @@ export function useDirectStream(options = {}) {
123
123
  function resetAccumulator(runId = null) {
124
124
  accumulator = createMessagePartsAccumulator(runId);
125
125
  activeRunId.value = runId;
126
- streamingText.value = "";
126
+ streamingText.value = null;
127
127
  streamingThinking.value = "";
128
128
  }
129
129
  function beginMessage() {
@@ -199,7 +199,7 @@ export function useDirectStream(options = {}) {
199
199
  if (status === "completed" || status === "waiting_messages") {
200
200
  const result = {
201
201
  status,
202
- text: streamingText.value
202
+ text: streamingText.value ?? ""
203
203
  };
204
204
  if (!optimisticResultReported) {
205
205
  optimisticResultReported = true;
@@ -287,11 +287,11 @@ export function useDirectStream(options = {}) {
287
287
  if (event.kind !== "message-parts" || typeof event.runId !== "string" || typeof event.deltaSeq !== "number" || !Number.isInteger(event.deltaSeq) || event.deltaSeq < 0 || !isRecord(event.event)) {
288
288
  continue;
289
289
  }
290
- const accumulated = applyMessagePartsEvent(accumulator, event);
290
+ const current = applyMessagePartsEvent(accumulator, event);
291
291
  activeRunId.value = accumulator.runId;
292
- if (accumulated.changed) {
293
- streamingText.value = accumulated.text;
294
- streamingThinking.value = accumulated.thinking;
292
+ if (current.changed) {
293
+ streamingText.value = current.text;
294
+ streamingThinking.value = current.thinking;
295
295
  }
296
296
  }
297
297
  }
@@ -1,11 +1,18 @@
1
1
  import type { Message } from '../types/schemas/chat/conversations.js';
2
2
  import type { ReasoningStep } from './reasoning.js';
3
+ type ParsedReasoning = {
4
+ steps: ReasoningStep[];
5
+ thinkingTexts: Set<string>;
6
+ currentAnswerParts: string[];
7
+ currentAnswerStepTexts: Set<string>;
8
+ };
3
9
  export type ReasoningCache = {
4
- bySignature: Map<string, ReasoningStep[]>;
10
+ bySignature: Map<string, ParsedReasoning>;
5
11
  byReference: WeakMap<unknown[], {
6
12
  messageId: string;
7
- parsedSteps: ReasoningStep[];
13
+ parsed: ParsedReasoning;
8
14
  }>;
9
15
  };
10
16
  export declare function createReasoningCache(): ReasoningCache;
11
17
  export declare function resolveReasoningDisplayMessages(messages: Message[], cache?: ReasoningCache): Message[];
18
+ export {};
@@ -46,27 +46,30 @@ function groupReasoningSteps(parsedSteps) {
46
46
  function getReasoningCacheKey(messageId, reasoningData) {
47
47
  return `${messageId}:${JSON.stringify(reasoningData)}`;
48
48
  }
49
- function getParsedReasoningSteps(cache, messageId, reasoningData) {
49
+ function getParsedReasoning(cache, messageId, reasoningData) {
50
50
  const referenceHit = cache.byReference.get(reasoningData);
51
51
  if (referenceHit?.messageId === messageId)
52
- return referenceHit.parsedSteps;
52
+ return referenceHit.parsed;
53
53
  const cacheKey = getReasoningCacheKey(messageId, reasoningData);
54
- let parsedSteps = cache.bySignature.get(cacheKey);
55
- if (!parsedSteps) {
56
- parsedSteps = parseReasoningFromPolling(reasoningData);
54
+ let parsed = cache.bySignature.get(cacheKey);
55
+ if (!parsed) {
56
+ parsed = parseReasoningFromPolling(reasoningData);
57
57
  for (const staleKey of cache.bySignature.keys()) {
58
58
  if (staleKey.startsWith(`${messageId}:`))
59
59
  cache.bySignature.delete(staleKey);
60
60
  }
61
- cache.bySignature.set(cacheKey, parsedSteps);
61
+ cache.bySignature.set(cacheKey, parsed);
62
62
  }
63
- cache.byReference.set(reasoningData, { messageId, parsedSteps });
64
- return parsedSteps;
63
+ cache.byReference.set(reasoningData, { messageId, parsed });
64
+ return parsed;
65
65
  }
66
66
  function parseReasoningFromPolling(reasoning) {
67
67
  const allSteps = [];
68
68
  const seenContentIds = /* @__PURE__ */ new Set();
69
69
  const seenTexts = /* @__PURE__ */ new Set();
70
+ const thinkingTexts = /* @__PURE__ */ new Set();
71
+ let currentAnswerParts = [];
72
+ let currentAnswerStepTexts = /* @__PURE__ */ new Set();
70
73
  for (const item of reasoning) {
71
74
  if (!item || typeof item !== "object")
72
75
  continue;
@@ -98,15 +101,23 @@ function parseReasoningFromPolling(reasoning) {
98
101
  const message = itemObj.message;
99
102
  if (!Array.isArray(message.content))
100
103
  continue;
104
+ const answerParts = [];
105
+ const answerStepTexts = /* @__PURE__ */ new Set();
101
106
  for (const contentPart of message.content) {
102
107
  if (!contentPart || typeof contentPart !== "object")
103
108
  continue;
104
109
  const part = contentPart;
105
- if (part.type === "text" && typeof part.text === "string") {
110
+ if ((part.type === "text" || part.type === "thinking") && typeof part.text === "string") {
111
+ if (part.type === "text")
112
+ answerParts.push(part.text);
113
+ if (part.type === "thinking")
114
+ thinkingTexts.add(part.text);
106
115
  if (seenTexts.has(part.text))
107
116
  continue;
108
117
  seenTexts.add(part.text);
109
118
  allSteps.push(part.text);
119
+ if (part.type === "text")
120
+ answerStepTexts.add(part.text);
110
121
  } else if (part.type === "tool_use" && part.id && part.name) {
111
122
  const toolId = part.id;
112
123
  if (seenContentIds.has(`tool_use:${toolId}`))
@@ -130,6 +141,10 @@ function parseReasoningFromPolling(reasoning) {
130
141
  });
131
142
  }
132
143
  }
144
+ if (!itemObj.parent_tool_use_id && answerParts.length > 0) {
145
+ currentAnswerParts = answerParts;
146
+ currentAnswerStepTexts = answerStepTexts;
147
+ }
133
148
  } else if (itemObj.type === "text" && typeof itemObj.text === "string") {
134
149
  if (seenTexts.has(itemObj.text))
135
150
  continue;
@@ -158,21 +173,23 @@ function parseReasoningFromPolling(reasoning) {
158
173
  });
159
174
  }
160
175
  }
161
- return groupReasoningSteps(allSteps);
176
+ return { steps: groupReasoningSteps(allSteps), thinkingTexts, currentAnswerParts, currentAnswerStepTexts };
162
177
  }
163
178
  export function resolveReasoningDisplayMessages(messages, cache = createReasoningCache()) {
164
179
  const renderedSteps = /* @__PURE__ */ new Set();
165
180
  return messages.map((message) => {
166
181
  if (message.role !== "assistant" || !message.reasoningData || !Array.isArray(message.reasoningData))
167
182
  return message;
168
- const parsedSteps = getParsedReasoningSteps(cache, message.id, message.reasoningData);
183
+ const parsed = getParsedReasoning(cache, message.id, message.reasoningData);
169
184
  const displaySteps = [];
170
- for (const step of parsedSteps) {
185
+ const currentAnswerMatches = message.content === parsed.currentAnswerParts.join("");
186
+ for (const step of parsed.steps) {
171
187
  const key = getReasoningStepKey(step);
172
188
  if (renderedSteps.has(key))
173
189
  continue;
174
- if (typeof step === "string" && message.content && step === message.content)
190
+ if (typeof step === "string" && currentAnswerMatches && parsed.currentAnswerStepTexts.has(step) && !parsed.thinkingTexts.has(step)) {
175
191
  continue;
192
+ }
176
193
  renderedSteps.add(key);
177
194
  displaySteps.push(step);
178
195
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "3.16.0-rc.0",
3
+ "version": "3.16.0-rc.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {