Skip to content

Commit

Permalink
@llm-ui/csv (#227)
Browse files Browse the repository at this point in the history
Adds a new type of block: csv.
  • Loading branch information
richardgill authored May 28, 2024
1 parent 6d59c83 commit d20764b
Show file tree
Hide file tree
Showing 29 changed files with 845 additions and 8 deletions.
3 changes: 2 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"@llm-ui/markdown",
"@llm-ui/code",
"@llm-ui/shared",
"@llm-ui/json"
"@llm-ui/json",
"@llm-ui/csv"
]
],
"linked": [],
Expand Down
7 changes: 7 additions & 0 deletions .changeset/hot-melons-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@llm-ui/shared": minor
"@llm-ui/json": minor
"@llm-ui/csv": minor
---

Add @llm-ui/csv
1 change: 1 addition & 0 deletions .github/workflows/canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ jobs:
cat << EOF > .changeset/force-canary-build.md
---
'@llm-ui/json': patch
'@llm-ui/csv': patch
'@llm-ui/code': patch
'@llm-ui/markdown': patch
'@llm-ui/react': patch
Expand Down
2 changes: 2 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const packageCodeBlocks = "packages/code";
const packageButtons = "packages/buttons";
const packageShared = "packages/shared";
const packageJson = "packages/json";
const packageCsv = "packages/csv";
const packageCli = "packages/create-llm-ui";

const toolingGen = "tooling/gen";
Expand All @@ -35,6 +36,7 @@ const reactProjects = [
packageButtons,
packageShared,
packageJson,
packageCsv,
];
const reactProjectsGlob = `{${reactProjects.join(",")}}`;
const typescriptProjects = [
Expand Down
48 changes: 48 additions & 0 deletions packages/csv/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@llm-ui/csv",
"version": "0.0.1",
"type": "module",
"license": "MIT",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"files": [
"dist"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsup",
"build-watch": "tsup --watch",
"typecheck": "tsc --noEmit",
"test": "vitest"
},
"dependencies": {
"@llm-ui/react": "workspace:*",
"@llm-ui/shared": "workspace:*"
},
"devDependencies": {
"@llm-ui/tsconfig": "workspace:*",
"@types/react": "18.2.60",
"@types/react-dom": "18.2.19",
"tsup": "^8.0.2",
"typescript": "^5.4.3"
},
"peerDependencies": {
"react": "^18.0.0"
}
}
22 changes: 22 additions & 0 deletions packages/csv/src/block.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { LLMOutputBlock } from "@llm-ui/react";
import { csvBlockLookBack } from "./lookback";
import { findCompleteCsvBlock, findPartialCsvBlock } from "./matchers";
import { CsvBlockOptions } from "./options";

export const csvBlock = (
userOptions?: Partial<CsvBlockOptions>,
): LLMOutputBlock => {
return {
findCompleteMatch: findCompleteCsvBlock(userOptions),
findPartialMatch: findPartialCsvBlock(userOptions),
lookBack: csvBlockLookBack(userOptions),
component: ({ blockMatch }) => (
<div>
<a href="https://llm-ui.com/docs/blocks/csv">
Docs to setup your own component
</a>
<pre>{blockMatch.output}</pre>
</div>
),
};
};
43 changes: 43 additions & 0 deletions packages/csv/src/csvBlockExample.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, expect, it } from "vitest";
import { csvBlockExample } from "./csvBlockExample";
import { CsvBlockOptions } from "./options";

type TestCase = {
name: string;
example: string[];
options?: Partial<CsvBlockOptions>;
expected: string;
};

describe("csvBlockExample", () => {
const testCases: TestCase[] = [
{
name: "single",
example: ["abc"],
expected: "⦅abc⦆",
},
{
name: "multiple",
example: ["abc", "def"],
expected: "⦅abc,def⦆",
},
{
name: "custom delimiter",
example: ["abc", "def"],
options: { delimiter: ";" },
expected: "⦅abc;def⦆",
},
{
name: "custom start and end chars",
example: ["abc", "def"],
options: { startChar: "x", endChar: "y" },
expected: "xabc,defy",
},
];

testCases.forEach(({ name, example, options, expected }) => {
it(name, () => {
expect(csvBlockExample(example, options)).toEqual(expected);
});
});
});
9 changes: 9 additions & 0 deletions packages/csv/src/csvBlockExample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { CsvBlockOptions, getOptions } from "./options";

export const csvBlockExample = (
example: string[],
userOptions?: Partial<CsvBlockOptions>,
): string => {
const { startChar, endChar, delimiter } = getOptions(userOptions);
return `${startChar}${example.join(delimiter)}${endChar}`;
};
76 changes: 76 additions & 0 deletions packages/csv/src/csvBlockPrompt.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { describe, expect, it } from "vitest";
import { csvBlockPrompt } from "./csvBlockPrompt";

describe("csvBlockPrompt", () => {
it("simple", () => {
expect(
csvBlockPrompt({
name: "simple",
examples: [["abc"]],
}),
).toMatchInlineSnapshot(`
"You can respond with a simple component by wrapping a , separated string in ⦅⦆ tags.
Examples:
⦅abc⦆"
`);
});

it("complex", () => {
expect(
csvBlockPrompt({
name: "complex",

examples: [
["abc", "def"],
["ghi", "jkl"],
],
}),
).toMatchInlineSnapshot(`
"You can respond with a complex component by wrapping a , separated string in ⦅⦆ tags.
Examples:
⦅abc,def⦆
⦅ghi,jkl⦆"
`);
});

it("custom delimiter", () => {
expect(
csvBlockPrompt({
name: "complex",

examples: [
["abc", "def"],
["ghi", "jkl"],
],
userOptions: { delimiter: ";" },
}),
).toMatchInlineSnapshot(`
"You can respond with a complex component by wrapping a ; separated string in ⦅⦆ tags.
Examples:
⦅abc;def⦆
⦅ghi;jkl⦆"
`);
});
it("custom start and end chars", () => {
expect(
csvBlockPrompt({
name: "complex",

examples: [
["abc", "def"],
["ghi", "jkl"],
],
userOptions: { startChar: "x", endChar: "y" },
}),
).toMatchInlineSnapshot(`
"You can respond with a complex component by wrapping a , separated string in xy tags.
Examples:
xabc,defy
xghi,jkly"
`);
});
});
18 changes: 18 additions & 0 deletions packages/csv/src/csvBlockPrompt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { csvBlockExample } from "./csvBlockExample";
import { CsvBlockOptions, getOptions } from "./options";

export const csvBlockPrompt = ({
name,
examples,
userOptions,
}: {
name: string;
examples: string[][];
userOptions?: Partial<CsvBlockOptions>;
}): string => {
const { startChar, endChar, delimiter } = getOptions(userOptions);
const examplePrompts = examples.map((example) =>
csvBlockExample(example, userOptions),
);
return `You can respond with a ${name} component by wrapping a ${delimiter} separated string in ${startChar}${endChar} tags.\n\nExamples: \n${examplePrompts.join(`\n`)}`;
};
6 changes: 6 additions & 0 deletions packages/csv/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export { csvBlock } from "./block";
export { csvBlockExample } from "./csvBlockExample";
export { csvBlockPrompt } from "./csvBlockPrompt";
export { csvBlockLookBack } from "./lookback";
export { findCompleteCsvBlock, findPartialCsvBlock } from "./matchers";
export type { CsvBlockOptions } from "./options";
Loading

0 comments on commit d20764b

Please sign in to comment.