Alchemist Operations

Alchemist: Embedded Agent — Inline Panel

Embed a native chat panel as part of your application interface.

|View as Markdown
Chipp EngineeringEngineering at Chipp
|1 min read
#alchemist#embedded-agent#ui#inline-panel

The inline panel is a full-height, native chat interface that lives in your application alongside other content. Unlike the floating widget, it is part of your page layout and does not float over content.

When to use the inline panel

Use the inline panel when:

  • You want the agent as a main navigation item (e.g., a “Chat” or “Assistant” tab)
  • The agent is a core feature of your application, not supplementary
  • You want the agent interface to span the full height of its container
  • You need fine-grained control over where the panel appears (sidebar, main column, modal, etc.)

For a persistent button that floats over your entire site, use the floating widget instead.

Basic setup

1. Install the feature pack

Follow the installation guide first. Once installed, your project has a ChatPanel component ready to import.

2. Import the component

In your page or layout file:

tsx
import { ChatPanel } from '@/components/embedded-agent/ChatPanel';

3. Render the panel

Place the component in your layout:

tsx
<div className="flex h-screen">
  <nav className="w-48 border-r">
    {/* Sidebar navigation */}
  </nav>
  <main className="flex-1 flex flex-col">
    {/* Your main content */}
    <ChatPanel 
      projectId="proj_..."
      sessionEndpoint="/api/chat/session"
    />
  </main>
</div>

The panel fills its container. Wrap it in a flex container with flex-1 to make it responsive.

Component props

typescript
interface ChatPanelProps {
  // Required
  projectId: string;              // Your Alchemist project ID
  sessionEndpoint: string;        // Your backend session endpoint (e.g., /api/chat/session)
  
  // Optional: Identity
  externalUserId?: string;        // Authenticated user ID (email for email-based auth)
  metadata?: Record<string, any>; // Additional user context (e.g., { "plan": "pro" })
  
  // Optional: Appearance
  theme?: 'light' | 'dark';       // Default: inherits from page
  placeholder?: string;           // Chat input placeholder
  welcomeMessage?: string;        // Initial message before first user input
  
  // Optional: Behavior
  maxMessages?: number;           // Limit conversation length (default: unlimited)
  autoScroll?: boolean;           // Auto-scroll to latest message (default: true)
  allowFileUpload?: boolean;      // Allow users to upload files (default: true)
  
  // Optional: Callbacks
  onSessionStart?: (sessionId: string) => void;  // Fired when session is created
  onMessageSent?: (message: string) => void;     // Fired after user message
  onError?: (error: EmbedError) => void;         // Fired on chat errors
}

Authentication & identity

Authenticated users (email-based)

If your application has logged-in users, pass their identity to the panel:

tsx
const user = useAuthContext(); // Your app's auth hook

<ChatPanel
  projectId="proj_..."
  sessionEndpoint="/api/chat/session"
  externalUserId={user.email}  // ← User's email
  metadata={{ plan: user.plan, account_id: user.id }}
/>

Your backend endpoint receives the externalUserId and metadata, mints a Chipp session, and returns a token. The user’s conversations are retained across browser sessions.

Anonymous/visitor mode

For unauthenticated visitors, omit the externalUserId:

tsx
<ChatPanel
  projectId="proj_..."
  sessionEndpoint="/api/chat/session"
  // externalUserId omitted → anonymous mode
/>

The backend mints an anonymous session (stored in a browser cookie). The conversation is retained for the duration of the session (browser tab).

Upgrading from anonymous to authenticated

If a user starts as anonymous and later logs in, call the upgrade endpoint:

tsx
const sessionId = await fetch('/api/chat/session/upgrade', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    currentSessionId: previousSessionId,
    externalUserId: user.email,
  }),
});

The conversation is merged into the authenticated session.

Styling & customization

The panel uses your project’s design system by default (no iframe, no style isolation). It inherits colors, fonts, and spacing from your application’s CSS.

Theme override

Force light or dark mode:

tsx
<ChatPanel theme="dark" />

Custom CSS classes

Wrap the panel in a container and apply custom styles:

tsx
<div className="custom-chat-wrapper">
  <ChatPanel projectId="..." sessionEndpoint="..." />
</div>
css
.custom-chat-wrapper :global(.embed-panel) {
  border-left: 4px solid var(--primary);
}

Placeholder & welcome messages

Customize input placeholders and initial prompts:

tsx
<ChatPanel
  projectId="..."
  sessionEndpoint="..."
  placeholder="Ask me anything about your account..."
  welcomeMessage="👋 Hi! I can help you find information. What would you like to know?"
/>

Error handling

Errors are emitted via the onError callback. Common errors:

typescript
interface EmbedError {
  code: string;           // e.g., 'AUTH_FAILED', 'SESSION_EXPIRED', 'NETWORK_ERROR'
  message: string;        // Human-readable message
  retryable: boolean;     // Whether the error can be retried
  originalError?: Error;  // Underlying JS error
}

Example handler:

tsx
const handleError = (error: EmbedError) => {
  if (error.code === 'SESSION_EXPIRED') {
    // Prompt user to re-authenticate
    window.location.href = '/login';
  } else if (error.retryable) {
    console.log('Temporary error; will retry automatically.');
  } else {
    showToast(`Chat unavailable: ${error.message}`);
  }
};

<ChatPanel
  projectId="..."
  sessionEndpoint="..."
  onError={handleError}
/>

Responsive layout

The panel fills its container. For responsive designs:

tsx
// Mobile: Panel as full-height overlay
// Desktop: Panel in a sidebar

import { useMediaQuery } from '@/hooks/use-media-query';

const isMobile = useMediaQuery('(max-width: 768px)');

{isMobile ? (
  <Modal open={showChat} onOpenChange={setShowChat}>
    <ChatPanel projectId="..." sessionEndpoint="..." />
  </Modal>
) : (
  <div className="flex">
    <aside className="w-96">
      <ChatPanel projectId="..." sessionEndpoint="..." />
    </aside>
    <main className="flex-1">{/* Page content */}</main>
  </div>
)}

File uploads & attachments

By default, users can attach files to messages (images, PDFs, etc.). Disable this if needed:

tsx
<ChatPanel
  projectId="..."
  sessionEndpoint="..."
  allowFileUpload={false}
/>

Uploaded files are processed by the bound Chipp agent according to its configuration (vision, file reading, etc.).

Conversation history

The panel automatically loads previous messages from the current session. To reset or start a new conversation, reload the panel or create a fresh session token (by updating the externalUserId prop).

Troubleshooting

”Session endpoint returned 401”

  • Verify your backend session endpoint is correctly configured.
  • Ensure your project’s client key is valid: check Project Settings → API Keys.
  • If using authenticated mode, confirm the externalUserId is being passed correctly.

”Chat not loading / shows error spinner indefinitely”

  • Check browser console for network errors.
  • Verify the bound Chipp agent is online (check Chipp workspace).
  • Confirm projectId and sessionEndpoint are correctly set.

”Messages not persisting across sessions”

  • In authenticated mode, ensure externalUserId is consistent (same email).
  • In anonymous mode, sessions are cookie-based; clearing cookies will reset the conversation.

Styling does not match my design system

  • The panel inherits from your page’s CSS. Verify your design system CSS is loaded before the panel renders.
  • Use custom CSS classes (see “Styling & customization” above) to override specific elements.

Next steps