Related: Capability Registry | Resource-Scoped RAG | Agent Architecture
Implements: RP Documentation Plan Tasks 8-10 (
cyberteam_drupal/docs/superpowers/plans/2026-03-24-rp-documentation-pages.md)
The chatbot is embedded on resource provider (RP) documentation pages (e.g., Delta, Anvil). It acts as a full assistant that happens to know about that resource — same capabilities as the floating widget, but with RP context baked in. This spec covers how the capability registry and UKY resource-scoped RAG integrate to deliver that experience.
This is not a separate system. It extends the capability registry with a resource_context parameter so the same endpoint, same UI code, and same agent serve both the general floating widget and the RP-embedded chatbot.
-
Two independent chatbot instances on RP pages. The floating widget stays general. The embedded chatbot has RP context pre-loaded. Separate sessions, separate conversations. The embedded one advertises full capabilities (not just Q&A), so users discover the floating widget isn't the only way in.
-
The agent owns section awareness. The agent caches which documentation sections each RP has populated, fetched from the Drupal
/api/resourcesendpoint. No per-request Drupal calls. The capabilities endpoint stays fast (<10ms). -
Unified capabilities endpoint.
GET /api/v1/capabilities?resource_context=deltareturns capabilities filtered and augmented for that RP. Same endpoint the floating widget uses without the parameter. One endpoint, one UI flow — context shapes what you see. -
Adaptive layout. The response includes a
layouthint (flatorcategories) based on total capability count. RP embeds with ≤8 items get a flat list of suggested questions. The floating widget with 15+ items gets category grouping. The UI renders based on the hint — no special "embedded mode" logic. -
RP context via data attribute. The Drupal template puts
data-resource-context="delta"on the embed div.headerfooter.jsreads it and passesresourceContextto theqaBot()initialization. This keeps the chatbot portable — any page that knows the RP slug can embed it.
The RP documentation template includes:
<div class="embedded-qa-bot" data-resource-context="test-alpha-9999"></div>headerfooter.js detects the data-resource-context attribute and passes it to the chatbot:
qaBot({
target: embeddedTarget,
embedded: true,
resourceContext: embeddedTarget.dataset.resourceContext,
// ... other props
});On open, the chatbot calls:
GET /api/v1/capabilities?resource_context=delta
If no resource_context, returns the standard category-grouped capabilities (existing behavior).
The agent maintains an in-memory cache of RP section data:
# Cache structure (refreshed on startup + TTL)
{
"delta": {
"title": "Delta",
"populated_sections": ["login", "file_transfer", "storage", "queue_specs", "top_software", "datasets"]
},
"anvil": {
"title": "Anvil",
"populated_sections": ["login", "storage", "queue_specs"]
}
}Cache source: GET /api/resources on the Drupal site. The existing endpoint already returns has_documentation per resource. We extend it to include a populated_sections array listing which documentation sections have data.
Cache refresh: On agent startup, then every 30 minutes (configurable via RP_CACHE_TTL_SECONDS). The resource list is slow-changing data — documentation sections don't change hourly.
Cache miss / invalid slug: If resource_context isn't in the cache, ignore it and return the standard capabilities response. Don't error.
The agent maps populated sections to suggested questions:
| Section | Suggested Question |
|---|---|
login |
How do I log in? |
file_transfer |
How do I transfer files? |
storage |
What storage is available? |
queue_specs |
How do I submit a job? |
top_software |
What software is available? |
datasets |
What datasets are available? |
These are defined in the agent's capability registry code, not fetched from Drupal. Drupal says which sections exist; the agent decides what to ask about them.
Always-present capabilities (regardless of populated sections):
- "Get help with a [Resource Name] issue" (support/tickets)
- "Check my usage on [Resource Name]" (analytics, auth-required)
The resource name is interpolated from the cached title.
For GET /api/v1/capabilities?resource_context=delta with a fully-documented resource:
{
"resource_context": {
"slug": "delta",
"title": "Delta"
},
"layout": "flat",
"capabilities": [
{
"id": "ask_about_resource",
"label": "How do I log in?",
"description": "Get help logging in to Delta",
"section": "login"
},
{
"id": "ask_about_resource",
"label": "How do I transfer files?",
"description": "Learn about file transfer options on Delta",
"section": "file_transfer"
},
{
"id": "ask_about_resource",
"label": "What storage is available?",
"description": "Explore storage options and quotas on Delta",
"section": "storage"
},
{
"id": "ask_about_resource",
"label": "How do I submit a job?",
"description": "Learn about queues and job submission on Delta",
"section": "queue_specs"
},
{
"id": "ask_about_resource",
"label": "What software is available?",
"description": "See frequently used software on Delta",
"section": "top_software"
},
{
"id": "ask_about_resource",
"label": "What datasets are available?",
"description": "Browse datasets available on Delta",
"section": "datasets"
},
{
"id": "open_ticket",
"label": "Get help with a Delta issue",
"description": "Create a support ticket",
"requires_auth": false
},
{
"id": "check_usage",
"label": "Check my usage on Delta",
"description": "View resource usage and performance data",
"requires_auth": true,
"locked": true
}
],
"is_authenticated": false,
"login_url": "/login?redirect=..."
}Layout logic: If len(capabilities) <= 8, set layout: "flat". Otherwise, layout: "categories" and group into the standard category structure. Most RP embeds will have 5-8 items and get flat layout.
For the standard floating widget (no resource_context), the response is unchanged — categories with the full capability list per the existing spec.
The chatbot UI checks response.layout:
flat: Render capabilities as a list of suggested-question buttons. Label them "Try asking:" to signal these are examples. Text input is always visible and prominent.categories: Render as category buttons (existing behavior from capability registry spec).
The resource_context from the page is included in every query the chatbot sends:
POST /api/v1/query
{
"query": "How do I submit a GPU job?",
"resource_context": "delta"
}When resource_context is present in a query:
- The classify node routes normally (RAG, domain agent, tools)
- For RAG queries,
UKYClient.ask()includesrp_namein the request body and setsX-Originto the RP slug - If UKY responds out-of-scope (heuristic until
in_scopefield is available), retry withoutrp_namefor general RAG - The fallback is transparent to the user — they get an answer either way
- For non-RAG queries (tickets, XDMoD), the resource context is included as additional context in the agent's state but doesn't change routing
/api/resources endpoint — extend the list response to include populated_sections per resource:
{
"nid": 123,
"title": "Delta",
"resource_id": "delta",
"populated_sections": ["login", "file_transfer", "storage", "queue_specs", "top_software"],
"has_documentation": true
}The sections are derived from checking which field_rp_* fields and paragraph references are non-empty.
RP documentation template — add data-resource-context to the embedded chatbot div (already partially implemented).
- RP section cache: New
RPSectionCacheclass that fetches and caches/api/resourcesdata. Refreshed at startup + TTL. - Capabilities endpoint: Accept optional
resource_contextquery param. When present, build RP-scoped response using cache + section-to-question mapping. - Layout logic: Count capabilities, return
layout: "flat"or"categories". - Query endpoint: Accept optional
resource_contextin request body (per existing UKY scoped RAG spec). - UKY client: Pass
rp_nameandX-Originheader (per existing UKY scoped RAG spec).
qa-bot-core: AcceptresourceContextprop. Include inresource_contextfield on query POST. Pass to capabilities endpoint as query param.access-qa-bot: Accept and pass throughresourceContextprop.- UI rendering: Support
layout: "flat"— render as suggested-question buttons with "Try asking:" label. Text input always visible. headerfooter.js: Readdata-resource-contextfrom embedded target, pass asresourceContexttoqaBot().
- User clicks "Ask about Delta" button → chatbot container expands
- Welcome message: "I can help you with Delta. Here are some things to try, or ask me anything."
- Suggested question buttons rendered flat (from capabilities endpoint)
- Text input with placeholder: "Ask about Delta..."
- User clicks a button or types — query includes
resource_context: "delta" - Agent answers from RP-scoped RAG when possible, falls back to general
Unchanged. Standard category buttons, no resource context. Independent session.
If an RP has minimal documentation (e.g., only a description), the capabilities response will have fewer suggested questions — just the always-present ones (support, usage). The flat layout still works. The chatbot is still useful for general ACCESS questions about that resource type.
- RP slug mapping — The
resource_idin Drupal (e.g.,test-alpha-9999) may not match UKY's valid RP slugs (e.g.,delta,anvil). Need a mapping between CiDeR resource IDs and UKY slugs. This could live in the agent's config or be a field on the Drupal node. - Welcome message personalization — Should the embedded chatbot's welcome message reference the user's allocations on this specific resource (from the personalized capabilities endpoint)? e.g., "I see you have allocation TG-CIS123456 on Delta."
- UKY
in_scopefield — Still pending from UKY. Until available, use text heuristic for fallback detection.