Skip to main content

An LLM-powered agent assistant for Datasette

Project description

datasette-agent

PyPI Changelog Tests License

An LLM-powered agent assistant for Datasette

Installation

Install this plugin in the same environment as Datasette.

datasette install datasette-agent

Usage

Visit /-/agent to start a conversation with the chat assistant.

The agent uses datasette-llm to call language models. Configure a default model for it before visiting /-/agent, for example in datasette.yml:

plugins:
  datasette-llm:
    default_model: gpt-5.4-mini

The "Explore with AI agent" entries that appear in the database and table action menus launch a background agent that explores the selected database or table and writes a report. Reports live under /-/agent/explore/.

Visit /-/agent/background to launch background agents directly. Each one is given a goal and runs toward it without further input. The listing includes a Stop button for cancelling agents that are still running.

Permissions

This plugin registers three independent permissions:

  • datasette-agent — required to use the chat assistant under /-/agent.
  • datasette-agent-explore — required to see the "Explore with AI agent" entries in the database/table action menus and to use the explorer routes under /-/agent/explore/.
  • datasette-agent-background — required to use the spawn_background_agent and check_background_agent tools from chat, and to access the /-/agent/background page and /-/agent/api/background/* endpoints. The background-agent endpoints require both datasette-agent and datasette-agent-background.

The three permissions are independent: an actor may hold any subset. The --root user holds all of them.

Registering additional tools from plugins

Other Datasette plugins can register additional tools for the agent using the register_agent_tools plugin hook.

Defining a tool

Create a Datasette plugin that implements the register_agent_tools hook, returning a list of AgentTool instances:

from datasette import hookimpl
from datasette_agent.tools import AgentTool


@hookimpl
def register_agent_tools(datasette):
    return [
        AgentTool(
            name="my_tool",
            description="Description of what this tool does, used by the LLM to decide when to call it.",
            input_schema={
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "The query to run",
                    },
                    "style": {
                        "type": "string",
                        "enum": ["brief", "detailed"],
                        "description": "Output style",
                    },
                },
                "required": ["query"],
            },
            fn=my_tool_handler,
            # Optional: name a Datasette permission action that gates this tool.
            # required_permission="myplugin-write",
        ),
    ]

Gating a tool with a permission

AgentTool accepts an optional required_permission: str | None field. When set, the agent harness calls datasette.allowed(action=required_permission, actor=actor) for the current actor before sending the tool list to the LLM. If the actor lacks the permission, the tool is filtered out of the list — the model never sees it and cannot call it. There is no runtime "permission denied" branch your fn needs to handle.

Your plugin is responsible for registering the action via Datasette's register_actions plugin hook:

from datasette import hookimpl
from datasette.permissions import Action
from datasette_agent.tools import AgentTool


@hookimpl
def register_actions():
    return [
        Action(
            name="myplugin-write",
            description="Allow my plugin's write tools",
        ),
    ]


@hookimpl
def register_agent_tools(datasette):
    return [
        AgentTool(
            name="my_write_tool",
            description="Writes things",
            input_schema={"type": "object", "properties": {}},
            fn=my_write_handler,
            required_permission="myplugin-write",
        ),
    ]

For a working example see this plugin's own spawn_background_agent and check_background_agent tools, which use required_permission="datasette-agent-background".

Tool handler function

Each tool's fn must be an async function that accepts datasette and actor as keyword arguments, plus any parameters defined in input_schema. It must return a JSON string:

import json


async def my_tool_handler(datasette, actor, query, style=None):
    # Do work here...
    return json.dumps({
        "result": "Tool output that the LLM will see",
    })

To render rich HTML inline in the chat UI, include an _html key in the returned JSON. Any top-level key whose name starts with _ is removed before the tool result is sent to the LLM, so the HTML is shown to the user but not passed back to the model:

return json.dumps({
    "_html": '<div class="my-widget">Rich content here</div>',
    "summary": "Widget rendered successfully",
})

Rendering custom HTML from tools

Tool plugins can render rich HTML inline in the chat UI by returning a JSON object with an _html key. The HTML is rendered directly in the conversation. The remaining keys are returned to the LLM as the tool result, with any key whose name starts with _ removed first.

Example tool implementation:

import json

async def _render_widget(datasette, actor, database, sql):
    html = (
        '<script src="/-/static-plugins/my-plugin/widget.js" type="module"></script>\n'
        '<my-widget>\n'
        f'<script type="application/json">{json.dumps({"database": database, "sql": sql})}</script>\n'
        '</my-widget>'
    )
    return json.dumps({
        "_html": html,
        "database": database,
        "sql": sql,
        "summary": "Widget rendered successfully",
    })

The _html value is inserted into the chat as raw HTML, so it can include custom elements, scripts, and styles. The other keys (database, sql, and summary in this example) are what the LLM receives as the tool result.

If your plugin runs SQL and displays the results in HTML, add a link below the rendered output using Datasette Agent's built-in SQL link styling:

<p class="agent-sql-edit-link"><a href="/data/-/query?sql=select+1">View SQL query</a></p>

Example plugins

CLI commands

Interactive chat

Start an interactive chat session with the agent from the command line:

datasette agent chat mydata.db

You can pass multiple database files, use :memory: for an in-memory database, specify a model, or send a single prompt:

datasette agent chat mydata.db -m gpt-5.4-mini
datasette agent chat mydata.db -m gpt-5.4-mini -p "List all tables"

Options:

  • -p, --prompt — Send a single prompt and exit (non-interactive mode)
  • -m, --model — LLM model to use

Listing available tools

To see all registered agent tools, grouped by plugin:

datasette agent tools

Output:

agent:
  list_databases_and_tables
    List all available databases and their tables
  describe_table
    Get column names, types, and foreign keys for a table
  sql_query
    Execute a read-only SQL query against a database

Add --json for machine-readable output:

datasette agent tools --json

Development

To set up this plugin locally, first checkout the code. Run the tests like this:

cd datasette-agent
uv run pytest

To run the development server with a persistent internal database and GPT-5.5 as the model:

uv run datasette --internal internal.db \
  --root --secret 1 \
  -s plugins.datasette-llm.default_model gpt-5.5

Add extra database files to that command to enable the agent to query them.

Credits

This plugin vendors streaming-markdown by Damian Tarnawski, MIT licensed.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

datasette_agent-0.1a3.tar.gz (71.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

datasette_agent-0.1a3-py3-none-any.whl (59.3 kB view details)

Uploaded Python 3

File details

Details for the file datasette_agent-0.1a3.tar.gz.

File metadata

  • Download URL: datasette_agent-0.1a3.tar.gz
  • Upload date:
  • Size: 71.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for datasette_agent-0.1a3.tar.gz
Algorithm Hash digest
SHA256 ff8b5ca85d0c1d30ea404ce5329fe4479ae6a811a0268d349866d21024d35d85
MD5 3273df72838ddd4468befeca6fa22e16
BLAKE2b-256 b5867a9365b4694d2e642ea705b27b9f913f8a6b9a7a9722e66a3bb195bcfb18

See more details on using hashes here.

Provenance

The following attestation bundles were made for datasette_agent-0.1a3.tar.gz:

Publisher: publish.yml on datasette/datasette-agent

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datasette_agent-0.1a3-py3-none-any.whl.

File metadata

  • Download URL: datasette_agent-0.1a3-py3-none-any.whl
  • Upload date:
  • Size: 59.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for datasette_agent-0.1a3-py3-none-any.whl
Algorithm Hash digest
SHA256 06cd3471cd54ff896e884437bcf624fb69cd28b4550d2acc60b16256bc2327d2
MD5 ac524bffcff429fd570366da46e4fdf7
BLAKE2b-256 20d39226fc7f25e2bedef2ce640c0956d985c33628a2b9f8cc5172094a53f39a

See more details on using hashes here.

Provenance

The following attestation bundles were made for datasette_agent-0.1a3-py3-none-any.whl:

Publisher: publish.yml on datasette/datasette-agent

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page