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
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"];