diff --git a/CHANGELOG.md b/CHANGELOG.md index 72acb7c299..8089d2ec0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/app/src/lib/copilot/a2ui.ts b/app/src/lib/copilot/a2ui.ts index 1019e53777..cfe223da47 100644 --- a/app/src/lib/copilot/a2ui.ts +++ b/app/src/lib/copilot/a2ui.ts @@ -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 = 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[0], - ), - ], + extends: [standardUI.omit([...OMITTED_STANDARD_COMPONENTS])], components: {}, }); @@ -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 = { diff --git a/app/tests/a2ui.test.tsx b/app/tests/a2ui.test.tsx index 26209716e9..f899796b92 100644 --- a/app/tests/a2ui.test.tsx +++ b/app/tests/a2ui.test.tsx @@ -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"; @@ -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); diff --git a/docs/configuration.md b/docs/configuration.md index 4eb693e489..af126cf2f6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -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.