mirror of https://github.com/buster-so/buster.git
update to streaming
This commit is contained in:
parent
ededd23024
commit
d437535d50
|
@ -24,10 +24,11 @@ const remarkPlugins = [remarkGfm];
|
|||
const AnimatedMarkdown: React.FC<AnimatedMarkdownProps> = ({
|
||||
content,
|
||||
animation = 'fadeIn',
|
||||
animationDuration = 700,
|
||||
animationDuration = 300,
|
||||
animationTimingFunction = 'ease-in-out',
|
||||
isStreamFinished = true,
|
||||
stripFormatting = false,
|
||||
|
||||
className
|
||||
}) => {
|
||||
const optimizedContent = useMemo(() => {
|
||||
|
@ -48,11 +49,7 @@ const AnimatedMarkdown: React.FC<AnimatedMarkdownProps> = ({
|
|||
components={components}
|
||||
// remarkPlugins are used to extend or modify the Markdown parsing behavior.
|
||||
// Here, remarkGfm enables GitHub Flavored Markdown features (like tables, strikethrough, task lists).
|
||||
remarkPlugins={remarkPlugins}
|
||||
// rehypePlugins are used to transform the resulting HTML AST after Markdown is parsed.
|
||||
// rehypeRaw allows raw HTML in the Markdown to be parsed and rendered (be cautious with untrusted content).
|
||||
// rehypePlugins={rehypePlugins}
|
||||
>
|
||||
remarkPlugins={remarkPlugins}>
|
||||
{optimizedContent}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,15 @@ import { cva } from 'class-variance-authority';
|
|||
import { StreamingMessageCode } from '../../../streaming/StreamingMessageCode';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
|
||||
// Create a context to track list item state
|
||||
const ListItemContext = React.createContext<{
|
||||
isInListItem: boolean;
|
||||
initialHadParagraph: boolean | null;
|
||||
}>({
|
||||
isInListItem: false,
|
||||
initialHadParagraph: null
|
||||
});
|
||||
|
||||
type MarkdownComponentProps = {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
|
@ -26,8 +35,16 @@ export const ParagraphComponent: React.FC<MarkdownComponentProps> = ({
|
|||
style,
|
||||
...rest
|
||||
}) => {
|
||||
const listItemContext = React.useContext(ListItemContext);
|
||||
|
||||
// If we're in a list item that started without paragraphs, don't render the p tag
|
||||
if (listItemContext.isInListItem && listItemContext.initialHadParagraph === false) {
|
||||
// Return children directly without the p tag wrapper
|
||||
return <>{animateTokenizedText(children, rest)}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<p style={style} className={className}>
|
||||
<p style={style} className={className} data-testid="paragraph-component">
|
||||
{animateTokenizedText(children, rest)}
|
||||
</p>
|
||||
);
|
||||
|
@ -218,7 +235,7 @@ export const UnorderedListComponent: React.FC<MarkdownComponentProps> = ({
|
|||
...rest
|
||||
}) => {
|
||||
return (
|
||||
<ul style={style} className={cn(className, 'mt-1 space-y-1', 'list-inside')}>
|
||||
<ul style={style} className={cn(className, 'mt-1 space-y-1', 'list-inside', 'list-disc')}>
|
||||
{children}
|
||||
</ul>
|
||||
);
|
||||
|
@ -231,7 +248,10 @@ export const OrderedListComponent: React.FC<MarkdownComponentProps & { start?: n
|
|||
start
|
||||
}) => {
|
||||
return (
|
||||
<ol style={style} className={cn(className, 'mt-1 space-y-1', 'list-inside')} start={start}>
|
||||
<ol
|
||||
style={style}
|
||||
className={cn(className, 'mt-1 space-y-1', 'list-inside', 'list-decimal')}
|
||||
start={start}>
|
||||
{children}
|
||||
</ol>
|
||||
);
|
||||
|
@ -248,13 +268,15 @@ export const ListItemComponent: React.FC<MarkdownComponentProps> = ({
|
|||
style={style}
|
||||
className={cn(
|
||||
className,
|
||||
// Ensure proper vertical alignment
|
||||
'[&>span]:align-top'
|
||||
|
||||
'[&_span]:inline',
|
||||
// // Normal text flow
|
||||
'whitespace-normal',
|
||||
// Fix alignment of content
|
||||
'[&>span]:inline [&>span]:align-top',
|
||||
'[&>p]:inline [&>p]:align-top'
|
||||
)}>
|
||||
{animateTokenizedText(children, {
|
||||
...rest,
|
||||
doNotAnimateInitialText: true
|
||||
})}
|
||||
{animateTokenizedText(children, rest)}
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -40,6 +40,7 @@ const TokenizedText: React.FC<TokenizedTextProps> = React.memo(
|
|||
(chunk: string, index: number) => (
|
||||
<span
|
||||
key={`animated-chunk-${index}`}
|
||||
className={chunk.trim().length > 0 ? 'whitespace-pre-wrap' : ''}
|
||||
style={doNotAnimateInitialText && index === 0 ? {} : animationStyle}>
|
||||
{chunk}
|
||||
</span>
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
@keyframes buster-fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes buster-blurIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
filter: blur(4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
filter: blur(0px);
|
||||
}
|
||||
from {
|
||||
opacity: 0;
|
||||
filter: blur(3px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
filter: blur(0px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes buster-typewriter {
|
||||
|
@ -71,7 +71,10 @@
|
|||
}
|
||||
|
||||
@keyframes buster-bounceIn {
|
||||
0%, 40%, 80%, 100% {
|
||||
0%,
|
||||
40%,
|
||||
80%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
20% {
|
||||
|
@ -83,7 +86,8 @@
|
|||
}
|
||||
|
||||
@keyframes buster-elastic {
|
||||
0%, 100% {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
10% {
|
||||
|
@ -145,7 +149,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
:root {
|
||||
--buster-marker-animation: none;
|
||||
}
|
||||
|
||||
|
@ -155,4 +159,4 @@
|
|||
|
||||
.buster-code-block {
|
||||
animation: var(--buster-marker-animation);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ const actualTokenArray = [
|
|||
{
|
||||
token:
|
||||
"Looking at the database context, I can see there's a `customer` model that serves as the comprehensive customer model for customer relationship management and purchase behavior analysis. ",
|
||||
delayMs: 800
|
||||
delayMs: 300
|
||||
},
|
||||
{
|
||||
token: 'The customer is identified by `customerid` which is a unique identifier. ',
|
||||
|
@ -91,11 +91,11 @@ const actualTokenArray = [
|
|||
{
|
||||
token:
|
||||
'The customer model also has relationships to `person` (for individual customers) and `store` (for store customers), as well as connections to `sales_order_header` for tracking customer orders. ',
|
||||
delayMs: 900
|
||||
delayMs: 500
|
||||
},
|
||||
{
|
||||
token: 'This gives me a clear way to identify customers in the system.\n\n',
|
||||
delayMs: 8400
|
||||
delayMs: 1000
|
||||
},
|
||||
// {
|
||||
// token: '## PAUSE 400ms seconds\n\n',
|
||||
|
@ -198,13 +198,7 @@ const StreamingDemo: React.FC<{ animation: MarkdownAnimation }> = ({ animation }
|
|||
return (
|
||||
<div className="flex w-full space-y-4 space-x-4">
|
||||
<div className="w-1/2">
|
||||
<AppMarkdownStreaming
|
||||
content={output}
|
||||
isStreamFinished={isStreamFinished}
|
||||
animation={animation}
|
||||
animationDuration={700}
|
||||
animationTimingFunction="ease-in-out"
|
||||
/>
|
||||
<AppMarkdownStreaming content={output} isStreamFinished={isStreamFinished} />
|
||||
</div>
|
||||
<div className="flex w-1/2 flex-col space-y-2 rounded-md border border-gray-200 p-4">
|
||||
<h1 className="bg-gray-100 p-2 text-2xl font-bold">ACTUAL OUTPUT FROM LLM</h1>
|
||||
|
@ -482,7 +476,7 @@ export const ParagraphToListTransition: Story = {
|
|||
<AppMarkdownStreaming
|
||||
content={output}
|
||||
isStreamFinished={isStreamFinished}
|
||||
animation="fadeIn"
|
||||
animation="blurIn"
|
||||
animationDuration={700}
|
||||
animationTimingFunction="ease-in-out"
|
||||
/>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { markdownLookBack } from '@llm-ui/markdown';
|
|||
import { throttleBasic, useLLMOutput } from '@llm-ui/react';
|
||||
import { LLMAnimatedMarkdown } from './AnimatedMarkdown/LLMAnimatedMarkdown';
|
||||
import CodeComponentStreaming from './CodeComponentStreaming';
|
||||
import React, { useContext } from 'react';
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
import type {
|
||||
MarkdownAnimation,
|
||||
MarkdownAnimationTimingFunction
|
||||
|
@ -14,7 +14,7 @@ import { cn } from '@/lib/classMerge';
|
|||
|
||||
const throttle = throttleBasic({
|
||||
// show output as soon as it arrives
|
||||
readAheadChars: 0,
|
||||
readAheadChars: 10,
|
||||
// stay literally at the LLM’s pace
|
||||
targetBufferChars: 10,
|
||||
adjustPercentage: 0.4,
|
||||
|
@ -26,9 +26,9 @@ const throttle = throttleBasic({
|
|||
const AppMarkdownStreaming = ({
|
||||
content,
|
||||
isStreamFinished,
|
||||
animation,
|
||||
animationDuration,
|
||||
animationTimingFunction,
|
||||
animation = 'blurIn',
|
||||
animationDuration = 300,
|
||||
animationTimingFunction = 'linear',
|
||||
className,
|
||||
stripFormatting = false
|
||||
}: {
|
||||
|
@ -40,7 +40,7 @@ const AppMarkdownStreaming = ({
|
|||
className?: string;
|
||||
stripFormatting?: boolean;
|
||||
}) => {
|
||||
const { blockMatches, isFinished, ...rest } = useLLMOutput({
|
||||
const { blockMatches, isFinished } = useLLMOutput({
|
||||
llmOutput: content,
|
||||
fallbackBlock: {
|
||||
component: LLMAnimatedMarkdown,
|
||||
|
|
Loading…
Reference in New Issue