feat(ai): custom prompt templates and global instructions with loading-consistency hardening#4064
feat(ai): custom prompt templates and global instructions with loading-consistency hardening#4064Abeautifulsnow wants to merge 11 commits into
Conversation
Two-layer custom prompt system for the DB Assistant: - Global custom instructions: always injected into every AI conversation, stored in app_state (ai_global_custom_instructions key) - Scenario prompt templates: multi-select per panel session, stored in a new prompt_templates table with backend-authoritative validation (char-based length limits, case-insensitive duplicate names, upsert preserving created_at) Injection is unified across relational and vector database prompt paths via buildCustomInstructionLines, placed after behavior rules and before schema context; baseline output stays byte-identical when no custom content is set. Template data is intentionally excluded from cloud sync. AppError now carries a StatusCode so validation errors return 400 and missing resources 404 instead of blanket 500.
…nt failure C02: saveGlobalInstructions now stores the backend-trimmed value in the reactive store (matching what a subsequent init() would return). S01: confirmDeleteTemplate clears templateDeleteConfirm in finally so the reference does not survive a failed deletion. S02: promptTemplateStore gains ensureLoaded() for idempotent retry on prior init failure; AiAssistant.vue watches showTemplateSelector to call it, so the selector gracefully self-heals when the backend was not ready during App mount.
When the user switches to a different connection or database, the scenario template checkboxes now reset to empty — a fresh database context warrants a clean template selection rather than inheriting the previous connection's choices. Global instructions are unaffected (they are always-on by design).
…mplates # Conflicts: # crates/dbx-web/src/routes/agents.rs
t8y2
left a comment
There was a problem hiding this comment.
The Rust changes do not currently compile under the repository CI, so this cannot be merged yet.
AppError was changed from a tuple struct to named fields, but the test targets still contain 21 accesses through error.0 across routes such as sql_file.rs and table_import.rs. The same CI run also fails Clippy on the newly added unused name25 variable in storage.rs.
Please update all affected test call sites to use the named field/API, remove the unused test variable, and rerun the full Rust CI until both rust-fmt-clippy and rust-test pass.
The reset watcher returned a fresh array literal `[connection.id, database]` as its getter, so Vue's Object.is comparison always saw a change and cleared the selection on any dependency invalidation — e.g. the 30s backup scheduler replacing connection objects mid agent-run — even when id/database were unchanged. Return a stable primitive key so the reset fires only on real connection/database switches.
AppError became a struct with named `message`/`status` fields, but the route tests still accessed the error string via the old tuple index `.0`, breaking compilation (E0609). Switch those accesses to `.message`. Also drop a dead shadowed `name25` binding in dbx-core storage tests that tripped `-D warnings` in CI.
已经全量修复。 |
t8y2
left a comment
There was a problem hiding this comment.
request changes: the previous Rust CI blocker is fixed, but three consistency issues remain.
-
apps/desktop/src/components/editor/EditorSettingsDialog.vue:1971can copy an empty value while the fire-and-forget prompt store initialization is still running. When the load later succeeds, the textarea is not refreshed, so pressing Save can silently erase existing global instructions. Please await the load when entering the AI tab and add a delayed-load regression test. -
apps/desktop/src/components/editor/AiAssistant.vue:1457awaitsensureLoaded()before establishing a synchronous send guard. Two rapid submissions can both pass the initialisGeneratingcheck and resume into separate agent runs. Please acquire a send-pending guard before the first await and test two concurrent sends against a deferred load. -
crates/dbx-core/src/storage.rs:1637relies on SQLiteLOWER()for backend-authoritative case-insensitive uniqueness, but the frontend applies JavaScript Unicode case conversion. Names such asÄ规则andä规则can therefore coexist in storage while the frontend treats them as duplicates. Please use a consistent normalization/comparison strategy and cover non-ASCII case pairs.
Evidence: SQLite built-in scalar functions:
“all ASCII characters converted to lower case.”
变更说明
feat(ai): custom prompt templates and global instructions with loading-consistency hardening
Body
Summary
Two-layer custom prompt system for the DB Assistant: global instructions (always-on, injected into every AI conversation) and scenario prompt templates (multi-select per panel, stored in a
prompt_templatestable). The injection path is unified throughbuildCustomInstructionLines, placed after behavior rules and before schema context. Baseline output stays byte-identical when no custom content is configured.Follow-up commits harden loading consistency: a request sent during initialization now awaits the single in-flight load, a failed load blocks the request with a localized error rather than silently omitting instructions, stale template selections are cleaned up on delete, and template checkboxes reset when switching connection/database.
What Changed
Core Feature (392e659)
app_stateunder theai_global_custom_instructionskey and always injected into every AI conversation.str::chars().count()created_atAppErrornow carries an HTTPStatusCodeso validation errors return 400 and missing resources return 404 instead of a blanket 500.prompt_template.rsfor CRUD, new Taure (promptTemplateStore.ts), and settings UI(EditorSettingsDialog.vue).Review Fixes (a569fe8)
saveGlobalInstructionsnow stores the backend-trimmed value in the reactive store, matching whatinit()would return on next load.confirmDeleteTemplateclearstemplateDeleteConfirmin afinallyblock so the reference doesn't survive a failed deletion.promptTemplateStoregainsensureLoaded()for idempotent retry;AiAssistant.vuewatchesshowTemplateSelectorto call it, so the selector self-heals when the backend was unavailable duringAppmount.UX / Selection State (dba2328)
Loading-Consistency Guard (7b9ba0c)
AiAssistant.send()awaits the single in-flightensureLoaded()before mutating conversation state or invoking the agent stream.Array.from()matches Ruststr::chars().count()for consistent frontend/backend length limits.AppError Refactoring (part of 392e659)
AppErrorwas changed from a tuple struct (AppError(String)) to a named-field struct that carries an HTTPStatusCodealongside the message. This enables the new prompt-template endpoints to return 400 (validation failure) and 404 (not found) instead of a blanket 500 for everything.This is a purely mechanical change: every existing call site across ~30 route files was updated from
.map_err(AppError)/AppError("...")to.map_err(AppError::from)/AppError::from("..."). The defaultFrom<String>andFrom<&str>impls still produce a 500, so no existing route behavior changed — only the new prompt-template code usesbad_request()andnot_found().Files Changed
crates/dbx-core/src/prompt_template.rs,crates/dbx-core/src/storage.rs,crates/dbx-web/src/routes/prompt_template.rs,crates/dbx-web/src/error.rs, `src-tauri/src/commands/prompt_templatapps/desktop/src/components/editor/AiAssistant.vue,EditorSettingsDialog.vue,apps/desktop/src/stores/promptTemplateStore.ts,apps/desktop/src/types/promptTemplate.ts,apps/desktop/src/lib/ai/ai.tsen,es,it,ja,pt-BR,zh-CN,zh-TW)packages/app-tests/aiCustomPrompt.test.ts,packages/app-tests/promptTemplateStore.test.tsTesting
promptTemplateStore.test.tscovers store initialization, CRUD, and theensureLoadedretry path.aiCustomPrompt.test.tscovers prompt constom instructions, template injection ordering, global-instructioninclusion, and Unicode character counting.Screenshots / UX
Settings dialog — Global instructions tab
Settings dialog — Scenario templates tab
Assistant panel — template selector
Breaking Changes
None. Existing conversations and prompt paths are unchanged when no custom instructions or templates are configured.
变更类型
涉及前端
验证
make check通过make cargo-check-fast通过关联 Issue
Related #3975