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
17 changes: 3 additions & 14 deletions .github/required-checks.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
# 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
required-check-names-guard.yml|verify-required-check-names
codeql.yml|Analyze (Go)
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
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
merge-method: squash
1 change: 1 addition & 0 deletions .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 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
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
4 changes: 4 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ type Config struct {
// Nil means enabled (default behavior).
ResponsesWebsocketEnabled *bool `yaml:"responses-websocket-enabled,omitempty" json:"responses-websocket-enabled,omitempty"`

// ResponsesCompactEnabled gates the /v1/responses/compact route rollout.
// Nil means enabled (default behavior).
ResponsesCompactEnabled *bool `yaml:"responses-compact-enabled,omitempty" json:"responses-compact-enabled,omitempty"`

// GeminiKey defines Gemini API key configurations with optional routing overrides.
GeminiKey []GeminiKey `yaml:"gemini-api-key" json:"gemini-api-key"`

Expand Down
11 changes: 11 additions & 0 deletions internal/config/responses_compact_toggle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package config

// IsResponsesCompactEnabled reports whether /v1/responses/compact is enabled.
// Default is true when config or toggle is unset.
func (c *Config) IsResponsesCompactEnabled() bool {
if c == nil || c.ResponsesCompactEnabled == nil {
return true
}
return *c.ResponsesCompactEnabled
}

2 changes: 1 addition & 1 deletion pkg/llmproxy/access/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func ApplyAccessProviders(manager *sdkaccess.Manager, oldCfg, newCfg *config.Con
}

existing := manager.Providers()
configaccess.Register((*config.SDKConfig)(&newCfg.SDKConfig))
configaccess.Register(&newCfg.SDKConfig)
providers, added, updated, removed, err := ReconcileProviders(oldCfg, newCfg, existing)
if err != nil {
log.Errorf("failed to reconcile request auth providers: %v", err)
Expand Down
1 change: 1 addition & 0 deletions pkg/llmproxy/api/aliases.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var (
WithEngineConfigurator = api.WithEngineConfigurator
WithLocalManagementPassword = api.WithLocalManagementPassword
WithKeepAliveEndpoint = api.WithKeepAliveEndpoint
WithPostAuthHook = api.WithPostAuthHook
WithRequestLoggerFactory = api.WithRequestLoggerFactory
NewServer = api.NewServer
)
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/internal/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
8 changes: 8 additions & 0 deletions pkg/llmproxy/config/sdk_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ package config

import internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"

// Config is an alias to internal/config.Config.
type Config = internalconfig.Config

// SDKConfig is an alias to internal/config.SDKConfig.
type SDKConfig = internalconfig.SDKConfig

// StreamingConfig is an alias to internal/config.StreamingConfig.
type StreamingConfig = internalconfig.StreamingConfig

var (
LoadConfig = internalconfig.LoadConfig
SaveConfigPreserveComments = internalconfig.SaveConfigPreserveComments
)
6 changes: 6 additions & 0 deletions sdk/api/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/gin-gonic/gin"
internalapi "github.com/router-for-me/CLIProxyAPI/v6/internal/api"
"github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
"github.com/router-for-me/CLIProxyAPI/v6/internal/logging"
Expand Down Expand Up @@ -44,3 +45,8 @@ func WithKeepAliveEndpoint(timeout time.Duration, onTimeout func()) ServerOption
func WithRequestLoggerFactory(factory func(*config.Config, string) logging.RequestLogger) ServerOption {
return internalapi.WithRequestLoggerFactory(factory)
}

// WithPostAuthHook registers a hook to be called after auth record creation.
func WithPostAuthHook(hook auth.PostAuthHook) ServerOption {
return internalapi.WithPostAuthHook(hook)
}
2 changes: 1 addition & 1 deletion sdk/auth/codex.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"strings"
"time"

"github.com/router-for-me/CLIProxyAPI/v6/pkg/llmproxy/auth/codex"
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/codex"
"github.com/router-for-me/CLIProxyAPI/v6/pkg/llmproxy/browser"
// legacy client removed
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
Expand Down