Skip to content

feat: add System Prompts library for Expand Prompt button#9152

Open
Pfannkuchensack wants to merge 8 commits into
invoke-ai:mainfrom
Pfannkuchensack:feat/system-prompts-library
Open

feat: add System Prompts library for Expand Prompt button#9152
Pfannkuchensack wants to merge 8 commits into
invoke-ai:mainfrom
Pfannkuchensack:feat/system-prompts-library

Conversation

@Pfannkuchensack
Copy link
Copy Markdown
Collaborator

@Pfannkuchensack Pfannkuchensack commented May 10, 2026

Summary

  • Add system_prompts SQLite table (migration 32) seeded with 7 curated default prompts: an InvokeAI Default (mirrors the hardcoded fallback in text_llm_pipeline.DEFAULT_SYSTEM_PROMPT) plus 6 prompts adapted from FLUX.2, HunyuanImage 3.0, Qwen-Image (edit + 2512), Z-Image and HiDream
  • Add CRUD service layer + REST router at /api/v1/system_prompts, with per-user ownership and optional sharing (modeled on the workflow library): user_id + is_public columns, CurrentUserOrDefault dependency on every endpoint, owner-or-admin checks for PATCH/DELETE, and own+public scoping for LIST in multi-user mode
  • Add RTK Query endpoints, management modal (list/create/edit/delete) and a system-prompt picker in the Expand Prompt popover
  • Persist last picked system prompt + text-LLM model via Redux (expandPrompt slice)
  • Frontend respects ownership: useCanEditSystemPrompt hook hides edit/delete on prompts the user does not own, list items show System / Shared badges, and the form exposes a Share with everyone toggle when multi-user is enabled
  • Adds a sibling node to TextLLMInvocation that takes a SystemPromptField (a
    DB-backed preset reference) instead of a free-text system prompt.

Related Issues / Discussions

None.

QA Instructions

Backend — single-user (default)

  1. Start a fresh dev server: uv run --extra cuda invokeai-web — migration 32 runs, creates the system_prompts table (with user_id + is_public columns) and seeds 7 default prompts as user_id='system', is_public=TRUE.
  2. Verify via REST:
    • GET /api/v1/system_prompts/ → 7 records
    • POST /api/v1/system_prompts/ with {"name":"Test","content":"foo"} → 200; record echoed back with is_public=true (single-user default)
    • PATCH /api/v1/system_prompts/i/{id} with {"name":"Test2"} → record updated
    • DELETE /api/v1/system_prompts/i/{id} → record gone; subsequent GET returns 404
  3. Restart the app — deleted defaults stay deleted (fixed UUIDs + INSERT OR IGNORE).

Backend — multi-user
4. Run with multi-user enabled and create two non-admin users (alice, bob) plus an admin.
5. As alice: POST a private prompt → it is owned by alice, is_public=false.
6. As bob: GET /api/v1/system_prompts/ → sees the 7 system defaults plus only public prompts; does not see alice's private prompt.
7. As bob: PATCH or DELETE of alice's prompt id → 403 (or 404 if he does not even know the id, depending on path).
8. As alice: PATCH … {"is_public": true} → flips her prompt to shared; bob's next GET now includes it.
9. As admin: GET returns all prompts (including alice/bob's privates), and PATCH/DELETE on any prompt succeeds.

Frontend
10. Install at least one Text-LLM model (e.g. via the Model Manager starter list).
11. In the Generate tab, click the Expand Prompt button (sparkle icon) over a non-empty prompt.
12. The popover now shows a System Prompt combobox above the model picker — pick one of the seeded defaults, pick a model, click Expand. The positive prompt is replaced; Ctrl+Z restores the original.
13. Click the pencil icon next to the system-prompt combobox → management modal opens.
- Single-user: Create / Edit / Delete buttons appear on every row.
- Multi-user (non-admin): Edit / Delete only appear on prompts you own; system/shared prompts show a badge instead.
- Multi-user: opening the form for an owned prompt shows the Share with everyone checkbox.
14. Reload the browser → the previously picked system prompt and model are still selected (Redux persistence).
15. Lint + test suite: pnpm lint && pnpm test:no-watch from invokeai/frontend/web/ (1 pre-existing flaky timer test in navigation-api.test.ts is unrelated).

Merge Plan

This PR adds a new SQLite migration (32) and a new persisted Redux slice. Notes for reviewers / mergers:

  • Migration 32 creates the system_prompts table with user_id + is_public already in the schema and seeds 7 default rows owned by system (public). For dev databases that already ran an earlier revision of this migration without the multi-user columns, the migration also runs PRAGMA table_info and ALTER TABLE … ADD COLUMN to backfill the columns — safe and idempotent.
  • The migration only touches its own table; no impact on workflows, style presets, etc.
  • Redux slice migration: the new expandPrompt slice has a migrate step (sets _version: 1 if missing) — an old persisted state without this slice simply starts from initial state, no breaking change for users.
  • No coordination with other PRs needed; standard merge.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Tests added / updated (if applicable)
  • ❗Changes to a redux slice have a corresponding migration
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

- Add system_prompts SQLite table (migration 32) seeded with 6 curated
  default prompts adapted from FLUX.2, HunyuanImage 3.0, Qwen-Image,
  Z-Image and HiDream
- Add CRUD service layer + REST router at /api/v1/system_prompts
- Add RTK Query endpoints, management modal (list/create/edit/delete)
  and a system-prompt picker in the Expand Prompt popover
- Persist last picked system prompt + text-LLM model via Redux
- Migration 32 now adds user_id + is_public columns and seeds the 6 default
  prompts as user_id='system', is_public=TRUE;
- Storage layer gains optional user_id scoping on get_many/update/delete,
  and create requires user_id + is_public
- Router uses CurrentUserOrDefault: list scopes to own+public, GET returns
  403 for foreign private prompts, PATCH/DELETE require owner or admin
- Frontend adds useCanEditSystemPrompt hook, hides edit/delete on prompts
  the user does not own, shows System/Shared badges in the list, and
  exposes a 'Share with everyone' toggle in the form when multiuser is on
- Critical: migration_32.py had a 7-space indent on the second cursor.execute(),
  raising IndentationError on import and blocking server startup. Re-indent and
  restore the ALTER TABLE backfill block lost in the previous edit.
- Medium: drop the heavy import of invokeai.backend.text_llm_pipeline from the
  migration (which would pull torch+transformers into the migrator import path).
  Inline DEFAULT_SYSTEM_PROMPT verbatim and rename the seeded row to "Default";
  the value still mirrors text_llm_pipeline.DEFAULT_SYSTEM_PROMPT.
- Medium: add 7 storage-layer tests covering own/public/admin scoping and the
  no-mutate guarantees on non-owner update/delete, plus 9 router tests with JWT
  auth covering 401/403/404 paths, owner is_public flip, and admin override.
- Conftest and the existing workflows-multiuser test fixtures now wire a real
  SqliteSystemPromptRecordsStorage so InvocationServices construction succeeds
  with the new required parameter.
@github-actions github-actions Bot added api python PRs that change python files services PRs that change app services frontend PRs that change frontend files python-tests PRs that change python tests labels May 10, 2026
…zation fixture

The new required InvocationServices parameter broke 122 unrelated tests in
tests/app/routers/test_multiuser_authorization.py because that file builds its
own InvocationServices. Add SqliteSystemPromptRecordsStorage to its fixture
the same way the workflows-multiuser fixture and the global conftest were
updated in the previous commit.
…ow node

Adds a sibling node to TextLLMInvocation that takes a SystemPromptField (a
DB-backed preset reference) instead of a free-text system prompt. Selecting a
preset in the workflow editor pulls its content from the System Prompts library
at run time. The original TextLLMInvocation is unchanged, so users keep the
free-text option and can pick the appropriate node per workflow.

- New SystemPromptField primitive in app/invocations/fields.py
- Shared _run_text_llm helper extracted from TextLLMInvocation; both nodes use it
- Frontend wires SystemPromptField as a new stateful field type analogous to
  StylePresetField (zod schemas, type guards, builders, slice action, color,
  Combobox renderer backed by useListSystemPromptsQuery)
- Pytest covers both behaviours: DB lookup happens with the configured id and
  forwards the resolved content; SystemPromptNotFoundError short-circuits the
  pipeline call so the LLM is not invoked
@github-actions github-actions Bot added the invocations PRs that change invocations label May 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api frontend PRs that change frontend files invocations PRs that change invocations python PRs that change python files python-tests PRs that change python tests services PRs that change app services

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant