assistant-ui icon indicating copy to clipboard operation
assistant-ui copied to clipboard

reasoning as a content part, ie how to format the <think> block of reasoning models

Open doebi opened this issue 1 year ago • 6 comments

Hi @Yonom, I follow your work on this project for some time and already use it in a project.

We are currently looking for a way to better utilize the output wrapped inside the <think> block of current reasoning models qwq and deepseek-r1.

In your code I see you are already working on adding Reasoning as its own content part and I would like to know where I would go about parsing the llm output to extract the content from <think> into the reasoning content part. Thanks

doebi avatar Mar 07 '25 11:03 doebi

Update: I was able to use wrapLanguageModel and extractReasoningMiddleware from Vercel SDK to extract and receive Reasoningtokens, but some parts do not yet seem to fully support this:

⨯ [Error: failed to pipe response] {
  [cause]: [Error: Unhandled chunk type: reasoning]
}

https://sdk.vercel.ai/docs/reference/ai-sdk-core/extract-reasoning-middleware

doebi avatar Mar 07 '25 15:03 doebi

I was still at 0.7.xupdating to 0.8.xhelped, yet the Reasoningcontent part is now understood, but not displayed

doebi avatar Mar 07 '25 16:03 doebi

Maybe that helps in adding some nice UI to that mentioned approach above.

Image

As a workaround for now i added a custom component to render the thinking in a collapsable box with left border, but wasn't so far able to figure out how to add thought duration, that would need type changes so i can add it to ReasoningContentPartComponent

ReasoningPreview.tsx

import { ReasoningContentPartComponent } from "@assistant-ui/react";
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";

export const ReasoningPreview: ReasoningContentPartComponent = ({
  text,
  status,
  duration. // <- TODO: This needs to be added
}) => {
  const [isCollapsed, setIsCollapsed] = useState(true);
  return (
    <div className="mb-4 flex w-full flex-col gap-3 border-l-2 py-1">
      <div className="flex items-center gap-2 px-4">
        {status.type === "running" ?
          <span className="text-muted-foreground text-sm">Thinking...</span>
          : null}
        {status.type === "complete" ?
          <span className="text-muted-foreground text-sm">Thought for {status?.duration || 'some'} seconds</span>
          : null}
        {status.type === "incomplete" ?
          <span className="text-muted-foreground text-sm">Thinking Incomplete, Reason: {status.reason}</span>
          : null}
        {status.type === "requires-action" ?
          <span className="text-muted-foreground text-sm">Thinking Requires Action, Reason: {status.reason}</span>
          : null}
        <div className="flex-grow" />
        <Button
          variant="ghost"
          size="icon"
          onClick={() => setIsCollapsed(!isCollapsed)}
        >
          {isCollapsed ? <ChevronDownIcon /> : <ChevronUpIcon />}
        </Button>
      </div>
      {!isCollapsed && (
        <div className="px-4 text-sm text-muted-foreground">
          {text}
        </div>
      )}
    </div>
  );
};

And registering it in the AssistantMessage component like this:

const AssistantMessage: FC = () => {
  return (
    <MessagePrimitive.Root className="...">
      <div className="...">
        <MessagePrimitive.Content
          components={{
            Text: MarkdownText,
            Reasoning: ReasoningPreview,
          }}
        />
      </div>

    ...

    </MessagePrimitive.Root>
  );
};

vincenzodomina avatar Mar 25 '25 17:03 vincenzodomina

Where can I find a complete and runnable example?

Euraxluo avatar Mar 29 '25 04:03 Euraxluo

https://github.com/assistant-ui/assistant-ui/pull/1735/files

do you have example?

Euraxluo avatar Mar 29 '25 06:03 Euraxluo

this was merged in #1875

AVGVSTVS96 avatar Jun 02 '25 23:06 AVGVSTVS96