Skip to content

API reference

Autodoc of the public surface re-exported from django_ag_ui. Everything below is importable directly, e.g. from django_ag_ui import ToolRegistry.

Registry

ToolRegistry

An ordered, named collection of server-side tools.

State lives on the instance: a DjangoAGUIView holds one registry; tests build a fresh registry per scenario. The registry derives a JSON Schema for each tool at registration (including the x-destructive / x-category extensions) and can dispatch sync or async callables.

register

register(spec: ToolSpec) -> ToolBinding

Register spec and return its binding.

Raises:

Type Description
ValueError

when spec.name is already registered.

get

get(name: str) -> ToolBinding

Return the binding for name or raise KeyError.

call

call(name: str, arguments: dict[str, Any]) -> Any

Dispatch a sync call to the registered tool.

Refuses coroutine functions to avoid silently returning an un-awaited coroutine. Use :meth:acall for async tools.

acall async

acall(name: str, arguments: dict[str, Any]) -> Any

Dispatch an async call; transparently awaits sync callables.

tool

tool(
    registry: ToolRegistry,
    *,
    name: str | None = None,
    description: str | None = None,
    destructive: bool = False,
    category: ToolCategory = ToolCategory.OTHER,
    confirm: str | None = None,
    summary: str | None = None,
) -> Callable[[F], F]

Register the decorated callable on registry as a tool.

The tool's name defaults to the function name; its description defaults to the first paragraph of the function's docstring. Both can be overridden via the keyword arguments. confirm supplies a human-readable confirmation prompt for a destructive tool (surfaced as x-confirm); summary a short display label (surfaced as x-summary).

build_input_schema

build_input_schema(
    fn: Callable[..., Any],
    *,
    destructive: bool = False,
    category: ToolCategory = ToolCategory.OTHER,
    confirm: str | None = None,
    summary: str | None = None,
) -> dict[str, Any]

Derive a JSON Schema object from fn's parameters.

Supports the primitive types used by the built-in tool surface: str, int, float, bool, list[T], dict[str, Any], and X | None unions. Anything richer falls back to an empty schema fragment (no type constraint), which is still wire-valid JSON Schema.

The destructive flag is stamped at the schema root as x-destructive; category as x-category; confirm (when given) as x-confirm. AG-UI passes these extensions through verbatim, and frontends read x-destructive / x-confirm to gate execution behind a confirmation step.

ToolSpec dataclass

Canonical declaration of a server-side tool.

A ToolSpec bundles the callable with the metadata the registry needs to expose it to a Pydantic-AI agent and to a frontend: a stable name, a human-facing description, a destructive risk flag, and a coarse category.

name instance-attribute

name: str

Stable identifier exposed to the agent. Must be unique within a :class:~django_ag_ui.registry.tool_registry.ToolRegistry.

fn instance-attribute

fn: Callable[..., Any]

The Python callable that implements the tool. Must have typed parameters; the registry derives a JSON Schema from the signature.

description instance-attribute

description: str

User-facing summary shown to the agent. The first line is what most clients display.

destructive class-attribute instance-attribute

destructive: bool = False

If True, calling this tool may mutate state. The registry stamps x-destructive: true into the tool's JSON Schema so frontends can gate it behind a confirmation step.

category class-attribute instance-attribute

category: ToolCategory = OTHER

Coarse capability grouping. Surfaced as x-category in the JSON Schema.

confirm class-attribute instance-attribute

confirm: str | None = None

Optional human-readable confirmation prompt for a destructive tool (e.g. "Activate this project?"). Stamped as x-confirm so the frontend can show it instead of a generic "Run ?".

summary class-attribute instance-attribute

summary: str | None = None

Optional short label for the tool (e.g. "Query orders"). Stamped as x-summary so the frontend shows it on the tool-call card instead of the raw tool name.

ToolBinding dataclass

A registered tool plus the JSON Schema derived from its signature.

The schema is computed once at registration (including the x-destructive / x-category extensions) and carried alongside the spec so tool listings don't re-introspect on every request.

ToolCategory

Bases: str, Enum

Coarse grouping for a tool, surfaced to the agent and the UI.

Categories are advisory metadata: they let a frontend group tools, let a system prompt reason about capability classes, and let a project apply category-wide policy. They do not by themselves gate execution — that is the destructive flag's job.

X_DESTRUCTIVE_KEY module-attribute

X_DESTRUCTIVE_KEY = 'x-destructive'

X_CATEGORY_KEY module-attribute

X_CATEGORY_KEY = 'x-category'

X_CONFIRM_KEY module-attribute

X_CONFIRM_KEY = 'x-confirm'

X_SUMMARY_KEY module-attribute

X_SUMMARY_KEY = 'x-summary'

Skills

SkillRegistry

An ordered, named collection of :class:SkillSpecs.

State lives on the instance (like :class:~django_ag_ui.registry.tool_registry.ToolRegistry). :meth:payload produces the JSON-serialisable catalog the frontend consumes (camelCase keys, optional fields omitted when default).

register

register(spec: SkillSpec) -> SkillSpec

Register spec; raise ValueError if the name is taken.

add

add(
    name: str,
    title: str,
    prompt: str,
    *,
    description: str | None = None,
    send_immediately: bool = False,
    chip: bool = False,
) -> SkillSpec

Construct a :class:SkillSpec and register it (convenience).

payload

payload() -> list[dict[str, Any]]

The client catalog: a list of skill dicts with camelCase keys.

SkillSpec dataclass

A pre-defined prompt offered to the user (a "skill").

Serialised into the client catalog the frontend surfaces as chips and/or the /-command palette. Skills are data, not callables — the prompt is a static string (it may contain {placeholder}s the client fills from its skill context before sending).

name instance-attribute

name: str

Stable id; the /token in the palette. Unique within a registry.

title instance-attribute

title: str

Label shown in chips and the palette.

prompt instance-attribute

prompt: str

The prompt inserted (or sent). May contain {placeholder}s.

description class-attribute instance-attribute

description: str | None = None

Optional secondary line shown in the palette.

send_immediately class-attribute instance-attribute

send_immediately: bool = False

Send on pick instead of pre-filling the input. Surfaced as sendImmediately.

chip class-attribute instance-attribute

chip: bool = False

Also surface as a chip (the palette shows all skills regardless).

SkillsView

A read-only endpoint returning a :class:SkillRegistry's client catalog.

A callable instance (like :class:~django_ag_ui.agent.agui_view.DjangoAGUIView) so it can hold the registry and a project can mount several. GET returns the JSON skill list the frontend fetches via data-skills-url.

Skill prompts can encode internal workflows — worth gating. The view carries the same authentication seam as DjangoAGUIView (require_authenticated / get_user, sync or async hooks), so one policy can cover the agent endpoint and its catalogs. Defaults stay open for backwards compatibility — lock the catalog down whenever the agent endpoint is locked down.

Agent and view

DjangoAGUIView

An async Django view that serves an AG-UI endpoint.

Bridges a Django HttpRequest to Pydantic-AI's AGUIAdapter without Starlette: it parses the posted RunAgentInput, builds a Pydantic-AI Agent from the server-side tool registry, and returns a StreamingHttpResponse of AG-UI events (Server-Sent Events). Frontend tools declared in the request are merged by the adapter automatically.

The view is a callable instance, so configuration lives on self and a project can mount several with independent registries. model, instructions, and audit_logger fall back to the DJANGO_AG_UI settings when not passed explicitly (tests inject a TestModel).

Authentication is the host's responsibility. Tools (and the drf-mcp bridge) act as request.user; if your middleware hasn't authenticated the request, that is AnonymousUser — a data-exposure footgun. Pass require_authenticated=True to fail closed (401 for unauthenticated requests), and/or a get_user(request) hook to establish the user (e.g. from a token) before tools run. get_user may be sync or async; a sync hook runs off the event loop, so a plain ORM token → User lookup (Token.objects.select_related("user").get(key=...).user) is fully supported. A hook that raises propagates as an unhandled error (500) — return AnonymousUser (or None) for a clean 401 instead.

CSRF: the view defaults to csrf_exempt=True because AG-UI clients typically authenticate via headers (Bearer / API key), where CSRF does not apply. If your deployment authenticates with session cookies, pass csrf_exempt=False and send the CSRF token from the client — tools act as request.user, so a cookie-auth endpoint without CSRF protection lets any third-party page drive the agent as the logged-in user (mitigated, not eliminated, by Django's default SameSite=Lax cookie).

ToolsView

A read-only endpoint returning the agent's server-tool catalog (GET, JSON).

A callable instance (like :class:~django_ag_ui.agent.agui_view.DjangoAGUIView) holding the same :class:~django_ag_ui.registry.tool_registry.ToolRegistry the view uses. GET returns the :func:build_tool_catalog list the web component fetches via data-tools-url to label tool-call cards for server-side tools (whose schema never reaches the browser).

The catalog names every server tool the agent can wield — an inventory worth gating. The view carries the same authentication seam as DjangoAGUIView (require_authenticated / get_user, sync or async hooks), so one policy can cover the agent endpoint and its catalogs. Defaults stay open for backwards compatibility — lock the catalog down whenever the agent endpoint is locked down.

get_urls

get_urls(
    view: DjangoAGUIView,
    prefix: str = "agent/",
    *,
    skills: SkillRegistry | None = None,
    tools: ToolRegistry | None = None,
) -> list[URLPattern]

Return URL patterns mounting view at <prefix> (POST, SSE).

When skills is given, also mounts a read-only skills catalog at <prefix>skills/ (GET, JSON) for the web component's data-skills-url.

When tools (the same :class:ToolRegistry the view uses) is given, also mounts a read-only tool catalog at <prefix>tools/ (GET, JSON) for the component's data-tools-url — friendly card labels for server-side tools.

Include the result from your project's root URLconf::

urlpatterns = [
    ...,
    path("", include(get_urls(DjangoAGUIView(registry), tools=registry))),
]

build_agent

build_agent(registry: ToolRegistry, config: AgentConfig) -> Agent[None, Any]

Build a Pydantic-AI Agent from a registry and an :class:AgentConfig.

Each registry tool is registered as a plain Pydantic-AI tool. When config.audit_logger is set, every tool call is timed and recorded; the wrapper preserves the original signature so Pydantic-AI's schema generation is unaffected. Frontend tools declared in the AG-UI RunAgentInput are merged automatically by the adapter and are not registered here.

model_settings / retries tune the model; toolsets and capabilities compose external Pydantic-AI toolsets/capabilities (e.g. an MCP-client toolset) alongside the registry tools, so the agent can reach beyond the registered set.

AgentConfig dataclass

Resolved construction parameters for a Pydantic-AI Agent.

Bundles everything :func:~django_ag_ui.agent.agent_factory.build_agent needs so the call site passes one record instead of a long keyword list. The view resolves these from explicit overrides and the DJANGO_AG_UI settings; toolsets and capabilities arrive already resolved to instances (not dotted paths).

model instance-attribute

model: Any

The Pydantic-AI model (a model string or Model instance).

instructions class-attribute instance-attribute

instructions: str | None = None

System/instructions prompt for the agent.

audit_logger class-attribute instance-attribute

audit_logger: AuditLogger | None = None

Wraps every server-side tool call for timing + success/failure records. None means no auditing (a no-op logger).

model_settings class-attribute instance-attribute

model_settings: dict[str, Any] | None = None

Pydantic-AI ModelSettings (temperature, max_tokens, …).

retries class-attribute instance-attribute

retries: int | None = None

Default tool/output retry budget.

toolsets class-attribute instance-attribute

toolsets: Sequence[Any] | None = None

Extra Pydantic-AI toolsets composed alongside the registry tools.

capabilities class-attribute instance-attribute

capabilities: Sequence[Any] | None = None

Pydantic-AI capabilities passed to the Agent.

AgentFactoryFn

Bases: Protocol

The escape-hatch signature for DJANGO_AG_UI['AGENT_FACTORY'].

A dotted path to a callable matching this shape fully replaces the built-in :func:~django_ag_ui.agent.agent_factory.build_agent, giving a project complete control over Pydantic-AI Agent construction (custom model providers, output types, toolsets, instrumentation, …). It receives the per-request server-side tool registry and the resolved settings.

DEFAULT_SYSTEM_PROMPT module-attribute

DEFAULT_SYSTEM_PROMPT = "You are an assistant embedded in a web application. You can call tools to read data and to drive the user interface on the user's behalf. Prefer the most specific tool available. For a destructive action, call the tool directly with the right arguments — the interface shows the user an explicit confirmation before it runs, so do NOT ask for confirmation in text or wait for the user to say yes. Briefly state what you are doing. When the user refers to something by name, use a listing or search tool's arguments to find it and then act on the result — don't stop after the lookup. Treat 'open', 'go to', or 'show me' as a request to navigate. Always finish your turn with a short reply or a completed action — never an empty turn. Keep replies concise."

Configuration

AppSettings dataclass

Snapshot of the user-configurable DJANGO_AG_UI settings.

Built fresh on every read so test overrides take effect immediately (Django's settings object is the cache, not this dataclass).

model instance-attribute

model: Any

The Pydantic-AI model: a "provider:name" string (e.g. "anthropic:claude-sonnet-4.6") or a pre-built Model instance. Optional here; the agent factory raises a clear error if it is unset when an agent is actually built.

api_key instance-attribute

api_key: str | None

API key handed to the provider when MODEL is a "provider:name" string, so the key comes from settings rather than the environment. Ignored when PROVIDER is set or MODEL is already a Model.

provider instance-attribute

provider: Any

Optional Pydantic-AI Provider instance (or dotted path to one) used to build the model — for a custom base_url / client. When set, MODEL is just the model name's "provider:name" string and API_KEY is ignored (the provider carries its own credentials).

auto_confirm instance-attribute

auto_confirm: bool

When True, destructive tools do not require client-side confirmation. Surfaced to the frontend so it can skip the modal.

audit_logger instance-attribute

audit_logger: str | None

Dotted path to an AuditLogger implementation. None means use the package default (a no-op logger).

system_prompt instance-attribute

system_prompt: str | None

Optional override for the agent's system prompt.

model_settings instance-attribute

model_settings: dict[str, Any] | None

Pydantic-AI ModelSettings (e.g. {"temperature": 0.2, "max_tokens": 1024}) passed straight to the Agent. None leaves the model defaults untouched.

retries instance-attribute

retries: int | None

Default tool/output retry budget passed to the Agent. None uses Pydantic-AI's default.

agent_factory instance-attribute

agent_factory: str | None

Dotted path to a callable (registry, settings) -> Agent that fully replaces the built-in factory — the escape hatch for arbitrary Pydantic-AI configuration. None uses the built-in :func:~django_ag_ui.agent.agent_factory.build_agent.

toolsets instance-attribute

toolsets: tuple[str, ...]

Dotted paths to extra Pydantic-AI toolsets (or zero-arg callables returning one) merged into the agent's catalog — e.g. an MCP-client toolset or the drf-mcp bridge. Empty by default.

capabilities instance-attribute

capabilities: tuple[str, ...]

Dotted paths to Pydantic-AI capabilities (or zero-arg callables returning one) passed to the Agent. Empty by default.

conversation_store instance-attribute

conversation_store: str | None

Dotted path to a ConversationStore for server-side persistence. None keeps the server stateless (the default NullConversationStore).

drf_mcp_server instance-attribute

drf_mcp_server: str | None

Dotted path to a drf-mcp-server MCPServer instance whose tools are exposed to the agent in-process (requires the [drf-mcp] extra). None disables the bridge.

get_settings

get_settings() -> AppSettings

Read the active DJANGO_AG_UI settings dict into an AppSettings.

Policy and audit

needs_confirmation

needs_confirmation(binding: ToolBinding, *, auto_confirm: bool) -> bool

Return whether binding should prompt for confirmation.

A tool needs confirmation when it is destructive and the project has not opted into autopilot (auto_confirm=False). The actual modal is rendered client-side; this helper is the canonical server-side statement of the rule, used to annotate tool listings and to keep the confirmation policy in one place.

AuditLogger

Bases: Protocol

Sink for tool-invocation records.

Implementations are free to drop, sample, or forward events. The package ships a no-op default (NullAuditLogger) and a logging-backed implementation (LoggingAuditLogger); projects supply their own by setting DJANGO_AG_UI["AUDIT_LOGGER"] to a dotted path.

AuditEvent dataclass

A single tool invocation as seen by the audit logger.

Arguments are stored as a string (typically JSON-encoded) to keep audit records cheap to serialize and to discourage retention of sensitive raw values.

NullAuditLogger

Discards every event. The default when no audit logger is configured.

LoggingAuditLogger

Writes audit events to the Python logging framework.

Successful invocations log at INFO; failures log at WARNING with the error message included.

resolve_audit_logger

resolve_audit_logger(dotted_path: str | None) -> AuditLogger

Instantiate the audit logger referenced by a dotted path.

None yields a NullAuditLogger. The path must point to a class importable with no arguments; consumers that need a parameterised logger should construct the instance themselves and pass it to the view directly.

Conversation persistence

ConversationStore

Bases: Protocol

Pluggable server-side persistence for AG-UI conversations.

Resolved from DJANGO_AG_UI["CONVERSATION_STORE"]. The package ships a no-op default (NullConversationStore — the server stays stateless) and a session-backed implementation; projects supply their own (a DB model, Redis, …) by pointing the setting at a dotted path. All methods are async so an implementation can use the async ORM or a network backend.

Conversation dataclass

A persisted conversation, keyed by thread_id.

messages are AG-UI Message objects (the wire shape the client speaks), so a store round-trips them verbatim. owner_id scopes the conversation to a user for authorization.

NullConversationStore

The default store: no-op, keeping the server stateless.

load always returns None and save / delete do nothing, so the conversation lives entirely in the client's posted history (today's behaviour). The view treats this store as "persistence off".

DjangoSessionConversationStore

Conversation persistence in the Django session (no migration).

Conversations are namespaced by thread_id within the logged-in user's session, so scoping to the user is implicit and durability spans that user's browser session. The batteries-included server-side store; for cross-device or audited persistence, supply a model-backed store instead.

ModelConversationStore

Bases: ABC

Abstract base for a model-backed (or any sync) ConversationStore.

Provides the async wrapping and per-request owner scoping; a subclass implements the three synchronous row operations against its own Django model (cross-device, auditable persistence). Kept model-agnostic on purpose — the package ships no concrete model so it forces no migration; consumers define the model, its fields, and the owner relationship.

Example::

class MyStore(ModelConversationStore):
    def _fetch(self, thread_id, owner_id):
        row = MyConversation.objects.filter(
            thread_id=thread_id, owner_id=owner_id,
        ).first()
        return None if row is None else Conversation(...)
    def _store(self, conversation, owner_id): ...
    def _remove(self, thread_id, owner_id): ...

resolve_conversation_store

resolve_conversation_store(dotted_path: str | None) -> ConversationStore

Instantiate the conversation store referenced by a dotted path.

None yields a NullConversationStore (persistence off). The path must point to a class importable with no arguments; a store needing constructor arguments should be wired by the consumer.

Internal helpers

These are not part of the public re-export surface but are referenced from the guides.

resolve_dotted_instances

resolve_dotted_instances(paths: Sequence[str]) -> list[Any]

Resolve dotted paths into instances, for TOOLSETS / CAPABILITIES.

Each path resolves to either an instance (used as-is) or a zero-arg callable / class returning one (invoked). The results are passed to :func:~django_ag_ui.agent.agent_factory.build_agent to compose external Pydantic-AI toolsets and capabilities alongside the registry tools.

build_model

build_model(model: str, *, api_key: str | None = None, provider: Any = None) -> Any

Build a Pydantic-AI model from a "provider:name" string + explicit key.

Delegates the provider: prefix → Model-class resolution to Pydantic-AI's own :func:infer_model, supplying a provider_factory that injects the credentials instead of letting Pydantic-AI read them from the environment:

  • provider (a Provider instance, or a dotted path to one) takes precedence — used as-is, so it can carry a custom base_url / client.
  • otherwise api_key is passed to the prefix's default Provider class (resolved via :func:infer_provider_class).

Because the prefix map lives in Pydantic-AI, every provider it knows works automatically (anthropic, openai, openai-responses, google, groq, bedrock, …) — there is no hand-maintained table to drift out of date.

A bare model name Pydantic-AI can map to a provider (e.g. claude-… → anthropic) is accepted too; only when the provider can't be resolved at all is an error raised.

Raises:

Type Description
ImproperlyConfigured

when the model's provider can't be resolved — an unknown / uninferable prefix, or the matching provider extra not installed. Set PROVIDER to a Provider instance or dotted path for anything Pydantic-AI can't infer.

build_tool_catalog

build_tool_catalog(registry: ToolRegistry) -> list[dict[str, Any]]

The agent's server-tool catalog for the frontend to label tool-call cards.

Server-side tools (the @tool registry, and drf-mcp tools when DJANGO_AG_UI['DRF_MCP_SERVER'] is set) execute server-side, so their JSON Schema never reaches the browser — the web component can't read an x-summary off it. This catalog is the channel for those labels: the component fetches it via data-tools-url and maps tool name → label.

Each entry is {"name", "summary", "description"?}. summary is always present, resolved from the single source of truth with a fallback chain:

  • registry tools → @tool(summary=…) → a prettified name;
  • drf-mcp tools → display_nametitle → a prettified name.

description (a longer blurb for tooltips) is included when available (ToolSpec.description / drf-mcp display_descriptiondescription). Registry tools win on name collisions.

DrfMcpToolset

Bases: ExternalToolset[Any]

Exposes a drf-mcp MCPServer's tools as a Pydantic-AI toolset.

Built per request so the acting user is the current AG-UI user. Tool schemas and execution both route through drf-mcp's own handlers, so the advertised parameters, serializer validation, and permissions match the HTTP transport exactly.

exclude_names carries the @tool registry's names: on a collision the registry tool wins (the same rule build_tool_catalog applies) and the drf-mcp twin is skipped — otherwise pydantic-ai raises UserError for the duplicate name at run time.

get_tools async

get_tools(ctx: Any) -> Any

Load tool defs from drf-mcp's tools/list once, then defer to base.

Loading runs in a thread (sync_to_async) because the sync handle_tools_list may evaluate per-user listing permissions against the DB, which Django forbids on the async event loop.