Galadri

Rich Results

Render tool results as interactive maps, cards, and structured UI instead of raw JSON.

Most AI APIs return plain text. Galadri returns structured data your UI can render as interactive maps, scrollable cards, and visual components. The AI gets compact summaries to stay efficient while your client gets the full picture.

Chat surfaces only

Rich results are a chat-surface feature. Email, SMS, and voice channels do not expose display directives, maps, cards, suggested prompts, or mutation cards to the model. Those channels should receive only the finished delivered content.

Overview

When an agent calls tools like vehicle search, gas price lookup, or Google Maps, the API returns full structured data in tool_result events. Instead of displaying raw JSON, you can render this data as interactive maps, scrollable cards, and other visual components.

The system works in three layers:

  • Stable IDs — Every result item has a unique _id field (e.g., listing_0, station_3) that the agent uses to reference specific results in conversation.
  • Display capabilities — Per-agent configuration that tells the AI what your UI can render, so it adapts its text accordingly.
  • Client-side rendering — Your app receives the full result data via SSE and renders it using your own components.

No extra API calls needed

Rich results use data you already receive. The tool_result SSE event contains the full structured data. You just need to parse it and render the appropriate UI.

Stable IDs

Every array item in a tool result includes an _id field. The agent uses these IDs when referencing results in its response text (e.g., "Station 3 has the best price" corresponds to station_2 in the data).

ID prefixes by tool

FieldTypeDescription
listing_Nvehicle-searchVehicle sales listings
place_Ngoogle-maps-searchPlaces from Google Maps
station_Ngas-price-lookupGas stations with fuel prices
station_Nev-charger-lookup / ev-charger-advancedEV charging stations
shop_Nrepair-booking (find_shops)Repair shops
incident_Ntraffic (nearby_incidents)Traffic incidents
Tool result with _id fields
{"type": "tool_result", "id": "tc_1", "results": [
  {
    "tool": "gas-price-lookup",
    "status": "success",
    "data": {
      "stations": [
        {
          "_id": "station_0",
          "brand": "Shell",
          "address": "1234 E Camelback Rd, Phoenix, AZ",
          "latitude": 33.5092,
          "longitude": -112.0148,
          "distance_miles": 0.8,
          "prices": [
            {"fuel_type": "regular", "price_per_gallon": 3.29},
            {"fuel_type": "premium", "price_per_gallon": 3.89}
          ]
        },
        {
          "_id": "station_1",
          "brand": "Chevron",
          "address": "5678 N 7th St, Phoenix, AZ",
          "latitude": 33.5187,
          "longitude": -112.0651,
          "distance_miles": 1.4,
          "prices": [
            {"fuel_type": "regular", "price_per_gallon": 3.35}
          ]
        }
      ]
    }
  }
]}

Display Capabilities

Display capabilities tell the agent what visual components your UI supports. The agent adjusts its text responses accordingly. For example, if your UI renders maps, the agent might say "See the locations on the map below" instead of listing every address. If maps are disabled, it will describe locations in full text.

Within a chat-capable surface, rich rendering is explicit. If the assistant does not emit a display directive, clients should not invent fallback maps or cards from tool results on their own.

Configure display capabilities per agent in the Galadri Console under the "Display Capabilities" section. They are stored as part of the agent configuration and loaded automatically on each chat request.

Available capabilities

FieldTypeDescription
mapsbooleanInteractive maps with location markers, route polylines, and traffic visualization. Affects google-maps-search, gas-price-lookup, ev-charger-lookup, ev-charger-advanced, traffic, and fleet-routing results.
vehicle_cardsbooleanVehicle listing cards with photos, price, specs, and dealer info. Affects vehicle-search results.
station_cardsbooleanStation cards for gas prices and EV charger availability. Affects gas-price-lookup, ev-charger-lookup, and ev-charger-advanced results.
shop_cardsbooleanRepair shop and place cards with ratings, address, and contact info. Affects repair-booking (find_shops) and google-maps-search results.
booking_confirmbooleanBooking confirmation cards. When enabled, repair bookings show a confirmation UI before executing. When disabled, bookings execute immediately.
data_cardsobjectData mutation cards showing create/update/delete confirmations. Update cards should focus on only the fields the agent actually changed.
data_cards.enabledbooleanShow data mutation confirmation cards.

All capabilities default to true. If you do not set display capabilities, the agent assumes your UI supports everything. Set capabilities to false for types your UI does not render, so the agent provides richer text descriptions instead.

Text-only mode

If your UI is text-only (no maps, no cards), set all capabilities to false. The agent will describe all results in full text, including addresses, prices, ratings, and directions.

Mutation card exception

Data mutation cards are the one system-generated exception. In chat UIs, Galadri may show create, update, or delete confirmations even when the assistant did not explicitly place a display directive for them. Do not mirror those cards into email, SMS, or voice outputs.

Rendering Tool Results

When you receive a tool_result event, check the tool slug and render the appropriate UI. Here is a minimal example of dispatching tool results to different renderers:

Dispatching tool results to renderers
function renderToolResult(tool: string, data: any) {
  switch (tool) {
    case "vehicle-search":
      return <VehicleCards listings={data.listings} />;

    case "google-maps-search":
      return <PlaceCards places={data.results} />;

    case "gas-price-lookup":
      return <GasStationCards stations={data.stations} />;

    case "ev-charger-lookup":
    case "ev-charger-advanced":
      return <EvChargerCards stations={data.stations} />;

    case "repair-booking":
      if (data.shops) return <RepairShopCards shops={data.shops} />;
      return null;

    case "traffic":
      if (data.incidents) return <IncidentCards incidents={data.incidents} />;
      return null;

    default:
      return null; // Fall back to text
  }
}

Maps

Most location-based tools return latitude and longitude on each result item. You can plot these on an interactive map with numbered markers. The following tools include coordinates:

Maps only render when the assistant explicitly emits a display directive that requests a map layout. There is no automatic fallback map at the bottom of the message.

When available, route cards can also own the external navigation handoff. If the inline route map already renders an "Open directions in Google Maps" control, the assistant should reference that card instead of printing a second Markdown navigation link in the text. Larger optimized routes may render the inline map without that control.

  • google-maps-search — place markers
  • gas-price-lookup — station markers
  • ev-charger-lookup / ev-charger-advanced — charger markers
  • repair-booking (find_shops) — shop markers
  • traffic (nearby_incidents) — incident markers
  • traffic (route_traffic) — route polyline via route_points
  • fleet-routing (optimize_route) — optimized stop markers plus a Google-rendered route polyline
Extracting map markers from tool results
function extractMarkers(tool: string, data: any) {
  const items =
    data.stations || data.results || data.shops ||
    data.incidents || data.optimized_order || [];

  return items
    .filter((item: any) => item.latitude != null && item.longitude != null)
    .map((item: any, i: number) => ({
      id: item._id || `marker_${i}`,
      lat: item.latitude,
      lng: item.longitude,
      label: item.name || item.brand || item.category || `Result ${i + 1}`,
    }));
}

Result Schemas by Tool

Each tool returns data in a consistent structure. Below are the key fields available for rendering. All array items include an _id field for referencing.

Returns data.listings[]

FieldTypeDescription
_idstringStable ID (e.g., "listing_0")
yearintegerModel year
makestringVehicle make
modelstringVehicle model
trimstringTrim level
price_usdnumberListing price
milesnumberOdometer reading
car_typestringBody type (sedan, SUV, etc.)
photo_urlstringPrimary photo URL
listing_urlstringLink to dealer listing
dealer_namestringDealer name
dealer_citystringDealer city
dealer_statestringDealer state

google-maps-search

Returns data.results[]

FieldTypeDescription
_idstringStable ID (e.g., "place_0")
namestringPlace name
addressstringFormatted address
ratingnumberGoogle rating (1-5)
latitudenumberLatitude
longitudenumberLongitude
open_nowbooleanCurrently open
phonestringPhone number
websitestringWebsite URL

gas-price-lookup

Returns data.stations[]

FieldTypeDescription
_idstringStable ID (e.g., "station_0")
brandstringGas station brand
namestringStation name
latitudenumberLatitude
longitudenumberLongitude
distance_milesnumberDistance from search location
pricesarrayFuel prices: [{fuel_type, price_per_gallon}]

ev-charger-lookup / ev-charger-advanced

Returns data.stations[]

FieldTypeDescription
_idstringStable ID (e.g., "station_0")
namestringStation name
networkstringCharging network (ChargePoint, Tesla, etc.)
latitudenumberLatitude
longitudenumberLongitude
distance_milesnumberDistance from search location
portsobjectPort counts: {dc_fast, level_2} (basic lookup)
available_pointsintegerCurrently available ports (advanced only)
total_charging_pointsintegerTotal ports (advanced only)
connectorsarrayConnector details: [{type, max_power_kw}] (advanced only)

repair-booking (find_shops)

Returns data.shops[]

FieldTypeDescription
_idstringStable ID (e.g., "shop_0")
namestringShop name
addressstringFull address
ratingnumberRating
distancestringDistance from search location
latitudenumberLatitude (when available)
longitudenumberLongitude (when available)
phonestringPhone number
pricingstringEstimated pricing

traffic

The nearby_incidents action returns data.incidents[]. The route_traffic action returns data.route_points[] for polyline rendering.

Incident fields

FieldTypeDescription
_idstringStable ID (e.g., "incident_0")
categorystringIncident type (Accident, Construction, etc.)
descriptionstringIncident description
severityintegerSeverity (1=minor, 2=moderate, 3=major, 4=severe)
latitudenumberLatitude
longitudenumberLongitude
delay_secondsnumberEstimated delay in seconds
fromstringRoad/location name

Data Flow

Tool results flow through two paths simultaneously:

  • To your client — Full structured data with all fields, coordinates, photos, and URLs via the tool_result SSE event. This is what you render.
  • To the AI agent — Compact one-line summaries (e.g., "station_0: Shell, regular $3.29/gal, 0.8 mi"). This keeps the agent's context window efficient while it still has enough information to discuss results by ID.

This split means the agent will reference results by their _id or index number in its response text, while your UI can show the full rich data alongside that text.

Recommended rendering order

For each assistant message, render in this order: (1) assistant text, (2) interactive map (if location data present), (3) result cards in a horizontal scroll strip, (4) data mutation cards (if data was saved).

See also: Chat API tool_result events and Tools reference