Skip to content

feat: Add a custom per-user system prompt setting guarded by an admin setting - #733

Merged
marcelklehr merged 3 commits into
mainfrom
feat/user-provided-system-prompt
Nov 9, 2026
Merged

marcelklehr merged 3 commits into
mainfrom
feat/user-provided-system-prompt

Conversation

@marcelklehr

@marcelklehr marcelklehr commented Nov 9, 2026 •

Copy link
Copy Markdown
Member
Pasted image Pasted image " />

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI (N/A)

Signed-off-by: Marcel Klehr <mklehr@gmx.net>
@marcelklehr
marcelklehr requested a review from julien-nc November 9, 2026 09:11
@coderabbitai

coderabbitai Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Explain Change →

Walkthrough

The change adds an admin setting that controls whether users can add personal chat instructions. The personal settings interface exposes a text field with a 4,000-character limit. The server rejects longer values. When the setting is enabled, ChatService adds a non-empty personal instruction to the system message for a new session after the existing instructions.

Merge Risk: Low · up to 9999d

The feature is mergeable with bounded follow-up: users can lose instructions when closing the tab immediately after editing, and emoji-heavy instructions reach the input limit prematurely.

Pre-merge checks: Passed 5

Check name Status Explanation
Title check Passed The title states the main change: a per-user custom system prompt (personal chat instructions) controlled by an admin setting. The changeset adds the personal instructions field, the admin toggle, the…
Description check Passed The description contains two screenshots of the personal and administrator Assistant settings pages, which show the new custom chat instructions field and the admin toggle that the changeset adds. It …
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title states the main change: a per-user custom system prompt (personal chat instructions) controlled by an admin setting. The changeset adds the personal instructions field, the admin toggle, the length limit, and the ChatService logic that appends user instructions to the system message. The title is specific and matches the primary change.

Full details: Description check

Explanation

The description contains two screenshots of the personal and administrator Assistant settings pages, which show the new custom chat instructions field and the admin toggle that the changeset adds. It also includes an AI-generation disclosure. The description is brief but relates directly to the changeset.

Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 4 functions across 6 files. (2 skipped: 2 unsupported.)

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/components/AdminSettings.vue

typescript-eslint does not support TS 7.0.
Please see https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/#running-side-by-side-with-typescript-6.0 to run typescript-eslint using the TS 6 API.
See also typescript-eslint/typescript-eslint#10940 for tracking typescript-eslint's support for TS >=7.1

Oops! Something went wrong! :(

ESLint: 10.14.0

Error: typescript-eslint does not support TS 7.0.
at Object. (/.eslint-tmp/node_modules/typescript-eslint/dist/index.js:52:11)
at Module._compile (node:internal/modules/cjs/loader:1929:14)
at Object..js (node:internal/modules/cjs/loader:2060:10)
at Module.load (node:internal/modules/cjs/loader:1651:32)
at Module._load (node:internal/modules/cjs/loader:1443:12)
at wrapModuleLoad (node:internal/modules/cjs/loader:261:19)
at loadCJSModuleWithModuleLoad (node:internal/modules/esm/translators:372:15)
at ModuleWrap. (node:internal/modules/esm/translators:244:9)
at ModuleJob.run (node:internal/modules/esm/module_job:561:25)
at async node:internal/modules/esm/loader:647:26

src/components/PersonalSettings.vue

ESLint skipped: the matched ESLint configuration already failed (config-incompatibility).

PHPStan (2.3.1)

PHPStan reported 3 non-file analysis error(s); analysis is incomplete

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.

Share

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
@marcelklehr marcelklehr changed the title feat: Add a custom per-user system prompt setting feat: Add a custom per-user system prompt setting guarded by an admin setting Nov 9, 2026
Comment thread src/components/PersonalSettings.vue Resolved
Signed-off-by: Marcel Klehr <mklehr@gmx.net>

@julien-nc julien-nc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm!

v-model="state.chat_personal_instructions"
class="chat-instructions__field"
:label="t('assistant', 'Custom instructions')"
:maxlength="state.chat_personal_instructions_max_length"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably stricter than the server-side max (that uses mb_strlen). All good but not exactly the same max between frontend and backend.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Nitpick comments: 1

lib/Service/ChatService.php: 1

78-83: Trivial · Maintainability & Code Quality · Low value

Add session-creation tests for personal instructions.

Assert the persisted system message for enabled, disabled, empty, and distinct per-user instructions. The agency tests supply a system message directly, so they would not catch a regression that omits personal instructions or ignores the admin setting.

Review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 30d75feb-2a77-4272-a7d6-3b49c545c12a
Commits

Reviewing files that changed from the base of the PR and between 9e9a064 and 9999d58.

Files selected for processing: 8

  • lib/AppInfo/Application.php
  • lib/Controller/ConfigController.php
  • lib/Service/ChatService.php
  • lib/Settings/Admin.php
  • lib/Settings/Personal.php
  • src/components/AdminSettings.vue
  • src/components/PersonalSettings.vue
  • tests/unit/Service/ChatServiceAgencyTest.php

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

v-model="state.chat_personal_instructions"
class="chat-instructions__field"
:label="t('assistant', 'Custom instructions')"
:maxlength="state.chat_personal_instructions_max_length"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Low value

Supported by static analysis

Script executed:

rg -n 'maxlength|defineProps' node_modules/@nextcloud/vue/dist/components/NcTextArea* node_modules/@nextcloud/vue/src/components/NcTextArea* 2>/dev/null | head -60

Repository: nextcloud/assistant

Length of output: 157


Script executed:

git diff 9e9a064a37de8ef5a55711e821a7ce0b11f34ea7 9999d58647a9b86e75af314fa1605f330f2018eb -- src/components/PersonalSettings.vue lib/Controller/ConfigController.php lib/Service/ChatService.php lib/Settings/Personal.php
nl -ba src/components/PersonalSettings.vue | sed -n '1,245p'
nl -ba lib/Controller/ConfigController.php | sed -n '1,200p'
nl -ba lib/Service/ChatService.php | sed -n '1,180p'
nl -ba lib/Settings/Personal.php | sed -n '1,150p'
rg -n -F -e 'chat_personal_instructions' -e '"@nextcloud/vue"' -- lib src package.json package-lock.json tests || test "$?" -eq 1

Repository: nextcloud/assistant

Length of output: 39103


Web query:

@nextcloud/vue 9.9.0 NcTextArea source maxlength native textarea forwarding

Result:

Sources consulted:
- UNPKG — https://app.unpkg.com/%40nextcloud/vue%408.28.0/files/dist/chunks
- Releases · nextcloud-libraries/nextcloud-vue · GitHub — https://github.com/nextcloud-libraries/nextcloud-vue/releases
- Issues · nextcloud-libraries/nextcloud-vue · GitHub — https://github.com/nextcloud-libraries/nextcloud-vue/issues
- registration/src/AdminSettings.vue at master · nextcloud/registration · GitHub — https://github.com/nextcloud/registration/blob/master/src/AdminSettings.vue
- …and 21 more

Script executed:

python3 - <<'PY'
import json
p=json.load(open('package-lock.json'))
for k,v in p.get('packages',{}).items():
    if k.endswith('node_modules/@nextcloud/vue'):
        print(k, v.get('version'), v.get('resolved'))
PY
nl -ba lib/AppInfo/Application.php | sed -n '45,75p'
rg -n -F -e 'mb_internal_encoding' -e 'mbstring.internal_encoding' -e 'MAX_CHAT_PERSONAL_INSTRUCTIONS_LENGTH' -- . ':!package-lock.json' || test "$?" -eq 1

Repository: nextcloud/assistant

Length of output: 3323


Web query:

HTML textarea maxlength UTF-16 code units user input specification MDN

Result:

Sources consulted:
- maxlength HTML attribute - HTML | MDN — https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/maxlength
- <textarea> HTML textarea element - HTML | MDN — https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/textarea
- <input> HTML input element - HTML | MDN — https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input
- minlength HTML attribute - HTML | MDN — https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/minlength
- HTMLTextAreaElement: maxLength property - Web APIs | MDN — https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement/maxLength
- …and 11 more

Web query:

Nextcloud IConfig setUserValue configvalue maximum length database emoji preferences

Result:

Sources consulted:
- server/lib/private/AllConfig.php at master · nextcloud/server · GitHub — https://github.com/nextcloud/server/blob/master/lib/private/AllConfig.php
- UserConfig — Nextcloud 35 Developer Manual — https://docs.nextcloud.com/server/stable/developer_manual/digging_deeper/config/userconfig.html
- server/apps/files/lib/Service/UserConfig.php at master · nextcloud/server · GitHub — https://github.com/nextcloud/server/blob/master/apps/files/lib/Service/UserConfig.php
- Configuration — Nextcloud 35 Developer Manual — https://docs.nextcloud.com/server/stable/developer_manual/basics/storage/configuration.html
- server/lib/private/AppConfig.php at master · nextcloud/server · GitHub — https://github.com/nextcloud/server/blob/master/lib/private/AppConfig.php
- …and 13 more

Script executed:

curl -fsSL --max-time 20 https://raw.githubusercontent.com/nextcloud-libraries/nextcloud-vue/v9.13.1/src/components/NcTextArea/NcTextArea.vue | nl -ba | sed -n '1,260p'
printf '\n--- supported server versions ---\n'
rg -n -F -e 'nextcloud min-version' -e 'nextcloud max-version' -e 'nextcloud/server' -- appinfo/info.xml composer.json composer.lock || test "$?" -eq 1

Repository: nextcloud/assistant

Length of output: 7853


Web query:

PHP mb_strlen default encoding internal encoding UTF-8 default documentation

Result:

Sources consulted:
- PHP: mb_strlen - Manual — https://www.php.net/manual/en/function.mb-strlen.php
- PHP: Description of core php.ini directives - Manual — https://www.php.net/manual/en/ini.core.php
- PHP: mb_strlen - Manual — https://www.php.net/mb-strlen
- PHP: mb_internal_encoding - Manual — https://www.php.net/manual/en/function.mb-internal-encoding.php
- PHP: mb_internal_encoding - Manual — https://www.php.net/mb_internal_encoding
- …and 7 more

Web query:

@nextcloud/vue 9.13.1 NcTextArea maxlength attrs textarea source github

Result:

Sources consulted:
- nextcloud-vue/src/components/NcTextArea/NcTextArea.vue at v9.13.1 · nextcloud-libraries/nextcloud-vue · GitHub — https://github.com/nextcloud-libraries/nextcloud-vue/blob/v9.13.1/src/components/NcTextArea/NcTextArea.vue
- nextcloud-vue/package.json at main · nextcloud-libraries/nextcloud-vue · GitHub — https://github.com/nextcloud-libraries/nextcloud-vue/blob/main/package.json
- @nextcloud/vue - npm — https://www.npmjs.com/package/%40nextcloud/vue?activeTab=versions
- Releases · nextcloud-libraries/nextcloud-vue · GitHub — https://github.com/nextcloud-libraries/nextcloud-vue/releases
- …and 5 more

Script executed:

set -o pipefail
printf '%s\n' '--- Nextcloud 35 AllConfig ---'
curl -fsSL --max-time 20 https://raw.githubusercontent.com/nextcloud/server/stable35/lib/private/AllConfig.php | nl -ba | sed -n '/function setUserValue(/,+43p'
printf '%s\n' '--- Nextcloud 35 UserConfig relevant methods ---'
curl -fsSL --max-time 20 https://raw.githubusercontent.com/nextcloud/server/stable35/lib/private/Config/UserConfig.php | nl -ba | grep -n -E 'setValueMixed|setTypedValue|configvalue|function setValue' 
printf '%s\n' '--- lockfile entry ---'
rg -n -A 8 -F '"node_modules/@nextcloud/vue": {' -- package-lock.json

Repository: nextcloud/assistant

Length of output: 4243


Web query:

Nextcloud server preferences configvalue column text type length oc_preferences schema 35 user config setValueMixed

Result:

Sources consulted:
- nextcloud-server-35.0.0.tar.gz: .../UserConfig.php | Fossies — https://fossies.org/linux/www/nextcloud-server-35.0.0.tar.gz/server-35.0.0/lib/private/Config/UserConfig.php?M=1988
- UserConfig — Nextcloud 35 Developer Manual — https://docs.nextcloud.com/server/stable/developer_manual/digging_deeper/config/userconfig.html
- OCS user preferences API — Nextcloud 35 Developer Manual — https://docs.nextcloud.com/server/stable/developer_manual/client_apis/OCS/ocs-user-preferences-api.html
- server/lib/private/AllConfig.php at master · nextcloud/server · GitHub — https://github.com/nextcloud/server/blob/master/lib/private/AllConfig.php
- …and 74 more

Script executed:

set -o pipefail
curl -fsSL --max-time 20 https://raw.githubusercontent.com/nextcloud/server/stable35/lib/private/Config/UserConfig.php | nl -ba | sed -n '895,935p;1120,1320p'
printf '\n--- initial schema preferences ---\n'
curl -fsSL --max-time 20 https://raw.githubusercontent.com/nextcloud/server/stable35/core/Database/InstallStructure.php | nl -ba | grep -n -C 12 -F "'preferences'"

Repository: nextcloud/assistant

Length of output: 10269


Align the textarea limit with the server’s character count.

When instructions contain supplementary Unicode characters, the textarea can stop input before the server’s 4,000-character limit. For example, it cannot accept 3,000 emoji that the server permits. Validate using the same character count on both sides.

:label="t('assistant', 'Custom instructions')"
:maxlength="state.chat_personal_instructions_max_length"
resize="vertical"
@update:model-value="delayedValueUpdate(state.chat_personal_instructions, 'chat_personal_instructions')" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Data Integrity & Integration · Quick win

Supported by static analysis

Script executed:

rg -n 'function delay|const delay|export.*delay|beforeUnmount|unmounted|beforeunload|optionsToSave' src/utils.js src/components/PersonalSettings.vue src/components/AdminSettings.vue

Repository: nextcloud/assistant

Length of output: 789


Script executed:

git diff 9e9a064a37de8ef5a55711e821a7ce0b11f34ea7 9999d58647a9b86e75af314fa1605f330f2018eb -- src/components/PersonalSettings.vue lib/Controller/ConfigController.php lib/Service/ChatService.php lib/Settings/Personal.php
nl -ba src/utils.js | sed -n '1,100p'
nl -ba src/components/PersonalSettings.vue | sed -n '1,250p'
nl -ba lib/Controller/ConfigController.php | sed -n '1,200p'
nl -ba lib/Service/ChatService.php | sed -n '1,200p'
nl -ba lib/Settings/Personal.php | sed -n '1,150p'
rg -n -F -e 'beforeunload' -e 'pagehide' -e 'visibilitychange' -e 'chat_personal_instructions' -e 'delayedValueUpdate' -- src lib tests

Repository: nextcloud/assistant

Length of output: 43049


Web query:

HTML standard closing document tab timers task execution stop document unload setTimeout

Result:

Sources consulted:
- HTML Standard — https://html.spec.whatwg.org/multipage/document-lifecycle.html
- HTML Standard — https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html
- HTML Standard, Edition for Web Developers — https://html.spec.whatwg.org/dev/browsing-the-web.html
- …and 18 more

Script executed:

rg -n -F -e 'PersonalSettings' -e 'personalSettings' -e 'beforeunload' -e 'pagehide' -e 'visibilitychange' -e 'optionsToSave' -- src templates appinfo lib tests
rg -n -F -e 'MAX_CHAT_PERSONAL_INSTRUCTIONS_LENGTH' -e "config#setConfig" -e "'/config'" -- lib appinfo
rg --files src templates | rg -i 'personal|settings'

Repository: nextcloud/assistant

Length of output: 1891


Protect pending instruction edits when leaving the page.

If the user closes the tab within two seconds of editing, the save may never run. New conversations then use the previous instructions. Show the save state and warn users before they leave with unsaved changes.

@marcelklehr
marcelklehr merged commit 910b302 into main Nov 9, 2026
16 checks passed
@marcelklehr
marcelklehr deleted the feat/user-provided-system-prompt branch November 9, 2026 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants