Repository navigation
0.2.20
Added
-
--llm-provider openai_compatible: any OpenAI-compatible gateway or
server — model routers and LLM gateways (LiteLLM, Requesty, OrcaRouter,
Portkey) and self-hosted servers (vLLM, llama.cpp, LM Studio, Ollama), reached
byOPENAI_COMPATIBLE_BASE_URL, withAI_MODELnamed as the endpoint names
it and an optionalOPENAI_COMPATIBLE_API_KEY(OPENAI_API_KEYis never sent
there). Pydantic AI and Pydantic Deep Agents projects; OpenAI embeddings for
RAG go to the same endpoint; Pydantic Deep Agents projects need
pydantic-deep 0.3.50, which also compacts long conversations on that endpoint.
One provider instead of a named one per gateway (#141, #156) -
--memory: persistent per-user agent memory (Pydantic AI + PostgreSQL) —
the assistant keeps a Markdown notebook per user with the
pydantic-ai-harnessMemorycapability, reads it back at the start of
every conversation, and getswrite_memory/read_memory/
delete_memory/search_memorytools, each rendered as a chat card. Users
inspect, edit and delete every file on Settings → Memory
(/api/v1/me/memory, optimistic-concurrency versions). With--teamseach
organisation gets its own notebook, so nothing learned in one is injected
into another; channel bots use memory only in one-to-one chats; deleting a
user deletes their memory; a notebook holds at most 100 files. Stored in the
harness's ownagent_memory*tables, which Alembic autogenerate now leaves
alone (as it does the TODO planner'stodos). Activated at runtime with
ENABLE_MEMORY=true. Contributed by @OchnikBartek (#133)
Changed
- Generated projects run Valkey instead of Redis —
valkey/valkey:9-alpinein the compose files, the GitHub Actions and GitLab
CI services, andvalkey-server/valkey-clifor the prod command and
health checks. Redis 8 is licensed RSALv2/SSPLv1/AGPLv3; Valkey is the
BSD-licensed fork and speaks the same protocol, so theredisclient,
REDIS_*settings and theredisservice name are unchanged. Existing
projects: Valkey cannot load an RDB written by Redis 7.4 (Can't handle RDB format version 12); the template keeps only cache, broker and rate-limit
data there, so move/data/dump.rdbaside (or recreate theredis_data
volume) before the first start. The prod health check gained a
start_period, so a restarted container is not judged on its stale status - Renovate no longer proposes Postgres or Milvus major updates: a new major
changes the on-disk format of existing projects' volumes (Postgres needs
pg_upgrade), so each will be done deliberately, with a migration note
Fixed
Generated projects had drifted from the libraries they resolve to: main's CI
failed in 13 jobs, and several breakages were runtime-only.
- Every PostgreSQL project failed to run Alembic and SQLAdmin — SQLAlchemy
2.1 maps a barepostgresql://URL to psycopg 3, which the template does not
install.DATABASE_URL_SYNCnow namespostgresql+psycopg2://, which also
holds under SQLAlchemy 2.0 (pinned by SQLModel); the deep-research TODO pool
strips the driver back off for asyncpg - Tool results crashed every PydanticAI and PydanticDeep chat turn —
Pydantic AI 2.0 replacedFunctionToolResultEvent.resultwith.part, and the
agent session still read.result, whichtycould not see through an
untyped event. Thepydantic-ai-slimfloor is now 2.0.0 - PydanticDeep projects could not build their agent — pydantic-deep 0.3.45
moved to Pydantic AI workspaces: the assistant now usesStateWorkspace, or
DaytonaWorkspacewith the Daytona sandbox backend (which now installs thedaytona
extra instead of silently falling back to in-memory), and uploads go through
DeepAgentDeps.upload_file. Since pydantic-deep installspydantic-ai-slim,
the selected provider's SDK is now declared, as for PydanticAI projects - The project-scoped PydanticDeep chat WebSocket could never have worked —
it calledget_agent(context=...), which takes no such argument, and a
stream_events()method that does not exist. It now streams
run_stream_events() - MCP connections failed on mcp 2.x — the streamable-HTTP transport is
streamable_http_clientwith headers on anhttpx2client, and the OAuth
discovery helpers returnhttpx2requests that anhttpxclient cannot
send. Both ported;mcp>=2.0.0is now declared - Code execution and skills broke on new majors —
pydantic-monty1.0
renamedmax_duration_secstomax_feed_duration_secs, and
pydantic-ai-skills2.0 removedSkillsToolsetforSkillsCapability
(skills now load throughload_capability) - OpenAI embeddings relied on another package's dependency —
openaiis
now declared whenever OpenAI embeddings are selected, so a non-OpenAI
provider no longer leaves the embedding service without its SDK docker-compose.prod.ymlwas rejected outright for Celery and Taskiq
projects —celery_workerandtaskiq_workerrunreplicas: 2and also set
container_name, which has to be unique, so Compose v2 refused the whole
project (can't set container_name and celery_worker as container name must be unique). The two workers no longer name their containers- A PydanticDeep chat forgot the conversation on every turn — the template
assumed pydantic-deep kept history between runs; it keeps none
(history_messages_pathis only an archive for searching compacted history).
The chat session now carries the conversation's messages from turn to turn,
and its in-memory workspace documents too, so files written in one turn are
there in the next; another conversation starts afresh. The project chat
socket and channel bots replay their history as well (#175) - Channel bots handed the model the new message twice — the message was
stored before the conversation history was loaded, so it arrived as the
prompt and again at the end of the history; it is now loaded first, for every
framework - Skill cards rendered as generic tool calls — since
pydantic-ai-skills
2.0 a skill loads throughload_capability(id)and returns its instructions,
but the chat, the demo replay and the step captions still looked for 1.x's
load_skill/list_skillsand an XML result. They now recognise
load_capabilityand show the skill's name and the start of its
instructions (#177)
Security
- Any signed-in user could write into another user's conversation — the
RESTPOST /conversations/{id}/messagesnever passed the caller to
ConversationService.add_message, so neither ownership nor a share was
checked, and a read-only (view) share recipient could append too. Over the
chat WebSocket,persist_user_turnlogged a refused conversation as a failed
write and carried on: the turn ran, and the agent's reply was saved into a
conversation the user could only read, or could not see at all.add_message
now requires the owner or aneditshare, and the WebSocket refuses such a
turn with anerrorevent before the agent runs. Reported by
@failsafesecurity (#143) - Upgraded the generator's locked
aiohttp,anyio,multidict,pyjwt,
urllib3andvirtualenvpast the advisoriespip-auditreports