Docs
CSV block

CSV block


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

Buttons:
⦅Star ⭐,Confetti 🎉⦆

5x

Installation

pnpm add @llm-ui/csv

Quick start

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 { parseJson5 } from "@llm-ui/json";
import { type LLMOutputComponent } from "@llm-ui/react";

const ButtonsComponent: LLMOutputComponent = ({ blockMatch }) => {
  if (!blockMatch.isVisible) {
    return null;
  }
  const { data: buttons, error } = buttonsSchema.safeParse(
    parseJson5(blockMatch.output),
  );

  if (error) {
    return <div>{error.toString()}</div>;
  }
  return (
    <div>
      {buttons.buttons.map((button, index) => (
        <button key={index}>{button.text}</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 { markdownLookBack } from "@llm-ui/markdown";
import { useLLMOutput, type LLMOutputComponent, useStreamExample } from "@llm-ui/react";
import parseHtml from "html-react-parser";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { jsonBlock } from "@llm-ui/json";

const example = `Buttons:

【{type:"buttons",buttons:[{text:"Star ⭐"}, {text:"Confetti 🎉"}]}】
`;

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


const { blockMatches } = useLLMOutput({
  llmOutput: output,
  blocks: [
    {
      ...jsonBlock("buttons"),
      component: ButtonsComponent, // from step 3
    },
  ],
  fallbackBlock: {
    lookBack: markdownLookBack(),
    component: MarkdownComponent, // from step 1
  },
  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 { jsonBlockPrompt } from "@llm-ui/json";

const prompt = jsonBlockPrompt({
  name: "Button",
  schema: buttonsSchema, // use schema from step 2
  examples: [
    { type: "buttons", buttons: [{ text: "Button 1" }, { text: "Button 2" }] },
  ]
});

Generates:

You can respond with a Button component by wrapping JSON in 【】.
The JSON schema is:
{"type":"object","properties":{"type":{"type":"string","const":"buttons"},"buttons":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false}}},"required":["type","buttons"]}

Examples: 
【{"type":"buttons","buttons":[{"text":"Button 1"},{"text":"Button 2"}]}】

You can also hardcode the prompt into your application.

Options

Default options:

{
  startChar: "⦅",
  endChar: "⦆",
  delimiter: ",", // the seperator between items
  allIndexesVisible: true,
  visibleIndexes: [],
}

allIndexesVisible

allIndexesVisible: true

Generates ‘visibleText’ as the reponse is parsed:

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

blockMatch.isVisible;
// => true

blockMatch.output;
// => "B"
// then
// => "Bu"
// then
// => "But"

allIndexesVisible: false

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

When a partial block is parsed:

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

blockMatch.isVisible;
// => false

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

When the whole block is parsed:

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

blockMatch.isVisible;
// => true

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

visibleIndexes

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

{
  allIndexesVisible: false,
  visibleIndexes: [0], // only the first item is 'visible'
}

Prompts

csvBlockPrompt

Returns a full prompt to send to the LLM.

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

csvBlockPrompt({
  name: "Button",
  examples: [
    ["Button 1", "Button 2"]
  ],
  // optional
  options: {
    startChar: "【",
    endChar: "】",
    delimiter: ",",
  },
});
// =>
"
You can respond with a Button component by wrapping a , separated string in ⦅⦆ tags.

Examples:
⦅Button 1,Button2⦆
"

csvBlockExample

Generates a single CSV block usage example.

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

jsonBlockExample({
  example: ["Button 1", "Button 2"],
  // optional
  options: {
    startChar: "【",
    endChar: "】",
    delimiter: ",",
  },
});
// =>
⦅Button 1,Button2⦆

CSV block functions

csvBlock

Returns a CSV block object to be used by useLLMOutput.

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

const options = {
  startChar: "【",
  endChar: "】",
  delimiter: ",",
  allIndexesVisible: true,
};

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.

Example:

⦅Button 1,Button2⦆

Accepts options parameter.

findPartialCsvBlock

Find a partial CSV block in a string.

Example:

⦅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("Button 1,Button2");
// =>
["Button 1", "Button 2"];