Releases: vstorm-co/full-stack-ai-agent-template
Releases · vstorm-co/full-stack-ai-agent-template
0.2.12
[0.2.12] - 2026-06-17
Added
- Deep Research mode (
enable_deep_research,--deep-research, PydanticAI only) — turns the assistant into a deep-research agent: a TODO planner (pydantic-ai-todo), parallel researcher/analyst/writer subagents (subagents-pydantic-ai), and an automatic context manager (summarization-pydantic-ai). The planner clarifies scope, plans the work, delegates web research to subagents, and composes a cited report; progress streams to a dedicated live research panel (plan checklist, subagent status cards, context-usage meter) while the final report streams back as a normal message. TODO state persists in PostgreSQL when available, else in memory. Activated at runtime withENABLE_DEEP_RESEARCH=true; a client can opt a single turn out withdeep_research=false. Gated behind the new flag and PydanticAI-only by a config validator — when off, the template generates exactly as before. Ships with a stop control and a per-turn research store in the web chat (#90)
Fixed
make installfailed withFailed to spawn: pre-commit— dev tools (pytest,ruff,ty,pre-commit, …) lived under[project.optional-dependencies].dev, which uv only installs via its deprecateddev-extra special-casing; on uv versions whereuv sync --devtargets the PEP 735 group instead, they were skipped entirely. Moved them to[dependency-groups]souv sync/uv sync --devinstall them deterministically and--no-dev(prod Dockerfile) still excludes them (#95, #101)make docker-db(and the otherdocker-*/docker-prod-*/docker-redistargets) erroreddocker-compose: No such file or directory— those recipes shelled out to the legacy Compose v1 binary while the dev/quickstart targets already used Compose v2. Every recipe now invokesdocker compose; thedocker-compose.*.ymlfilenames are unchanged (#96, #100)- Frontend Docker build (
make dev-frontend) failed to build the image — the Dockerfile copied abun.lockb*glob that never matched the current textbun.lock(sobun install --frozen-lockfilehad no lockfile and errored), the healthcheck shelled out to acurltheoven/bunimage doesn't ship, and theNEXT_PUBLIC_*client vars were never passed as build args. Copiesbun.lock*, passes the public env vars as build args (Dockerfile + compose), probes the healthcheck with bun'sfetch, chowns the copiedpublic/, and guardsparseLoadSkillResultsobun run buildtype-checks (#97, #99) - PydanticDeep OpenAI models are now routed to the OpenAI Responses API (#93)
- Taskiq worker and scheduler containers stay healthy; the taskiq-only worker regression test is removed from celery/arq/none projects so it doesn't linger as an empty file (#94)
ty checkon generated projects — the admin/user routes for SQLite were syncdefs that neverawaited the always-asyncUserService, so they returned un-awaited coroutines (real bug); they are nowasync. The service's id parameters are typed per database (UUIDfor Postgres,strfor SQLite/MongoDB) via aUserIdalias, the agent's capability list is typedlist[Any], the admin-stats best-effort model imports are feature-gated instead of importing absent modules, and theAdminServicesession is typedAnyso one implementation serves async/sync. A minimal SQLite project now type-checks clean (0 diagnostics)
Dependencies
- Pinned
fastapi>=0.135.3,<0.137in generated backends — FastAPI 0.137 made prefix-lessinclude_routerreject the documented@router.get("")empty-path idiom, breaking every generated project at import (#90) - Added
pydantic-ai-todo,subagents-pydantic-ai, andsummarization-pydantic-aito generated backends whenenable_deep_researchis on (#90) - Bumped
codecov/codecov-action6 → 7 in the CI actions group (#89)
0.2.11
[0.2.11] - 2026-06-12
Added
- AntV advanced-diagram tools + interactive maps (
enable_antv_charts,--antv-charts) — adds anmcp-server-chartDocker sidecar exposing AntV diagram tools (flowchart, mind-map, org-chart, sankey, waterfall, funnel, treemap, radar, histogram, boxplot, dual-axes) and a nativecreate_maptool (Leaflet/OpenStreetMap) with a typedMapMarkerschema that prevents empty-marker validation errors from weaker models.create_mapis wired into all 6 agent frameworks; the AntV diagrams render server-side via the sidecar. Web chat renders maps withreact-leaflet(MapMessage/MapLeaflet) and AntV diagrams as images in the tool-call card. Opt-in and profile-gated —ENABLE_ANTV_CHARTS=falseby default, sidecar started withdocker compose --profile antv up -d; for prod self-host GPT-Vis-SSR viaANTV_VIS_REQUEST_SERVERinstead of AntV's public render backend (#83) ask_usertool (PydanticAI) — the agent can pause a run to put one or more questions to the user and resume with their answers, for intake/setup flows and mid-run clarifications. Backed by a WebSocket pause/resume inAgentSessionand an interactive multi-stepQuestionPromptcard in the frontend (numbered options, free-form answers, skip). System-prompt guidance steers the model to use it only when a missing detail would genuinely change what it does next (#88)run_pythoncode execution (enable_code_execution,--code-execution, PydanticAI only) — arun_pythontool backed by thepydantic-montysandboxed interpreter. In one tool turn the model can compute projections/aggregations and callcreate_chart/create_map/current_datetimedirectly from inside the sandbox; visualizations created in-code stream to the session as live, persisted interactive cards (the sametool_call/tool_resultpair as a direct call). Restricted stdlib (math,asyncio,json,datetime,re); activated at runtime withENABLE_CODE_EXECUTION=true. Temporary shim until PydanticAI's officialCodeExecutionToolsetships (#88)- Skills system (
enable_skills,--skills, PydanticAI only) — apydantic-ai-skillsSkillsToolsetthat loadsSKILL.mdfiles frombackend/skills/as agent tools (the model picks a skill, then follows its instructions). Ships the loader only — drop your own skills in; the toolset no-ops when the directory is empty. Frontend rendersload_skill/list_skillstool calls as clean skill cards. Pairs with code execution for skills that compute (#88)
Fixed
- MCP connection leak in CrewAI —
get_antv_crewai_toolsnow memoizes the started MCP adapter so it's started once per process, not once per request (#83) - Per-request event-loop blocking in LangChain/LangGraph/DeepAgents — AntV tool discovery is memoized so
_run_synconly blocks on the first request (#83) - antvis-chart Docker healthcheck — plain
wgetwas rejected by the streamable-HTTP endpoint; replaced with anodeone-liner. Added coordinate-bounds validation toMapSpec._validate_center(matching per-marker validation) and removed the deadparse_map_specexport (#83) - Streamed tool-call args could be a raw string —
agent_sessionnow usesargs_as_dict(raise_if_invalid=False)so tool-call cards always receive a dict; a stray/duplicateask_user_responseframe (e.g. after a reconnect) is dropped instead of surfacing a spurious "Empty message" error (#88)
Changed
- Generated-project ruff tests run via
uvx—test_template_integration.py/test_message_ratings.pyinvokeuvx rufffrom the project'sbackend/dir (matching the post-gen hook) instead ofuv run ruff, avoiding a strayVIRTUAL_ENVbreaking local runs. New matrix configspydantic_ai_code_executionandpydantic_ai_skillsso both new paths are linted + type-checked in CI (#88)
Removed
ai_agent_test/generated snapshot — removed the 710-file generated project that was committed to the repo root; it only served to confuse Renovate and tooling (thetemplate/source is the durable artifact). Added to.gitignore
Dependencies
pydantic-monty>=0.0.18andpydantic-ai-skills>=0.11.0added to generated backends whenenable_code_execution/enable_skillsare on;react-leafletadded to the template frontend and anmcp-server-chartsidecar to the compose files whenenable_antv_chartsis on
0.2.10
[0.2.10] - 2026-05-27
Added
- Frontend
PageHerocomponent — sharedcomponents/dashboard/page-hero.tsx(151 lines) used by admin, billing, settings, KB, organizations, and dashboard pages so headers share a single typographic rhythm and breadcrumb pattern instead of each page hand-rolling its own - Chat controls panel — new
components/chat/chat-controls.tsx(586 lines) consolidates model picker, knowledge-base toggles, and conversation settings into one panel. Replaces the splitchat-settings.tsx(deleted) andkb-selector.tsx(deleted) — fewer UI surfaces, no more duplicate KB lookups - Auth screens redesigned — login, register, reset-password, and forgot-password forms now use a split-screen layout (product pitch on the right, form on the left), proper OAuth divider, and consistent form spacing. Generated
(auth)/layout.tsx+magic-link-sent/page.tsxupdated to match
Changed
- Marketing site refreshed — hero, pricing teaser, final CTA, marketing footer rebuilt with cleaner typography, monthly/annual toggle on pricing, and configurable footer columns (
footer-config.ts) - Knowledge bases page redesigned —
kb/kb-list.tsxrewritten (266-line refactor) with card layout, per-base actions, and clearer empty-state copy - Dashboard / admin / settings / billing pages — every page header migrated to the new
PageHero; layout, sidebar, and command palette tightened. Globals CSS adjusted for the new spacing scale - README — banner image at the top replaced with the live chat demo video so the first thing a visitor sees is the product running; merged the separate "web search" and "chart generation" demos into one (the new demo covers both); refreshed every product screenshot from
assets/new2/; pruned ~40 unused assets (assets/new/,assets/chat/, old marketing PNGs, button SVGs)
Fixed
- No-AI projects (
use_ai=False) failed to build — generator now removes every AI-only surface that previously leaked through and broke imports:conversation/conversation_share/message_rating/user_slash_commandmodels + repos + services, theagent,admin_conversations,admin_ratings, andme_slash_commandsroutes, and the corresponding frontend pages (chat/,admin/conversations,admin/ratings,settings/slash-commands), API proxies, and data hooks. RAG-off projects also drop the KB UI (components/kb,app/api/kb,(dashboard)/kb,use-knowledge-bases.ts,types/knowledge-base.ts) sonext buildno longer fails on orphaned imports - Backend modules unconditionally pulled in chat code —
api/deps.py,api/routes/v1/__init__.py,db/models/__init__.py,repositories/__init__.py,schemas/__init__.py,services/admin.py,repositories/user.py, andadmin.py(SQLAdmin) haduse_database-gated imports ofConversation/Message/MessageRating/etc. that crashed in no-AI projects. Re-gated touse_aiand added literal-zero fallbacks for conversation counts in admin stats / user listings (PG, SQLite, MongoDB) - CLI: Slack/Telegram channels silently accepted without an AI framework —
ProjectConfigvalidation now rejects--slack/--telegramwhenuse_aiis off (the channel adapters only exist to relay messages toAgentInvocationService), with a quick-fix message temperatureforwarded to reasoning models (gpt-5.5, o1) — those models reject the parameter entirely, soAssistantAgentno longer falls back tosettings.AI_TEMPERATURE; it staysNoneand is only forwarded toModelSettingswhen the caller explicitly sets itreranker.pyCohere init imported the SDK only to discard it — replaced thefrom cohere import AsyncClientprobe withimportlib.util.find_spec("cohere")so we check availability without polluting imports- Auth components barrel exported password forms in OAuth-only builds —
components/auth/index.tsnow gates the local-auth form exports behinduse_local_auth, so OAuth-only projects don't ship dead code that references missing endpoints - README demo videos invisible on GitHub —
<video>tags fromraw.githubusercontent.comdon't render in the GitHub README viewer (only<img>autoplays from raw URLs). Converted both chat and RAG demo.mp4s to optimized.gifs (960px, 10fps, palette-quantized — 5.8 MB and 3.0 MB respectively, ~80% smaller than source) and switched the README to<img>tags. Removed the unused.mp4sources - Renovate scanned the generated
ai_agent_test/snapshot — opened useless PRs against the snapshot'spackage.json/docker-compose.ymlinstead of the actualtemplate/source, so every accepted bump would silently revert on the next regeneration. AddedignorePaths: ["ai_agent_test/**", "**/node_modules/**"]so future bumps target the template files
Dependencies
- Generator CI Python pinned to 3.14 (was 3.12) —
.github/workflows/{ci,docs,release}.yml(#75) aquasecurity/trivy-action→ v0.36.0 in generated projects' CI (#74)milvusdb/milvusDocker tag → v2.6.17 in generateddocker-compose.{dev,prod,}.yml(#77)qdrant/qdrantDocker tag → v1.18.1 in generateddocker-compose.{dev,prod,}.yml(#78)quay.io/coreos/etcdDocker tag → v3.6.11 (Milvus dependency) in generateddocker-compose.{dev,prod,}.yml(#80)prettier-plugin-tailwindcss→ ^0.8.0 in template frontendpackage.json(#81 bumped only the snapshot; the template source is the durable fix)
0.2.9
[0.2.9] - 2026-05-17
Added
- Chart generation tool (
enable_charts) — optionalcreate_charttool letting the agent produce line/bar/pie/area/scatter charts. Returns a validatedChartSpec(data, series, custom style/palette/legend/axis) as JSON, so the same payload flows to every surface. Registered in all 6 agent frameworks (PydanticAI, LangChain, LangGraph, CrewAI, DeepAgents, PydanticDeep). Web chat renders it interactively with Recharts; Slack/Telegram get a server-side PNG via matplotlib (charts_channel_png, gated dep, with a markdown-table fallback). Wizard prompt +--chartsCLI flag +enable_chartscookiecutter var - Portable
fetch_urltool (web_fetch_tool) — SSRF-safe "read this web page" tool for LangChain/LangGraph/CrewAI/DeepAgents (which had no model-native web-fetch). Reusesapp.core.sanitize.validate_webhook_url, re-validates every redirect hop, caps size/timeout, extracts readable text (BeautifulSoup). PydanticAI/PydanticDeep keep their nativeWebFetch. Closes the gap whereenable_web_fetchwas a silent no-op for those frameworks - Web Search & Fetch offered for every agent framework in the interactive wizard (was PydanticAI-only); CrewAI now actually attaches
search_web/fetch_urlto the research agent (they were registered but never used) - Gated test suites:
test_chart_tool.py,test_fetch_url.py,test_web_search.py
Changed
- Refreshed default AI models — OpenAI →
gpt-5.5, Anthropic →claude-opus-4-7, OpenRouter →anthropic/claude-opus-4-7, multi-provider →openai/gpt-5.5(Google staysgemini-2.5-flash). UpdatedAI_AVAILABLE_MODELS(GPT-5.x frontier line; full Claude Opus/Sonnet/Haiku line) and Claude pricing in billingMODEL_COSTS; synced.env/.env.example/docs - Rewrote the default agent system prompt (outcome-first style) — real personality + answering policy + formatting. The RAG variant is no longer a straitjacket: the agent answers general-knowledge questions directly instead of replying "not in the knowledge base", and treats
search_documentsas a tool to use when relevant with a retrieval budget and citations web_searchtool returns structured JSON (WebSearchResults) instead of ad-hoc text, so the chat UI renders clickable titles, domains and snippets (addedparse_web_search). Fixed stale frontend detection that meant the rich card never showed for LangChain/LangGraph/DeepAgents- Removed the
.env.prod/.env.prod.exampleabstraction — production now uses the samebackend/.envas dev (it already contained every variable.env.prod.exampledefined).docker-compose.prod.ymlreadsenv_file: ./backend/.env;make prodchecks forbackend/.envand passes--env-file backend/.envto Compose..env.prodremoved from.gitignore. Migration: move any values from your old.env.prodintobackend/.env(gitignored) on the server - Chat UI: ordered message timeline — a streamed assistant turn now renders as an ordered sequence of parts (thinking → tools → text → …) in true chronological order instead of three fixed slots, so multi-step turns display correctly. Provider-agnostic; CrewAI keeps its multi-message layout
- Tool-call cards redesigned — collapsed by default to a clickable bar (tool name + input hint, e.g. the query/URL), expand to the formatted view,
</>toggle for arguments + raw output. Chart cards auto-expand (they're only useful when visible). New generic fallback renderer displays any newly added backend tool sensibly with no frontend changes; removed the per-tool status icon
Fixed
- LangChain/LangGraph/DeepAgents streaming: tool-call arguments were empty in the web UI — the token-chunk path emitted a premature
tool_callwithargs: {}and poisoned the shared dedup set, suppressing the complete event. Theupdatesstream is now the single source of truth (full args), with no duplicate/empty card - Reasoning ("thinking") now streams reliably — extracted via a shared helper, with a fallback that emits reasoning from the final message for providers that don't stream it as chunks
AIMessageChunkmerge crash —Additional kwargs key created_at … unsupported type <class 'float'>on the OpenAI Responses API. Usage is now summed viaadd_usageinstead of merging whole chunks- DeepAgents tool calls never fired —
_stream_update_eventchecked the graph node"agent", butcreate_deep_agent(deepagents 0.6.1 → LangChaincreate_agent) names it"model". Tool calls, args and reasoning are now emitted for DeepAgents KBSelectorinfiniteGET /api/kbrequest loop when a workspace has no knowledge bases — a length-based effect re-fired after every empty fetch; replaced with a one-shot ref guardtest_agents.pywas not provider-gated — it patched OpenAI-only symbols, breaking generation/tests for Anthropic/Google/OpenRouter (and the LangChain branch). PydanticAI now patches the single_build_modelseam with a realTestModel; LangChain patches the provider-correct chat class
0.2.8
[0.2.8] - 2026-05-11
Added
- Email module (
enable_email) — Transactional email system with three providers: Resend (async), SMTP (aiosmtplib), and Log (dev/test). Pre-rendered HTML/text templates stored inemails/compiled/using[[variable]]substitution.EmailServicefacade with convenience wrappers for all email types: welcome, password reset, invitation, payment succeeded/failed, trial ending/expired, subscription canceled/changed, low credits, newsletter welcome - Email triggers wired —
UserService.register()firessend_welcome;InvitationService.invite()firessend_invitation; billing webhook handlers fire payment/subscription lifecycle emails using Stripe customer data (fail-open — email errors never break webhook processing) - Stripe billing — rate limiting per plan (
enable_rate_limiting) — Sliding window rate limiter backed by Redis sorted sets (ZADD/ZREMRANGEBYSCORE/ZCARD pipeline) with in-memory fallback.RateLimitRulefrozen dataclass (per_user, per_org, per_ip, configurable periods);RateLimitCategoryconstants; data-driven plan features overrideDEFAULT_RATE_LIMITS;make_rate_limit_dep(category)factory for FastAPIDepends(); admin bypass; fail-open on Redis error; HTTP 429 withRetry-Afterheader - Extended frontend billing dashboard —
SubscriptionPanelwith 4 states (free/trial/active/canceled), cancel/reactivate dialogs, plan details;CreditsPanelwith balance display, low-credit alert, top-up button, transaction history with type badges;/billing/subscriptionpage with live plan cards;/billing/creditspage.useSubscription,useCredits,usePlanshooks added touse-billing.ts - Admin user management —
GET/PATCH/DELETE /admin/users/{id}endpoints (requiresis_app_adminflag).POST /admin/users/{id}/impersonateissues a short-lived (1h) JWT token to act as any user — token is returned in the API response. Frontend:/admin/userspage with search, role toggle, delete, and impersonation-token-to-clipboard button;/admin/page.tsxoverview with navigation cards;useAdminUsershook - UsageService —
app/services/usage.py— recordsUsageEventin DB, computes credits viausage_to_credits(), debits org credits viaCreditService.debit(). All 3 DB variants (PG/SQLite/MongoDB). Wiring point for agent invocations - Usage dashboard —
/billing/usagefrontend page: total credits/tokens/calls KPI cards, recharts bar chart of credits by model, per-model breakdown table, CSV export of credit transaction history - Anomaly detection service (
enable_usage_anomaly_detection) —anomaly_detection.py— spike detection: current-hour credits vs rolling 24h average; alert if ratio > 3×; optional Slack webhook notification (enable_slack_alerts,SLACK_ANOMALY_WEBHOOK_URLsetting) - Newsletter signup (
enable_newsletter_signup) —POST /newsletter/signupendpoint;NewsletterSignupReact component; sends welcome email via email service - Changelog page (
enable_changelog) —/changelogroute with release history, change type badges (feat/fix/improvement/chore) - Pricing comparison page (
enable_comparison_pages) —/pricingroute fetches live plans from API; monthly/annual toggle; plan feature list; trial days display; "Get Started" CTA
Fixed
- Email templates excluded by
.gitignore— Renamedemails/dist/→emails/compiled/to avoid the genericdist/gitignore rule silently stripping all compiled templates from generated projects billing/facade.pyimportsusage_event_repounconditionally —app.repositories.usage_eventwas imported in both PostgreSQL and SQLite branches regardless ofenable_credits_system; projects generated withenable_billing=True+enable_credits_system=Falsecrashed on startup withImportErrorfunc.case()SQLAlchemy 2.x crash inmessage_rating_repo—func.case((condition, value), else_=0)raisedTypeError: Function.__init__() got an unexpected keyword argument 'else_'; replaced withcase(...)imported directly fromsqlalchemy- Credits/usage dashboard widgets shown when
enable_credits_system=False—UsageTimelineandTopModelscomponents were gated onenable_billinginstead ofenable_credits_system; they fetch from/billing/me/credits/usage/...endpoints that don't exist without the credits system, producing silent 404s and empty charts - MongoDB projects:
admin.pyused SQLAlchemyfunc.count—AdminServicenow has separate{%- if use_postgresql or use_sqlite %}/{%- elif use_mongodb %}branches; the MongoDB branch uses BeanieDocument.find().count()and returns[], 0for Stripe events (not applicable to MongoDB projects) - Admin
GET /conversations/{id}returned 404 for other users' conversations —get_conversationandlist_messagesroute handlers now resolveuid = None if current_user.role == "admin" else current_user.id;user_id=Nonein the service layer bypasses the ownership check, allowing admins to read any conversation - Frontend
?id=URL param blocked by ownership guard —fetchConversationsremoved theresponse.items.some(c => c.id === urlId)check before loading messages; any?id=value is now attempted unconditionally and a 404/403 from the server clears the ID silently (non-admins are still protected server-side) - Admin conversations page "View" opened an in-page read-only preview — Replaced the custom preview panel with a
Linkto/chat?id=<conversation_id>; admins now land on the full chat UI with the real message history - Admin ratings page "Export" returned 404 —
window.openwas targeting/api/v1/admin/ratings/export(direct backend path) instead of/api/admin/ratings/export(the Next.js proxy route that attaches the auth cookie) - CI integration test failures —
generated_project_fullfixture now setsfrontend=FrontendType.NEXTJS(required whenoauth_provider=GOOGLE); MongoDB repository__init__.pyimport guard split so only SQL-only repos (chat_file_repo) are excluded from MongoDB projects; SQLite auth test fixed a mock type mismatch (MagicMock→AsyncMock)
0.2.7
[0.2.7] - 2026-04-26
Fixed
- Disabled features no longer leak generated files —
post_gen_project.pynow removes channel adapters/routes/services/repos/schemas/models/commands/migrations whenuse_telegramanduse_slackare both off, RAG sync infrastructure (sync_log,sync_source,rag_documentfiles) when RAG is off, DeepAgents project scaffolding when not selected, leftover test stubs for disabled modules, and emptydocker-composeplaceholders when Docker is disabled agent.py(DeepAgents): undefinedfile_ids— WebSocket payload parsing now extractsfile_ids = raw_data.get("file_ids", [])before use (previously raisedNameErrorat runtime)agent.py(CrewAI): missingConversationUpdateimport — Added to imports so title-update path no longer crashesrag.py:complete_synccalled on wrong service — Was invoked onSyncSourceService; now correctly routed toRAGSyncServiceIngestionService()instantiated without required args — AddedIngestionService.from_settings()classmethod factory; routes/commands/workers now use itmessage_rating_reponot exported — Added toapp/repositories/__init__.py; admin ratings flow no longer fails on importConversationService.get_conversation_with_messagesmissing on SQL backends — Previously only existed on MongoDB; added to PostgreSQL + SQLite variantsProjectService.list/ChannelBotService.listshadowed builtinlist— Renamed tolist_for_user()/list_all()(withfind_active()andlist_by_platform()helpers added)ModuleNotFoundError: No module named 'app.rag.connectors'—post_gen_project.pywas deleting the entirerag/connectors/directory when neither Google Drive nor S3 ingestion was enabled, butsync_source.pyalways importsCONNECTOR_REGISTRYfrom that package. Directory is now preserved; only the individual connector files are removed- Empty
tasks/channel.pygenerated for projects without Telegram or Slack — Added removal to thenot use_telegram and not use_slackpost-gen hook block - Frontend TypeScript strict mode errors — Fixed 26
noUncheckedIndexedAccessandundefined-assignability violations across 7 files:rag/page.tsx(array index + file loop guard),chat-container.tsx(model selectoruseStatetype + fallback),chat-input.tsx(speech recognition result guard),tool-approval-dialog.tsx(editedArgsindex fallback),tool-call-card.tsx(regex capture group fallbacks),breadcrumb.tsx(route segment index),theme-toggle.tsx(persisted zustand state hydration)
Changed
- Strict layered architecture enforced across the template — Routes call services only (via FastAPI
Depends); services call repositories only; repositories are the sole layer permitted to talk to the database. Worker tasks, channel adapters, CLI commands, and webhook handlers no longer perform raw DB operationsadmin_conversations.pyrewritten to useConversationSvc.admin_list_with_users()andUserSvc.admin_list_with_counts()telegram_webhook.py/slack_webhook.pyuseChannelBotSvcviaDependsinstead of opening their own DB sessions (bot_service.find_active(bot_id))agent.pyusesconv_service.list_attached_files(file_ids)instead of rawselect(ChatFile)queries- Worker tasks (
rag_tasks._run_ingestion,_run_sync,_update_status,_update_sync_log) refactored onto the service layer - CLI commands (
commands/channel.py,seed.py,rag.py) refactored with a_channel_service()context-manager helper - All
self.db.execute/commit/addremoved from services; sessions auto-commit viaget_db_session/get_db_context/get_worker_db_context
- Repositories expanded with the queries services now need —
conversation.admin_list_with_users()+export_chunk()(3 backends);user.list_query()+admin_list_with_counts()+delete_non_admins()+has_any()(3 backends);chat_file.get_many()+link_to_message();message_rating.get_user_ratings_for_messages()+get_rating_counts_for_messages()+get_ratings_with_users_for_messages();webhook.create_delivery()+save_delivery();sync_log.create(sync_source_id=...);rag_document.delete_by_collection() - Routes thinned to HTTP-only layer — All response-object construction moved from route handlers into services:
admin_ratings.py: CSV/JSON export helpers (_csv_escape,_csv_row_values,_serialize_csv_row,_validate_export_format,_export_disposition,_json_export_response,_stream_csv_sync/async) andexport_ratings()moved toMessageRatingService. Route reduced to a singlereturn await rating_service.export_ratings(...)callsessions.py:SessionReadconstruction inlist_sessionsmoved toSessionService.list_sessions()which now returnsSessionListResponsedirectlyrag.py:SyncSourceRead(...)construction moved toSyncSourceService._to_read()(used bylist_sources,create_source,update_source);RAGSyncLogItem(...)moved toRAGSyncService.list_sync_logs()→ returnsRAGSyncLogList;RAGTrackedDocumentItem(...)moved toRAGDocumentService.list_documents()→ returnsRAGTrackedDocumentList;RAGDocumentItem(...)moved toBaseVectorStore.get_document_list();ConnectorInfo/ConnectorListconstruction moved toSyncSourceService.list_connectors()oauth.py: Three-step find/link/create OAuth flow extracted toUserService.get_or_create_oauth_user()(all 3 DB variants);google_callbackreduced to a single service callusers.py: RawAnnotated[User, Depends(RoleChecker(UserRole.ADMIN))]replaced withCurrentAdminalias throughout;Depends(get_current_user)replaced withCurrentUser- Unused
current_user/admin_userroute parameters that only provided auth enforcement renamed to_: CurrentAdmin/_: CurrentUseracross all affected routers
agent.py: inline imports moved to module level —from datetime import datetime, UTC,import json,from pydantic_ai.messages import BinaryContent,from app.services.file_storage import get_file_storage, and pydantic_deep session/project service imports were scattered across WebSocket handler bodies; all moved to the top of their respective framework blocksconversations.py: Direct field mutationdata.user_id = current_user.idreplaced withdata = data.model_copy(update={"user_id": current_user.id})(Pydantic v2 safe update); inlineConversationShareSvcimport moved to module level; section-divider comments (# Message Rating Endpoints,# Sharing endpoints) removed
Security
- Generator dependency floors raised —
pyproject.tomlruntime/dev/docs floors bumped to currently-used versions:click>=8.3.0,cookiecutter>=2.7.0,rich>=15.0.0,questionary>=2.1.0,pydantic>=2.13.0,pydantic-settings>=2.13.0,email-validator>=2.3.0,pytest>=9.0.0,pytest-cov>=7.0.0,ruff>=0.14.0,ty>=0.0.31,pre-commit>=4.0.0,mkdocs>=1.6.1,mkdocs-material>=9.7.0,pymdown-extensions>=10.20. Brings in upstream security/bug fixes pip-auditCI: CVE-2026-3219 (pip 26.0.1) added to ignore list — Vulnerability inpipitself with no fix version published yet; documented in the workflow alongside the other ignored CVEs
Added
- Config validator: CrewAI + Logfire combination rejected — Raises
ValueErrorat config time; documents an upstream OpenTelemetry/logfire ≥ 4.30 conflict with CrewAI - Conditional pydantic pin for CrewAI — Generated
pyproject.tomlpinspydantic[email]>=2.11.0,<2.12when CrewAI is selected (CrewAI is incompatible with pydantic 2.12) - Stricter
tyrules in generatedpyproject.toml—unknown-argument,invalid-await,invalid-context-manager,missing-argument,not-iterable,invalid-return-type,invalid-type-formpromoted towarn - Integration test matrix —
TestGeneratedTemplateMatrixnow exercises 14 framework/database/RAG/channel combinations (project names prefixed withmatrix_to avoid package-name collisions). Suite: 405 passed, 3 skipped services/health.py— Newbuild_health_response(status, checks, details)helper extracted fromhealth.pyroute;health.pynow imports and calls it instead of defining it inlineservices/agent.py— NewAgentConnectionManagerclass extracted from all 5 AI framework blocks inagent.py. Single canonical implementation shared viafrom app.services.agent import AgentConnectionManagerUserService.get_or_create_oauth_user(provider, provider_id, email, full_name)— Encapsulates the find-by-oauth-id → find-by-email → link-or-create orchestration that was previously duplicated inline in all threegoogle_callbackroute handlersSyncSourceService._to_read(source)/list_connectors()—_to_readconverts aSyncSourceORM model toSyncSourceRead(includingjson.loadsfor SQLite config);list_connectors()is a@staticmethodthat iteratesCONNECTOR_REGISTRYand returnsConnectorListwithout requiring a DB sessionBaseVectorStore.get_document_list(collection_name)— Concrete (non-abstract) method on the base class; callsget_documents()and maps toRAGDocumentList. All vector store implementations (Milvus, Qdrant, ChromaDB, pgvector) inherit it automatically
0.2.6
[0.2.6] - 2026-04-18
Added
- Message rating feature — Users can rate AI assistant messages with thumbs up/down and optional feedback comments. Toggle behavior: clicking same button removes rating, clicking opposite button changes it. Only assistant messages are rateable
- Backend:
MessageRatingmodel (PostgreSQL/SQLite/MongoDB, SQLAlchemy/SQLModel), repository + service + schema layers,POST /conversations/{id}/messages/{messageId}/rateendpoint. Ratings persisted tomessage_ratingstable with unique constraint per user/message andCHECKconstraint on rating values (1/-1). Optional comment field (up to 2000 chars). Supports all 3 database variants - Admin API:
GET /admin/ratings(paginated list with filters),GET /admin/ratings/summary(aggregate stats),GET /admin/ratings/export(CSV/JSON download).GET /admin/conversations(paginated listing). All admin routes require admin role - WebSocket integration: Ratings data (user's rating, like/dislike counts) included in streaming message events and conversation history loading
- Frontend:
RatingButtonscomponent with like/dislike icons, comment dialog on dislike, optimistic count updates. Integrated intomessage-item.tsxfor assistant messages. Admin pages for ratings management and conversations listing - Frontend proxy routes:
POST/DELETE /api/conversations/{id}/messages/{messageId}/rateproxies,GET /api/v1/admin/ratings,/summary,/exportroutes,lib/admin-auth.tsutility for admin API calls - Documentation:
docs/howto/use-ratings.mduser guide, updateddocs/architecture.mdanddocs/permissions.md - Tests: 660+ lines of tests covering config validation, model generation, repository/service/route layers, all database variants
- Backend:
Security
- Removed JWT from WebSocket URL query string — WS auth now uses
Sec-WebSocket-Protocol(access_token.<JWT>) instead of?token=..., so tokens no longer leak into access logs orRefererheaders. Backend echoes the chosen application subprotocol back onaccept() - Removed
/api/auth/tokenhttpOnly downgrade endpoint —access_tokenis now returned in the body of/auth/login,/auth/me, and/auth/refreshproxy responses and kept in memory only (never persisted) - CSV export injection hardening — Admin ratings CSV export now prefixes cells starting with
= + - @(or tab/CR) with a single quote, preventing formula execution when opened in Excel/Sheets - Rating comments stored raw — Dropped
html.escapefrom comment sanitization; comments are rendered via React (auto-escaped) and CSV-escaped separately, so the DB stores original text
Changed
- Streaming admin ratings CSV export —
/admin/ratings/export?export_format=csvnow streams row-by-row via an async/sync generator instead of buffering the whole dataset in memory
0.2.5
[0.2.5] - 2026-04-12
Added
Conversation Sharing + Admin Conversation Browser
- Conversation sharing — Share conversations with other users (direct share by user ID) or generate public share links (UUID4 token). Permission levels:
view(read-only) andedit(can add messages). Owner can share, list shares, and revoke access. Recipients can also leave shared conversations ConversationSharemodel — New DB model across all 5 variants (PG+SQLModel, PG+SQLAlchemy, SQLite+SQLModel, SQLite+SQLAlchemy, MongoDB). Fields: conversation_id, shared_by, shared_with, share_token, permission. Unique constraint on (conversation_id, shared_with)- Share endpoints —
POST /conversations/{id}/shares(share or generate link),GET /conversations/{id}/shares(list shares, owner only),DELETE /conversations/{id}/shares/{share_id}(revoke),GET /conversations/shared-with-me(list shared with current user),GET /conversations/shared/{token}(public access, no auth) - Admin conversation browser — Admin-only endpoints:
GET /admin/conversations(paginated, searchable by title, filterable by user_id, includes message_count and user_email),GET /admin/conversations/{id}(full conversation with messages),GET /admin/conversations/users(user list with conversation counts, searchable) - Share dialog component — Frontend dialog to share conversations: user search input, permission dropdown (view/edit), generate share link with copy button, list current shares with revoke
- Admin conversations page —
/admin/conversationspage with tabs (Conversations/Users), table views, search, click-to-preview (read-only), user → conversations drill-down - Public shared page —
/shared/[token]SSR page renders conversation transcript without sidebar or input. Clean read-only view using server-side fetch - Frontend hooks —
useConversationShares(share, fetch, revoke, shared-with-me) anduseAdminConversations(admin list, users, detail preview)
Slack Multi-Bot Channel Integration
- Slack adapter —
SlackAdapter(ChannelAdapter)supporting both Events API (production webhook) and Socket Mode (development polling). Thread-aware: Slack thread replies foldthread_tsintoplatform_chat_id({channel}:{thread_ts}) so each thread gets its ownChannelSessionandConversation - Events API webhook —
POST /slack/{bot_id}/eventsendpoint handles Slack URL verification challenge and event dispatch. Signature verified via HMAC-SHA256 (v0={timestamp}:{body}) with 5-minute replay-protection window. Fire-and-forget background dispatch meets Slack's 3s response requirement - Socket Mode (dev) — Supervised polling loop with
slack-sdk'sSocketModeClient. Bot-scoped tasks with 5s back-off restart on crash. Lifecycle managed in app lifespan alongside Telegram polling use_slackcookiecutter variable — Gates all Slack infrastructure. CLI interactive prompt for "Enable Slack integration" added alongside Telegram. Enables:slack-sdk>=3.35.0, Slack-specific config vars (SLACK_SIGNING_SECRET,SLACK_BOT_TOKEN,SLACK_APP_TOKEN),POST /slack/{bot_id}/eventsroute- Shared channel infrastructure expanded — All 14 shared files (
ChannelAdapterbase, models, repos, services, router, commands) gated fromuse_telegram→use_telegram or use_slackso both platforms share the same session/identity/bot management layer - Group chat concurrency control — Per-chat
asyncio.Lock(keyed on{bot_id}:{platform_chat_id}) inChannelMessageRouter.route(). Serializes concurrent messages from the same group/channel to prevent: duplicateChannelSessioncreation (DB constraint violation), interleaved agent invocations on the sameConversation, and rate-limit counter races. Affects both Telegram groups and Slack channels
Telegram Multi-Bot Channel Integration
- Full Telegram bot integration — Multi-bot support with polling and webhook delivery modes, encrypted token storage (Fernet), in-memory rate limiting (token-bucket per user per bot), and role-based access policies (open, whitelist, jwt_linked, group_only)
- Channel adapter architecture — Abstract
ChannelAdapterbase class with concreteTelegramAdapter(aiogram v3). Adapter registry pattern for future platform extensions (Discord, Slack, etc.) - Channel message router — 8-step processing pipeline: load bot, check access, handle commands (/start, /new, /help, /link, /unlink, /project), resolve identity, resolve session, rate-limit, invoke agent, send reply
- 3 new DB models —
ChannelBot(encrypted token, access policy, webhook config),ChannelIdentity(platform user ↔ app user linking with link codes),ChannelSession(bot+chat → conversation mapping) - Admin API routes — Full CRUD for bot management (
/channels/bots), activate/deactivate, webhook register/delete, session listing. All endpoints require admin role with properChannelBotCreate/ChannelBotUpdate/ChannelBotReadschemas - Webhook endpoint —
POST /telegram/{bot_id}/webhookwith signature verification, fire-and-forget async processing to stay within Telegram's 5s timeout - Supervised polling — Per-bot polling loop with 5s back-off restart on crash, managed via lifespan startup/shutdown
- CLI commands —
channel-list-bots,channel-add-bot,channel-webhook-register,channel-webhook-delete,channel-test-message AgentInvocationService— Framework-agnostic non-streaming agent invocation for all 6 AI frameworks, used by Telegram channel routeruse_telegramcookiecutter variable — Gates all Telegram code via Jinja2 conditionals. CLI interactive prompt added
PydanticDeep Framework (6th AI Framework)
- PydanticDeep integration — Deep agentic coding assistant built on pydantic-ai with filesystem tools (ls, read_file, write_file, edit_file, glob, grep), task management, subagent delegation, skills system, memory persistence, and context discovery
- Sandbox environment selection in CLI — New interactive prompt when selecting DeepAgents or PydanticDeep:
- PydanticDeep: Docker sandbox (default), Daytona workspace, State (in-memory)
- DeepAgents: Docker sandbox (default), State (in-memory)
sandbox_backendcookiecutter variable — ConfiguresPYDANTIC_DEEP_BACKEND_TYPE/DEEPAGENTS_BACKEND_TYPEin generated Settings- File upload to sandbox workspace — When users attach files in chat, files are written to the Docker/Daytona sandbox via
docker cp(or backend API) so the agent can access them withread_file. File paths are automatically included in the user message. Falls back to inline content for StateBackend - Project-scoped WebSocket endpoint —
ws/projects/{project_id}/chats/{conversation_id}for shared Docker containers per project
PydanticAI Capabilities
- WebSearch and WebFetch as default capabilities — All PydanticAI agents now include
WebSearch()andWebFetch()capabilities. Provider-adaptive: uses builtin when the model supports it natively, falls back to DuckDuckGo (search) and markdownify (fetch) - pydantic-ai bumped to >=1.80.0 with
duckduckgoandweb-fetchextras for local fallback support
Changed
- Removed LocalBackend from PydanticDeep — Server-side filesystem backends are not appropriate for web apps. Only Docker/Daytona sandbox and StateBackend are supported
- Removed
PYDANTIC_DEEP_WORKSPACE_DIRsetting — No longer needed without LocalBackend
Fixed
Telegram Channel Code Review Fixes
channels/router.py— Maderoute()alwaysasync def(was sync for SQLite, causingasyncio.get_event_loop().run_until_complete()crash). Removed broken_handle_command_sync,_resolve_identity_sync,_resolve_session_syncmethods. Added SQLite branches to all async methodschannels/router.py— Fixed/linkcommand: replaced non-existentchannel_link_repo.redeem_code()withchannel_identity_repo.get_by_link_code(). Code is invalidated after usechannels/router.py— Fixedbot.encrypted_token→bot.token_encryptedin_send_reply(). Fixedbot.system_prompt→bot.system_prompt_overrideandbot.model_override→bot.ai_model_overridechannels/router.py— Fixed MongoDB import paths:from app.db.models.channel import→from app.db.models.channel_identity import/from app.db.models.channel_session importchannels/router.py— Added_parse_policy()helper to normalizeaccess_policyfrom JSON string (SQLite) or dict (PostgreSQL/MongoDB)channels/telegram.py— Removed module-level singleton that conflicted with lifespan-managed adapter inmain.py. Fixed SQLite_handle_updatetoawait router.route()api/routes/v1/channels.py— Fixed all service method names (service.list_bots()→service.list(), etc.). Replaceddata: Anywith properChannelBotCreate/ChannelBotUpdateschemas. Addedresponse_modelto all endpoints. Made SQLite webhook routesasync(was usingasyncio.run()inside running loop)api/routes/v1/telegram_webhook.py— Fixed SQLite branch toawait router.route()(route is now always async)services/channel_bot.py— Generatewebhook_secretviasecrets.token_urlsafe(32)whenwebhook_mode=True(was alwaysNone). Addedlist_sessions()method to all 3 backendsrepositories/channel_session.py— Addedlist_by_bot()andcount_by_bot()functions to all 3 backendscommands/channel.py— Fixedbot.encrypted_token→bot.token_encrypted. Fixedchannel_bot_repo.list_all(platform=...)(no such parameter) → conditionalget_by_platform(). Fixedencrypted_token=→token_encrypted=in create
Tooling
- CI: MongoDB job — Added missing
ty checkstep (was present in minimal and PostgreSQL jobs but absent from MongoDB) - Template pre-commit — Bumped ruff-pre-c...
0.2.4
[0.2.4] - 2026-04-09
Security
- SSRF protection for webhook URLs (CWE-918) — Added
validate_webhook_url()inapp/core/sanitize.pythat blocks private/reserved/loopback/link-local/multicast/CGNAT IPs, validates DNS resolution against internal networks, rejects non-http(s) schemes and URLs with credentials. Validation enforced at webhook create, update, and delivery time across all three database variants (PostgreSQL, SQLite, MongoDB). IncludesSSRFBlockedErrorexception with proper 422 responses and 39 unit tests. (PR #62)
0.2.3
[0.2.3] - 2026-04-05
Added
.claude/directory in generated projects — Full Claude Code project structure so generated projects work as AI-native codebases out of the boxsettings.json— Auto-allow permissions for safe operations (Read, Glob, Grep, git, pytest, ruff, ty, alembic)rules/architecture.md— Layered architecture patterns (Routes → Services → Repositories), DI withAnnotatedaliases,db.flush()convention, domain exceptionsrules/code-style.md— Type hints (str | None), naming conventions table, import ordering (stdlib → third-party → local), ruff configrules/schemas-models.md— Pydantic v2*Create/*Update/*Read/*Listpattern,BaseSchemawithConfigDict, SQLAlchemyMapped[]columns,TimestampMixinrules/exceptions-security.md— Domain exception hierarchy (AppException→NotFoundError, etc.), JWT/bcrypt patterns,RoleChecker, API key verificationrules/api-conventions.md— REST design, pagination (Query(ge=0, le=100)), auth deps (CurrentUser/CurrentAdmin/ValidAPIKey), response format, file uploadrules/testing.md— Async test patterns,httpx.AsyncClient, fixtures, exception testing withpytest.raisesrules/frontend.md— Next.js 15 App Router, Server Components, Tailwind conventions (auto-removed when frontend disabled)commands/review.md—/project:reviewslash command: checks changes against architecture, types, security, and runs lintingcommands/add-endpoint.md—/project:add-endpointslash command: scaffolds full CRUD (schema → model → repo → service → deps → route → migration → test)commands/fix-issue.md—/project:fix-issueslash command: traces through layers, fixes, tests, lints
- Enhanced
CLAUDE.md— Rewritten with precise patterns from the actual codebase: architecture layers, DI pattern, schema conventions, exception table, response format examples, key conventions
Changed
- Replaced mypy with ty — Astral's Rust-based type checker (from the makers of ruff/uv). Updated across:
pyproject.toml, Makefile, CI (GitHub Actions + GitLab CI), pre-commit config,.gitignore - Dependency version bumps — All generated project dependencies updated to latest stable versions:
- Core: FastAPI 0.135.3, uvicorn 0.43.0, Pydantic 2.12.0, pydantic-settings 2.13.0
- Database: SQLAlchemy 2.0.40, asyncpg 0.31.0, alembic 1.18.0, sqlmodel 0.0.38, motor 3.7.0, beanie 1.29.0
- AI Frameworks: pydantic-ai 1.77.0, langchain 1.2.0, langchain-openai 1.1.0, langgraph 0.4.0, langgraph-checkpoint 4.0.0, crewai 1.13.0
- Vector Stores: pymilvus 2.6.0, qdrant-client 1.14.0, chromadb 1.5.0
- Infra: redis 7.3.0, celery 5.6.0, sentry-sdk 2.53.0, logfire 4.30.0, sqladmin 0.24.0, boto3 1.42.0
- Dev: pytest 9.0.0, ruff 0.15.0, ty 0.0.29