Render an AIDP agent's answers as inline charts inside an Oracle APEX chat. When the agent replies with a category-to-number breakdown (e.g. "sales by region"), a horizontal bar, pie, vertical bar, line, donut, pyramid, or funnel chart appears directly beneath the answer in the chat transcript.
The trick is a small, agent-agnostic chart marker: the agent appends a hidden marker to its reply, and a bit of JavaScript in the APEX chat region turns that marker into a chart. Because the marker is the same regardless of how the agent is built, a low-code AIDP agent and a high-code agent both work with the identical front-end.
- What this sample is (and isn't)
- Features
- How it works
- Prerequisites
- Step-by-step
- The chart marker
- Troubleshooting
- Security notes
- Folder layout
This sample is only the chart layer - the two chart-specific pieces you add on top of a working agent chat. It is not a full chat application.
To get an inline chart in APEX, you need all four of these:
| # | What you need | Provided by |
|---|---|---|
| 1 | A deployed AIDP agent connected to your data (low-code or high-code) | You |
| 2 | An APEX chat region that sends messages to the agent and shows the reply | You - or build one with the sibling samples below |
| 3 | The agent taught to emit the chart marker | This sample → agent/ |
| 4 | APEX code that reads the marker and draws the chart | This sample → apex/render_chart_marker.js |
Items 1-2 are generic chat plumbing, already covered by
../invoke-agent-flows-from-apex/ (calling an agent flow from APEX) and
../../../aidp_chat_client/ (a standalone chat client). Bring your own chat
surface; this sample adds the charts to it (items 3-4). The renderer is
self-contained - it loads Chart.js on demand and needs no extra CSS.
- Inline charts rendered in the chat transcript, right under each answer.
- Seven chart types from one marker:
hbar,pie,bar,line,donut,pyramid,funnel. - Agent-agnostic: low-code (pasted instructions) and high-code (a prompt rule) emit the identical marker.
- No build step: one plain JS file; Chart.js is fetched on demand only for the donut.
flowchart TD
U(["User"]) -->|"1 - asks a question"| CHAT["APEX chat region"]
CHAT -->|"2 - POST to the agent endpoint"| AGENT{{"AIDP agent<br/>(low-code or high-code)"}}
AGENT <-->|"3 - query data over MCP"| DATA[("Data source<br/>e.g. OAC")]
AGENT -->|"4 - reply carries a hidden CHART marker"| CHAT
CHAT -->|"5 - raw reply text"| RENDER["render_chart_marker.js"]
RENDER -->|"6 - chart drawn inline"| U
This sample provides two of those pieces: the chart rule the agent follows
(agent/), which drives step 4, and the renderer
(apex/render_chart_marker.js) at steps 5-6. The
chat region, the agent, and the data source are yours.
The agent carries the chart in its reply as a hidden HTML comment, so it never shows in the chat bubble:
Here is sales by region:
| Region | Sales |
| North | 120 | ...
<!--CHART:{"type":"hbar","title":"Sales by region","data":[...]}-->
The full marker format and type list are in The chart marker below.
- An AIDP agent connected to your data, deployed, and reachable from APEX. It can be low-code or high-code (this sample supports both).
- An Oracle APEX workspace and app with a chat region that sends the user's message to the agent and shows the agent's reply.
- A data source your agent can query. This sample was built against Oracle Analytics Cloud (OAC) over MCP, but the chart layer does not care where the numbers come from.
If you do not yet have #1 and #2, set those up first using the sibling samples linked above, then come back here to add charts.
Pick the path that matches how your agent is built. Both produce the same marker, so you only ever do one of these.
Low-code agent
- Open your agent in the AIDP Workbench and edit its Instructions.
- Copy the chart-rule block from
agent/low-code-instructions.mdand paste it after your existing data/query instructions. - Save.
High-code agent
- Open
agent/high-code-marker-snippet.py. - Copy the
CHART_BLOCK_RULEstring and add it to your agent's prompt rules (for the AIDP Python SDK this isprompt.extra_rules; for a raw system prompt, append it to the system prompt text). The file shows both. - Your agent's own setup and auth stay as they are - if you connect to OAC over MCP, the wiring example is
../../code-first/mcp-examples/single-agent-mcp-session-variables.py.
Verify Step 1: ask the agent "break down sales by region". The raw reply should end with a line like:
<!--CHART:{"type":"hbar","title":"Sales by region","data":[{"label":"North","value":120}, ...]}-->
These steps add the chart renderer to an existing chat region - they do not create the chat region itself (that is the prerequisite above). Do this in App Builder on the app that has your chat page:
- Open your app in App Builder.
- Go to Shared Components → under Files, click Static Application Files.
- Click Create File (or Upload File), select
apex/render_chart_marker.js, and click Create. APEX now serves it at#APP_FILES#render_chart_marker.js. - Open the page that has your chat region in Page Designer.
- In the Rendering tree, select the page root node (the very top), then in the Property Editor open the JavaScript group.
- In File URLs, add this line:
#APP_FILES#render_chart_marker.js - Click Save.
This loads the renderer and defines a global ACPChart object (methods
renderAfter(), renderInto(), scan()). Nothing draws yet - Step 3 calls it.
Prefer not to upload a file? Instead of steps 2-3, paste the entire contents of
render_chart_marker.jsinto the region's JavaScript → Function and Global Variable Declaration.
The renderer needs the raw agent reply (still containing the marker). Run
ACPChart right after your chat renders an assistant answer. The standard APEX
way, using a Dynamic Action:
- In Page Designer, add a Dynamic Action on your chat region that fires when a new answer appears (for many chat regions that is the region's After Refresh event; adapt it to your chat's own event).
- Add a True action → Execute JavaScript Code with:
// Each assistant answer element should carry data-agent-answer and still // contain the raw marker in its text. Then: ACPChart.scan(document.getElementById('YOUR_CHAT_REGION_STATIC_ID'), '[data-agent-answer]');
- Click Save, then Run the page.
If instead your chat region renders each answer itself in JavaScript, just call
ACPChart.renderAfter(answerEl, rawText) as you append the answer - where
answerEl is the answer's element and rawText is the raw reply (with the
marker).
Gotcha - the marker must reach the browser. The renderer can only draw what it can see. If your region strips HTML comments or only stores a "cleaned" reply, keep the raw reply available to the client (e.g. in the element's text or a data attribute) so the marker survives to Step 3.
This is your existing chat plumbing; the chart layer rides on top of it. If you still need to wire it:
- In the AIDP Workbench, open your deployed agent and go to its Details.
- Copy the agent's endpoint / invocation URL.
- Configure your APEX chat region to POST the user's message to that endpoint
and display the reply. See
../invoke-agent-flows-from-apex/for a full worked example of calling an agent from APEX, and../../../aidp_chat_client/for a standalone chat client.
The agent appends one HTML comment on its own final line, after the prose. Because it is a comment it never shows in the bubble; the renderer reads it from the raw reply:
<!--CHART:{"type":"hbar","title":"Sales by region","data":[{"label":"North","value":120},{"label":"South","value":90}]}-->
Fields
| Field | Type | Notes |
|---|---|---|
type |
string | One of hbar, pie, bar, line, donut, pyramid, funnel (lowercase). |
title |
string | Short chart title shown above the chart. |
data |
array | One object per category: { "label": "<category>", "value": <number> }. |
value is a plain number (no quotes, thousands separators, or currency); include
every row you listed. Valid JSON between CHART: and --> - no code fences, no
second comment, nothing after -->.
Types
type |
Renders as | Good for |
|---|---|---|
hbar |
Horizontal bar (default, auto) | Rankings and comparisons; long labels. |
pie |
Pie (auto for composition) | Share of a whole. |
bar |
Vertical bar | Comparisons where a vertical layout reads better. |
line |
Line (auto for trends over time) | A value across an ordered sequence. |
donut |
Donut with a centre total | Composition with the total called out (name it). |
pyramid |
Pyramid | Ranked stages / hierarchy. |
funnel |
Funnel | Stages that shrink (e.g. a pipeline). |
The agent auto-picks only hbar, pie, or line; bar, donut,
pyramid, and funnel appear only when the user names them. The full
selection rule the agent follows lives in the agent/ files.
| Symptom | Likely cause | Fix |
|---|---|---|
| No chart appears | The marker never reached the browser | Confirm the raw reply (with the <!--CHART...--> comment) is what Step 3 reads; don't strip comments server-side. |
| No chart, but the raw reply has a marker | Renderer not loaded, or not called | Confirm render_chart_marker.js is on the page (Step 2) and renderAfter/scan runs after the answer renders (Step 3). |
| Donut shows as a full pie / is blank | Chart.js failed to load | Check the network request to the pinned Chart.js URL; the CDN host must be reachable. All other types use JET and need no CDN. |
| Chart renders twice | scan() ran more than once on the same answer |
The data-acp-charted guard prevents this per element; make sure you reuse the same element, not a re-created one. |
| Agent shows a table but no marker | The chart rule isn't in effect | Re-check Step 1 - the rule must be pasted/added after the agent's other instructions, and saved/redeployed. |
- This sample contains no secrets and no environment-specific values - all example data is invented. Keep it that way in your fork.
- The agent endpoint URL, any credentials, and your data connection live in your APEX app and agent config, not in these files.
- The renderer sanitizes nothing itself; it only reads a marker and draws a chart. Keep using your chat region's existing HTML sanitization for the answer text (this sample assumes markdown is rendered and sanitized upstream).
apex-chat-charts-over-oac-mcp/
├── README.md <- you are here (the guide + the marker spec)
├── agent/
│ ├── low-code-instructions.md <- paste into a low-code agent's Instructions
│ └── high-code-marker-snippet.py <- add the rule to a high-code agent's prompt
└── apex/
└── render_chart_marker.js <- paste into APEX; turns the marker into a chart