feat(ui): admin prompt library and prompt selection in the preset/partition editors - #836
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds an admin Prompt Library with CRUD API support, template validation and previewing, prompt selection for presets and partitions, admin navigation and routing, plus related UI styling and test updates. ChangesPrompt management and configuration
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Admin
participant Router
participant PromptsPage
participant PromptAPI
Admin->>Router: open /prompts
Router->>PromptsPage: render admin page
PromptsPage->>PromptAPI: listAllPrompts()
PromptAPI-->>PromptsPage: PromptResponse[]
Admin->>PromptsPage: create or update prompt
PromptsPage->>PromptAPI: createPrompt() or updatePrompt()
PromptAPI-->>PromptsPage: mutation result
Possibly related issues
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
ui/src/pages/admin/presets.tsx (3)
605-643: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winNew
PromptSelectreuses the same"__default__"sentinel asFeatureToggle, and a prompt literally named__default__would be unselectable.Both this component and
FeatureToggle(line 582) hardcode"__default__"as the "use default" sentinel value for theSelect. Since promptnameis free text with no visible reserved-word restriction (seeprompts.tsx's create/edit form), a prompt named exactly__default__would always render as "Use default" and never be individually selectable in any of the three pickers (here,FeatureToggle, and the partition detail generation-prompt picker). Consider centralizing the sentinel as a shared exported constant (e.g. inprompt-meta.ts) and either validating against it in the prompt name field, or picking a sentinel value guaranteed not to collide with a real prompt name.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/src/pages/admin/presets.tsx` around lines 605 - 643, Prevent prompt names from colliding with the "__default__" Select sentinel used by PromptSelect, FeatureToggle, and the partition detail generation-prompt picker. Centralize the sentinel in a shared exported prompt-meta constant, then validate or reject that value in the prompt create/edit name field so prompts named "__default__" cannot be saved while preserving the existing default-selection behavior.
664-664: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate
promptsByTypefilter helper (3rd occurrence).Same one-liner as
IndexationPresetForm(line 288, pre-existing) and now also added inui/src/pages/admin/partitions/detail.tsx. See consolidated comment for a shared-helper suggestion.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/src/pages/admin/presets.tsx` at line 664, Remove the duplicate local promptsByType helper from the presets page and reuse the shared helper established for prompt-type filtering, ensuring existing callers retain the same filtering behavior. Check the corresponding IndexationPresetForm and partitions detail implementations to consolidate rather than adding another identical filter.
581-596: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winHardcoded
"__default__"sentinel duplicated across prompt pickers.Will be covered by the consolidated comment alongside the new
PromptSelectcomponent and the partition detail picker.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/src/pages/admin/presets.tsx` around lines 581 - 596, The prompt picker hardcodes the "__default__" sentinel, which should be shared across prompt-selection controls. Update the Select value handling, onValueChange mapping, and default SelectItem in the prompt picker to reuse the centralized sentinel from the new PromptSelect implementation, preserving the existing empty-string behavior.ui/src/lib/prompt-meta.ts (1)
66-105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider typing prompt-type params/keys as
PromptTypeinstead ofstring.
PROMPT_TYPE_VARIABLESisRecord<string, TemplateVariable[]>, andvalidatePlaceholders/renderPreviewtakepromptType: string. Since all 7PromptTypemembers are already covered, switching toRecord<PromptType, TemplateVariable[]>and typed params would catch a missing variables entry at compile time if a new prompt type is ever added, instead of silently falling back to[]. This also propagates loosely asstringintoPromptTemplateEditor's prop inprompts.tsx.♻️ Proposed tightening
-export const PROMPT_TYPE_VARIABLES: Record<string, TemplateVariable[]> = { +export const PROMPT_TYPE_VARIABLES: Record<PromptType, TemplateVariable[]> = { ... }; -export function validatePlaceholders(content: string, promptType: string) { +export function validatePlaceholders(content: string, promptType: PromptType) { ... } -export function renderPreview(content: string, promptType: string): PreviewSegment[] { +export function renderPreview(content: string, promptType: PromptType): PreviewSegment[] { ... }Also applies to: 115-141
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/src/lib/prompt-meta.ts` around lines 66 - 105, Introduce the existing PromptType type for prompt-type keys and parameters: change PROMPT_TYPE_VARIABLES to Record<PromptType, TemplateVariable[]> and type validatePlaceholders’ promptType accordingly, preserving all seven existing entries. Propagate PromptType through the related renderPreview and PromptTemplateEditor prompt-type props instead of accepting string, and remove the permissive fallback that can hide missing prompt-type definitions.ui/src/pages/admin/partitions/detail.tsx (1)
119-125: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate
promptsByTypefilter helper.This one-line
(type) => prompts.filter(p => p.prompt_type === type)is now defined three times across the codebase (here, and twice inui/src/pages/admin/presets.tsx). See the consolidated comment for a shared-helper suggestion.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/src/pages/admin/partitions/detail.tsx` around lines 119 - 125, Remove the local promptsByType helper from the partition detail component and reuse the shared prompt-type filtering helper introduced for this duplicate logic. Update its call sites to use the shared helper with promptsData, preserving the existing PromptResponse[] filtering behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ui/src/pages/admin/prompts.tsx`:
- Around line 72-88: Update the onSuccess handlers for setDefaultMut and
deleteMut to invalidate both ["prompts-library"] and ["prompts-for-presets"],
matching createMut and updateMut. Preserve the existing success toasts and error
handling.
---
Nitpick comments:
In `@ui/src/lib/prompt-meta.ts`:
- Around line 66-105: Introduce the existing PromptType type for prompt-type
keys and parameters: change PROMPT_TYPE_VARIABLES to Record<PromptType,
TemplateVariable[]> and type validatePlaceholders’ promptType accordingly,
preserving all seven existing entries. Propagate PromptType through the related
renderPreview and PromptTemplateEditor prompt-type props instead of accepting
string, and remove the permissive fallback that can hide missing prompt-type
definitions.
In `@ui/src/pages/admin/partitions/detail.tsx`:
- Around line 119-125: Remove the local promptsByType helper from the partition
detail component and reuse the shared prompt-type filtering helper introduced
for this duplicate logic. Update its call sites to use the shared helper with
promptsData, preserving the existing PromptResponse[] filtering behavior.
In `@ui/src/pages/admin/presets.tsx`:
- Around line 605-643: Prevent prompt names from colliding with the
"__default__" Select sentinel used by PromptSelect, FeatureToggle, and the
partition detail generation-prompt picker. Centralize the sentinel in a shared
exported prompt-meta constant, then validate or reject that value in the prompt
create/edit name field so prompts named "__default__" cannot be saved while
preserving the existing default-selection behavior.
- Line 664: Remove the duplicate local promptsByType helper from the presets
page and reuse the shared helper established for prompt-type filtering, ensuring
existing callers retain the same filtering behavior. Check the corresponding
IndexationPresetForm and partitions detail implementations to consolidate rather
than adding another identical filter.
- Around line 581-596: The prompt picker hardcodes the "__default__" sentinel,
which should be shared across prompt-selection controls. Update the Select value
handling, onValueChange mapping, and default SelectItem in the prompt picker to
reuse the centralized sentinel from the new PromptSelect implementation,
preserving the existing empty-string behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d2960a1a-9da0-4ac7-bdb0-e641b1988d59
📒 Files selected for processing (15)
ui/src/components/layout/sidebar.tsxui/src/components/ui/tabs.tsxui/src/lib/api/partitions.tsui/src/lib/api/prompts.test.tsui/src/lib/api/prompts.tsui/src/lib/permissions.tsui/src/lib/prompt-meta.tsui/src/pages/admin/jobs/list.test.tsxui/src/pages/admin/jobs/list.tsxui/src/pages/admin/models.tsxui/src/pages/admin/partitions/detail.tsxui/src/pages/admin/presets.test.tsxui/src/pages/admin/presets.tsxui/src/pages/admin/prompts.tsxui/src/router.tsx
Group it under Retrieval (not Generation), add its dropdown to the retrieval preset editor, and drop it from the partition generation-prompts section.
…er prompt Drop spoken_style_answer from the library and partition editor; the Answer concern now holds a single 'Final answer prompt' selected per partition.
…nswer-prompt Move the partition's answer-prompt select onto the same row as Indexation Preset / Retrieval Preset / Chat LLM (4-column row) instead of a separate block below Chat History Depth; rename the library concern group Answer -> Final Answer.
The shared SelectTrigger is w-fit, so the Chat LLM value overflowed its cell and overlapped the neighbour once the row held four selects. Constrain the config-row triggers to w-full, wrap the row responsively (2 cols, 4 on lg), and widen the form so the four selects have room.
…th input Use the same 2/4-col grid for the read-only info row (dimension/embedder/docs) as the config row so their columns line up; cap the chat-history-depth number input width instead of stretching it full-width like the description.
Default prompts now show a disabled delete button with a tooltip explaining it can't be removed until another prompt is promoted to default, instead of hiding delete entirely (which looked like the feature was missing).
Show All / Active / Completed / Failed / Cancelled instead of the shouty uppercase labels; the underlying tab values stay uppercase for the filter logic.
Use the same outline buttons with icon+label (Set Default / Edit / Delete) in a flex-wrap row, instead of the odd ghost/icon-only delete — so prompt cards look uniform with the rest of the admin console.
Tabs across Jobs/Presets/Models/partitions now use text-xs font-semibold on a shorter list, matching the library concern filters for a uniform look. Update the jobs test for the title-cased tab labels.
hedhoud
left a comment
There was a problem hiding this comment.
The latest update is a clean rebase, so the functional patch is unchanged. The inline comments cover two behavior blockers and three user-facing correctness issues that should be addressed before approval. The existing unresolved cache-invalidation comment also remains valid.
- Match the API's template grammar instead of a /{(\\w+)}/ regex: doubled
braces are literal, a lone brace is an error, and conversions/format specs/
attribute access reduce to the root field. Malformed or unknown-variable
templates are now blocked before the request rather than coming back 422.
Verbatim prompt types are not format-validated at all, matching the backend.
- Surface a load error on the library page. A failed request rendered as an
empty successful library, telling an admin there were no prompts during an
outage; cached data now stays visible across a failed refetch.
- Invalidate the presets' prompt query when a prompt is promoted or deleted,
so the preset pickers stop showing a stale default badge or a deleted prompt.
- Warn before a rename that drops selections. References are by name, so
renaming a prompt in use silently falls its partitions back to the default.
- Only send generation_prompt_names when this editor changed it. The backend
validates every name, so resubmitting an untouched-but-stale reference would
422 the whole PATCH and block unrelated edits — unrecoverable for a non-admin
owner, whose picker is disabled.
- Namespace prompt picker option values so a prompt named __default__ is still
selectable; the fallback sentinel can no longer collide with a real name.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
ui/src/lib/prompt-meta.ts (1)
185-203: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy liftMake preview use the same formatter grammar.
The regex only handles exact
{name}placeholders: valid escaped braces remain doubled, and valid formatted fields are not substituted. Tokenize with the same grammar used for validation so the preview matches runtime rendering.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/src/lib/prompt-meta.ts` around lines 185 - 203, Update the preview formatter around the regex-based tokenization loop to reuse the same placeholder grammar and parsing logic as validation, including escaped braces and formatted fields. Ensure recognized variables are substituted from varMap while preserving literal text and unresolved placeholders according to runtime rendering behavior, rather than relying on the exact `{name}` regex.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@ui/src/lib/prompt-meta.ts`:
- Around line 185-203: Update the preview formatter around the regex-based
tokenization loop to reuse the same placeholder grammar and parsing logic as
validation, including escaped braces and formatted fields. Ensure recognized
variables are substituted from varMap while preserving literal text and
unresolved placeholders according to runtime rendering behavior, rather than
relying on the exact `{name}` regex.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 804aeb6c-5e8f-4741-96f8-c181a1a10854
📒 Files selected for processing (16)
ui/src/components/layout/sidebar.tsxui/src/components/ui/tabs.tsxui/src/lib/api/partitions.tsui/src/lib/api/prompts.test.tsui/src/lib/api/prompts.tsui/src/lib/permissions.tsui/src/lib/prompt-meta.test.tsui/src/lib/prompt-meta.tsui/src/pages/admin/jobs/list.test.tsxui/src/pages/admin/jobs/list.tsxui/src/pages/admin/models.tsxui/src/pages/admin/partitions/detail.tsxui/src/pages/admin/presets.test.tsxui/src/pages/admin/presets.tsxui/src/pages/admin/prompts.tsxui/src/router.tsx
🚧 Files skipped from review as they are similar to previous changes (12)
- ui/src/pages/admin/jobs/list.tsx
- ui/src/pages/admin/models.tsx
- ui/src/pages/admin/jobs/list.test.tsx
- ui/src/lib/permissions.ts
- ui/src/pages/admin/presets.test.tsx
- ui/src/components/ui/tabs.tsx
- ui/src/pages/admin/partitions/detail.tsx
- ui/src/lib/api/prompts.ts
- ui/src/lib/api/prompts.test.ts
- ui/src/pages/admin/prompts.tsx
- ui/src/lib/api/partitions.ts
- ui/src/pages/admin/presets.tsx
The preview still used the old /{(\\w+)}/ regex while validation had moved to
a Formatter-compatible scan, so the two disagreed: escaped braces stayed
doubled in the preview and a field carrying a format spec or conversion was
shown raw instead of substituted.
Extract the tokenizer both now share, so what an author sees in the preview
is what the pipeline renders. Verbatim prompt types preview unchanged, since
they are never .format-ed.
|
@hedhoud thanks for the review — all five points are addressed, could you take another look when you have a moment?
Also from CodeRabbit's follow-up: the preview shared the old regex, so it disagreed with the new validation on escaped braces and formatted fields — both now share one tokenizer. Covered by new unit tests in |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ui/src/lib/prompt-meta.test.ts`:
- Around line 107-110: Strengthen the test for format and conversion handling in
the “substitutes a field carrying a format spec or conversion” case by asserting
the exact renderPreview/flat output rather than using toContain. Verify the
intended semantics for {:>12} padding and !r quoting, or explicitly assert that
these modifiers are deliberately stripped.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 5934fa08-a2a8-434a-8c4d-36f67f39bad3
📒 Files selected for processing (2)
ui/src/lib/prompt-meta.test.tsui/src/lib/prompt-meta.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- ui/src/lib/prompt-meta.ts
The assertions used toContain, so they passed whether or not a field's conversion (!r) and format spec (:>12) were honoured — the test named the behaviour without pinning it. Assert the exact output and document the choice: modifiers are recognised but deliberately not emulated, because the substituted value is the illustrative sample rather than the runtime one, so reimplementing Python's format mini-language in TypeScript would be a second grammar to keep in sync for no gain.
hedhoud
left a comment
There was a problem hiding this comment.
The latest update addresses the earlier rename, stale-reference, error-state, sentinel, and cache findings. Three correctness issues still need attention before approval: runtime-unsafe template expressions, silent pinning of the current default in indexation presets, and the one-page prompt-library limit.
- Mirror the API's stricter placeholder rule: a conversion, format spec or attribute/index access is rejected instead of being reduced to its root name, which previously let the editor accept templates the pipeline cannot render. - Stop auto-selecting the only prompt in a FeatureToggle. After seeding a type usually has exactly one prompt — its global default — so opening and saving an indexation preset pinned that name and silently detached the preset from any future default. An empty value is a real 'use default' choice; models have no such fallback and keep the behaviour. - Follow pagination when listing the library. A single capped request hid prompts past the cap and reported a partial count as the total, leaving them unmanageable on the page and unselectable in every picker.
|
@hedhoud all three are addressed — replies inline on each thread. The placeholder one turned out to be a backend bug as well: the same root-reduction was in Ready for another look when you have a moment. |
hedhoud
left a comment
There was a problem hiding this comment.
Rechecked the latest update. The three blocking issues are addressed, and the focused tests and UI build pass locally. This is ready from my side.
The API now returns spoken_style_answer, which this page deliberately does not surface — it is driven by a chat metadata flag, not by anything configurable here — but the header counted every prompt the API returned, so it advertised eight prompts above seven cards. Count the managed types only. Adding the type to PROMPT_GROUPS later is all that's needed to surface it.
Admin UI for the prompt library added in #835. Stacked on that PR — review
and merge it first; this branch targets
feat/pm-backend, so the diff hereis UI-only.
Part of #772.
What it adds
Prompt Library page (
/admin/prompts, new sidebar entry) — prompts groupedby concern (Answer / Indexation / Retrieval) as cards, with a drawer editor
offering an edit tab and a preview tab that renders the template against sample
values. Insert-variable buttons only offer the placeholders the type actually
accepts, so the editor can't produce a template the API would reject.
Each card carries a used-by badge and a default badge, and exposes the same
card-action affordances as Presets and Models rather than a new pattern.
Selection lives in the editors that own the setting, not in a separate
assignment screen:
topic-tagging prompts; retrieval presets pick query-contextualizer, plus HyDE
or multi-query, shown only when the retriever type actually uses them.
Every picker offers "Use default", which clears the name and falls back to the
type's global default.
Also in here
A few consistency fixes this page surfaced: the Jobs, Presets and Models tabs
now match the Library's tab sizing, the Jobs status tabs are title-cased, the
Default badge renders identically in Prompts and Models, and the partition
config rows no longer overflow their grid cells.
Verification
tsc -bclean, 176 vitest tests green, production build clean. eslint reportsone pre-existing warning in
models.tsx, untouched here.Driven end-to-end against a live deployment: creating a prompt, selecting it in
a preset and in a partition, and confirming the selection reached the model.
Summary by CodeRabbit
New Features
/promptsadmin route.Improvements
generation_prompt_names; refined several admin UI details (tabs, badges, layout/spacing).Tests