Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 12 additions & 31 deletions docs/content/docs/cookbooks/booking-assistant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -176,47 +176,28 @@ The result holds the top six stays and, when the budget left some out, how many

### 3. Choose the components

Use the built-in form controls, a few components for summaries and messages, and one component of your own:
Use the chat library, React UI's built-in library for Agent Interface answers. It has the form controls, summaries, and messages a booking needs, so the example replaces only its date picker:

```ts title="src/library.ts"
import { createLibrary } from "@openuidev/react-lang";
import { openuiChatLibrary } from "@openuidev/react-ui/genui-lib";
import { DatePicker } from "./components/date-picker";

export const library = createLibrary({
root: "Stack",
components: [
...[
"Stack",
"CardHeader",
"TextContent",
"Callout",
"Table",
"Col",
"Form",
"FormControl",
"Input",
"Select",
"SelectItem",
"Chips",
"ChipItem",
"OptionCards",
"OptionCard",
"Image",
"Buttons",
"Button",
].map((name) => openuiLibrary.components[name]),
DatePicker,
openuiChatLibrary.components.FollowUpBlock,
openuiChatLibrary.components.FollowUpItem,
],
root: openuiChatLibrary.root,
componentGroups: openuiChatLibrary.componentGroups,
components: Object.values({ ...openuiChatLibrary.components, DatePicker }),
});
```

`Form` groups the fields and checks their validation rules when the person clicks a primary button. `Chips` covers short choices, such as stars and amenities. `OptionCards` shows the stays as selectable cards, each with an `Image` of the hotel. By default, an option card shows its image as a small thumbnail; one CSS rule in [styles.css](https://github.com/thesysdev/openui/blob/main/examples/cookbooks/booking-assistant/src/app/styles.css) stretches it across the card.
Every answer is one `Card`. `Form` groups the fields and checks their validation rules when the person clicks a primary button. `Chips` covers short choices, such as stars and amenities. `OptionCards` shows the stays as selectable cards, each with an `Image` of the hotel. By default, an option card shows its image as a small thumbnail; one CSS rule in [styles.css](https://github.com/thesysdev/openui/blob/main/examples/cookbooks/booking-assistant/src/app/styles.css) stretches it across the card.

React UI's own `DatePicker` stores JavaScript `Date` objects. The model can only write text, so it cannot prefill one, and a submitted `Date` reaches the model as a UTC timestamp that can fall on the day before. The example replaces it with a component that has the same name and props but reads and writes `YYYY-MM-DD` strings:

```tsx title="src/components/date-picker.tsx"
export const DatePicker = defineComponent({
name: "DatePicker",
props: openuiLibrary.components.DatePicker.props,
props: openuiChatLibrary.components.DatePicker.props,
description:
"A single date. Prefill it by passing a YYYY-MM-DD string as value; " +
"the form submits YYYY-MM-DD.",
Expand All @@ -233,7 +214,7 @@ export const DatePicker = defineComponent({
});
```

Reusing the built-in props keeps `DatePicker` valid inside `FormControl`, and the new description tells the model the format. The [complete component](https://github.com/thesysdev/openui/blob/main/examples/cookbooks/booking-assistant/src/components/date-picker.tsx) also registers validation rules and closes the calendar once a date is picked. Generate the server specification from the library with `npm run generate`, which also runs before development and builds.
Spreading it after the chat library's components replaces the built-in `DatePicker`, which has the same name. Reusing the built-in props keeps it valid inside `FormControl`, and the new description tells the model the format. The [complete component](https://github.com/thesysdev/openui/blob/main/examples/cookbooks/booking-assistant/src/components/date-picker.tsx) also registers validation rules and closes the calendar once a date is picked. Generate the server specification from the library with `npm run generate`, which also runs before development and builds.

### 4. Describe the booking flow

Expand Down Expand Up @@ -309,7 +290,7 @@ This example runs locally without authentication. Before deploying it, add [auth

## References

- [`@openuidev/react-ui`](/docs/api-reference/react-ui): `AgentInterface` and the built-in component libraries.
- [`@openuidev/react-ui`](/docs/api-reference/react-ui): `AgentInterface` and the chat library.
- [`@openuidev/react-headless`](/docs/api-reference/react-headless): `fetchLLM()`, `openAIReadableStreamAdapter()`, and `useOpenuiCloudStorage()`, which the example imports through `@openuidev/react-ui`.
- [`@openuidev/react-lang`](/docs/api-reference/react-lang): `createLibrary()` and `defineComponent()`.
- [`@openuidev/lang-core`](/docs/openui-lang/system-prompts): `generateSystemPrompt()`.
Expand Down
29 changes: 4 additions & 25 deletions docs/content/docs/cookbooks/conversational-analytics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -140,33 +140,13 @@ The query in the same file uses parameterized SQL. Missing times are left out of

### 3. Choose the components

Use a small selection from the built-in library, plus the chat library's follow-up suggestions:
Use the chat library, React UI's built-in library for Agent Interface answers. Its tables, charts, and follow-up suggestions cover every answer, so the example uses it as is:

```ts title="src/library.ts"
import { createLibrary } from "@openuidev/react-lang";
import { openuiChatLibrary, openuiLibrary } from "@openuidev/react-ui/genui-lib";

export const library = createLibrary({
root: "Stack",
components: [
...[
"Stack",
"Card",
"CardHeader",
"TextContent",
"LineChart",
"BarChart",
"Series",
"Table",
"Col",
].map((name) => openuiLibrary.components[name]),
openuiChatLibrary.components.FollowUpBlock,
openuiChatLibrary.components.FollowUpItem,
],
});
export { openuiChatLibrary as library } from "@openuidev/react-ui/genui-lib";
```

Clicking a follow-up sends its text as the next question.
Every answer is one `Card`: a `Table` or `BarChart` for a ranking, a `LineChart` for a lap-by-lap comparison, and a `FollowUpBlock` at the end. Clicking a follow-up sends its text as the next question.

Generate the server specification from the same library used by the renderer:

Expand Down Expand Up @@ -274,9 +254,8 @@ This example runs locally without authentication. Before deploying it, add [auth

## References

- [`@openuidev/react-ui`](/docs/api-reference/react-ui): `AgentInterface` and the built-in component libraries.
- [`@openuidev/react-ui`](/docs/api-reference/react-ui): `AgentInterface` and the chat library.
- [`@openuidev/react-headless`](/docs/api-reference/react-headless): `fetchLLM()`, `openAIReadableStreamAdapter()`, and `useOpenuiCloudStorage()`, which the example imports through `@openuidev/react-ui`.
- [`@openuidev/react-lang`](/docs/api-reference/react-lang): `createLibrary()`.
- [`@openuidev/lang-core`](/docs/openui-lang/system-prompts): `generateSystemPrompt()`.
- [`@openuidev/server`](/docs/api-reference/server#conversation-history): `storeChatCompletionHistory()`.
- [`@openuidev/cli`](/docs/api-reference/cli): `openui generate`.
Expand Down
56 changes: 10 additions & 46 deletions docs/content/docs/cookbooks/document-comparison.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -175,55 +175,20 @@ The example keeps all 1,564 embeddings in memory and compares them in JavaScript

### 3. Choose the components

Use a selection from the built-in library that covers every part of a comparison, the chat library's follow-up suggestions, and one component of your own:
Use the chat library, React UI's built-in library for Agent Interface answers. It already has every part of a comparison, so the example uses it as is:

```ts title="src/library.ts"
import { createLibrary } from "@openuidev/react-lang";
import { openuiChatLibrary, openuiLibrary } from "@openuidev/react-ui/genui-lib";
import { Sources } from "./components/sources";

export const library = createLibrary({
root: "Stack",
components: [
...[
"Stack",
"CardHeader",
"TextContent",
"Table",
"Col",
"BarChart",
"HorizontalBarChart",
"LineChart",
"Series",
"Callout",
"TagBlock",
].map((name) => openuiLibrary.components[name]),
Sources,
openuiChatLibrary.components.FollowUpBlock,
openuiChatLibrary.components.FollowUpItem,
],
});
export { openuiChatLibrary as library } from "@openuidev/react-ui/genui-lib";
```

`Table` holds the page-cited values, the three chart types cover single values, trends, and breakdowns, and `Callout` flags gaps and conflicts.

`Sources` wraps the source strip that React UI already provides. `defineComponent` gives it a name, a schema the model fills in, and a description for the prompt:

```tsx title="src/components/sources.tsx"
export const Sources = defineComponent({
name: "Sources",
props: z.object({ items: z.array(CardSourceSchema) }),
description:
"A strip of source cards. Each item is { title, sourceName, url }: title is the quote, sourceName names the document and page, and url opens the page.",
component: ({ props }) => (
<CardSourceProvider sources={onePerPage(props.items ?? [])}>
<SourceStrip />
</CardSourceProvider>
),
});
Every answer is one `Card`. `Table` holds the page-cited values, `BarChart`, `LineChart`, and `HorizontalBarChart` cover single values, trends, and breakdowns, `Callout` flags gaps and conflicts, and `FollowUpBlock` suggests criteria to add. The `Card`'s second argument lists the sources, and the `Card` renders them as a strip of source cards below the answer:

```text
root = Card([header, table, chart, gap, followUps], sources)
sources = [{title: "“Revenue for the fiscal year was $10.0 billion, up 5% from a year ago.”", sourceName: "Company A · Page 12", url: "https://example.com/company-a.pdf#page=12"}]
```

Each card shows the site's icon, the company and page, and the quote. `onePerPage` merges two quotes from the same page into one card. Generate the server specification from the same library with `npm run generate`, which also runs before development and builds.
Each card shows the site's icon, the company and page, and the quote, and opens the report at that page. The prompt names the components an answer uses and rules out form controls. Generate the server specification from the same library with `npm run generate`, which also runs before development and builds.

### 4. Customize Agent Interface

Expand Down Expand Up @@ -347,7 +312,7 @@ The prompt turns the evidence into a consistent answer:
- **A table** with one short, page-cited value per cell, such as “$215.9 billion (p. 37)”: one row per criterion and one column per document, or one row per part when a criterion has several, such as each company's segments.
- **Charts chosen from the data**, each at full width: a line chart when a report gives several years of figures, one horizontal bar chart with a bar per company and segment for a breakdown, and a bar chart for one value per document. A document without a value is left out of the chart rather than plotted as zero.
- **Callouts** for information that conflicts or is not directly comparable, such as different fiscal year ends, and for criteria a document does not disclose.
- **Sources**, one card per quoted page with the company, page, and a quote of at most 20 words, linked to that page of the PDF.
- **Sources** in the `Card`'s second argument, one per quoted page with the company, page, and a quote of at most 20 words, linked to that page of the PDF.

## Verify it works

Expand Down Expand Up @@ -375,9 +340,8 @@ This example runs locally without authentication. Before deploying it, add [auth

## References

- [`@openuidev/react-ui`](/docs/api-reference/react-ui): `AgentInterface` and the built-in component libraries.
- [`@openuidev/react-ui`](/docs/api-reference/react-ui): `AgentInterface` and the chat library.
- [`@openuidev/react-headless`](/docs/api-reference/react-headless): `fetchLLM()`, `openAIReadableStreamAdapter()`, and `useOpenuiCloudStorage()`, which the example imports through `@openuidev/react-ui`.
- [`@openuidev/react-lang`](/docs/api-reference/react-lang): `createLibrary()` and `defineComponent()`.
- [`@openuidev/lang-core`](/docs/openui-lang/system-prompts): `generateSystemPrompt()`.
- [`@openuidev/server`](/docs/api-reference/server#conversation-history): `storeChatCompletionHistory()`.
- [`@openuidev/cli`](/docs/api-reference/cli): `openui generate`.
Expand Down
2 changes: 1 addition & 1 deletion examples/cookbooks/booking-assistant/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Try:
| ------------------------------------- | --------------------------------------------------------------------- |
| `src/lib/trivago.ts` | MCP client for trivago's accommodation search, and its filter options |
| `src/lib/tools/search-stays.ts` | Function schema, argument validation, budget filter, and results |
| `src/library.ts` | Shared components for the prompt and renderer |
| `src/library.ts` | The chat library, with its DatePicker replaced |
| `src/components/date-picker.tsx` | A DatePicker that the model can prefill with YYYY-MM-DD dates |
| `src/lib/prompt.ts` | Booking rules and one example for each step of the flow |
| `src/lib/gateway-history.ts` | Loads a thread's stored turns from Gateway as chat messages |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
useStateField,
} from "@openuidev/react-lang";
import { DatePicker as DateField } from "@openuidev/react-ui";
import { openuiLibrary } from "@openuidev/react-ui/genui-lib";
import { openuiChatLibrary } from "@openuidev/react-ui/genui-lib";
import { useEffect, useMemo, useState } from "react";

// React UI's DatePicker stores Date objects: the model cannot prefill one, and a submitted
Expand All @@ -28,7 +28,7 @@ const toIsoDate = (date?: Date) =>

export const DatePicker = defineComponent({
name: "DatePicker",
props: openuiLibrary.components.DatePicker.props,
props: openuiChatLibrary.components.DatePicker.props,
description:
"A single date. Prefill it by passing a YYYY-MM-DD string as value; the form submits YYYY-MM-DD.",
component: ({ props }) => {
Expand Down
6 changes: 3 additions & 3 deletions examples/cookbooks/booking-assistant/src/lib/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const day = (date: string) =>
function examples(today: string) {
const friday = addDays(today, (5 - new Date(today).getUTCDay() + 7) % 7);
const sunday = addDays(friday, 2);
const tripForm = `root = Stack([intro, trip], "column", "m")
const tripForm = `root = Card([intro, trip])
intro = TextContent("Here's what I understood. Fill in anything missing, then find stays.")
trip = Form("trip", tripButtons, [destination, checkIn, checkOut, adults, budget, stars, rating, musts])
tripButtons = Buttons([Button("Find stays", Action([@ToAssistant("Find stays")]), "primary")])
Expand All @@ -30,13 +30,13 @@ stars = FormControl("Hotel stars", Chips("stars", "multiple", [ChipItem("3", "3
rating = FormControl("Guest rating", Chips("min_guest_rating", "single", [ChipItem("any", "Any"), ChipItem("8.0", "8.0+"), ChipItem("8.5", "8.5+")], null, "any"))
musts = FormControl("Must-haves", Chips("amenities", "multiple", [ChipItem("freeWiFi", "Free WiFi"), ChipItem("breakfastIncluded", "Breakfast included"), ChipItem("freeCancellation", "Free cancellation"), ChipItem("pool", "Pool")]))
`;
const chooseStay = `root = Stack([header, choose], "column", "m")
const chooseStay = `root = Card([header, choose])
header = CardHeader("Stays in Lisbon for your dates", "${day(friday)} – ${day(sunday)} · 2 nights · 2 adults · 4★")
choose = Form("choose", chooseButtons, [pick])
chooseButtons = Buttons([Button("Review stay", Action([@ToAssistant("Review stay")]), "primary"), Button("Change search", Action([@ToAssistant("Change search")]), "secondary")])
pick = FormControl("Choose a stay", OptionCards("stay", "single", [OptionCard("https://www.trivago.com/example-riverside-deal", "Riverside Hotel", "€215 a night · €429 total · 4★ · 8.3 (7,960 reviews) · 0.2 km to Praça do Comércio · via Hotel Site", Image("Riverside Hotel", "https://example.com/riverside.jpg")), OptionCard("https://www.trivago.com/example-castle-deal", "Castle View Suites", "€176 a night · €353 total · 4★ · 8.4 (623 reviews) · 0.4 km to the castle · via Booking.com", Image("Castle View Suites", "https://example.com/castle.jpg"))], {required: true}))
`;
const review = `root = Stack([header, details, handoff, actions], "column", "m")
const review = `root = Card([header, details, handoff, actions])
header = CardHeader("Review your stay", "Riverside Hotel · Lisbon")
details = Table([Col("Detail", ["Check-in", "Check-out", "Guests", "Hotel", "Price per night", "Total", "Book on"]), Col("Value", ["${day(friday)}", "${day(sunday)} · 2 nights", "2 adults · 1 room", "4★ · 8.3 guest rating", "€215", "€429", "Hotel Site"])])
handoff = Callout("info", "You'll finish booking on Hotel Site", "trivago found this price. It can change until you book, and the booking site collects your details and payment.")
Expand Down
36 changes: 6 additions & 30 deletions examples/cookbooks/booking-assistant/src/library.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,11 @@
import { createLibrary } from "@openuidev/react-lang";
import { openuiChatLibrary, openuiLibrary } from "@openuidev/react-ui/genui-lib";
import { openuiChatLibrary } from "@openuidev/react-ui/genui-lib";
import { DatePicker } from "./components/date-picker";

// The same subset generates the server's specification and renders in the client.
// The chat library generates the server's specification and renders in the client.
// Its DatePicker is replaced so dates can be prefilled and submitted as YYYY-MM-DD.
export const library = createLibrary({
root: "Stack",
components: [
...[
"Stack",
"CardHeader",
"TextContent",
"Callout",
"Table",
"Col",
"Form",
"FormControl",
"Input",
"Select",
"SelectItem",
"Chips",
"ChipItem",
"OptionCards",
"OptionCard",
"Image",
"Buttons",
"Button",
].map((name) => openuiLibrary.components[name]),
// Replaces React UI's DatePicker so dates can be prefilled and submitted as YYYY-MM-DD.
DatePicker,
// Follow-up suggestions live in the chat library.
openuiChatLibrary.components.FollowUpBlock,
openuiChatLibrary.components.FollowUpItem,
],
root: openuiChatLibrary.root,
componentGroups: openuiChatLibrary.componentGroups,
components: Object.values({ ...openuiChatLibrary.components, DatePicker }),
});
4 changes: 2 additions & 2 deletions examples/cookbooks/conversational-analytics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The tool supports `fastest_laps` (one best recorded lap per driver) and `lap_tim
| `scripts/prepare-data.ts` | Download OpenF1's race snapshot and prepare SQLite |
| `src/lib/f1-data.ts` | Source validation, importer, database, and driver catalog |
| `src/lib/tools/lap-times.ts` | Function schema, argument validation, and read-only query |
| `src/library.ts` | Shared components for the prompt and renderer |
| `src/library.ts` | The chat library, shared by the prompt and renderer |
| `src/lib/prompt.ts` | Gateway instructions and the supported data scope |
| `src/lib/gateway-history.ts` | Loads a thread's stored turns from Gateway as chat messages |
| `src/app/api/chat/route.ts` | `runTools()` generation, streaming, and turn storage |
Expand Down Expand Up @@ -83,6 +83,6 @@ In the browser, inspect `query_lap_times` under **Behind the scenes**, watch par

## Adapt it

Replace `queryLapTimes` with a query to your database or API and update the function schema and prompt. Extend `src/library.ts` to support additional presentations, then regenerate the specification.
Replace `queryLapTimes` with a query to your database or API and update the function schema and prompt. Add your own components to `src/library.ts` for other presentations, then regenerate the specification.

To run the tool and prompt on an agent framework instead, see the [LangGraph Platform](../../agent-frameworks/langgraph-platform), [Vercel AI SDK](../../agent-frameworks/vercel-ai-sdk), [Vercel Eve](../../agent-frameworks/vercel-eve), [Mastra](../../agent-frameworks/mastra), and [Google ADK](../../agent-frameworks/google-adk) examples.
Loading
Loading