Example

Agent Chat Interface

A complete agent chat interface built with ASH UI components. Includes message display, tool call visualization, streaming text, and thinking blocks.

Interactive Demo

Click "Run Demo" to see the full agent interaction flow with thinking, tool calls, and streaming response.

Agent Chat
Can you help me create a new React component?
I'd be happy to help you create a React component! What kind of component would you like to build?
I need a button component with loading state

Implementation

AgentChat.tsx
import {
  MessageList,
  ToolCallCard,
  StreamingText,
  LoadingIndicator,
  ThemeProvider,
} from '@ash-cloud/ash-ui';
import type { NormalizedEntry } from '@ash-cloud/ash-ui/types';
import { useState } from 'react';

export function AgentChat() {
  const [entries, setEntries] = useState<NormalizedEntry[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [streamingContent, setStreamingContent] = useState<string>();

  const sendMessage = async (content: string) => {
    // Add user message
    setEntries(prev => [...prev, {
      id: crypto.randomUUID(),
      entryType: 'user_message',
      content,
      timestamp: new Date().toISOString(),
    }]);

    setIsLoading(true);

    // Connect to SSE endpoint
    const response = await fetch('/api/agent/send', {
      method: 'POST',
      body: JSON.stringify({ message: content }),
    });

    const reader = response.body?.getReader();
    const decoder = new TextDecoder();

    while (true) {
      const { done, value } = await reader?.read() ?? { done: true };
      if (done) break;

      const chunk = decoder.decode(value);
      const events = chunk.split('\n\n').filter(Boolean);

      for (const event of events) {
        const data = JSON.parse(event.replace('data: ', ''));

        switch (data.type) {
          case 'thinking':
            setEntries(prev => [...prev, {
              id: data.id,
              entryType: 'thinking',
              content: data.content,
              timestamp: new Date().toISOString(),
            }]);
            break;

          case 'tool_call':
            setEntries(prev => [...prev, {
              id: data.id,
              entryType: 'tool_call',
              content: '',
              toolCall: data.toolCall,
              timestamp: new Date().toISOString(),
            }]);
            break;

          case 'text_delta':
            setStreamingContent(prev => (prev ?? '') + data.content);
            break;

          case 'message_complete':
            setEntries(prev => [...prev, {
              id: data.id,
              entryType: 'assistant_message',
              content: streamingContent ?? '',
              timestamp: new Date().toISOString(),
            }]);
            setStreamingContent(undefined);
            break;
        }
      }
    }

    setIsLoading(false);
  };

  return (
    <ThemeProvider defaultTheme="dark">
      <div className="flex flex-col h-screen">
        <MessageList
          entries={entries}
          loading={isLoading}
          streamingContent={streamingContent}
          className="flex-1"
        />
        <ChatInput onSend={sendMessage} disabled={isLoading} />
      </div>
    </ThemeProvider>
  );
}