Tool surface¶
The agent reaches its goals through two families of tools:
- Server-side tools run in-process inside the
DjangoAGUIView. They are read-only and registered on theToolRegistrythatget_urls()mounts. This package contributes theshell.*(ORM) andintrospect.*(Django/admin introspection) categories. - Frontend tools are declared by the
<ag-ui-chat>Web Component on every run, so the catalog reflects what's actually on the current page. This package registers the admin-aware handlers —ui_read.*,ui_write.*,ui_generic.*, andnav.*— instatic/django_admin_agent/admin_tools.js. They execute in the browser and drive the DOM.
Server-side tools¶
shell.* — ORM queries (read-only)¶
Registered by register_shell_tools(). Every tool resolves the model by
app_label + model and returns JSON-safe dicts (dates, UUIDs, Decimals, lazy
translations flattened via DjangoJSONEncoder).
| Tool | Signature | Returns |
|---|---|---|
query_model |
(app_label, model, filter=None, exclude=None, order_by=None, select_related=None, prefetch_related=None, fields=None, limit=50, offset=0) |
List of row dicts. limit is hard-capped at 1000. |
get_model_instance |
(app_label, model, pk, select_related=None, fields=None) |
One row dict, or None when not found. |
count_model |
(app_label, model, filter=None, exclude=None) |
Row count. |
inspect_model_schema |
(app_label, model) |
Field types, nullability, relations, indexes, db_table, Meta ordering. |
filter and exclude accept ORM lookup kwargs, e.g.
{"email__icontains": "@foo"}. When fields is omitted, every concrete field
is projected.
introspect.* — Django & admin introspection (read-only)¶
Registered by register_introspect_tools().
| Tool | Signature | Returns |
|---|---|---|
list_installed_apps |
() |
Configured apps: labels, names, module, path, model count. |
list_models |
(app_label=None) |
Installed models with table + Meta flags. |
list_urls |
(prefix=None) |
Every registered route (pattern, name, view), optionally filtered by pattern substring. |
list_signals |
() |
Django's built-in signals and their connected receivers. |
get_settings_summary |
() |
A curated, redacted subset of settings. |
list_admin_models |
() |
Every model registered with admin.site, with list_display / list_filter / search_fields and reverse-resolved changelist + add URLs. |
inspect_modeladmin |
(app_label, model) |
The registered ModelAdmin's options. |
Settings are redacted
get_settings_summary returns only an allowlist of safe keys. SECRET_KEY,
API tokens, and database passwords are never included; DATABASES is
surfaced with PASSWORD and raw OPTIONS redacted.
Unfold-aware introspection
inspect_modeladmin and list_admin_models read every attribute
defensively via getattr, so they transparently surface both standard
Django options and additive ones that subclasses add (e.g. Unfold's tabs,
list_fullwidth, list_filter_submit, compressed_fields) through a single
code path.
These categories are bundled by register_admin_tools(registry), and
build_default_registry() returns a fresh registry with both registered — the
default get_urls() uses it.
Frontend tools¶
Declared per-run by the Web Component and executed in the browser against
Django admin's structural DOM contracts (#id_<field> inputs,
_save/_continue/_addanother submit names, name="_selected_action" row
checkboxes, a name="action" bulk-action <select> plus a name="index" Go
button, the #changelist-filter sidebar). All per-element state (the
button-handle map) is closure-local, so a page may safely mount more than one
sidebar.
ui_read.* — page introspection (read-only)¶
| Tool | Reads |
|---|---|
get_form_state |
The change-form's fields with name, current value, type, and label. |
get_changelist_state |
Visible rows (pk + text), active filters, and selected rows. |
get_visible_buttons |
Actionable buttons/links, each tagged with an opaque handle for click_button. |
get_page_snapshot |
A generic structured snapshot (URL, title, whether a change form / changelist is present, headings). |
ui_write.* — DOM driving (destructive, animated)¶
Each carries the x-destructive flag (see confirmation).
| Tool | Action |
|---|---|
fill_field |
Type a value into a change-form text field (animated). |
select_option |
Choose a <select> option by value or visible text. |
toggle_checkbox |
Set a checkbox checked/unchecked. |
click_button |
Click a button by the opaque handle from get_visible_buttons. |
submit_form |
Submit the change form (save / save_and_continue / save_and_add_another). Navigates. |
apply_filter |
Apply a changelist sidebar filter. Navigates. |
select_changelist_rows |
Tick row checkboxes for the given primary keys. |
run_admin_action |
Select a bulk action and click Go. Navigates. |
ui_generic.* — fallback for custom widgets¶
| Tool | Action |
|---|---|
fill_dom_element |
Fill an element by CSS selector or visible label (destructive). |
click_dom_element |
Click an element by CSS selector or visible label (destructive). |
read_dom_element |
Read an element's text/value by selector or label (read-only). |
Prefer the typed ui_write.* tools; reach for ui_generic.* only when no typed
tool covers the element.
nav.* — browser navigation¶
| Tool | Action |
|---|---|
open_changelist |
Navigate to a model's changelist, with optional filters. Navigates. |
open_changeform |
Open a model's add form (no pk) or edit form for a pk. Navigates. |
navigate_to |
Navigate to an arbitrary URL (fallback). Navigates. |
nav.* builds admin URLs from the data-admin-base attribute the server
injects, so the browser never has to reverse named routes. Alongside these,
the agent can jump to a server-built route map
via the component's list_routes / navigate_to_route. The map now includes a
dynamic change route per model — its path is templated as
/admin/app/model/:pk/change/, and navigate_to_route's params fill the
:pk segment — so the agent can edit a specific record by intent.
Destructive confirmation¶
AG-UI has no built-in risk flag, so destructive tools carry JSON-Schema
extensions that this package attaches when it builds each tool's parameter
schema. The schema() helper in admin_tools.js takes six arguments —
(properties, required, destructive, navigates, confirm, summary) — and emits
the matching extension keys:
| Argument | Schema key | Effect in the Web Component |
|---|---|---|
destructive |
x-destructive |
Gates the call behind a confirmation card. |
navigates |
x-navigates |
Marks the call as triggering a reload (see below). |
confirm |
x-confirm |
Human-readable prompt the inline confirmation card shows for the action. |
summary |
x-summary |
Friendly label shown on the tool-call card instead of the raw tool name. |
The Web Component reads x-destructive client-side and shows an inline
confirmation card before dispatching to the local handler; when x-confirm
is present it uses that wording. Accepting runs the handler with its animation;
rejecting posts a "user declined" tool result so the agent acknowledges it on
its next turn. x-summary (when set) gives the tool-call card a readable title.
The two highest-stakes admin tools, submit_form and run_admin_action, set
both confirm and summary. Setting AUTO_CONFIRM to
True skips the confirmation — the autopilot toggle.
Navigating tools and the resumable loop¶
The Django admin is a multi-page application: a save, filter, or navigation is a full page reload. An agent turn is a client-side loop whose message history and pending tool calls live only in browser memory — so a naive reload mid-turn would orphan the run.
Tools that trigger a reload carry an x-navigates flag (above, marked
Navigates). The Web Component handles them as first-class checkpoints:
- The handler records the pending tool call, then triggers the reload (a real navigation, a filter link, or a form submit).
- On the page it lands on, the resumer completes that tool's result using the
landed page as the payload — the new URL and title, plus any Django
validation errors rendered as
.errorlist(e.g. after a failed save). This package supplies that payload via the element'snavigationResulthook inadmin_tools.js. - The loop continues from the appended result, so multi-step tasks that navigate complete cleanly across reloads.
The resumable-loop machinery itself lives in the Web Component; this package's contribution is classifying which admin tools navigate and providing the landed-page result shape.