Entity Picker

Let the user choose one or more real records the agent fetched, with search, groups and keyboard support.

Pattern

≈ 844 tokens per use (tool 492 · guidance 220 · typical payload 132)How token costs are estimated

Example: Entity Picker

Pull last week's results for our Meta ads.

Meta ad account

Acme Retailact_7781203345 · USD$18.2k
Acme Retail EUact_7781209912 · EUR€6.9k
Acme Outletact_5530117720 · USD$2.1k
Acme Brandact_5530118841 · USD · paused$0

When to use

  • The user must choose which real records to work with: an ad account, campaigns, files, teammates.
  • The agent fetched more than one candidate and can't tell which the user means.

When not to use

  • Only one record matches. Use it and say which one.
  • The choices are options or preferences, not records. Use the Question Stepper.
  • The agent hasn't fetched the records yet. Fetch them first; never list records from memory.

How the agent should use it

  • Every entity comes from a tool result, with its real id.
  • Use description to tell similar records apart: an account id, an email, a path.
  • Use group when records belong to different parents, e.g. campaigns by ad account.
  • Set multiple and max only when the next step can take several records.
  • Act only on the ids returned with select.

Interaction

InputAction
Typing in SearchFilters by label, description, meta and group. The result count is announced
Esc in SearchClears the search
↓ in SearchMoves into the list
↑ ↓ Home EndMove through the options
Space or EnterSelect the option (toggle it when multiple)

The list is an ARIA listbox (aria-multiselectable when multiple) that tracks the active option with aria-activedescendant; groups are labelled by their headings. Search appears automatically above 6 records, and lists longer than 8 rows scroll inside the card. Once max is reached, the remaining options are disabled until one is cleared.

Installation

npx shadcn@latest add https://your-site.com/r/entity-picker.json

Usage

app/api/chat/route.ts
import { tool } from "ai"
import { entityPickerTool } from "@/components/genui/entity-picker.schema"

const tools = {
  // No execute(): the call is completed by the user's choice in the UI.
  [entityPickerTool.name]: tool({
    description: entityPickerTool.description,
    inputSchema: entityPickerTool.inputSchema,
  }),
}
components/message.tsx
import { EntityPicker } from "@/components/genui/entity-picker"

function ToolPart({ part, addToolResult }) {
  if (part.type !== "tool-pick_entities") return null

  return (
    <EntityPicker
      {...part.input}
      status={part.state === "input-streaming" ? "streaming" : undefined}
      result={part.state === "output-available" ? part.output : undefined}
      onResult={(result) =>
        addToolResult({
          tool: "pick_entities",
          toolCallId: part.toolCallId,
          output: result,
        })
      }
    />
  )
}

The agent receives one of:

{ action: "select", ids: ["120210", "330102"] }
{ action: "cancel" }

With LangGraph

Push it from a graph node with ui.push({ name: "pick_entities", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.

Restoring from history

Pass the stored result back as result. The picker lists the chosen records (or the cancelled state) and never calls onResult again.

<EntityPicker
  {...part.input}
  result={part.state === "output-available" ? part.output : undefined}
/>

Schema

PropTypeNotes
titlestringWhat to pick: "Choose an ad account"
descriptionstring?One short line under the title, only for a consequence the user should know (e.g. what access is granted)
entities{ id, label, description?, meta?, group? }[]Data-bound. 1–200 records, most relevant first
multiplebooleanDefault false
minnumber?Fewest picks. Default 1
maxnumber?Most picks, when multiple
searchableboolean?Default: on above 6 records
submitLabelstring?Verb for the confirm button. Default "Select"

Host props: status (streaming, ready, submitting, selected, cancelled), onResult, result and className.

Source

On this page