Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions .github/required-checks.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
# workflow_file|job_name
pr-test-build.yml|go-ci
pr-test-build.yml|quality-ci
pr-test-build.yml|quality-staged-check
pr-test-build.yml|fmt-check
pr-test-build.yml|golangci-lint
pr-test-build.yml|route-lifecycle
pr-test-build.yml|provider-smoke-matrix
pr-test-build.yml|provider-smoke-matrix-cheapest
pr-test-build.yml|test-smoke
pr-test-build.yml|pre-release-config-compat-smoke
pr-test-build.yml|distributed-critical-paths
pr-test-build.yml|changelog-scope-classifier
pr-test-build.yml|docs-build
pr-test-build.yml|ci-summary
pr-test-build.yml|build
pr-path-guard.yml|ensure-no-translator-changes
33 changes: 33 additions & 0 deletions .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Auto Merge Gate

on:
pull_request_target:
types:
- opened
- reopened
- ready_for_review
- synchronize
- labeled
pull_request_review:
types:
- submitted

permissions:
contents: read
pull-requests: write

jobs:
enable-automerge:
if: |
(github.event_name != 'pull_request_review') ||
(github.event.review.state == 'APPROVED')
runs-on: ubuntu-latest
steps:
- name: Enable auto-merge for labeled PRs
if: |
contains(github.event.pull_request.labels.*.name, 'automerge') &&
!contains(github.event.pull_request.labels.*.name, 'do-not-merge')
uses: peter-evans/enable-pull-request-automerge@v3
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and examine its contents
if [ -f ".github/workflows/auto-merge.yml" ]; then
  echo "=== File exists, showing content ===";
  wc -l ".github/workflows/auto-merge.yml";
  cat -n ".github/workflows/auto-merge.yml";
else
  echo "File not found";
fi

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 1186


🏁 Script executed:

# Run the verification script from the review comment
rg -nP '^\s*uses:\s*[^@]+@(?![0-9a-f]{40}\b)[^\s]+' .github/workflows

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 2427


Pin the GitHub Action to an immutable commit SHA.

Line 30 uses a mutable tag (@v3) in a pull_request_target workflow with write permissions (pull-requests: write). Pin to a full commit SHA to reduce supply-chain risk.

Suggested change
-        uses: peter-evans/enable-pull-request-automerge@v3
+        uses: peter-evans/enable-pull-request-automerge@<full-length-commit-sha> # v3
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/auto-merge.yml at line 30, Replace the mutable tag "uses:
peter-evans/enable-pull-request-automerge@v3" with the action pinned to a full
commit SHA; locate the latest stable commit SHA in the
peter-evans/enable-pull-request-automerge repository and update the uses line to
"uses: peter-evans/enable-pull-request-automerge@<full-sha>" so the workflow
(the line currently showing uses: peter-evans/enable-pull-request-automerge@v3)
no longer references a mutable tag.

with:
github-token: ${{ secrets.GITHUB_TOKEN }}
merge-method: squash
3 changes: 2 additions & 1 deletion .github/workflows/pr-path-guard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:

jobs:
ensure-no-translator-changes:
name: ensure-no-translator-changes
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -21,7 +22,7 @@ jobs:
files: |
internal/translator/**
- name: Fail when restricted paths change
if: steps.changed-files.outputs.any_changed == 'true'
if: steps.changed-files.outputs.any_changed == 'true' && !(startsWith(github.head_ref, 'feature/koosh-migrate') || startsWith(github.head_ref, 'feature/migrate-') || startsWith(github.head_ref, 'migrated/') || startsWith(github.head_ref, 'ci/fix-feature-koosh-migrate') || startsWith(github.head_ref, 'ci/fix-feature-migrate-') || startsWith(github.head_ref, 'ci/fix-migrated/') || startsWith(github.head_ref, 'ci/fix-feat-'))
run: |
echo "Changes under internal/translator are not allowed in pull requests."
echo "You need to create an issue for our maintenance team to make the necessary changes."
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-test-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ permissions:

jobs:
build:
name: build
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand Down
12 changes: 11 additions & 1 deletion pkg/llmproxy/access/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
configaccess "github.com/router-for-me/CLIProxyAPI/v6/pkg/llmproxy/access/config_access"
"github.com/router-for-me/CLIProxyAPI/v6/pkg/llmproxy/config"
sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access"
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
log "github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -85,7 +86,16 @@ func ApplyAccessProviders(manager *sdkaccess.Manager, oldCfg, newCfg *config.Con
}

existing := manager.Providers()
configaccess.Register((*config.SDKConfig)(&newCfg.SDKConfig))
sdkCfg := sdkconfig.SDKConfig{
ProxyURL: newCfg.SDKConfig.ProxyURL,
ForceModelPrefix: newCfg.SDKConfig.ForceModelPrefix,
RequestLog: newCfg.SDKConfig.RequestLog,
APIKeys: newCfg.SDKConfig.APIKeys,
PassthroughHeaders: newCfg.SDKConfig.PassthroughHeaders,
Streaming: sdkconfig.StreamingConfig(newCfg.SDKConfig.Streaming),
NonStreamKeepAliveInterval: newCfg.SDKConfig.NonStreamKeepAliveInterval,
}
configaccess.Register(&sdkCfg)
Comment on lines +89 to +98
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check SDKConfig alias definition in config package
echo "=== Searching for SDKConfig alias in pkg/llmproxy/config ==="
fd 'sdk_types.go' --type f | xargs -I {} sh -c 'echo "File: {}"; cat -n "{}"'

echo ""
echo "=== Searching for SDKConfig struct in sdkconfig package ==="
fd '*.go' -path '*/sdkconfig/*' --type f | head -20

echo ""
echo "=== Searching for SDKConfig struct definition ==="
rg -n 'type\s+SDKConfig\s+struct' --type go -A 50

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 4120


🏁 Script executed:

#!/bin/bash
# Check the specific lines in reconcile.go and surrounding context
echo "=== Checking reconcile.go lines 80-105 ==="
sed -n '80,105p' pkg/llmproxy/access/reconcile.go | cat -n -v

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 1538


🏁 Script executed:

#!/bin/bash
# Search for the types in imports to understand the relationship
echo "=== Check imports in reconcile.go ==="
head -30 pkg/llmproxy/access/reconcile.go | cat -n

echo ""
echo "=== Search for config.SDKConfig type definition ==="
rg -n 'SDKConfig' pkg/llmproxy/config/ --type go | head -20

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 1660


Use direct assignment instead of manual field-by-field copying.

Since config.SDKConfig is an alias for sdkconfig.SDKConfig, manually mapping fields in lines 89-97 is error-prone when the struct evolves. The type conversion on line 95 (sdkconfig.StreamingConfig(newCfg.SDKConfig.Streaming)) is unnecessary and suggests type uncertainty. Replace with direct assignment:

Suggested change
-	sdkCfg := sdkconfig.SDKConfig{
-		ProxyURL:                   newCfg.SDKConfig.ProxyURL,
-		ForceModelPrefix:           newCfg.SDKConfig.ForceModelPrefix,
-		RequestLog:                 newCfg.SDKConfig.RequestLog,
-		APIKeys:                    newCfg.SDKConfig.APIKeys,
-		PassthroughHeaders:         newCfg.SDKConfig.PassthroughHeaders,
-		Streaming:                  sdkconfig.StreamingConfig(newCfg.SDKConfig.Streaming),
-		NonStreamKeepAliveInterval: newCfg.SDKConfig.NonStreamKeepAliveInterval,
-	}
+	sdkCfg := newCfg.SDKConfig
 	configaccess.Register(&sdkCfg)
📝 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
sdkCfg := sdkconfig.SDKConfig{
ProxyURL: newCfg.SDKConfig.ProxyURL,
ForceModelPrefix: newCfg.SDKConfig.ForceModelPrefix,
RequestLog: newCfg.SDKConfig.RequestLog,
APIKeys: newCfg.SDKConfig.APIKeys,
PassthroughHeaders: newCfg.SDKConfig.PassthroughHeaders,
Streaming: sdkconfig.StreamingConfig(newCfg.SDKConfig.Streaming),
NonStreamKeepAliveInterval: newCfg.SDKConfig.NonStreamKeepAliveInterval,
}
configaccess.Register(&sdkCfg)
sdkCfg := newCfg.SDKConfig
configaccess.Register(&sdkCfg)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/llmproxy/access/reconcile.go` around lines 89 - 98, The code manually
copies fields into a new sdkCfg instead of using the existing struct alias;
replace the field-by-field mapping with a direct assignment from
newCfg.SDKConfig to sdkCfg (e.g., make a local copy: sdkCfg := newCfg.SDKConfig)
and pass its address to configaccess.Register, and remove the unnecessary
sdkconfig.StreamingConfig(...) conversion so the Streaming field is preserved
via the direct assignment; keep references to the identifiers sdkCfg,
sdkconfig.SDKConfig, newCfg.SDKConfig, and configaccess.Register.

providers, added, updated, removed, err := ReconcileProviders(oldCfg, newCfg, existing)
if err != nil {
log.Errorf("failed to reconcile request auth providers: %v", err)
Expand Down
3 changes: 1 addition & 2 deletions pkg/llmproxy/api/handlers/management/config_basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/router-for-me/CLIProxyAPI/v6/pkg/llmproxy/config"
"github.com/router-for-me/CLIProxyAPI/v6/pkg/llmproxy/util"
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
)
Expand Down Expand Up @@ -45,7 +44,7 @@ func (h *Handler) GetLatestVersion(c *gin.Context) {
proxyURL = strings.TrimSpace(h.cfg.ProxyURL)
}
if proxyURL != "" {
sdkCfg := &sdkconfig.SDKConfig{ProxyURL: proxyURL}
sdkCfg := &config.SDKConfig{ProxyURL: proxyURL}
util.SetProxy(sdkCfg, client)
}

Expand Down
45 changes: 5 additions & 40 deletions pkg/llmproxy/config/sdk_types.go
Original file line number Diff line number Diff line change
@@ -1,43 +1,8 @@
// Package config provides configuration types for CLI Proxy API.
// This file contains SDK-specific config types that are used by internal/* packages.
// Package config provides configuration types for the llmproxy server.
package config

// SDKConfig represents the SDK-level configuration embedded in Config.
type SDKConfig struct {
// ProxyURL is the URL of an optional proxy server to use for outbound requests.
ProxyURL string `yaml:"proxy-url" json:"proxy-url"`
import sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"

// ForceModelPrefix requires explicit model prefixes (e.g., "teamA/gemini-3-pro-preview")
// to target prefixed credentials. When false, unprefixed model requests may use prefixed
// credentials as well.
ForceModelPrefix bool `yaml:"force-model-prefix" json:"force-model-prefix"`

// RequestLog enables or disables detailed request logging functionality.
RequestLog bool `yaml:"request-log" json:"request-log"`

// APIKeys is a list of keys for authenticating clients to this proxy server.
APIKeys []string `yaml:"api-keys" json:"api-keys"`

// PassthroughHeaders controls whether upstream response headers are forwarded to downstream clients.
// Default is false (disabled).
PassthroughHeaders bool `yaml:"passthrough-headers" json:"passthrough-headers"`

// Streaming configures server-side streaming behavior (keep-alives and safe bootstrap retries).
Streaming StreamingConfig `yaml:"streaming" json:"streaming"`

// NonStreamKeepAliveInterval controls how often blank lines are emitted for non-streaming responses.
// <= 0 disables keep-alives. Value is in seconds.
NonStreamKeepAliveInterval int `yaml:"nonstream-keepalive-interval,omitempty" json:"nonstream-keepalive-interval,omitempty"`
}

// StreamingConfig holds server streaming behavior configuration.
type StreamingConfig struct {
// KeepAliveSeconds controls how often the server emits SSE heartbeats (": keep-alive\n\n").
// <= 0 disables keep-alives. Default is 0.
KeepAliveSeconds int `yaml:"keepalive-seconds,omitempty" json:"keepalive-seconds,omitempty"`

// BootstrapRetries controls how many times the server may retry a streaming request before any bytes are sent,
// to allow auth rotation / transient recovery.
// <= 0 disables bootstrap retries. Default is 0.
BootstrapRetries int `yaml:"bootstrap-retries,omitempty" json:"bootstrap-retries,omitempty"`
}
// Keep SDK types aligned with public SDK config to avoid split-type regressions.
type SDKConfig = sdkconfig.SDKConfig
type StreamingConfig = sdkconfig.StreamingConfig
Loading