fix: apply custom prompts to new sessions#301
fix: apply custom prompts to new sessions#301mason5052 wants to merge 2 commits intovxcontrol:mainfrom
Conversation
Custom prompts saved via the Settings -> Prompts UI are persisted to the database, but every assistant and flow session creation path was using templates.NewDefaultPrompter() with a leftover TODO, so user overrides never reached the agents and Langfuse traces always showed the defaults. Add a controller-side helper that loads the user's saved prompts and overlays them on the compiled defaults. Prompt types the user has not customized continue to use the defaults; an empty body row is treated as no override (the UI uses delete to reset). A database error fails session creation explicitly instead of silently falling back. Wire the helper into the four affected call sites in NewAssistantWorker, LoadAssistantWorker, NewFlowWorker, and LoadFlowWorker. Closes vxcontrol#300 Signed-off-by: mason5052 <ehehwnwjs5052@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a long-standing bug where user-customized system prompts (saved via Settings → Prompts) were never applied to newly created assistant and flow sessions, because controllers always instantiated templates.NewDefaultPrompter().
Changes:
- Added controller-side prompter construction that loads per-user prompt overrides from the DB and overlays them onto compiled defaults.
- Updated assistant + flow worker creation/loading paths to use the user-aware prompter and to fail explicitly on DB prompt-load errors.
- Added unit tests covering default behavior, overrides, empty-body handling, and DB error propagation.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
backend/pkg/controller/prompter.go |
Adds newUserPrompter / buildUserPrompter to merge user overrides with default templates and return a flow prompter. |
backend/pkg/controller/prompter_test.go |
Adds unit tests validating merge behavior, fallback to defaults, and error propagation. |
backend/pkg/controller/flow.go |
Switches flow worker creation/loading to use newUserPrompter instead of NewDefaultPrompter(). |
backend/pkg/controller/assistant.go |
Switches assistant worker creation/loading to use newUserPrompter instead of NewDefaultPrompter(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address two reviewer concerns on the issue vxcontrol#300 follow-up: * Replace the `DumpTemplates()` -> `json.Unmarshal` round-trip in buildUserPrompter with a new `templates.LoadDefaultPromptsMap()` helper that returns the embedded defaults as a `PromptsMap` directly. `defaultPrompter.DumpTemplates()` now delegates to the same helper, so the JSON output for that API is unchanged. * Add an optional `prompter` field to `assistantWorkerCtx`. When set, `LoadAssistantWorker` reuses it instead of re-querying `GetUserPrompts` and re-merging defaults. `LoadFlowWorker` populates it once per flow load so multi-assistant flows pay the DB+merge cost a single time. Tests updated to match the new pure-merge `buildUserPrompter` signature; behavior for users with no overrides is unchanged. Signed-off-by: mason5052 <ehehwnwjs5052@gmail.com>
|
Pushed a follow-up commit (3278c62) addressing both Copilot review comments: 1. JSON round-trip in 2. N+1 prompter loads in Also updated the PR description to make the per-session snapshot semantics explicit: customizations edited mid-session are not applied retroactively; only sessions created/loaded after the edit pick them up. This is unchanged behavior, but worth calling out so future readers don't expect live mutation.
|
|
Hi, thanks for proposing those changes so quickly. Are there any updates on when this will be merged with main, to update Docker Hub with the new image. It would be great to be able to customize the system prompts. Seems like a pretty basic feature, but would be very helpful. |
Summary
Custom system prompts saved via the Settings -> Prompts UI are now applied to newly created assistant and flow sessions. Prior to this change, every session creation path used
templates.NewDefaultPrompter()(with a leftover TODO comment), so user customizations never reached the agents and Langfuse traces always showed the default templates.Problem
backend/pkg/controller/assistant.go(NewAssistantWorker,LoadAssistantWorker) andbackend/pkg/controller/flow.go(NewFlowWorker,LoadFlowWorker) all built the prompter fromtemplates.NewDefaultPrompter()and ignored the per-userpromptstable that the Prompts UI persists. The Custom badge in the UI was therefore decorative -- saved overrides had no effect on subsequent sessions.Solution
Introduce a small controller-side helper,
newUserPrompter, which:database.Querier.GetUserPrompts.templates.LoadDefaultPromptsMap()(a new helper that returns the embedded defaults as aPromptsMapdirectly, avoiding a JSON round-trip) and overlays each non-empty user override onto the resulting map.templates.NewFlowPrompter(merged)so prompt types the user never customized continue to resolve to the defaults.A database error fails session creation explicitly (
wrapErrorEndSpanon the existing langfuse span) instead of silently falling back to defaults. Empty bodies are skipped because the UI uses delete (or reset, which writes the default body back) to remove a customization, so an empty row is unexpected and would otherwise surface asErrTemplateNotFounddeep inside agent rendering.The helper lives in
pkg/controllerrather thanpkg/templatesto avoid creating a newtemplates -> databaseimport edge.To avoid an N+1 pattern on flow load,
assistantWorkerCtxcarries an optionalprompterfield.LoadFlowWorkerbuilds the user prompter once for the flow and reuses it across every assistant in that flow;LoadAssistantWorkerfalls back to building its own prompter when invoked outside a flow load.Lifecycle Semantics
NewFlowWorker,LoadFlowWorker,NewAssistantWorker, standaloneLoadAssistantWorker).User Impact
PromptTypethe user has overridden.Test Plan
go test ./pkg/controller/... ./pkg/templates/...(passes locally on Go 1.24)backend/pkg/controller/prompter_test.gocover:GetUserPrompts-> wrapped error propagated, nil prompter returnednewUserPrompterend-to-end with a fakedatabase.QuerierFollow-ups
backend/cmd/ftester/worker/tester.gocarries the same TODO and was intentionally left alone here -- it is a developer harness rather than a session creation path. Happy to fold it in if reviewers prefer one place.Closes #300