Skip to content

AI scenarios

Generative UI

Bit.BlazorUI.AI Scripted transport

The model chooses a component from a registry and supplies its properties. Anything it names that is not registered renders nothing.

Live demo

Ask about three cities: the answer is three weather cards the model asked for by name.

Components the model chose

Ask about three cities
Ask a question to get started.

How it works

The registry is the boundary. A model can only instantiate a component the application registered, and can only set the parameters that registration exposed. Event callbacks, render fragments, unmatched-attribute catch-alls and anything marked [BitAiGenerativeIgnore] are excluded, so a generated payload cannot wire up handlers or inject attributes. ForecastCard is an ordinary component of this site with ordinary parameters; nothing about it knows it is generative.

The transport writes its introduction as text, then sends one BitAiGenerativeUiEvent per card - the registered name, forecast_card, and the properties as JSON - and ends with more text. Each event becomes a BitAiGenerativeUiPart in the reply, between the text parts. Properties a model writes as it goes can stream as well: the same event with IsPartial set, sent again as more arrives, or BitAiGenerativeUiDeltaEvents carrying the raw JSON a piece at a time; the card is bound again as far as its properties have arrived.

registry.DescribeAll() produces the JSON Schema to put in the prompt, or to use as the schema of a render tool. Give the model the schema and it stops guessing component names. City is required here (the last argument of Register, or a [Required] on the parameter), and the descriptions of the parameters go into the schema.

Binding is BitAiGenerativePropsBinder.Bind(component, props), which the view runs on every change. Its BitAiGenerativeBindResult holds the Parameters the card renders with, and what did not bind: each Failures entry is a BitAiGenerativeBindError - its Kind (BitAiGenerativeBindErrorKind.Missing, a required property left out, or Unreadable, a value of the wrong shape, with the serializer's Detail and the ExpectedType), its PropertyName and ComponentName - and IgnoredProperties lists the names the component does not declare, which never reach it. IsComplete says whether anything failed. None of it is fatal: the card renders with what did bind. ShowBindingErrors of the view lists the failures under the card; an app runs the binder itself to check a payload - to log what a model got wrong while a prompt is developed, as the Binding a payload tab does.

Registering reads the component's parameters by reflection, so Register is marked as unsafe to trim. In a trimmed app, register the components from a method of your own that says why it is safe there - the component type is kept whole by the annotation of Register, so what remains to check is that its bindable parameters are plain data - as this site does for ForecastCard, whose parameters are strings and integers.

Components used

  • BitAiGenerativeUiView - renders the component the model named, with the properties it supplied, and lists what did not bind (ShowBindingErrors).
  • BitAiRendererBoundary - keeps a component that throws on what the model gave it to itself.

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.