For the full experience, visit the Agent Playground.
++ Oops! Something went wrong while streaming.{' '} + {streamingErrorMessage ? ( + <>{streamingErrorMessage}> + ) : ( + 'Please try refreshing the page or try again later.' + )} +
+ ) + } else if (message.content) { + messageContent = ( +
+
{reference.name}
+{reference.content}
+Reasoning
+STEP {index + 1}
+{stepTitle}
+{tools.tool_name}
+Image unavailable
+ + ${image.url} + ++ {errorMessage} +
+
+
+ {isMounted + ? truncateText(selectedEndpoint, 21) || + ENDPOINT_PLACEHOLDER + : 'http://localhost:7777'} +
+ +
+ {children}
+
+ )
+}
+
+const Blockquote = ({ className, ...props }: BlockquoteProps) => (
+
+)
+
+const AnchorLink = ({ className, ...props }: AnchorLinkProps) => (
+
+)
+
+const Heading1 = ({ className, ...props }: HeadingProps) => (
+
+)
+
+const Heading2 = ({ className, ...props }: HeadingProps) => (
+
+)
+
+const Heading3 = ({ className, ...props }: HeadingProps) => (
+
+)
+
+const Heading4 = ({ className, ...props }: HeadingProps) => (
+
+)
+
+const Heading5 = ({ className, ...props }: HeadingProps) => (
+
+)
+
+const Heading6 = ({ className, ...props }: HeadingProps) => (
+
+)
+
+const Img = ({ src, alt }: ImgProps) => {
+ const [error, setError] = useState(false)
+
+ if (!src) return null
+
+ return (
+ + {children} +
+) + +export default Paragraph diff --git a/src/components/ui/typography/Paragraph/constants.ts b/src/components/ui/typography/Paragraph/constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..e8296e3b1c3ce7d8e2e0b46b5137693b50de9b6d --- /dev/null +++ b/src/components/ui/typography/Paragraph/constants.ts @@ -0,0 +1,14 @@ +import { type ParagraphSizeMap } from './types' + +export const PARAGRAPH_SIZES: ParagraphSizeMap = { + xs: 'text-xs', + sm: 'text-sm', + default: 'text-base', + lg: 'text-lg', + lead: 'font-inter text-[1.125rem] font-medium leading-[1.35rem] tracking-[-0.01em] ', + title: 'font-inter text-[0.875rem] font-medium leading-5 tracking-[-0.02em]', + body: 'font-inter text-[0.875rem] font-normal leading-5 tracking-[-0.02em]', + mono: 'font-dmmono text-[0.75rem] font-normal leading-[1.125rem] tracking-[-0.02em]', + xsmall: + 'font-inter text-[0.75rem] font-normal leading-[1.0625rem] tracking-[-0.02em]' +} diff --git a/src/components/ui/typography/Paragraph/index.ts b/src/components/ui/typography/Paragraph/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..6132e55b61be2f1091042b4a633ef117476f80b7 --- /dev/null +++ b/src/components/ui/typography/Paragraph/index.ts @@ -0,0 +1,3 @@ +import Paragraph from './Paragraph' + +export default Paragraph diff --git a/src/components/ui/typography/Paragraph/types.ts b/src/components/ui/typography/Paragraph/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..66cf4ee32a9274f7fa856811b83f483786468aa6 --- /dev/null +++ b/src/components/ui/typography/Paragraph/types.ts @@ -0,0 +1,23 @@ +import type { ReactNode } from 'react' + +type ParagraphSizes = + | 'xs' + | 'sm' + | 'default' + | 'lg' + | 'lead' + | 'title' + | 'body' + | 'mono' + | 'xsmall' + +export interface ParagraphProps { + children: ReactNode + size?: ParagraphSizes + className?: string + id?: string +} + +export type ParagraphSizeMap = { + [key in ParagraphSizes]: string +} diff --git a/src/hooks/useAIResponseStream.tsx b/src/hooks/useAIResponseStream.tsx new file mode 100644 index 0000000000000000000000000000000000000000..655e02d01b51ea1dd077abdef3ff40c8acf38da4 --- /dev/null +++ b/src/hooks/useAIResponseStream.tsx @@ -0,0 +1,176 @@ +import { useCallback } from 'react' +import { type RunResponse } from '@/types/playground' + +/** + * Processes a single JSON chunk by passing it to the provided callback. + * @param chunk - A parsed JSON object of type RunResponse. + * @param onChunk - Callback to handle the chunk. + */ +function processChunk( + chunk: RunResponse, + onChunk: (chunk: RunResponse) => void +) { + onChunk(chunk) +} + +/** + * Parses a string buffer to extract complete JSON objects. + * + * This function discards any extraneous data before the first '{', then + * repeatedly finds and processes complete JSON objects. + * + * @param text - The accumulated string buffer. + * @param onChunk - Callback to process each parsed JSON object. + * @returns Remaining string that did not form a complete JSON object. + */ +/** + * Extracts complete JSON objects from a buffer string **incrementally**. + * - It allows partial JSON objects to accumulate across chunks. + * - It ensures real-time streaming updates. + */ +function parseBuffer( + buffer: string, + onChunk: (chunk: RunResponse) => void +): string { + let jsonStartIndex = buffer.indexOf('{') + let jsonEndIndex = -1 + + while (jsonStartIndex !== -1) { + let braceCount = 0 + let inString = false + + // Iterate through the buffer to find the end of the JSON object + for (let i = jsonStartIndex; i < buffer.length; i++) { + const char = buffer[i] + + // Check if the character is a double quote and the previous character is not a backslash + // This is to handle escaped quotes in JSON strings + if (char === '"' && buffer[i - 1] !== '\\') { + inString = !inString + } + + // If the character is not inside a string, count the braces + if (!inString) { + if (char === '{') braceCount++ + if (char === '}') braceCount-- + } + + // If the brace count is 0, we have found the end of the JSON object + if (braceCount === 0) { + jsonEndIndex = i + break + } + } + + // If we found a complete JSON object, process it + if (jsonEndIndex !== -1) { + const jsonString = buffer.slice(jsonStartIndex, jsonEndIndex + 1) + try { + const parsed = JSON.parse(jsonString) as RunResponse + processChunk(parsed, onChunk) + } catch { + // Skip invalid JSON, continue accumulating + break + } + buffer = buffer.slice(jsonEndIndex + 1).trim() + jsonStartIndex = buffer.indexOf('{') + jsonEndIndex = -1 + } else { + // No complete JSON found, wait for the next chunk + break + } + } + + return buffer +} + +/** + * Custom React hook to handle streaming API responses as JSON objects. + * + * This hook: + * - Accumulates partial JSON data from streaming responses. + * - Extracts complete JSON objects and processes them via onChunk. + * - Handles errors via onError and signals completion with onComplete. + * + * @returns An object containing the streamResponse function. + */ +export default function useAIResponseStream() { + const streamResponse = useCallback( + async (options: { + apiUrl: string + headers?: Record