Skip to content

feat(anthropic): add configurable prompt count for system blocks#390

Open
Kindness-Kismet wants to merge 2 commits intotbphp:mainfrom
Kindness-Kismet:main
Open

feat(anthropic): add configurable prompt count for system blocks#390
Kindness-Kismet wants to merge 2 commits intotbphp:mainfrom
Kindness-Kismet:main

Conversation

@Kindness-Kismet
Copy link

@Kindness-Kismet Kindness-Kismet commented Mar 5, 2026

变更内容 / Change Content

自查清单 / Checklist

  • 本地测试可工作

Summary by CodeRabbit

  • New Features

    • Anthropic System Prompt Count (0–100) added — 0 preserves original prompts; >0 enforces system block count.
    • UI controls and English/Japanese/Chinese translations for the new setting.
  • Behavior

    • For Anthropic requests excess blocks are merged into the last text block and missing blocks are padded.
  • Tests

    • Unit tests added for normalization and request handling.

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

Walkthrough

The PR adds an Anthropic-specific system prompt count feature: new model fields and constants, request normalization and enforcement in the proxy, service-level normalization, handler plumbing, tests, frontend form/display/localizations, and integration of the normalization into the request pipeline.

Changes

Cohort / File(s) Summary
Models & Constants
internal/models/types.go
Added MaxAnthropicSystemPromptCount = 100 and AnthropicSystemPromptCount int on Group.
HTTP Handler
internal/handler/group_handler.go
Added AnthropicSystemPromptCount to create/update request structs, params, and GroupResponse; populated in newGroupResponse.
Service Logic
internal/services/group_service.go
Extended GroupCreateParams/GroupUpdateParams; added normalizeAnthropicSystemPromptCount(channelType, count) and updated create/update flows to normalize and persist the value.
Proxy Request Processing
internal/proxy/request_helpers.go, internal/proxy/server.go
Added applyAnthropicSystemPromptCount() to merge/pad Anthropic system prompt blocks and wired it into the proxy pipeline (applied to final body before stream detection).
Backend Tests
internal/proxy/request_helpers_test.go, internal/services/group_service_anthropic_test.go
New tests cover merging/padding behavior and normalization/clamping logic across channels and edge counts.
Frontend Form & Display
web/src/components/keys/GroupFormModal.vue, web/src/components/keys/GroupInfoCard.vue
Added anthropic_system_prompt_count to form data, normalization/clamping on input, conditional UI input (0–100), included in submit payload; display row for anthropic groups.
Frontend Types & Locales
web/src/types/models.ts, web/src/locales/en-US.ts, web/src/locales/ja-JP.ts, web/src/locales/zh-CN.ts
Added optional anthropic_system_prompt_count?: number to Group type and three localization keys (label, tooltip, placeholder) in en/ja/zh locales.

Sequence Diagram

sequenceDiagram
    actor Client
    participant Handler as "HTTP Handler"
    participant Service as "Group Service"
    participant Server as "Proxy Server"
    participant Upstream as "Upstream API"

    Client->>Handler: Create/Update Group (anthropic_system_prompt_count)
    Handler->>Service: CreateGroup/UpdateGroup(params with count)
    Service->>Service: normalizeAnthropicSystemPromptCount(channelType, count)
    Service-->>Handler: Group (with normalized count)
    Handler-->>Client: GroupResponse (includes count)

    Client->>Server: Forward request (uses Group)
    Server->>Server: applyAnthropicSystemPromptCount(body, group)
    Server->>Upstream: Modified request (merged/padded system prompts)
    Upstream-->>Server: Response
    Server-->>Client: Response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

enhancement

Poem

🐇 I hopped through code with gentle paws,
Counting prompts and fixing laws.
Merge the extras, pad the rest,
Anthropic prompts now pass the test. ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete and lacks required sections. It's missing the related issue number, detailed change content in English, and proper checklist items from the template. Complete the description by filling in the issue number (Closes #), providing detailed change content in English, and properly marking the checklist items with full documentation.
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding configurable prompt count for Anthropic system blocks, which is the primary feature across backend and frontend modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/proxy/server.go (1)

110-114: ⚠️ Potential issue | 🟠 Major

Use the transformed request body for stream detection.

After Line [110] mutates the payload, Line [112] still checks IsStreamRequest against bodyBytes. That can select the wrong execution path when payload-level overrides affect stream.

💡 Suggested fix
-	isStream := channelHandler.IsStreamRequest(c, bodyBytes)
+	isStream := channelHandler.IsStreamRequest(c, finalBodyBytes)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/proxy/server.go` around lines 110 - 114, The code calls
channelHandler.IsStreamRequest(c, bodyBytes) before passing the mutated payload
to ps.executeRequestWithRetry, which can mis-detect streaming when payload-level
changes were applied by applyAnthropicSystemPromptCount; change the
IsStreamRequest call to use finalBodyBytes (the transformed request) so stream
detection uses the actual payload sent to executeRequestWithRetry (update the
call site that invokes IsStreamRequest and ensure the isStream boolean is
derived from finalBodyBytes).
🧹 Nitpick comments (1)
internal/services/group_service_anthropic_test.go (1)

16-20: Add explicit boundary test cases (0 and exact max).

Coverage is good overall; adding exact boundary assertions will make normalization intent fully explicit.

✅ Suggested test additions
 	tests := []struct {
 		name        string
 		channelType string
 		count       int
 		want        int
 	}{
 		{name: "non-anthropic forced zero", channelType: "openai", count: 5, want: 0},
+		{name: "anthropic zero kept", channelType: "anthropic", count: 0, want: 0},
 		{name: "negative clamped to zero", channelType: "anthropic", count: -1, want: 0},
 		{name: "within range kept", channelType: "anthropic", count: 2, want: 2},
+		{name: "exact max kept", channelType: "anthropic", count: models.MaxAnthropicSystemPromptCount, want: models.MaxAnthropicSystemPromptCount},
 		{name: "upper bound clamped", channelType: "anthropic", count: models.MaxAnthropicSystemPromptCount + 1, want: models.MaxAnthropicSystemPromptCount},
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/services/group_service_anthropic_test.go` around lines 16 - 20, Add
two explicit boundary table-driven tests in the same test slice used in
group_service_anthropic_test.go: one where channelType == "anthropic" and count
== 0 expecting want == 0, and another where channelType == "anthropic" and count
== models.MaxAnthropicSystemPromptCount expecting want ==
models.MaxAnthropicSystemPromptCount; ensure the new entries follow the existing
test entry shape (fields name, channelType, count, want) so the normalization
logic in the test exercises the exact lower and upper boundaries.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/proxy/request_helpers.go`:
- Around line 53-58: systemBlocks is being polluted with raw strings (via
asAnyArray branch and setSystemText) which yields invalid Claude payloads;
normalize any string system blocks into structured text objects before appending
or returning. Update the asAnyArray handling in request_helpers.go so that when
a system item is a string you convert it to an object like {type: "text", text:
"<string>"} before appending to systemBlocks, and modify setSystemText to return
structured text objects instead of raw strings; ensure both single-string and
array-of-string paths produce only structured text objects so merged system
arrays contain no raw strings.

In `@internal/services/group_service.go`:
- Around line 990-993: The channel type check in
normalizeAnthropicSystemPromptCount is case-sensitive; update it to perform a
case-insensitive comparison (e.g., use strings.EqualFold(channelType,
"anthropic") or compare strings.ToLower(channelType) == "anthropic") so inputs
like "Anthropic" or "ANTHROPIC" are treated the same; remember to add the
strings import if using strings.EqualFold or ToLower.

In `@web/src/components/keys/GroupFormModal.vue`:
- Around line 342-343: The submit handler in GroupFormModal.vue currently only
enforces the lower bound for anthropic_system_prompt_count and can send values
>100; update the submit logic that builds the payload to clamp
anthropic_system_prompt_count to the 0–100 range (e.g., Math.min(Math.max(value,
0), 100)) wherever the form value is serialized (the lines setting
anthropic_system_prompt_count from props.group and the duplicate occurrences
around lines 531-534), so the payload uses the same upper-bound enforcement as
the UI/backend and avoid drift.

---

Outside diff comments:
In `@internal/proxy/server.go`:
- Around line 110-114: The code calls channelHandler.IsStreamRequest(c,
bodyBytes) before passing the mutated payload to ps.executeRequestWithRetry,
which can mis-detect streaming when payload-level changes were applied by
applyAnthropicSystemPromptCount; change the IsStreamRequest call to use
finalBodyBytes (the transformed request) so stream detection uses the actual
payload sent to executeRequestWithRetry (update the call site that invokes
IsStreamRequest and ensure the isStream boolean is derived from finalBodyBytes).

---

Nitpick comments:
In `@internal/services/group_service_anthropic_test.go`:
- Around line 16-20: Add two explicit boundary table-driven tests in the same
test slice used in group_service_anthropic_test.go: one where channelType ==
"anthropic" and count == 0 expecting want == 0, and another where channelType ==
"anthropic" and count == models.MaxAnthropicSystemPromptCount expecting want ==
models.MaxAnthropicSystemPromptCount; ensure the new entries follow the existing
test entry shape (fields name, channelType, count, want) so the normalization
logic in the test exercises the exact lower and upper boundaries.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5e53a3bd-5c87-434d-9417-95bad4bff1be

📥 Commits

Reviewing files that changed from the base of the PR and between b0e156b and 39d8def.

📒 Files selected for processing (13)
  • internal/handler/group_handler.go
  • internal/models/types.go
  • internal/proxy/request_helpers.go
  • internal/proxy/request_helpers_test.go
  • internal/proxy/server.go
  • internal/services/group_service.go
  • internal/services/group_service_anthropic_test.go
  • web/src/components/keys/GroupFormModal.vue
  • web/src/components/keys/GroupInfoCard.vue
  • web/src/locales/en-US.ts
  • web/src/locales/ja-JP.ts
  • web/src/locales/zh-CN.ts
  • web/src/types/models.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
web/src/components/keys/GroupFormModal.vue (1)

55-55: Use the existing max constant in the template to avoid drift.

Line 711 hardcodes 100 while Line 55 already defines MAX_ANTHROPIC_SYSTEM_PROMPT_COUNT. Reusing the constant keeps UI and normalization limits aligned.

♻️ Proposed refactor
               <n-input-number
                 v-model:value="formData.anthropic_system_prompt_count"
                 :min="0"
-                :max="100"
+                :max="MAX_ANTHROPIC_SYSTEM_PROMPT_COUNT"
                 :placeholder="t('keys.anthropicSystemPromptCountPlaceholder')"
                 style="width: 100%"
               />

Also applies to: 711-711

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/keys/GroupFormModal.vue` at line 55, The template
currently hardcodes the value 100 (at the UI max on Line 711) which can drift
from the constant defined as MAX_ANTHROPIC_SYSTEM_PROMPT_COUNT; update the
template to reference that constant instead of the literal so the UI max stays
in sync with the normalization limit—use the MAX_ANTHROPIC_SYSTEM_PROMPT_COUNT
symbol from the script section (exported/defined at top) in the template binding
or computed property that supplies the max value for the input control.
internal/proxy/request_helpers.go (1)

35-35: Consider trimming channel type in the Anthropics guard.

Line 35 uses case-insensitive matching but not whitespace normalization; trimming here would make the guard more defensive against legacy or malformed persisted values.

♻️ Proposed tweak
-func applyAnthropicSystemPromptCount(bodyBytes []byte, group *models.Group) []byte {
-	if group == nil || !strings.EqualFold(group.ChannelType, "anthropic") {
+func applyAnthropicSystemPromptCount(bodyBytes []byte, group *models.Group) []byte {
+	if group == nil || !strings.EqualFold(strings.TrimSpace(group.ChannelType), "anthropic") {
 		return bodyBytes
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/proxy/request_helpers.go` at line 35, The guard that checks the
group's channel type uses strings.EqualFold but doesn't normalize whitespace, so
update the condition around "group" and "group.ChannelType" to compare
strings.TrimSpace(group.ChannelType) with "anthropic" (still using
strings.EqualFold) to defensively handle leading/trailing whitespace; keep the
nil check for "group" and ensure you import or reference strings.TrimSpace
alongside strings.EqualFold where the comparison is done.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/proxy/request_helpers.go`:
- Line 35: The guard that checks the group's channel type uses strings.EqualFold
but doesn't normalize whitespace, so update the condition around "group" and
"group.ChannelType" to compare strings.TrimSpace(group.ChannelType) with
"anthropic" (still using strings.EqualFold) to defensively handle
leading/trailing whitespace; keep the nil check for "group" and ensure you
import or reference strings.TrimSpace alongside strings.EqualFold where the
comparison is done.

In `@web/src/components/keys/GroupFormModal.vue`:
- Line 55: The template currently hardcodes the value 100 (at the UI max on Line
711) which can drift from the constant defined as
MAX_ANTHROPIC_SYSTEM_PROMPT_COUNT; update the template to reference that
constant instead of the literal so the UI max stays in sync with the
normalization limit—use the MAX_ANTHROPIC_SYSTEM_PROMPT_COUNT symbol from the
script section (exported/defined at top) in the template binding or computed
property that supplies the max value for the input control.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dcf63ea3-5d5a-4580-be2c-007f6957b64d

📥 Commits

Reviewing files that changed from the base of the PR and between 39d8def and 6ff363f.

📒 Files selected for processing (6)
  • internal/proxy/request_helpers.go
  • internal/proxy/request_helpers_test.go
  • internal/proxy/server.go
  • internal/services/group_service.go
  • internal/services/group_service_anthropic_test.go
  • web/src/components/keys/GroupFormModal.vue
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/services/group_service_anthropic_test.go
  • internal/proxy/server.go
  • internal/proxy/request_helpers_test.go

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