Admin wiring¶
This page covers how the sidebar is built and attached to the admin: the context the template needs, the route map and page map the agent uses to navigate, and the two attachment paths.
The sidebar context¶
build_sidebar_context() assembles everything the sidebar template renders:
| Key | Source | Used for |
|---|---|---|
endpoint |
reverse(ENDPOINT_URL_NAME) |
The AG-UI endpoint the chat POSTs to. |
title |
DJANGO_ADMIN_AGENT["TITLE"] |
The chat panel header. |
auto_confirm |
DJANGO_ADMIN_AGENT["AUTO_CONFIRM"] |
Surfaced to the component as autoConfirm. |
tool_display |
DJANGO_ADMIN_AGENT["TOOL_DISPLAY"] |
The data-tool-display attribute (minimal / compact / full). |
theme, density, placement, text_animation |
the matching DJANGO_ADMIN_AGENT keys |
Themeable presentation attributes (theme / density / placement / data-text-animation), rendered only when set. See Configuration → Presentation. |
skills |
DJANGO_ADMIN_AGENT["SKILLS"] or build_skills() |
The skill catalog (chips + /-command palette), embedded as a json_script. See Configuration → Skills. |
tools_url |
reverse("django_admin_agent_tools") (or None) |
URL of the server-tool label catalog, rendered as data-tools-url; the component fetches it to label server-tool cards. None when the endpoint wasn't mounted via get_urls. |
bootstrap_url |
static("django_admin_agent/admin_agent.js") |
The ES-module entry point. |
admin_base_url |
reverse("admin:index") (or /) |
Lets the frontend nav.* tools build changelist / changeform URLs without reversing named routes in the browser. |
route_map |
build_route_map() |
The navigable-route manifest (see below). |
The same helper backs both attachment paths, so the rendered sidebar is identical whichever you choose.
The sidebar template¶
templates/django_admin_agent/sidebar.html renders the <ag-ui-chat> custom
element with the endpoint, title, auto-confirm, tool-display, and admin-base
data attributes (plus data-slash-commands="true", and the optional theme /
density / placement / data-text-animation attributes when their settings
are set, plus data-tools-url when the catalog endpoint is mounted); embeds the
route map and the skill catalog as two json_script blocks
(#django-admin-agent-routes, #django-admin-agent-skills); and loads the
bootstrap module. The bootstrap (static/django_admin_agent/admin_agent.js)
then:
- Defines the
<ag-ui-chat>custom element from the vendored bundle. - Attaches the CSRF token as an
X-CSRFTokenheader so the endpoint accepts POSTs under the logged-in admin session. - Reads the auto-confirm flag and the route map off the element / page, setting
el.routeMap. - Reads the embedded skill catalog and calls
el.setSkills(...), and setsel.skillContextto a provider that derives{path}/{selected_ids}placeholder values from the current page. - Calls
registerAdminTools(el)to register the frontend tools.
Server-tool card labels are not embedded — the component fetches them from
data-tools-url (the <prefix>agent/tools/ catalog endpoint, named
django_admin_agent_tools), whose labels come from each tool's @tool(summary=).
The themeable attributes (theme / density / placement /
data-text-animation) and data-tool-display are read by the Web Component
itself; this template is the seam where the DJANGO_ADMIN_AGENT settings become
element attributes.
The route map¶
build_route_map() walks admin.site._registry and emits, per registered
model, a changelist route, an add route (when available), and a dynamic
change route, each shaped for the Web Component's routeMap:
The change route's path is a :pk template, e.g.
/admin/app/model/:pk/change/ — build_route_map reverses the change URL with
a sentinel pk and swaps it for the :pk placeholder. When the agent calls
navigate_to_route, the Web Component substitutes the :pk segment from the
call's params, so the agent can edit a specific record by intent.
The agent calls the component's list_routes to discover destinations and
navigate_to_route to jump to one, instead of guessing admin URL shapes. URLs
are reverse-resolved (admin:<app>_<model>_changelist / _add / _change), so
models without a resolvable route are simply skipped.
The page map¶
registerAdminTools also wires el.getPageMap — a per-run provider that
returns a compact snapshot of the current page (field names/types/labels and
button labels/handles, no values). The Web Component auto-injects it into each
run's context, so the agent knows the page's surface without first calling
get_form_state / get_visible_buttons. Exact live values are still fetched on
demand via the ui_read.* tools.
Attaching the sidebar¶
Template tag¶
@register.inclusion_tag("django_admin_agent/sidebar.html")
def django_admin_agent_sidebar() -> dict[str, Any]: ...
{% load django_admin_agent %} then {% django_admin_agent_sidebar %} in your
admin/base_site.html renders the sidebar. It is self-contained — it computes
its own context via build_sidebar_context(), so the admin site does not need
swapping. This is the common path.
SidebarAdminSite¶
SidebarAdminSite is a drop-in AdminSite whose each_context adds the
sidebar context under the django_admin_agent key, so a base template can
render the chat from {{ django_admin_agent }} without the tag. It calls
super().each_context() first, so all standard admin context keys pass through
unchanged. Use this when you already swap the admin site; otherwise prefer the
template tag.
Unfold compatibility shim¶
The frontend tool handlers work against Django Unfold unchanged because Unfold
preserves Django's structural DOM contracts. Two Unfold-only quirks are smoothed
by static/django_admin_agent/unfold_shim.js, which no-ops on vanilla admin —
see Unfold & vanilla support. There is no Python Unfold
dependency: Unfold is detected at runtime in the browser, never imported at
module load.