Spaces:
Sleeping
Sleeping
Andrew
commited on
Commit
·
f41db77
1
Parent(s):
4123958
(fix) Fix ChatMessage handling of streaming think segments
Browse files
src/lib/components/chat/ChatMessage.svelte
CHANGED
|
@@ -12,17 +12,18 @@
|
|
| 12 |
import CarbonPen from "~icons/carbon/pen";
|
| 13 |
import UploadedFile from "./UploadedFile.svelte";
|
| 14 |
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
import MarkdownRenderer from "./MarkdownRenderer.svelte";
|
| 21 |
import OpenReasoningResults from "./OpenReasoningResults.svelte";
|
| 22 |
import Alternatives from "./Alternatives.svelte";
|
| 23 |
import MessageAvatar from "./MessageAvatar.svelte";
|
| 24 |
import PersonaResponseCarousel from "./PersonaResponseCarousel.svelte";
|
| 25 |
-
|
|
|
|
| 26 |
|
| 27 |
interface Props {
|
| 28 |
message: Message;
|
|
@@ -92,14 +93,14 @@
|
|
| 92 |
// const urlNotTrailing = $derived(page.url.pathname.replace(/\/$/, ""));
|
| 93 |
// let downloadLink = $derived(urlNotTrailing + `/message/${message.id}/prompt`);
|
| 94 |
|
| 95 |
-
|
| 96 |
let hasServerReasoning = $derived(
|
| 97 |
reasoningUpdates &&
|
| 98 |
reasoningUpdates.length > 0 &&
|
| 99 |
!!message.reasoning &&
|
| 100 |
message.reasoning.trim().length > 0
|
| 101 |
);
|
| 102 |
-
|
| 103 |
|
| 104 |
$effect(() => {
|
| 105 |
if (isCopied) {
|
|
@@ -193,28 +194,27 @@
|
|
| 193 |
<IconLoading classNames="loading inline ml-2 first:ml-0" />
|
| 194 |
{/if}
|
| 195 |
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
{@const summary = isClosed
|
| 202 |
-
? thinkContent.trim().split(/\n+/)[0] || "Reasoning"
|
| 203 |
-
: "Thinking..."}
|
| 204 |
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
<div
|
| 212 |
-
class="prose max-w-none dark:prose-invert max-sm:prose-sm prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900"
|
| 213 |
-
>
|
| 214 |
-
<MarkdownRenderer content={part} loading={isLast && loading} />
|
| 215 |
-
</div>
|
| 216 |
{/if}
|
| 217 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
{:else}
|
| 219 |
<div
|
| 220 |
class="prose max-w-none dark:prose-invert max-sm:prose-sm prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900"
|
|
|
|
| 12 |
import CarbonPen from "~icons/carbon/pen";
|
| 13 |
import UploadedFile from "./UploadedFile.svelte";
|
| 14 |
|
| 15 |
+
import {
|
| 16 |
+
MessageUpdateType,
|
| 17 |
+
type MessageReasoningUpdate,
|
| 18 |
+
MessageReasoningUpdateType,
|
| 19 |
+
} from "$lib/types/MessageUpdate";
|
| 20 |
import MarkdownRenderer from "./MarkdownRenderer.svelte";
|
| 21 |
import OpenReasoningResults from "./OpenReasoningResults.svelte";
|
| 22 |
import Alternatives from "./Alternatives.svelte";
|
| 23 |
import MessageAvatar from "./MessageAvatar.svelte";
|
| 24 |
import PersonaResponseCarousel from "./PersonaResponseCarousel.svelte";
|
| 25 |
+
import ThinkingPlaceholder from "./ThinkingPlaceholder.svelte";
|
| 26 |
+
import { hasThinkSegments, splitThinkSegments } from "$lib/utils/stripThinkBlocks";
|
| 27 |
|
| 28 |
interface Props {
|
| 29 |
message: Message;
|
|
|
|
| 93 |
// const urlNotTrailing = $derived(page.url.pathname.replace(/\/$/, ""));
|
| 94 |
// let downloadLink = $derived(urlNotTrailing + `/message/${message.id}/prompt`);
|
| 95 |
|
| 96 |
+
let thinkSegments = $derived.by(() => splitThinkSegments(message.content));
|
| 97 |
let hasServerReasoning = $derived(
|
| 98 |
reasoningUpdates &&
|
| 99 |
reasoningUpdates.length > 0 &&
|
| 100 |
!!message.reasoning &&
|
| 101 |
message.reasoning.trim().length > 0
|
| 102 |
);
|
| 103 |
+
let hasClientThink = $derived(!hasServerReasoning && hasThinkSegments(message.content));
|
| 104 |
|
| 105 |
$effect(() => {
|
| 106 |
if (isCopied) {
|
|
|
|
| 194 |
<IconLoading classNames="loading inline ml-2 first:ml-0" />
|
| 195 |
{/if}
|
| 196 |
|
| 197 |
+
{#if hasClientThink}
|
| 198 |
+
{#each thinkSegments as part, _i}
|
| 199 |
+
{#if part && part.startsWith("<think>")}
|
| 200 |
+
{@const trimmed = part.trimEnd()}
|
| 201 |
+
{@const isClosed = trimmed.endsWith("</think>")}
|
|
|
|
|
|
|
|
|
|
| 202 |
|
| 203 |
+
{#if isClosed}
|
| 204 |
+
{@const thinkContent = trimmed.slice(7, -8)}
|
| 205 |
+
{@const summary = thinkContent.trim().split(/\n+/)[0] || "Reasoning"}
|
| 206 |
+
<OpenReasoningResults {summary} content={thinkContent} loading={false} />
|
| 207 |
+
{:else}
|
| 208 |
+
<ThinkingPlaceholder />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
{/if}
|
| 210 |
+
{:else if part && part.trim().length > 0}
|
| 211 |
+
<div
|
| 212 |
+
class="prose max-w-none dark:prose-invert max-sm:prose-sm prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900"
|
| 213 |
+
>
|
| 214 |
+
<MarkdownRenderer content={part} loading={isLast && loading} />
|
| 215 |
+
</div>
|
| 216 |
+
{/if}
|
| 217 |
+
{/each}
|
| 218 |
{:else}
|
| 219 |
<div
|
| 220 |
class="prose max-w-none dark:prose-invert max-sm:prose-sm prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900"
|