Docs
CSV block

CSV block


Allow LLMs to reply with CSV, which can be rendered as custom components in your application.

Buttons:
⦅buttons;Star ⭐;Confetti 🎉⦆

5x

Installation

pnpm add @llm-ui/csv

Quick start

View on GitHub

Install dependencies

pnpm add @llm-ui/csv @llm-ui/react @llm-ui/markdown react-markdown remark-gfm html-react-parser

Step 1: Create a markdown component

Create a component to render markdown using react-markdown.

import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { type LLMOutputComponent } from "@llm-ui/react";


// Customize this component with your own styling
const MarkdownComponent: LLMOutputComponent = ({ blockMatch }) => {
  const markdown = blockMatch.output;
  return <ReactMarkdown remarkPlugins={[remarkGfm]}>{markdown}</ReactMarkdown>;
};

Read more in the markdown block docs

Step 2: Create a custom block component

import { type LLMOutputComponent } from "@llm-ui/react";
import { parseCsv } from '@llm-ui/csv'

// Customize this component with your own styling
const ButtonsComponent: LLMOutputComponent = ({ blockMatch }) => {
  if (!blockMatch.isVisible) {
    return null;
  }
  const [_type, ...buttons] = parseCsv(blockMatch.output, {type: 'buttons'});

  return (
    <div>
      {buttons.map((buttonText, index) => (
        <button key={index}>{buttonText}</button>
      ))}
    </div>
  );
};

Step 3: Render custom blocks with llm-ui

Now we’ve created our components, we’re ready to use useLLMOutput to render language model output which contains markdown and buttons components.

import { csvBlock } from "@llm-ui/csv";
import { markdownLookBack } from "@llm-ui/markdown";
import {
  useLLMOutput,
  useStreamExample,
} from "@llm-ui/react";

const example = `
Buttons
⦅buttons,Button 1,Button2⦆
`;

const Example = () => {
  const { isStreamFinished, output } = useStreamExample(example);

  const { blockMatches } = useLLMOutput({
    llmOutput: output,
    blocks: [
      {
        ...csvBlock({type: 'buttons'}), // from step 2
        component: ButtonsComponent,
      },
    ],
    fallbackBlock: {
      component: MarkdownComponent, // from step 1
      lookBack: markdownLookBack(),
    },
    isStreamFinished,
  });
  return (
    <div>
      {blockMatches.map((blockMatch, index) => {
        const Component = blockMatch.block.component;
        return <Component key={index} blockMatch={blockMatch} />;
      })}
    </div>
  );
};

Step 4: Prompt LLM with your custom block

Generate the prompt for your JSON block:

import { csvBlockPrompt } from "@llm-ui/csv";

const prompt = csvBlockPrompt({
  name: "Buttons",
  examples: [["Button 1", "Button 2"]],
  options: {
    type: "buttons",
    delimiter: ";",
    startChar: "[",
    endChar: "]",
  },
});

Generates:

You can respond with a Buttons component using the following ; delimited syntax: [buttons;]

Examples: 
[buttons;Button 1;Button 2]

You can also hardcode the prompt into your application.

Options

{
  // Required:
  type: "buttons", // the first item in the CSV
  // Optional, defaults:
  startChar: "⦅",
  endChar: "⦆",
  delimiter: ",", // the seperator between items
  allIndexesVisible: true,
  visibleIndexes: [],
}

allIndexesVisible

allIndexesVisible: true

type is always skipped.

Generates ‘visibleText’ as the reponse is parsed:

⦅buttons,Button 1,But
blockMatch.visibleText;
// => "B"
// then
// => "Bu"
// then
// => "But"
// later..
// => "Button 1But"

blockMatch.isVisible;
// => true

blockMatch.output;
// => "buttons,B"
// then
// => "buttons,Bu"
// then
// => "buttons,But"
// later..
// => "buttons,Button 1,But"

allIndexesVisible: false

Generate no ‘visibleText’ until the whole block is parsed.

When a partial block is parsed:

⦅buttons,Button 1,But`
blockMatch.visibleText;
// => ""

blockMatch.isVisible;
// => false

blockMatch.output;
// => "buttons,Button 1,But"

When the whole block is parsed:

⦅buttons,Button 1,Button 2⦆
blockMatch.visibleText;
// => " "

blockMatch.isVisible;
// => true

blockMatch.output;
// => "buttons,Button 1,Button 2"

visibleIndexes

You can use visibleIndexes with allIndexesVisible: false to determine which array indexes are visible.

{
  type: "buttons",
  allIndexesVisible: false,
  visibleIndexes: [1], // only the first item (after the type) is 'visible'
}

Prompts

csvBlockPrompt

Returns a full prompt to send to the LLM.

import { csvBlockPrompt } from "@llm-ui/csv";

const prompt = csvBlockPrompt({
  name: "Buttons",
  examples: [["Button 1", "Button 2"]],
  options: {
    type: "buttons",
    delimiter: ";",
    startChar: "[",
    endChar: "]",
  },
});

Generates:

You can respond with a Buttons component using the following ; delimited syntax: [buttons;]

Examples: 
[buttons;Button 1;Button 2]

You can also hardcode the prompt into your application.

csvBlockExample

Returns a single CSV block usage example.

import { csvBlockExample } from "@llm-ui/csv";

const prompt = csvBlockExample(["Button 1", "Button 2"], {
  type: "buttons",
  delimiter: ";",
  startChar: "[",
  endChar: "]",
});

Generates:

[buttons;Button 1;Button 2]

CSV block functions

csvBlock

Returns a CSV block object to be used by useLLMOutput.

import { csvBlock } from "@llm-ui/csv";

const options = {
  type: "buttons", // the first item in the CSV
  delimiter: ";",
  startChar: "[",
  endChar: "]",
};

csvBlock(options);
// =>
{
  findCompleteMatch: findCompleteCsvBlock(options),
  findPartialMatch: findPartialCsvBlock(options),
  lookBack: csvBlockLookBack(options),
  component: () => <div>Json block</div>,
}

Accepts options parameter.

findCompleteCsvBlock

Finds a complete CSV block in a string.

For example:

⦅buttons,Button 1,Button2⦆

Accepts options parameter.

findPartialCsvBlock

Find a partial CSV block in a string.

For example:

⦅buttons,Button 1,But

csvBlockLookBack

Look back function for the CSV block.

Accepts options parameter.

Parse

parseCsv

Parse a CSV output string.

import { parseCsv } from "@llm-ui/csv";

parseCsv("buttons,Button 1,Button2");
// =>
["buttons", "Button 1", "Button 2"];