Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -4642,6 +4642,13 @@
{
"group": "Migrations",
"pages": [
{
"group": "Agno v3 Migration",
"pages": [
"other/v3-migration",
"other/v3-changelog"
]
},
{
"group": "Agno v2 Migration",
"pages": [
Expand Down
361 changes: 361 additions & 0 deletions other/v3-changelog.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,361 @@
---
title: Agno v3.0 Changelog
sidebarTitle: Agno v3.0 Changelog
description: "Full list of storage, parameter, and behavior changes introduced in Agno v3.0."
---

This release rebuilds the storage layer around a normalized runs table, extends
per-user isolation across the platform, and makes AgentOS background execution
durable.

The major changes are:

- Session runs are stored one row per run in a dedicated runs table.
- `user_id` scoping extends to metrics, schedules, evals, knowledge and vector databases.
- `background=True` on AgentOS is backed by a durable job queue that survives crashes and deploys.
- Database migrations run through the built-in `MigrationManager`, with schema versions tracked on every adapter.

## Storage

<Accordion title="Denormalized session storage">
- Runs are no longer stored as a JSON blob in the sessions table. Each run is
a row in the runs table (`agno_runs` by default) with `run_id`,
`session_id`, `run_type`, `run_index`, `user_id`, `status` and `run_data`.
- Saving a run writes one row instead of rewriting the whole session history.
This removes the quadratic write amplification and unbounded row growth of
the blob design.
- `session.runs` is still populated on read: sessions merge the runs table
with any legacy blob, so un-migrated sessions keep working.
- New direct accessors: `db.get_run(run_id)` and
`db.get_runs(session_id=..., user_id=..., status=..., limit=...)`.
- The v2 -> v3 migration preserves the legacy `runs` column as a backup.
Reclaim it with `db.cleanup_legacy_runs_column(force=True)` (SQL) or
`db.cleanup_legacy_runs_field(force=True)` (document/KV adapters) after
verifying the migration — the migration keeps every legacy blob as a backup,
so the unforced call refuses by design.
</Accordion>

<Accordion title="SQLite runs in WAL journal mode">
- `SqliteDb` and `AsyncSqliteDb` issue `PRAGMA journal_mode=WAL` on connect,
replacing SQLite's default DELETE journal (a journal create, double fsync and
delete on every commit).
- WAL is persistent on the database file and produces `-wal` and `-shm`
sidecar files next to it. Copy or back up all three together.
</Accordion>

<Accordion title="Built-in migrations and schema versioning">
- `MigrationManager(db).up()` walks all registered migrations for every table
and stamps the resulting schema version.
- Schema versions are tracked on every adapter, including the document and
key-value stores (MongoDB, Redis, Valkey, Firestore, DynamoDB, SurrealDB,
JSON, GCS JSON, in-memory). An unstamped database is treated as pre-v3 and
migrated.
- Migrations are idempotent and non-destructive. Failures raise and abort
before any version stamp is written.
</Accordion>

## Knowledge

<Accordion title="Insert API replaces add_content">
- `Knowledge.add_content` -> `insert()`
- `Knowledge.add_content_async` -> `ainsert()`
- `Knowledge.add_contents_async` -> `ainsert_many()`
</Accordion>

<Accordion title="Vector database changes">
- `LanceDb`: `use_tantivy` is removed; passing it now raises a `TypeError`.
- On schema-based stores, searching a pre-v3 vector table with a `user_id`
raises a `ValueError` directing you to the vector database migration,
instead of silently returning empty results. Schemaless stores (Qdrant,
Pinecone, Upstash, Chroma, MongoDB, OpenSearch, SurrealDB) treat pre-v3
documents as shared.
</Accordion>

## User Isolation

<Accordion title="Per-user scoping across the platform">
- `user_id` columns added to the schedules, schedule-runs and evals tables.
All user-facing read and write methods accept `user_id`.
- Metrics aggregate per user. The unique key changed from
`(date, aggregation_period)` to `(user_id, date, aggregation_period)`.
Sessions without a `user_id` aggregate into a shared bucket that
`get_metrics` maps back to `None`.
- Knowledge and vector database contents are scoped per user when isolation
is enabled. On schema-based stores, searching a pre-v3 vector table with a
`user_id` raises a `ValueError` directing you to the vector database
migration; schemaless stores treat pre-v3 documents as shared.
- Schedule polling (`claim_due_schedule` / `release_schedule`) stays
unscoped so background execution fires across all users; each schedule run
records the owner denormalized from its parent schedule.
</Accordion>

## AgentOS

<Accordion title="MCP server configuration">
- `AgentOS(enable_mcp_server=..., mcp_config=...)` is replaced by a single
`mcp_server=` parameter, which takes a bool or an `MCPServerConfig`.
</Accordion>

<Accordion title="Pagination is validated">
- Passing `page` without `limit`, or a `page` below 1, now raises a
`ValueError` instead of being silently ignored. Pages are 1-indexed.
</Accordion>

<Accordion title="Durable background execution">
- Accepted `background=True` requests are committed job rows that survive
crashes, restarts and deploys. Any replica's worker can claim and execute
them.
- Concurrency is bounded. Excess submissions wait in `pending` status instead
of overloading the process.
- Runs can be tailed (`stream=true`), resumed after a disconnect (`/resume`)
and cancelled from any replica.
- `Idempotency-Key` headers deduplicate resubmissions.
- Redis is optional coordination (live event streams, cross-replica
cancellation), never truth. A Redis fault degrades the live view; it cannot
lose or corrupt a run.
- Background execution requires a `db` on the component and returns a 400
without one.
- External framework agents (LangGraph, Claude, DSPy, etc.) stream inline
when `background=true` is requested; their runs are not resumable.
</Accordion>

<Accordion title="JWT middleware">
- `secret_key` removed from `JWTMiddleware` and `authorization_config`. Use
`verification_keys`, which takes a list of keys.
</Accordion>

<Accordion title="Metadata route consolidation">
- `GET /models` removed. Model data moved into `GET /config` under
`available_models`.
- `GET /` returns a minimal landing response linking to `/docs`, `/info` and
`/health`.
- `GET /info` is the single unauthenticated metadata endpoint.
</Accordion>

## Agents

<Accordion title="Removed parameters">
- `enable_user_memories` -> `update_memory_on_run`
- `search_session_history` -> `search_past_sessions`
- `num_history_sessions` -> `num_past_sessions_to_search`
- `num_past_session_runs` -> `num_past_session_runs_in_search`
- `reasoning=True` removed. Set `reasoning_model=<native reasoning model>`
explicitly.
- `continue_run` / `acontinue_run`: `updated_tools` removed. Pass
`requirements` (a list of `RunRequirement` from the paused run output).
</Accordion>


<Accordion title="Culture feature removed">
- The experimental culture feature is removed: `enable_agentic_culture`,
`add_culture_to_context`, `CulturalKnowledge`, the culture tools and the
`agno_culture` table.
- Use [Knowledge](/knowledge/overview) for shared cross-user information.
</Accordion>

## Teams & Workflows

<Accordion title="Keyword-only Workflow constructor">
- The `Workflow` constructor no longer accepts positional arguments:
`Workflow(name=..., steps=[...])`.
- `Team` still accepts `Team([agent_1, agent_2])`; the keyword form
`Team(members=[...])` is preferred but not required.
</Accordion>

<Accordion title="HITL configuration unified into HumanReview">
- Flat HITL kwargs on `Step`, `Steps`, `Loop`, `Condition` and `Router` are
removed: `requires_confirmation`, `confirmation_message`, `on_reject`,
`requires_user_input`, `user_input_message`, `user_input_schema`,
`requires_output_review`, `output_review_message`,
`requires_iteration_review`, `iteration_review_message`, `on_error`,
`hitl_max_retries`, `hitl_timeout`, `on_timeout`.
- Pass `human_review=HumanReview(...)` instead
(import from `agno.workflow.types`). Field names are unchanged except
`hitl_max_retries` -> `max_retries` and `hitl_timeout` -> `timeout`.
</Accordion>

## Tools

<Accordion title="Toolkit updates">
- `MCPToolbox`: `auth_tokens` and `auth_headers` removed. Use
`auth_token_getters`.
- Toolkits have an `id`, used by AgentOS to reference tools stably.
</Accordion>

<Accordion title="Workspace refuses credential files by default">
- `Workspace` now excludes env files (`.env*`, `*.env`) and conventional
credential files — private keys and keystores (`*.pem`, `*.key`, `id_rsa*`),
credential directories (`.ssh`, `.aws`, `.kube`), registry and host tokens
(`.npmrc`, `.netrc`, `.git-credentials`), credential data files
(`credentials.json`, `secrets.yaml`, `service_account*.json`) and Terraform
inputs (`*.tfvars`).
- An agent that reads one of these today starts getting a refusal. Re-allow a
specific path explicitly:

```python
Workspace(".", allow_paths=["config/credentials.json"])
```

- Committed templates go the other way and become readable
(`.env.example`, `.env.sample`, `.env.template`, `.env.dist`).
- `credentials.*` and `secrets.*` are deliberately absent from the list: they
would also refuse ordinary source such as `credentials.py`.
- Known limit: a hard link to an excluded file bypasses the boundary. Symlinks
are caught.
</Accordion>

<Accordion title="Removed and renamed toolkits">
- `MultiMCPTools` is removed, along with its `allow_partial_failure` parameter.
Use one `MCPTools` per server.
- The flat Google tool modules are removed: `agno.tools.gmail`,
`agno.tools.googlesheets`, `agno.tools.googlecalendar`,
`agno.tools.google_maps`, `agno.tools.google_drive` and
`agno.tools.google_bigquery`. Import from `agno.tools.google.*` instead.
- Google toolkits: `creds_path` -> `credentials_path`, `auth_port` ->
`oauth_port`.
- `SeltzTools`: `max_documents` -> `max_results`. Older `seltz` SDKs still
work through a fallback; `seltz>=1.2.0` is needed for the `scope`, domain
and date filters.
- `BrandfetchTools`: the `async_tools` parameter is removed.
- `StudioTool` -> `StudioTools`.
- `GDriveContextProvider` -> `GoogleDriveContextProvider`.
</Accordion>

<Accordion title="Removed toolkit methods">
- `DuckDuckGoTools.duckduckgo_search` -> `web_search`, and
`DuckDuckGoTools.duckduckgo_news` -> `search_news`. The toolkit now builds
on `WebSearchTools`, which supplies both methods.
- `FileTools.check_escape` -> `Toolkit._check_path`. `LocalFileSystemTools`
keeps its own `check_escape`, which is unaffected.
- `BrightDataTools.get_screenshot`: the unused `output_path` parameter is removed.
- `PgVector.enable_prefix_matching` is removed. It was a dead helper with no
effect on search.
</Accordion>

## Scheduler

<Accordion title="Schedule provenance columns">
- The schedules table gains eight nullable columns recording where a schedule
came from and who last touched it: `managed_by`, `target_type`, `target_id`,
`created_by_run_id`, `created_by_session_id`, `updated_by_run_id`,
`updated_by_session_id` and `disabled_reason`. `managed_by` and `target_id`
are indexed.
- The v3.0.0 migration adds the columns and indexes on SQLite and PostgreSQL
(sync and async). Existing rows are left as-is with `NULL` provenance; no
data is rewritten. MongoDB needs no schema change.
</Accordion>

<Accordion title="Schedule names are unique per user">
- The schedules unique key becomes `(user_id, name)`.
- If duplicate schedule names already exist, the v3.0.0 migration **aborts**
rather than stamping itself as done. Resolve the duplicates and re-run.
</Accordion>

<Accordion title="update_schedule column allow-list">
- `update_schedule` can only write `name`, `description`, `method`,
`endpoint`, `payload`, `cron_expr`, `timezone`, `timeout_seconds`,
`max_retries`, `retry_delay_seconds`, `enabled`, `next_run_at` and
`disabled_reason`. Any other key raises a `ValueError` naming the rejected
columns.
- Ownership, provenance and lock state are no longer writable through the
generic update path, so a name-keyed upsert cannot repoint who a schedule
belongs to or what it targets.
</Accordion>

## Evals

<Accordion title="Result file placeholder renamed">
- `store_result_in_file`: the `eval_id` parameter is renamed to `run_id`.
- `{eval_id}` is no longer accepted in `file_path_to_save_results` templates.
Use `{run_id}`.
- `POST /eval-runs` now returns the id the row was actually stored under
(`run_id`), instead of the eval object's `eval_id`. Every eval run gets its
own `run_id`.
- The eval classes no longer carry `eval_id`; results carry a per-run `run_id`,
so re-runs no longer overwrite each other's stored results.
</Accordion>

## Models

<Accordion title="Removed model APIs">
- The `agno.models.metrics` module and its `Metrics` alias are removed. Use
`agno.metrics` and `RunMetrics`.
- `Model.classify_error` -> `ModelProviderError.classify(error)`.
</Accordion>

<Accordion title="Mistral requires mistralai v2">
- The `mistralai` v1 compatibility layer is removed. `agno[mistral]` now
requires `mistralai>=2.0.0`.
- `agno[mistral]` is included in the `models` extra again.
</Accordion>

<Accordion title="Default model updates">
- `Cerebras` and `CerebrasOpenAI` default to `gpt-oss-120b`, replacing
`llama-4-scout-17b-16e-instruct`.
- OpenAI `reasoning_effort`, `reasoning_summary`, `service_tier` and
`verbosity` accept the full set of API values (including `none`, `xhigh`,
`max`, `scale`, `fast`, `ultrafast`) and any future string. This widens the
accepted types; no existing call breaks.
</Accordion>

## Learning

<Accordion title="Entity memory is isolated per user">
- `EntityMemoryStore` with `namespace="user"` did not isolate users: the row
key carried no user component, so two users recording the same entity name
and type shared one row. One user's facts overwrote the other's and leaked
into their prompt context.
- Row keys under `namespace="user"` now embed a digest of the `user_id`.
Global and custom namespaces are unchanged and do not re-key.
- Pre-v3 rows are re-keyed by the v3.0.0 migration, not at runtime. Run it
with the rest of your migrations, or call
`agno.learn.migrations.rekey_user_entity_learnings` directly. The
migration's `down()` refuses to reverse the re-key, since the old key
collides users by design.
- `EntityMemoryStore.delete` / `adelete` take a keyword-only `user_id` and
refuse `namespace="user"` deletes without it. `get` / `aget` now require a
`user_id` in that namespace instead of returning an arbitrary user's row.
</Accordion>

<Accordion title="Studio authors learning, not legacy memory">
- `enable_agentic_memory` and `memory_manager_id` are removed from every Studio
create/edit form (sync and async). Studio components declare memory through
`LearningMachine` instead: `learning_name` binds a registry-declared machine,
or `enable_learning=True` builds the default one.
- The `Agent` and `Team` constructor parameters are unchanged, as are
`Registry.memory_managers` and `resolve_memory_manager_reference`, so configs
stored with the legacy fields keep rehydrating. Only the Studio authoring
surface dropped them.
- Enabling learning on a component (a learning machine actually configured
via `learning_name` / `enable_learning`) clears `enable_agentic_memory` and
`memory_manager`: both register a tool named `update_user_memory`, and the
legacy one silently shadowed the store's. Setting them to `False`/`""`
leaves the legacy pair alone.
</Accordion>

<Accordion title="Removed learn aliases">
- `MemoriesConfig` -> `UserMemoryConfig`
- `MemoriesStore` -> `UserMemoryStore`
- `Decision` -> `DecisionLog`
</Accordion>

## Packaging

<Accordion title="Postgres extra installs a working driver">
- `agno[postgres]` installed `psycopg-binary` only, which ships the C
accelerator but no importable `psycopg` and no engine layer, so
`PostgresDb` raised `ModuleNotFoundError: No module named 'sqlalchemy'` on
a clean install. The extra now installs `psycopg`, `psycopg-binary` and
`sqlalchemy`.
- If you worked around this by installing `psycopg` or `sqlalchemy`
yourself, you can drop those pins.
</Accordion>

## Errors

<Accordion title="Clearer schema mismatch errors">
- SQL adapters raised `Table <name> has an invalid schema` with no next step.
The error now names the likely cause (a database created by an older Agno
version) and points at both fixes: `asyncio.run(MigrationManager(db).up())`
or `POST /databases/all/migrate` on AgentOS.
</Accordion>
Loading
Loading