Skip to content

feat: model-level system prompt with multi-language support - #6117

Closed
Wu-jiyan wants to merge 1 commit into
QuantumNous:mainfrom
Wu-jiyan:feat/model-system-prompt
Closed

feat: model-level system prompt with multi-language support#6117
Wu-jiyan wants to merge 1 commit into
QuantumNous:mainfrom
Wu-jiyan:feat/model-system-prompt

Conversation

@Wu-jiyan

@Wu-jiyan Wu-jiyan commented Jul 11, 2026

Copy link
Copy Markdown

功能说明

在模型管理页面为每个模型添加自定义系统提示词配置,支持多语言。

改动内容

后端

  • models 表新增 system_prompt (TEXT) 和 system_prompt_mode (INT) 字段
  • 四种模式:禁用(0) / 注入(1) / 覆写(2) / 附加(3)
  • 在 OpenAI、Claude、Gemini 三种请求路径中,模型级 System Prompt 优先于渠道级执行
  • 多语言 JSON 格式:{"en":"...","zh":"..."},按用户语言自动匹配

前端

  • 模型编辑抽屉新增 System Prompt 配置区域(模式选择 + 内容编辑)
  • 模型列表 /models/metadata 新增 System Prompt 列,鼠标悬停查看内容

处理优先级

请求 → 模型级 System Prompt → 渠道级 System Prompt → 发送上游

向后兼容

不配置时(mode=0)无任何影响,与原来完全一致。

Summary by CodeRabbit

  • New Features
    • Added configurable model-level system prompts with multilingual support.
    • Added modes for injecting, overriding, or appending prompts across supported AI providers.
    • Added model management UI for configuring and viewing system prompts.
    • Added localized prompt resolution with language fallbacks.
  • Improvements
    • Model-level prompts are applied consistently alongside existing channel-level prompts.

- Add system_prompt and system_prompt_mode fields to models table
- Support four modes: disabled, inject, override, append
- Apply model-level system prompt before channel-level in OpenAI/Claude/Gemini handlers
- Multi-language JSON format: {"en":"...","zh":"..."}, auto-matched to user language
- Frontend: System Prompt section in model edit drawer
- Frontend: System Prompt column in /models/metadata table
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds persisted model-level system prompts with multilingual resolution and configurable injection modes. Prompts are initialized in request context, applied across OpenAI-compatible, Gemini, and Claude requests, and managed through the model administration UI.

Changes

Model system prompt

Layer / File(s) Summary
Model prompt persistence and form contract
constant/context_key.go, model/model_meta.go, web/default/src/features/models/types.ts, web/default/src/features/models/lib/model-form.ts
Adds persisted prompt fields, update handling, active-model retrieval, frontend types, validation, default mapping, and payload transformation.
Prompt resolution and format-specific application
relay/helper/system_prompt.go
Defines prompt modes, resolves multilingual JSON or plain text prompts, and applies Inject, Override, or Append behavior to OpenAI, Gemini, and Claude request formats.
Relay handler integration
middleware/distributor.go, relay/claude_handler.go, relay/compatible_handler.go, relay/gemini_handler.go
Loads model prompt settings during channel setup and applies them before existing channel-level prompt handling.
Model prompt management UI
web/default/src/features/models/components/drawers/model-mutate-drawer.tsx, web/default/src/features/models/components/models-columns.tsx
Adds prompt mode and content editing controls plus a model-list column showing mode labels and resolved prompt text.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ModelConfiguration
  participant ChannelSetup
  participant RelayHandler
  participant ProviderRequest

  ModelConfiguration->>ChannelSetup: save and load model prompt settings
  ChannelSetup->>RelayHandler: place prompt and mode in request context
  RelayHandler->>ProviderRequest: apply model prompt before channel prompt
  ProviderRequest-->>RelayHandler: continue provider-specific request flow
Loading

Possibly related PRs

Suggested reviewers: seefs001

Poem

I’m a rabbit with prompts tucked neat,
In JSON carrots, bilingual treats.
Inject, append, or override with care,
Claude, Gemini, OpenAI share.
The model drawer now knows the way—
Hop-hop, prompts are set today!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding model-level system prompt support with multilingual handling.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
relay/helper/system_prompt.go (1)

4-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use common.UnmarshalJsonStr here relay/helper/system_prompt.go:4,30 imports encoding/json and parses a string with json.Unmarshal directly; switch this to common.UnmarshalJsonStr so JSON handling stays consistent with the rest of the codebase.

🤖 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 `@relay/helper/system_prompt.go` at line 4, Replace the direct encoding/json
usage in the system prompt parsing flow with common.UnmarshalJsonStr, including
updating the import and the call site near the existing JSON unmarshal logic.
Preserve the current target and error-handling behavior while using the shared
helper.

Sources: Coding guidelines, Learnings

🤖 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 `@model/model_meta.go`:
- Around line 220-230: Update GetModelSystemPrompt to distinguish
gorm.ErrRecordNotFound from other database errors; retain the silent empty
fallback for missing records, but log unexpected query failures before returning
"", 0 using the project’s established logging mechanism.

In `@web/default/src/features/models/components/models-columns.tsx`:
- Around line 53-65: Update resolveSystemPromptText to match the backend
fallback order: prefer the “en” value, then “zh”, then the first object value,
and finally the original prompt. Keep the existing JSON parsing and plain-text
behavior unchanged.

---

Nitpick comments:
In `@relay/helper/system_prompt.go`:
- Line 4: Replace the direct encoding/json usage in the system prompt parsing
flow with common.UnmarshalJsonStr, including updating the import and the call
site near the existing JSON unmarshal logic. Preserve the current target and
error-handling behavior while using the shared helper.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7731406a-58c5-4984-9bba-939a0170e720

📥 Commits

Reviewing files that changed from the base of the PR and between e400619 and 4cc8c27.

📒 Files selected for processing (11)
  • constant/context_key.go
  • middleware/distributor.go
  • model/model_meta.go
  • relay/claude_handler.go
  • relay/compatible_handler.go
  • relay/gemini_handler.go
  • relay/helper/system_prompt.go
  • web/default/src/features/models/components/drawers/model-mutate-drawer.tsx
  • web/default/src/features/models/components/models-columns.tsx
  • web/default/src/features/models/lib/model-form.ts
  • web/default/src/features/models/types.ts

Comment thread model/model_meta.go
Comment on lines +220 to +230

// GetModelSystemPrompt 根据模型名查找系统提示词配置
// 返回 systemPrompt(可能为多语言JSON: {"en":"...", "zh":"..."})和 mode(0=禁用,1=注入,2=覆写,3=附加)
func GetModelSystemPrompt(modelName string) (systemPrompt string, mode int) {
var m Model
err := DB.Where("model_name = ? AND status = ?", modelName, 1).First(&m).Error
if err != nil || m.SystemPrompt == "" || m.SystemPromptMode == 0 {
return "", 0
}
return m.SystemPrompt, m.SystemPromptMode
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Consider logging unexpected database errors in GetModelSystemPrompt.

The function returns "", 0 on any err != nil, which is a safe fallback. However, non-ErrRecordNotFound errors (e.g., connection failures) are silently swallowed with no log trail, making it difficult to diagnose why a model prompt isn't applying in production.

🛡️ Suggested improvement
 func GetModelSystemPrompt(modelName string) (systemPrompt string, mode int) {
 	var m Model
 	err := DB.Where("model_name = ? AND status = ?", modelName, 1).First(&m).Error
-	if err != nil || m.SystemPrompt == "" || m.SystemPromptMode == 0 {
+	if err != nil {
+		if !errors.Is(err, gorm.ErrRecordNotFound) {
+			logger.SysError("failed to query model system prompt for %s: %s", modelName, err.Error())
+		}
+		return "", 0
+	}
+	if m.SystemPrompt == "" || m.SystemPromptMode == 0 {
 		return "", 0
 	}
 	return m.SystemPrompt, m.SystemPromptMode
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// GetModelSystemPrompt 根据模型名查找系统提示词配置
// 返回 systemPrompt(可能为多语言JSON: {"en":"...", "zh":"..."})和 mode(0=禁用,1=注入,2=覆写,3=附加)
func GetModelSystemPrompt(modelName string) (systemPrompt string, mode int) {
var m Model
err := DB.Where("model_name = ? AND status = ?", modelName, 1).First(&m).Error
if err != nil || m.SystemPrompt == "" || m.SystemPromptMode == 0 {
return "", 0
}
return m.SystemPrompt, m.SystemPromptMode
}
// GetModelSystemPrompt 根据模型名查找系统提示词配置
// 返回 systemPrompt(可能为多语言JSON: {"en":"...", "zh":"..."})和 mode(0=禁用,1=注入,2=覆写,3=附加)
func GetModelSystemPrompt(modelName string) (systemPrompt string, mode int) {
var m Model
err := DB.Where("model_name = ? AND status = ?", modelName, 1).First(&m).Error
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
logger.SysError("failed to query model system prompt for %s: %s", modelName, err.Error())
}
return "", 0
}
if m.SystemPrompt == "" || m.SystemPromptMode == 0 {
return "", 0
}
return m.SystemPrompt, m.SystemPromptMode
}
🤖 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 `@model/model_meta.go` around lines 220 - 230, Update GetModelSystemPrompt to
distinguish gorm.ErrRecordNotFound from other database errors; retain the silent
empty fallback for missing records, but log unexpected query failures before
returning "", 0 using the project’s established logging mechanism.

Comment on lines +53 to +65
// 解析多语言系统提示词文本,优先中文
function resolveSystemPromptText(prompt: string): string {
try {
const obj = JSON.parse(prompt)
if (typeof obj === 'object' && obj !== null) {
return obj['zh'] || obj['en'] || Object.values(obj)[0] || prompt
}
} catch {
// 不是 JSON,纯文本直接返回
}
return prompt
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Align resolveSystemPromptText language fallback order with the backend.

The frontend resolver prioritizes zhen → first, while the backend's resolveModelSystemPrompt (in relay/helper/system_prompt.go) falls back enzh → first. For a prompt like {"en":"Hello","zh":"你好"}, the tooltip shows Chinese but a non-Chinese user receives English at request time. Aligning the fallback order to enzh → first would make the tooltip preview more representative.

💚 Proposed fix
 function resolveSystemPromptText(prompt: string): string {
   try {
     const obj = JSON.parse(prompt)
     if (typeof obj === 'object' && obj !== null) {
-      return obj['zh'] || obj['en'] || Object.values(obj)[0] || prompt
+      return obj['en'] || obj['zh'] || Object.values(obj)[0] || prompt
     }
   } catch {
     // 不是 JSON,纯文本直接返回
   }
   return prompt
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 解析多语言系统提示词文本,优先中文
function resolveSystemPromptText(prompt: string): string {
try {
const obj = JSON.parse(prompt)
if (typeof obj === 'object' && obj !== null) {
return obj['zh'] || obj['en'] || Object.values(obj)[0] || prompt
}
} catch {
// 不是 JSON,纯文本直接返回
}
return prompt
}
// 解析多语言系统提示词文本,优先中文
function resolveSystemPromptText(prompt: string): string {
try {
const obj = JSON.parse(prompt)
if (typeof obj === 'object' && obj !== null) {
return obj['en'] || obj['zh'] || Object.values(obj)[0] || prompt
}
} catch {
// 不是 JSON,纯文本直接返回
}
return prompt
}
🤖 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 `@web/default/src/features/models/components/models-columns.tsx` around lines
53 - 65, Update resolveSystemPromptText to match the backend fallback order:
prefer the “en” value, then “zh”, then the first object value, and finally the
original prompt. Keep the existing JSON parsing and plain-text behavior
unchanged.

@Wu-jiyan Wu-jiyan closed this Jul 11, 2026
@Wu-jiyan
Wu-jiyan deleted the feat/model-system-prompt branch July 11, 2026 12:21
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.

1 participant