Skip to content

Conversation

nekohy
Copy link
Contributor

@nekohy nekohy commented Sep 5, 2025

independent condition judgement file and interface, add pass,block,NewAPIInternalPromptToken condition.
It is used for the separate models priority for the future (:

Summary by CodeRabbit

  • New Features

    • Conditional parameter overrides with block and pass semantics.
    • Token-aware overrides that use prompt token counts to drive conditional logic.
    • Enhanced JSON condition evaluation: AND/OR logic, multiple comparison modes, negative-index support, and path rewriting.
  • Bug Fixes

    • More accurate error classification when overrides trigger a block vs. generic invalid-override errors.

Copy link
Contributor

coderabbitai bot commented Sep 5, 2025

Walkthrough

Refactors param-override to accept full RelayInfo, inject prompt-token metadata, and support conditional operations including block/pass. Adds a JSON condition evaluator, updates handlers to receive an isBlock flag from ApplyParamOverride, and introduces an error code for condition-triggered blocks.

Changes

Cohort / File(s) Summary of Changes
Param override engine
relay/common/override.go
ApplyParamOverride signature changed to ApplyParamOverride(jsonData []byte, info *RelayInfo) ([]byte, bool, error); adds InternalPromptTokensKey, NewAPIInternal; extends ParamOperation with To, Conditions, Logic, modes block/pass; injects internal token, normalizes negative indices, supports block/pass semantics, cleans up token, retains legacy fallback.
Condition evaluation module
relay/common/condition.go
New ConditionChecker and DefaultConditionChecker; adds ConditionOperation type and CheckConditions/CheckSingleCondition; implements JSON path resolution (gjson), negative-index handling, comparison modes (full/prefix/suffix/contains/gt/gte/lt/lte), inversion, and missing-key handling.
Relay handlers (call-site updates)
relay/claude_handler.go, relay/compatible_handler.go, relay/gemini_handler.go, relay/image_handler.go, relay/rerank_handler.go, relay/responses_handler.go
Call sites updated to jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info); introduce local isBlock; on override error: if isBlock return ErrorCodeContidionTriggerBlock, else ErrorCodeChannelParamOverrideInvalid.
Error codes
types/error.go
Adds ErrorCodeContidionTriggerBlock ErrorCode = "channel:condition_trigger_block" to ErrorCode constants.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Handler as Relay Handler
  participant Override as ApplyParamOverride
  participant Cond as ConditionChecker
  note over Handler,Override: Handler now passes full RelayInfo and receives isBlock flag
  Client->>Handler: Request (jsonData, info)
  Handler->>Override: ApplyParamOverride(jsonData, info)
  Override->>Override: inject NewAPIInternal (prompt tokens)
  Override->>Cond: CheckConditions (for ops with Conditions)
  Cond-->>Override: condition result / error
  Override->>Override: applyOperations (set/move/append/prepend/to/block/pass)
  alt block triggered
    Override-->>Handler: jsonData, isBlock=true, err
    Handler-->>Client: Error channel:condition_trigger_block
  else non-block error
    Override-->>Handler: jsonData, isBlock=false, err
    Handler-->>Client: Error channel:param_override_invalid
  else success
    Override-->>Handler: jsonData, isBlock=false, nil
    Handler-->>Client: Proceed with provider request
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • creamlike1024

Poem

I nibble through JSON, whisker-twitch delight,
I hide prompt tokens snug out of sight.
If conditions thump, I halt on the spot,
Else hop and rewrite each path that I spot.
A rabbit’s tweak — small hops, tidy byte. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title concisely summarizes the primary change of adding an independent condition judgment file and interface along with several new conditions, which aligns with the main objectives of this pull request.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c40e26 and a15fd7e.

📒 Files selected for processing (7)
  • relay/claude_handler.go (1 hunks)
  • relay/compatible_handler.go (1 hunks)
  • relay/gemini_handler.go (1 hunks)
  • relay/image_handler.go (1 hunks)
  • relay/rerank_handler.go (1 hunks)
  • relay/responses_handler.go (1 hunks)
  • types/error.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • relay/rerank_handler.go
  • types/error.go
🧰 Additional context used
🧬 Code graph analysis (5)
relay/claude_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (27-43)
types/error.go (3)
  • NewError (201-221)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (331-335)
relay/responses_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (27-43)
types/error.go (3)
  • NewError (201-221)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (331-335)
relay/gemini_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (27-43)
types/error.go (3)
  • NewError (201-221)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (331-335)
relay/compatible_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (27-43)
types/error.go (3)
  • NewError (201-221)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (331-335)
relay/image_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (27-43)
types/error.go (3)
  • NewError (201-221)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (331-335)

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
Contributor

@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: 4

Caution

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

⚠️ Outside diff range comments (1)
relay/gemini_handler.go (1)

231-242: Unify embeddings override path with ApplyParamOverride and handle block.

Embeddings still use the legacy merge; switch to the new flow and return 403 on block.

   // apply param override
   if len(info.ParamOverride) > 0 {
-    reqMap := make(map[string]interface{})
-    _ = common.Unmarshal(jsonData, &reqMap)
-    for key, value := range info.ParamOverride {
-      reqMap[key] = value
-    }
-    jsonData, err = common.Marshal(reqMap)
-    if err != nil {
-      return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
-    }
+    var isBlock bool
+    jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, info.PromptTokens)
+    if err != nil {
+      if isBlock {
+        return types.NewErrorWithStatusCode(err, types.ErrorCodeConditionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+      }
+      return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
+    }
   }
♻️ Duplicate comments (3)
relay/image_handler.go (2)

72-77: Return proper 4xx HTTP codes for block/invalid overrides

Align status codes with semantics (Forbidden/Bad Request) instead of default 500.

- return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
+ return types.NewErrorWithStatusCode(err, types.ErrorCodeContidionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
...
- return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
+ return types.NewErrorWithStatusCode(err, types.ErrorCodeChannelParamOverrideInvalid, http.StatusBadRequest, types.ErrOptionWithSkipRetry())

70-77: Also block when isBlock is true and err is nil

Mirror the defensive check to avoid proceeding on a silent block.

-                jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, info.PromptTokens)
-                if err != nil {
-                    if isBlock {
-                        return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
-                    }
-                    return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
-                }
+                jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, info.PromptTokens)
+                if isBlock && err == nil {
+                    return types.NewError(fmt.Errorf("blocked by condition"), types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
+                }
+                if err != nil {
+                    if isBlock {
+                        return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
+                    }
+                    return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
+                }
relay/responses_handler.go (1)

62-69: Guard against isBlock without error; use 4xx codes

Same concerns as other handlers: explicitly short-circuit on isBlock even if err is nil; prefer 403/400 for block/invalid override.

-            jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, info.PromptTokens)
-            if err != nil {
-                if isBlock {
-                    return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
-                }
-                return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
-            }
+            jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, info.PromptTokens)
+            if isBlock && err == nil {
+                return types.NewErrorWithStatusCode(fmt.Errorf("blocked by condition"), types.ErrorCodeContidionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+            }
+            if err != nil {
+                if isBlock {
+                    return types.NewErrorWithStatusCode(err, types.ErrorCodeContidionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+                }
+                return types.NewErrorWithStatusCode(err, types.ErrorCodeChannelParamOverrideInvalid, http.StatusBadRequest, types.ErrOptionWithSkipRetry())
+            }
🧹 Nitpick comments (16)
relay/common/condition.go (3)

26-33: Constrain Mode to known values (enum-like) to prevent config typos.

Using a dedicated type or constants reduces runtime errors from misspelled modes and improves IDE help.

Example additions (outside the shown range):

type ConditionMode string

const (
  ModeFull     ConditionMode = "full"
  ModePrefix   ConditionMode = "prefix"
  ModeSuffix   ConditionMode = "suffix"
  ModeContains ConditionMode = "contains"
  ModeGT       ConditionMode = "gt"
  ModeGTE      ConditionMode = "gte"
  ModeLT       ConditionMode = "lt"
  ModeLTE      ConditionMode = "lte"
)

Within this struct:

- Mode           string      `json:"mode"`             // full, prefix, suffix, contains, gt, gte, lt, lte
+ Mode           ConditionMode `json:"mode"`           // see ConditionMode constants

And where used:

// adjust call site
compareGjsonValues(value, targetValue, strings.ToLower(string(condition.Mode)))

54-82: Short-circuit evaluation and validate logic operand.

Avoid building a results slice; fail fast and error on unknown logic instead of silently defaulting to OR.

 func checkConditions(jsonStr string, conditions []ConditionOperation, logic string) (bool, error) {
   if len(conditions) == 0 {
     return true, nil // 没有条件,直接通过
   }
-  results := make([]bool, len(conditions))
-  for i, condition := range conditions {
-    result, err := checkSingleCondition(jsonStr, condition)
-    if err != nil {
-      return false, err
-    }
-    results[i] = result
-  }
-
-  if strings.ToUpper(logic) == "AND" {
-    for _, result := range results {
-      if !result {
-        return false, nil
-      }
-    }
-    return true, nil
-  } else {
-    for _, result := range results {
-      if result {
-        return true, nil
-      }
-    }
-    return false, nil
-  }
+  upper := strings.ToUpper(strings.TrimSpace(logic))
+  switch upper {
+  case "AND":
+    for _, cond := range conditions {
+      ok, err := checkSingleCondition(jsonStr, cond)
+      if err != nil {
+        return false, err
+      }
+      if !ok {
+        return false, nil
+      }
+    }
+    return true, nil
+  case "OR", "":
+    for _, cond := range conditions {
+      ok, err := checkSingleCondition(jsonStr, cond)
+      if err != nil {
+        return false, err
+      }
+      if ok {
+        return true, nil
+      }
+    }
+    return false, nil
+  default:
+    return false, fmt.Errorf("unsupported logic: %s (expected AND/OR)", logic)
+  }
 }

168-190: Use epsilon for float equality to avoid false negatives.

Direct float equality can be brittle; a tiny tolerance is safer.

 case gjson.Number:
-  return jsonValue.Num == targetValue.Num, nil
+  return math.Abs(jsonValue.Num-targetValue.Num) < 1e-9, nil

Add import (outside this range):

import "math"
relay/gemini_handler.go (1)

141-144: Avoid logging internal token metadata.

ApplyParamOverride may inject an internal key; scrub it before logging to prevent leaking internals in logs.

- logger.LogDebug(c, "Gemini request body: "+string(jsonData))
+ // redact internal keys before logging
+ var sanitized map[string]interface{}
+ if err := common.Unmarshal(jsonData, &sanitized); err == nil {
+   delete(sanitized, relaycommon.InternalPromptTokensKey)
+   if b, _ := common.Marshal(sanitized); b != nil {
+     logger.LogDebug(c, "Gemini request body: "+string(b))
+   } else {
+     logger.LogDebug(c, "Gemini request body: "+string(jsonData))
+   }
+ } else {
+   logger.LogDebug(c, "Gemini request body: "+string(jsonData))
+ }
relay/compatible_handler.go (3)

140-145: Return 4xx instead of default 500 for client/actionable errors

Both “block by condition” and “invalid param override” are client/actionable states; using NewError defaults to 500. Prefer explicit 4xx to reflect semantics and avoid noisy alerts.

Proposed tweak:

- return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
+ return types.NewErrorWithStatusCode(err, types.ErrorCodeContidionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
...
- return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
+ return types.NewErrorWithStatusCode(err, types.ErrorCodeChannelParamOverrideInvalid, http.StatusBadRequest, types.ErrOptionWithSkipRetry())

141-144: Typo in constant identifier “Contidion”

The exported identifier types.ErrorCodeContidionTriggerBlock contains a typo. The string value is correct, but the symbol name is error-prone.

Outside this file, consider in types/error.go:

// Preferred
const ErrorCodeConditionTriggerBlock ErrorCode = "channel:condition_trigger_block"

// Back-compat alias (deprecate later)
const ErrorCodeContidionTriggerBlock = ErrorCodeConditionTriggerBlock

Then update call sites gradually.


136-146: De-duplicate override-and-branch pattern across handlers

The same try-override/branch logic now appears in multiple handlers. A small helper will reduce drift and future bugs.

Example (outside this file):

// relay/common/override_helper.go
func ApplyOverrideOrBlock(jsonData []byte, info *relaycommon.RelayInfo) ([]byte, *types.NewAPIError) {
    if len(info.ParamOverride) == 0 {
        return jsonData, nil
    }
    jd, isBlock, err := relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, info.PromptTokens)
    if isBlock && err == nil {
        return nil, types.NewErrorWithStatusCode(fmt.Errorf("blocked by condition"), types.ErrorCodeConditionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
    }
    if err != nil {
        if isBlock {
            return nil, types.NewErrorWithStatusCode(err, types.ErrorCodeConditionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
        }
        return nil, types.NewErrorWithStatusCode(err, types.ErrorCodeChannelParamOverrideInvalid, http.StatusBadRequest, types.ErrOptionWithSkipRetry())
    }
    return jd, nil
}

Then call it from handlers.

relay/claude_handler.go (2)

91-97: Return a 4xx for condition-triggered blocks (avoid 500).

Condition-driven blocks are client/actionable states, not server faults. Set an explicit 403 (or 400) to prevent misclassification and retries.

-            if isBlock {
-                return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
-            }
+            if isBlock {
+                // 403 Forbidden better communicates condition-based denial
+                return types.NewErrorWithStatusCode(err, types.ErrorCodeContidionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+            }

91-97: Fix spelling in error code constant name ("Contidion" → "Condition").

The exported identifier has a typo, which propagates across call sites. Rename the constant and update references; the underlying string value can remain unchanged.

-// types/error.go
-ErrorCodeContidionTriggerBlock        ErrorCode = "channel:condition_trigger_block"
+// types/error.go
+ErrorCodeConditionTriggerBlock        ErrorCode = "channel:condition_trigger_block"
- return types.NewErrorWithStatusCode(err, types.ErrorCodeContidionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+ return types.NewErrorWithStatusCode(err, types.ErrorCodeConditionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
relay/rerank_handler.go (2)

62-67: Surface a 4xx for condition-blocks and keep retries skipped.

Mirror the Claude handler suggestion: send 403 (or 400) instead of falling back to default 500 semantics.

-            if isBlock {
-                return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
-            }
+            if isBlock {
+                return types.NewErrorWithStatusCode(err, types.ErrorCodeContidionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+            }

62-67: Align with corrected constant name if you accept the rename.

- return types.NewErrorWithStatusCode(err, types.ErrorCodeContidionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+ return types.NewErrorWithStatusCode(err, types.ErrorCodeConditionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
relay/common/override.go (5)

10-10: Internal key: consider a harder-to-collide name and document non-contract status.

To minimize accidental collisions with user fields, consider a more obviously reserved key (e.g., "__newapi_internal_prompt_tokens"). At minimum, add a comment stating it’s an internal, non-stable key.

-const InternalPromptTokensKey = "NewAPIInternalPromptTokens"
+// Internal implementation detail; not a public/contracted field.
+const InternalPromptTokensKey = "NewAPIInternalPromptTokens"

150-159: Clarify pass semantics (short-circuit) with a comment.

It currently returns immediately after deleting the internal key. Add a note so users don’t expect subsequent ops to run.

-        if op.Mode == "pass" {
+        if op.Mode == "pass" {
+            // Short-circuit: conditions satisfied → bypass remaining operations.
             // 移除添加的内部字段
             result, _ = sjson.Delete(result, InternalPromptTokensKey)
             return result, false, nil // 直接通过
         }

166-186: Improve error context to aid troubleshooting.

Include normalized paths in error messages.

-    default:
-        return "", false, fmt.Errorf("unknown operation: %s", op.Mode)
+    default:
+        return "", false, fmt.Errorf("unknown operation %q at path=%q from=%q to=%q", op.Mode, opPath, opFrom, opTo)
     }
     if err != nil {
-        return "", false, fmt.Errorf("operation %s failed: %v", op.Mode, err)
+        return "", false, fmt.Errorf("operation %s failed at path=%q from=%q to=%q: %v", op.Mode, opPath, opFrom, opTo, err)
     }

132-191: Add basic counters for observability (blocks/passes).

Expose metrics for block/pass events to ease debugging and alerting.

  • Track: condition_trigger_block_total, condition_trigger_pass_total
  • Labels: channel, model, operation, condition_logic

23-26: ApplyParamOverride signature update verified across all call sites

  • All handlers now pass info.PromptTokens and unpack the three‐value return (jsonData, isBlock, err).
  • Block error constant ErrorCodeContidionTriggerBlock is consistently referenced but its name contains a typo—rename it to ErrorCodeConditionTriggerBlock in types/error.go and update all usages.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3d0bf36 and 6a9763c.

📒 Files selected for processing (9)
  • relay/claude_handler.go (1 hunks)
  • relay/common/condition.go (1 hunks)
  • relay/common/override.go (3 hunks)
  • relay/compatible_handler.go (1 hunks)
  • relay/gemini_handler.go (1 hunks)
  • relay/image_handler.go (1 hunks)
  • relay/rerank_handler.go (1 hunks)
  • relay/responses_handler.go (1 hunks)
  • types/error.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
relay/claude_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (23-38)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
relay/gemini_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (23-38)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
relay/image_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (23-38)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
relay/rerank_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (23-38)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
relay/responses_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (23-38)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
relay/compatible_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (23-38)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
relay/common/override.go (1)
relay/common/condition.go (1)
  • CheckConditions (35-38)
🔇 Additional comments (4)
relay/common/condition.go (1)

84-111: Core evaluation flow looks solid.

Handling of missing keys with PassMissingKey and post-compare inversion is clear.

relay/compatible_handler.go (1)

138-145: Remove redundant block-without-error check applyOperations’s “block” path always returns a non-nil error alongside isBlock=true, so there is no scenario where isBlock==true and err==nil.

Likely an incorrect or invalid review comment.

relay/common/override.go (2)

35-38: Backward-compatibility fallback looks good.

Legacy map-based overrides continue to work and return isBlock=false, preserving prior behavior.


188-191: Cleanup after operations is correct.

The internal token field is removed at the end of normal processing.

Comment on lines +113 to +142
func processNegativeIndex(jsonStr string, path string) string {
re := regexp.MustCompile(`\.(-\d+)`)
matches := re.FindAllStringSubmatch(path, -1)

if len(matches) == 0 {
return path
}

result := path
for _, match := range matches {
negIndex := match[1]
index, _ := strconv.Atoi(negIndex)

arrayPath := strings.Split(path, negIndex)[0]
if strings.HasSuffix(arrayPath, ".") {
arrayPath = arrayPath[:len(arrayPath)-1]
}

array := gjson.Get(jsonStr, arrayPath)
if array.IsArray() {
length := len(array.Array())
actualIndex := length + index
if actualIndex >= 0 && actualIndex < length {
result = strings.Replace(result, match[0], "."+strconv.Itoa(actualIndex), 1)
}
}
}

return result
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect array path resolution for multiple negative indices (nested arrays).

arrayPath is derived from the original path for every match, so the second/next negative index may compute length from the wrong array. This mis-evaluates paths like a.-1.b.-1.

Apply a left-to-right replacement that recalculates arrayPath against the progressively updated path:

 func processNegativeIndex(jsonStr string, path string) string {
-  re := regexp.MustCompile(`\.(-\d+)`)
-  matches := re.FindAllStringSubmatch(path, -1)
-
-  if len(matches) == 0 {
-    return path
-  }
-
-  result := path
-  for _, match := range matches {
-    negIndex := match[1]
-    index, _ := strconv.Atoi(negIndex)
-
-    arrayPath := strings.Split(path, negIndex)[0]
-    if strings.HasSuffix(arrayPath, ".") {
-      arrayPath = arrayPath[:len(arrayPath)-1]
-    }
-
-    array := gjson.Get(jsonStr, arrayPath)
-    if array.IsArray() {
-      length := len(array.Array())
-      actualIndex := length + index
-      if actualIndex >= 0 && actualIndex < length {
-        result = strings.Replace(result, match[0], "."+strconv.Itoa(actualIndex), 1)
-      }
-    }
-  }
-
-  return result
+  re := regexp.MustCompile(`\.(-\d+)`)
+  result := path
+  for {
+    loc := re.FindStringSubmatchIndex(result)
+    if loc == nil {
+      break
+    }
+    negIndex := result[loc[2]:loc[3]] // e.g. "-1"
+    idx, _ := strconv.Atoi(negIndex)  // negative
+
+    arrayPath := result[:loc[0]]
+    if strings.HasSuffix(arrayPath, ".") {
+      arrayPath = arrayPath[:len(arrayPath)-1]
+    }
+    arr := gjson.Get(jsonStr, arrayPath)
+    if !arr.IsArray() {
+      // Not an array; stop to avoid infinite loop and let lookup fail naturally.
+      break
+    }
+    length := len(arr.Array())
+    actualIndex := length + idx
+    if actualIndex < 0 || actualIndex >= length {
+      // Out of range; leave as-is so gjson returns missing.
+      break
+    }
+    replacement := "." + strconv.Itoa(actualIndex)
+    // Replace this occurrence and continue scanning the updated path.
+    result = result[:loc[0]] + replacement + result[loc[1]:]
+  }
+  return result
 }
📝 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
func processNegativeIndex(jsonStr string, path string) string {
re := regexp.MustCompile(`\.(-\d+)`)
matches := re.FindAllStringSubmatch(path, -1)
if len(matches) == 0 {
return path
}
result := path
for _, match := range matches {
negIndex := match[1]
index, _ := strconv.Atoi(negIndex)
arrayPath := strings.Split(path, negIndex)[0]
if strings.HasSuffix(arrayPath, ".") {
arrayPath = arrayPath[:len(arrayPath)-1]
}
array := gjson.Get(jsonStr, arrayPath)
if array.IsArray() {
length := len(array.Array())
actualIndex := length + index
if actualIndex >= 0 && actualIndex < length {
result = strings.Replace(result, match[0], "."+strconv.Itoa(actualIndex), 1)
}
}
}
return result
}
func processNegativeIndex(jsonStr string, path string) string {
re := regexp.MustCompile(`\.(-\d+)`)
result := path
for {
loc := re.FindStringSubmatchIndex(result)
if loc == nil {
break
}
negIndex := result[loc[2]:loc[3]] // e.g. "-1"
idx, _ := strconv.Atoi(negIndex) // negative
arrayPath := result[:loc[0]]
if strings.HasSuffix(arrayPath, ".") {
arrayPath = arrayPath[:len(arrayPath)-1]
}
arr := gjson.Get(jsonStr, arrayPath)
if !arr.IsArray() {
// Not an array; stop to avoid infinite loop and let lookup fail naturally.
break
}
length := len(arr.Array())
actualIndex := length + idx
if actualIndex < 0 || actualIndex >= length {
// Out of range; leave as-is so gjson returns missing.
break
}
replacement := "." + strconv.Itoa(actualIndex)
// Replace this occurrence and continue scanning the updated path.
result = result[:loc[0]] + replacement + result[loc[1]:]
}
return result
}
🤖 Prompt for AI Agents
In relay/common/condition.go around lines 113 to 142, the function
processNegativeIndex computes arrayPath from the original path for every
negative-index match which breaks nested/multiple negative indices (e.g.,
a.-1.b.-1); change the loop to perform left-to-right replacements by deriving
arrayPath from the progressively updated result (use strings.Split(result,
negIndex)[0] instead of strings.Split(path, negIndex)[0]), update result
immediately after a successful replacement, and continue using the updated
result for subsequent matches (keeping the existing dot-trim and bounds checks).

Comment on lines 132 to 159
func applyOperations(jsonStr string, operations []ParamOperation, promptTokens int) (string, bool, error) {
// 添加PromptTokens到JSON中以便条件判断,强制覆盖
jsonStrWithTokens, err := sjson.Set(jsonStr, InternalPromptTokensKey, promptTokens)
if err != nil {
return "", false, fmt.Errorf("failed to add %s: %v", InternalPromptTokensKey, err)
}

result := jsonStrWithTokens
for _, op := range operations {
// 检查条件是否满足
ok, err := checkConditions(result, op.Conditions, op.Logic)
ok, err := CheckConditions(result, op.Conditions, op.Logic)
if err != nil {
return "", err
return "", false, err
}
if !ok {
continue // 条件不满足,跳过当前操作
}

// 处理block和pass操作
if op.Mode == "block" {
blockMessage := fmt.Sprintf("request blocked by conditions: %+v", op.Conditions)
return result, true, fmt.Errorf(blockMessage)
}
if op.Mode == "pass" {
// 移除添加的内部字段
result, _ = sjson.Delete(result, InternalPromptTokensKey)
return result, false, nil // 直接通过
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure internal token field is removed on the block path to prevent leaks.

If a future caller inspects the returned JSON on error, the internal field could leak. Delete it before returning on block.

-        if op.Mode == "block" {
-            blockMessage := fmt.Sprintf("request blocked by conditions: %+v", op.Conditions)
-            return result, true, fmt.Errorf(blockMessage)
-        }
+        if op.Mode == "block" {
+            blockMessage := fmt.Sprintf("request blocked by conditions: %+v", op.Conditions)
+            cleaned, _ := sjson.Delete(result, InternalPromptTokensKey)
+            return cleaned, true, fmt.Errorf(blockMessage)
+        }
🤖 Prompt for AI Agents
In relay/common/override.go around lines 132 to 159, the block path returns the
JSON containing the internal prompt-tokens field which may leak sensitive
internals to callers on error; before returning on op.Mode == "block" delete the
InternalPromptTokensKey from result (use sjson.Delete or equivalent), handle any
deletion error if needed, and then return the cleaned result with the same block
error and boolean flag so the internal field is not exposed.

Comment on lines 131 to 137
var isBlock bool
jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, info.PromptTokens)
if err != nil {
if isBlock {
return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
}
return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Return 403 and fix constant name when a block condition triggers.

Currently this returns a generic 500 via NewError and uses a misspelled constant. Treat blocks as a client/policy error.

-      var isBlock bool
-      jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, info.PromptTokens)
+      var isBlock bool
+      jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, info.PromptTokens)
       if err != nil {
-        if isBlock {
-          return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
-        }
+        if isBlock {
+          return types.NewErrorWithStatusCode(err, types.ErrorCodeConditionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+        }
         return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
       }

🏁 Script executed:

#!/bin/bash
# Verify all handlers use the corrected constant and set explicit status codes for block responses.
rg -nP 'ErrorCodeContidionTriggerBlock|ErrorCodeConditionTriggerBlock' -C2
rg -nP 'NewError\([^)]*ConditionTriggerBlock' -C2
rg -nP 'NewErrorWithStatusCode\([^)]*ConditionTriggerBlock' -C2

Length of output: 3075


Fix block condition error constant and return 403 status

  • Rename in types/error.go:
    - ErrorCodeContidionTriggerBlock ErrorCode = "channel:condition_trigger_block"
    + ErrorCodeConditionTriggerBlock ErrorCode = "channel:condition_trigger_block"
  • In each handler (relay/gemini_handler.go, responses_handler.go, rerank_handler.go, image_handler.go, compatible_handler.go, claude_handler.go), replace:
    - if isBlock {
    -   return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
    - }
    + if isBlock {
    +   return types.NewErrorWithStatusCode(err, types.ErrorCodeConditionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
    + }
  • Add import "net/http" to handlers that use NewErrorWithStatusCode.
📝 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
var isBlock bool
jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, info.PromptTokens)
if err != nil {
if isBlock {
return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
}
return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
// In relay/gemini_handler.go, add to your import block:
import (
// ... other imports ...
"net/http"
)
// …
var isBlock bool
jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, info.PromptTokens)
if err != nil {
if isBlock {
return types.NewErrorWithStatusCode(
err,
types.ErrorCodeConditionTriggerBlock,
http.StatusForbidden,
types.ErrOptionWithSkipRetry(),
)
}
return types.NewError(
err,
types.ErrorCodeChannelParamOverrideInvalid,
types.ErrOptionWithSkipRetry(),
)
}
🤖 Prompt for AI Agents
In relay/gemini_handler.go around lines 131 to 137, the code uses the misspelled
constant ErrorCodeContidionTriggerBlock and returns a generic NewError; rename
the constant to ErrorCodeConditionTriggerBlock and change the error return to
use types.NewErrorWithStatusCode(..., types.ErrorCodeConditionTriggerBlock,
http.StatusForbidden, types.ErrOptionWithSkipRetry()), and add import "net/http"
at the top of the file.

// channel error
ErrorCodeChannelNoAvailableKey ErrorCode = "channel:no_available_key"
ErrorCodeChannelParamOverrideInvalid ErrorCode = "channel:param_override_invalid"
ErrorCodeContidionTriggerBlock ErrorCode = "channel:condition_trigger_block"
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Fix typo in new error code constant (Condition).

Rename the exported identifier to avoid propagating a misspelling across the codebase and SDKs.

- ErrorCodeContidionTriggerBlock        ErrorCode = "channel:condition_trigger_block"
+ ErrorCodeConditionTriggerBlock        ErrorCode = "channel:condition_trigger_block"

Run to locate/update all call sites:


🏁 Script executed:

#!/bin/bash
rg -nP 'ErrorCodeContidionTriggerBlock|condition_trigger_block' -C2

Length of output: 2950


Rename ErrorCodeContidionTriggerBlock to ErrorCodeConditionTriggerBlock across the codebase

--- types/error.go
@@ 53
- ErrorCodeContidionTriggerBlock        ErrorCode = "channel:condition_trigger_block"
+ ErrorCodeConditionTriggerBlock        ErrorCode = "channel:condition_trigger_block"

Update every usage of the misspelled constant in these files (and any others):

  • relay/responses_handler.go
  • relay/rerank_handler.go
  • relay/image_handler.go
  • relay/gemini_handler.go
  • relay/claude_handler.go
  • relay/compatible_handler.go

Replace ErrorCodeContidionTriggerBlock with ErrorCodeConditionTriggerBlock, then rerun tests and regenerate any affected SDKs.

🤖 Prompt for AI Agents
In types/error.go around line 53, the constant name is misspelled as
ErrorCodeContidionTriggerBlock; rename it to ErrorCodeConditionTriggerBlock and
update every import-site usage across the codebase (specifically
relay/responses_handler.go, relay/rerank_handler.go, relay/image_handler.go,
relay/gemini_handler.go, relay/claude_handler.go, relay/compatible_handler.go)
to the new identifier, run the test suite to catch any remaining references, and
regenerate any affected SDKs or generated artifacts so symbols stay in sync.

Copy link
Contributor

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
relay/common/override.go (1)

150-153: Add tests for negative-index handling
processNegativeIndex is implemented in relay/common/condition.go (line 113) but isn’t covered by any tests. Add unit tests validating that op.Path, op.From and op.To correctly handle negative indices.

♻️ Duplicate comments (1)
relay/common/override.go (1)

154-158: Fix: internal tokens leak on block path; delete before returning.
Block currently returns result with NewAPIInternalPromptTokens. Clean it first to avoid leaking internals in error surfaces.

-        if op.Mode == "block" {
-            blockMessage := fmt.Sprintf("request blocked by conditions: %+v", op.Conditions)
-            return result, true, fmt.Errorf(blockMessage)
-        }
+        if op.Mode == "block" {
+            blockMessage := fmt.Sprintf("request blocked by conditions: %+v", op.Conditions)
+            cleaned, _ := sjson.Delete(result, InternalPromptTokensKey)
+            return cleaned, true, fmt.Errorf(blockMessage)
+        }
🧹 Nitpick comments (4)
relay/common/override.go (4)

14-14: Define mode string constants to prevent typos across call sites.
Consider centralizing allowed modes (delete, set, move, prepend, append, block, pass) as string consts to avoid stringly-typed bugs.

// Example (place near InternalPromptTokensKey)
const (
	ModeDelete  = "delete"
	ModeSet     = "set"
	ModeMove    = "move"
	ModePrepend = "prepend"
	ModeAppend  = "append"
	ModeBlock   = "block"
	ModePass    = "pass"
)

23-23: Nit: local parameter should be lowerCamelCase.
Rename PromptTokens -> promptTokens to follow Go naming conventions for locals.

-func ApplyParamOverride(jsonData []byte, paramOverride map[string]interface{}, PromptTokens int) ([]byte, bool, error) {
+func ApplyParamOverride(jsonData []byte, paramOverride map[string]interface{}, promptTokens int) ([]byte, bool, error) {
-    result, isBlock, err := applyOperations(string(jsonData), operations, PromptTokens)
+    result, isBlock, err := applyOperations(string(jsonData), operations, promptTokens)

156-156: Optional: avoid exposing policy details in public error text.
Consider a generic message (e.g., "request blocked by policy") and log details server-side to reduce information disclosure.

- blockMessage := fmt.Sprintf("request blocked by conditions: %+v", op.Conditions)
+ blockMessage := "request blocked by policy"

192-195: Nit: handle cleanup error or consistently ignore with a comment.
Currently ignoring sjson.Delete error. Either document intent or handle it to avoid silent failures.

-    result, _ = sjson.Delete(result, InternalPromptTokensKey)
+    // Best-effort cleanup; ignore error by design because token is internal-only.
+    result, _ = sjson.Delete(result, InternalPromptTokensKey)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6a9763c and 8fa1a88.

📒 Files selected for processing (1)
  • relay/common/override.go (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
relay/common/override.go (1)
relay/common/condition.go (1)
  • CheckConditions (35-38)
🔇 Additional comments (3)
relay/common/override.go (3)

10-10: LGTM: internal-prompt-tokens key introduction makes sense.
No issues with the exported const; aligns with later usage.


132-137: LGTM: safe token injection with error handling.
The internal token field is injected upfront with proper error propagation.


23-33: No action needed: all ApplyParamOverride call sites now pass three arguments and handle isBlock.

Copy link
Contributor

@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: 0

Caution

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

⚠️ Outside diff range comments (2)
relay/gemini_handler.go (1)

231-242: Embedding handler bypasses new conditional block logic

This manual override merge skips ApplyParamOverride, so pass/block conditions won’t run here. Align with other handlers and enforce policy.

-  // apply param override
-  if len(info.ParamOverride) > 0 {
-    reqMap := make(map[string]interface{})
-    _ = common.Unmarshal(jsonData, &reqMap)
-    for key, value := range info.ParamOverride {
-      reqMap[key] = value
-    }
-    jsonData, err = common.Marshal(reqMap)
-    if err != nil {
-      return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
-    }
-  }
+  // apply param override (supports pass/block conditions)
+  if len(info.ParamOverride) > 0 {
+    var isBlock bool
+    jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
+    if err != nil {
+      if isBlock {
+        return types.NewErrorWithStatusCode(err, types.ErrorCodeConditionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+      }
+      return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
+    }
+  }
relay/responses_handler.go (1)

44-50: Honor channel-level PassThroughBodyEnabled

Other handlers respect both global and channel settings. This endpoint ignores the channel flag, which can break per-channel pass-through.

-    if model_setting.GetGlobalSettings().PassThroughRequestEnabled {
+    if model_setting.GetGlobalSettings().PassThroughRequestEnabled || info.ChannelSetting.PassThroughBodyEnabled {
♻️ Duplicate comments (4)
relay/gemini_handler.go (1)

131-137: Return 403 and fix misspelled error code for block condition

Use the correct constant and return a 403 to signal a policy/block condition instead of a 500.

-      var isBlock bool
-      jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
+      var isBlock bool
+      jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
       if err != nil {
-        if isBlock {
-          return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
-        }
+        if isBlock {
+          return types.NewErrorWithStatusCode(err, types.ErrorCodeConditionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+        }
         return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
       }
relay/compatible_handler.go (1)

138-145: Use 403 and the corrected error code when a block condition triggers

Match the contract used elsewhere: explicit 403 + ErrorCodeConditionTriggerBlock.

-      var isBlock bool
-      jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
+      var isBlock bool
+      jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
       if err != nil {
-        if isBlock {
-          return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
-        }
+        if isBlock {
+          return types.NewErrorWithStatusCode(err, types.ErrorCodeConditionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+        }
         return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
       }
relay/claude_handler.go (1)

91-98: Fix constant name and return 403 for block condition

Align with the new condition system and correct the typo.

-      var isBlock bool
-      jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
+      var isBlock bool
+      jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
       if err != nil {
-        if isBlock {
-          return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
-        }
+        if isBlock {
+          return types.NewErrorWithStatusCode(err, types.ErrorCodeConditionTriggerBlock, http.StatusForbidden, types.ErrOptionWithSkipRetry())
+        }
         return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
       }
relay/common/override.go (1)

153-160: Prevent internal-field leak on block path

Remove NewAPIInternal before returning on block to avoid accidental exposure if callers inspect the returned JSON on error.

-        if op.Mode == "block" {
-            blockMessage := "request blocked by param override conditions"
-            return result, true, fmt.Errorf(blockMessage)
-        }
+        if op.Mode == "block" {
+            blockMessage := "request blocked by param override conditions"
+            cleaned, _ := sjson.Delete(result, InternalPromptTokensKey)
+            return cleaned, true, fmt.Errorf(blockMessage)
+        }
🧹 Nitpick comments (11)
relay/compatible_handler.go (1)

140-145: Optional: Param-override invalid should be a 400, not a 500

Consider signaling client error explicitly for malformed overrides.

-        return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
+        return types.NewErrorWithStatusCode(err, types.ErrorCodeChannelParamOverrideInvalid, http.StatusBadRequest, types.ErrOptionWithSkipRetry())
relay/image_handler.go (3)

55-57: Mark ConvertImageRequest errors as non-retryable

These are deterministic request-shape errors; align with other sites that use ErrOptionWithSkipRetry.

-            return types.NewError(err, types.ErrorCodeConvertRequestFailed)
+            return types.NewError(err, types.ErrorCodeConvertRequestFailed, types.ErrOptionWithSkipRetry())

68-78: Unnecessary guard before ApplyParamOverride

ApplyParamOverride already no-ops when ParamOverride is empty. Simplify and reduce branching.

-            // apply param override
-            if len(info.ParamOverride) > 0 {
-                var isBlock bool
-                jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
-                if err != nil {
-                    if isBlock {
-                        return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
-                    }
-                    return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
-                }
-            }
+            // apply param override (no-op when ParamOverride is empty)
+            var isBlock bool
+            jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
+            if err != nil {
+                if isBlock {
+                    return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
+                }
+                return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
+            }

73-75: Typo in error code identifier name

Identifier “ErrorCodeContidionTriggerBlock” has a typo. Consider renaming to ErrorCodeConditionTriggerBlock for clarity (string value can remain unchanged). Verify downstream references before renaming.

-                        return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
+                        return types.NewError(err, types.ErrorCodeConditionTriggerBlock, types.ErrOptionWithSkipRetry())
relay/rerank_handler.go (3)

28-30: Fix copy error message type

Message says ImageRequest but operation is for RerankRequest.

-        return types.NewError(fmt.Errorf("failed to copy request to ImageRequest: %w", err), types.ErrorCodeInvalidRequest, types.ErrOptionWithSkipRetry())
+        return types.NewError(fmt.Errorf("failed to copy request to RerankRequest: %w", err), types.ErrorCodeInvalidRequest, types.ErrOptionWithSkipRetry())

72-74: Use structured debug logging instead of println

For consistency with other handlers and to include request context.

-            println(fmt.Sprintf("Rerank request body: %s", string(jsonData)))
+            logger.LogDebug(c, fmt.Sprintf("Rerank request body: %s", string(jsonData)))

Add missing import:

 import (
   "bytes"
   "fmt"
   "io"
   "net/http"
   "one-api/common"
   "one-api/dto"
   relaycommon "one-api/relay/common"
   "one-api/relay/helper"
+  "one-api/logger"
   "one-api/service"
   "one-api/setting/model_setting"
   "one-api/types"

61-69: Unnecessary guard before ApplyParamOverride

Same simplification as other handlers.

-        // apply param override
-        if len(info.ParamOverride) > 0 {
-            var isBlock bool
-            jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
-            if err != nil {
-                if isBlock {
-                    return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
-                }
-                return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
-            }
-        }
+        // apply param override (no-op when ParamOverride is empty)
+        var isBlock bool
+        jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
+        if err != nil {
+            if isBlock {
+                return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
+            }
+            return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
+        }
relay/responses_handler.go (2)

72-75: Use structured debug logging instead of println

Align with logger usage elsewhere and include context.

-        println("requestBody: ", string(jsonData))
+        logger.LogDebug(c, fmt.Sprintf("responses request body: %s", string(jsonData)))

Add missing import:

 import (
   "bytes"
   "fmt"
   "io"
   "net/http"
   "one-api/common"
   "one-api/dto"
   relaycommon "one-api/relay/common"
   "one-api/relay/helper"
+  "one-api/logger"
   "one-api/service"
   "one-api/setting/model_setting"
   "one-api/types"
   "strings"

61-69: Unnecessary guard before ApplyParamOverride

Same rationale: ApplyParamOverride no-ops if empty.

-        if len(info.ParamOverride) > 0 {
-            var isBlock bool
-            jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
-            if err != nil {
-                if isBlock {
-                    return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
-                }
-                return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
-            }
-        }
+        var isBlock bool
+        jsonData, isBlock, err = relaycommon.ApplyParamOverride(jsonData, info)
+        if err != nil {
+            if isBlock {
+                return types.NewError(err, types.ErrorCodeContidionTriggerBlock, types.ErrOptionWithSkipRetry())
+            }
+            return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry())
+        }
relay/common/override.go (2)

27-43: Minor: rename param to idiomatic lowerCamel

Go style prefers relayInfo over Relayinfo; improves readability.

-func ApplyParamOverride(jsonData []byte, Relayinfo *RelayInfo) ([]byte, bool, error) {
-    if len(Relayinfo.ParamOverride) == 0 {
+func ApplyParamOverride(jsonData []byte, relayInfo *RelayInfo) ([]byte, bool, error) {
+    if len(relayInfo.ParamOverride) == 0 {
         return jsonData, false, nil
     }
-    if operations, ok := tryParseOperations(Relayinfo.ParamOverride); ok {
-        info := &NewAPIInternal{PromptTokens: Relayinfo.PromptTokens}
+    if operations, ok := tryParseOperations(relayInfo.ParamOverride); ok {
+        info := &NewAPIInternal{PromptTokens: relayInfo.PromptTokens}
         result, isBlock, err := applyOperations(string(jsonData), operations, info)
         return []byte(result), isBlock, err
     }
-    result, err := applyOperationsLegacy(jsonData, Relayinfo.ParamOverride)
+    result, err := applyOperationsLegacy(jsonData, relayInfo.ParamOverride)
     return result, false, err
 }

45-121: Stricter parsing when 'operations' key exists

If 'operations' exists but is malformed, silently falling back to legacy may apply unintended overrides. Consider returning an explicit error instead of fallback in this case.

Example approach:

  • tryParseOperations returns (ops, ok, parseErr)
  • If operations key present and parseErr != nil, propagate error; only fallback when key is absent.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8fa1a88 and 4c40e26.

📒 Files selected for processing (7)
  • relay/claude_handler.go (1 hunks)
  • relay/common/override.go (4 hunks)
  • relay/compatible_handler.go (1 hunks)
  • relay/gemini_handler.go (1 hunks)
  • relay/image_handler.go (1 hunks)
  • relay/rerank_handler.go (1 hunks)
  • relay/responses_handler.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
relay/image_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (27-43)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
relay/gemini_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (27-43)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
relay/rerank_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (27-43)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
relay/compatible_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (27-43)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
relay/common/override.go (2)
relay/common/relay_info.go (1)
  • RelayInfo (74-120)
relay/common/condition.go (1)
  • CheckConditions (35-38)
relay/claude_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (27-43)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
relay/responses_handler.go (2)
relay/common/override.go (1)
  • ApplyParamOverride (27-43)
types/error.go (3)
  • NewError (188-200)
  • ErrorCodeContidionTriggerBlock (53-53)
  • ErrOptionWithSkipRetry (297-301)
🔇 Additional comments (2)
relay/common/override.go (2)

10-14: Token injection shape looks good

Constant and struct for internal prompt-tokens are clear and scoped.


162-165: processNegativeIndex is already defined in relay/common/condition.go; no action required.

Likely an incorrect or invalid review comment.

@Calcium-Ion
Copy link
Collaborator

这个改动向下兼容吗

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.

2 participants