-
Notifications
You must be signed in to change notification settings - Fork 11.6k
feat: 渠道 System Prompt 支持变量替换 {model_name} {site_name} {site_url} #6119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| name: Build Docker image to GHCR | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| branch: | ||
| description: "要构建的分支名" | ||
| required: true | ||
| type: string | ||
| default: "feat/system-prompt-variables" | ||
|
|
||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| steps: | ||
| - name: Check out branch | ||
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | ||
| with: | ||
| ref: ${{ inputs.branch }} | ||
|
|
||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f | ||
|
|
||
| - name: Log in to GitHub Container Registry | ||
| uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Resolve image tag | ||
| id: tag | ||
| run: | | ||
| REPO=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') | ||
| TAG=$(echo "${{ inputs.branch }}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_.-]/-/g') | ||
|
Comment on lines
+37
to
+38
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win Script injection via unescaped
🔒 Proposed fix - name: Resolve image tag
id: tag
+ env:
+ BRANCH: ${{ inputs.branch }}
run: |
REPO=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
- TAG=$(echo "${{ inputs.branch }}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_.-]/-/g')
+ TAG=$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_.-]/-/g')
echo "repo=$REPO" >> "$GITHUB_OUTPUT"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "Building image: ghcr.io/${REPO}:$TAG"🧰 Tools🪛 zizmor (1.26.1)[error] 38-38: code injection via template expansion (template-injection): may expand into attacker-controllable code (template-injection) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| echo "repo=$REPO" >> "$GITHUB_OUTPUT" | ||
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | ||
| echo "Building image: ghcr.io/${REPO}:$TAG" | ||
|
|
||
| - name: Build & push | ||
| uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 | ||
| with: | ||
| context: . | ||
| push: true | ||
| tags: | | ||
| ghcr.io/${{ steps.tag.outputs.repo }}:${{ steps.tag.outputs.tag }} | ||
| cache-from: type=gha | ||
| cache-to: type=gha,mode=max | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -96,27 +96,28 @@ func GeminiHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *typ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| adaptor.Init(info) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if info.ChannelSetting.SystemPrompt != "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| systemPrompt := helper.ApplyChannelSystemPromptVariables(info.ChannelSetting.SystemPrompt, info) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if request.SystemInstructions == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.SystemInstructions = &dto.GeminiChatContent{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Parts: []dto.GeminiPart{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {Text: info.ChannelSetting.SystemPrompt}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {Text: systemPrompt}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if len(request.SystemInstructions.Parts) == 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.SystemInstructions.Parts = []dto.GeminiPart{{Text: info.ChannelSetting.SystemPrompt}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.SystemInstructions.Parts = []dto.GeminiPart{{Text: systemPrompt}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if info.ChannelSetting.SystemPromptOverride { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| common.SetContextKey(c, constant.ContextKeySystemPromptOverride, true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| merged := false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for i := range request.SystemInstructions.Parts { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if request.SystemInstructions.Parts[i].Text == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.SystemInstructions.Parts[i].Text = info.ChannelSetting.SystemPrompt + "\n" + request.SystemInstructions.Parts[i].Text | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.SystemInstructions.Parts[i].Text = systemPrompt + "\n" + request.SystemInstructions.Parts[i].Text | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| merged = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !merged { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.SystemInstructions.Parts = append([]dto.GeminiPart{{Text: info.ChannelSetting.SystemPrompt}}, request.SystemInstructions.Parts...) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.SystemInstructions.Parts = append([]dto.GeminiPart{{Text: systemPrompt}}, request.SystemInstructions.Parts...) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+99
to
+120
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
The thinking adapter at lines 73–89 can modify The system prompt expansion block and the thinking adapter block are independent (one modifies ♻️ Proposed fix: move system prompt expansion before thinking adapter adaptor.Init(info)
+ if info.ChannelSetting.SystemPrompt != "" {
+ systemPrompt := helper.ApplyChannelSystemPromptVariables(info.ChannelSetting.SystemPrompt, info)
+ if request.SystemInstructions == nil {
+ request.SystemInstructions = &dto.GeminiChatContent{
+ Parts: []dto.GeminiPart{
+ {Text: systemPrompt},
+ },
+ }
+ } else if len(request.SystemInstructions.Parts) == 0 {
+ request.SystemInstructions.Parts = []dto.GeminiPart{{Text: systemPrompt}}
+ } else if info.ChannelSetting.SystemPromptOverride {
+ common.SetContextKey(c, constant.ContextKeySystemPromptOverride, true)
+ merged := false
+ for i := range request.SystemInstructions.Parts {
+ if request.SystemInstructions.Parts[i].Text == "" {
+ continue
+ }
+ request.SystemInstructions.Parts[i].Text = systemPrompt + "\n" + request.SystemInstructions.Parts[i].Text
+ merged = true
+ break
+ }
+ if !merged {
+ request.SystemInstructions.Parts = append([]dto.GeminiPart{{Text: systemPrompt}}, request.SystemInstructions.Parts...)
+ }
+ }
+ }
+
if model_setting.GetGeminiSettings().ThinkingAdapterEnabled {
if isNoThinkingRequest(request) {
// check is thinking
if !strings.Contains(info.OriginModelName, "-nothinking") {
// try to get no thinking model price
noThinkingModelName := info.OriginModelName + "-nothinking"
containPrice := helper.HasModelBillingConfig(noThinkingModelName)
if containPrice {
info.OriginModelName = noThinkingModelName
info.UpstreamModelName = noThinkingModelName
}
}
}
if request.GenerationConfig.ThinkingConfig == nil {
gemini.ThinkingAdaptor(request, info)
}
}
- if info.ChannelSetting.SystemPrompt != "" {
- systemPrompt := helper.ApplyChannelSystemPromptVariables(info.ChannelSetting.SystemPrompt, info)
- ...
- }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package helper | ||
|
|
||
| import ( | ||
| "strings" | ||
|
|
||
| "github.com/QuantumNous/new-api/common" | ||
| relaycommon "github.com/QuantumNous/new-api/relay/common" | ||
| "github.com/QuantumNous/new-api/setting/system_setting" | ||
| ) | ||
|
|
||
| func ApplyChannelSystemPromptVariables(systemPrompt string, info *relaycommon.RelayInfo) string { | ||
| if systemPrompt == "" { | ||
| return systemPrompt | ||
| } | ||
| result := systemPrompt | ||
| result = strings.ReplaceAll(result, "{site_name}", common.SystemName) | ||
| result = strings.ReplaceAll(result, "{site_url}", system_setting.ServerAddress) | ||
| if info != nil && info.OriginModelName != "" { | ||
| result = strings.ReplaceAll(result, "{model_name}", info.OriginModelName) | ||
| } | ||
| return result | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Checkout persists GitHub token in
.git/config.actions/checkoutdefaults topersist-credentials: true, which writes theGITHUB_TOKENinto the local git config. During a Docker build, untrustedRUNsteps or a malicious Dockerfile could read and exfiltrate it. Setpersist-credentials: falsesince this job only needs the source tree, not git push access.🔒 Proposed fix
- name: Check out branch uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: ref: ${{ inputs.branch }} + persist-credentials: false📝 Committable suggestion
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 19-22: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
🤖 Prompt for AI Agents
Source: Linters/SAST tools