Skip to content
Open
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
17 changes: 11 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,17 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged.

### Generated interfaces use A2UI 1.0

With generative UI on, a Bot builds an interface from the A2UI 1.0 basic components instead of the
A2UI 0.9 ones. It's the same set of 18: text, images, icons, video, audio, rows, columns, lists,
cards, tabs, dividers, dialogs, buttons, checkboxes, text fields, date pickers, choice pickers and
sliders. They now take OpenBot's colours, radii and font, in light and dark. The gallery components a
Bot is granted are unchanged. `OPENBOT_GENERATIVE_UI=false` still switches it all off, on the server
as well as in the browser.
With generative UI on, a Bot builds an interface from CopilotKit's standard catalog on A2UI 1.0
instead of the 18 A2UI 0.9 basic components. That's 65 components for layout, text, inputs, tables,
charts, media and composites such as entity cards and image grids, including the A2UI 1.0 basic
components under their A2UI names. They take OpenBot's colours, radii and font, in light and dark.

Four are left out. Map needs a map library OpenBot does not ship. Favicon, LinkCard and Citation load
site icons from Google's favicon service, which would send every linked domain to Google.

None of these is listed in the components gallery, and an admin can't turn one off for a Bot. The
gallery components a Bot is granted are unchanged. `OPENBOT_GENERATIVE_UI=false` still switches it
all off, on the server as well as in the browser.

### CopilotKit 1.83

Expand Down
56 changes: 23 additions & 33 deletions app/src/lib/copilot/a2ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,31 @@ import {
type GenerativeUITheme,
standardUI,
} from "@copilotkit/react-core/v2/generative-ui";
import { SERIES_COLOURS } from "@/components/gallery/palette";

/**
* The A2UI 1.0 basic components: the same set the A2UI 0.9 basic catalog drew before. These are
* declarative primitives, not the separately granted OpenBot gallery components.
* The standard components a Bot is not given.
*
* - Map needs maplibre-gl, which OpenBot does not ship. Without it the SDK draws a list of places,
* which the model would then be promising as a map.
* - Favicon, LinkCard and Citation load site icons from Google's favicon service, which would send
* every linked domain to Google.
*/
export const A2UI_BASIC_COMPONENTS = [
"Text",
"Image",
"Icon",
"Video",
"AudioPlayer",
"Row",
"Column",
"List",
"Card",
"Tabs",
"Divider",
"Modal",
"Button",
"CheckBox",
"TextField",
"DateTimeInput",
"ChoicePicker",
"Slider",
export const OMITTED_STANDARD_COMPONENTS = [
"Map",
"Favicon",
"LinkCard",
"Citation",
] as const;

const basic: ReadonlySet<string> = new Set(A2UI_BASIC_COMPONENTS);

/**
* The components a Bot can draw an interface from: CopilotKit's standard catalog, cut down to the
* A2UI 1.0 basic components, which it carries under their A2UI names. The rest of the standard
* catalog stays out until an admin can see and govern it.
* The components a Bot can draw an interface from: CopilotKit's standard catalog, which carries the
* A2UI 1.0 basic components under their A2UI names, less the ones above. These are declarative
* primitives, not the separately granted OpenBot gallery components.
*/
export const OPENBOT_UI = createGenerativeUI({
id: "openbot/ui@1",
extends: [
standardUI.omit(
Object.keys(standardUI.manifestComponents).filter(
(name) => !basic.has(name),
) as Parameters<typeof standardUI.omit>[0],
),
],
extends: [standardUI.omit([...OMITTED_STANDARD_COMPONENTS])],
components: {},
});

Expand Down Expand Up @@ -98,6 +81,13 @@ export const OPENBOT_UI_THEME: GenerativeUITheme = {
"--a2ui-radius-2xl": radius(1.8),
"--a2ui-radius-3xl": radius(2.2),
"--a2ui-radius-4xl": radius(2.6),
// The gallery's fixed series colours, so a series reads the same in either kind of chart.
"--a2ui-chart-1": SERIES_COLOURS[0],
"--a2ui-chart-2": SERIES_COLOURS[1],
"--a2ui-chart-3": SERIES_COLOURS[2],
"--a2ui-chart-4": SERIES_COLOURS[3],
"--a2ui-chart-5": SERIES_COLOURS[4],
"--a2ui-chart-6": SERIES_COLOURS[5],
};

const GENERATIVE_UI = {
Expand Down
44 changes: 35 additions & 9 deletions app/tests/a2ui.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { afterAll, afterEach, beforeAll, expect, test } from "bun:test";
import { CopilotKitProvider } from "@copilotkit/react-core/v2";
import {
GenerativeUISurface,
standardUI,
useGenerativeUIRuntime,
} from "@copilotkit/react-core/v2/generative-ui";
import { GlobalRegistrator } from "@happy-dom/global-registrator";
import { cleanup, render } from "@testing-library/react";
import { useEffect } from "react";
import {
A2UI_BASIC_COMPONENTS,
generativeUiProviderOptions,
OMITTED_STANDARD_COMPONENTS,
OPENBOT_UI,
OPENBOT_UI_THEME,
} from "@/lib/copilot/a2ui";
Expand Down Expand Up @@ -95,16 +96,41 @@ test("disabled and unresolved deployments do not send the catalog", () => {
});
});

test("the catalog is the A2UI 1.0 basic components, without the gallery", () => {
test("the catalog is the standard one, less the omitted components and without the gallery", () => {
expect(OPENBOT_UI.id).toBe("openbot/ui@1");
// The same set the A2UI 0.9 basic catalog drew, under their A2UI names, and nothing else.
expect([...OPENBOT_UI.components.keys()].sort()).toEqual(
[...A2UI_BASIC_COMPONENTS].sort(),
const expected = Object.keys(standardUI.manifestComponents).filter(
(name) =>
!(OMITTED_STANDARD_COMPONENTS as readonly string[]).includes(name),
);
expect(A2UI_BASIC_COMPONENTS).toHaveLength(18);
// The rest of the standard catalog stays out until an admin can see and govern it.
expect(OPENBOT_UI.components.has("Table")).toBe(false);
expect(OPENBOT_UI.components.has("LinkCard")).toBe(false);
expect([...OPENBOT_UI.components.keys()].sort()).toEqual(expected.sort());
expect(expected).toHaveLength(65);
// The A2UI 1.0 basic components, under their A2UI names.
for (const name of [
"Text",
"Image",
"Icon",
"Video",
"AudioPlayer",
"Row",
"Column",
"List",
"Card",
"Tabs",
"Divider",
"Modal",
"Button",
"CheckBox",
"TextField",
"DateTimeInput",
"ChoicePicker",
"Slider",
])
expect(OPENBOT_UI.components.has(name)).toBe(true);
expect(OPENBOT_UI.components.has("Table")).toBe(true);
expect(OPENBOT_UI.components.has("Chart")).toBe(true);
// Map needs maplibre-gl; the other three send linked domains to Google's favicon service.
for (const name of ["Map", "Favicon", "LinkCard", "Citation"])
expect(OPENBOT_UI.components.has(name)).toBe(false);
// The gallery components keep their separate per-Bot tool and grant path.
expect(OPENBOT_UI.components.has("showTable")).toBe(false);
expect(OPENBOT_UI.components.has("askForm")).toBe(false);
Expand Down
5 changes: 3 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ at `agent-langgraph` on a laptop.
| `COMPOSIO_API_KEY` | unset | One key for the whole deployment, for the broker that holds people's accounts for a few hundred apps. Unset, there is nothing to connect, nothing to grant and no Composio tool for a Bot to call; what remains is one row that goes nowhere, under **More apps** on the admin Plugins page, naming this variable. See [Composio](plugins/composio.md). |

**`OPENBOT_GENERATIVE_UI`** enables generated interfaces by default: streamed HTML/CSS/JavaScript
in a sandboxed iframe, and A2UI 1.0 interfaces built from its 18 basic components. Their buttons send
their named action and the values entered back to the current conversation's Bot.
in a sandboxed iframe, and A2UI 1.0 interfaces built from CopilotKit's standard catalog (every
standard component except Map, Favicon, LinkCard and Citation). Their buttons send their named action
and the values entered back to the current conversation's Bot.
Set `OPENBOT_GENERATIVE_UI=false` or `0` to disable both. `true`, `1`, an empty value, or an unset
value leave the capability on. The server configures both runtime renderers and reports the same
setting through `/api/capabilities` to the browser.
Expand Down
Loading