Configure OpenClaw admission from bridge config
This commit is contained in:
parent
e27995a85b
commit
b39c9ec084
@ -12,6 +12,11 @@ upstream:
|
|||||||
gateway_url: "ws://127.0.0.1:18789/"
|
gateway_url: "ws://127.0.0.1:18789/"
|
||||||
opencode_url: "ws://127.0.0.1:38992"
|
opencode_url: "ws://127.0.0.1:38992"
|
||||||
|
|
||||||
|
openclaw_gateway:
|
||||||
|
max_active: 2
|
||||||
|
max_queued: 20
|
||||||
|
queue_timeout: "10m"
|
||||||
|
|
||||||
# Legacy/Reference structure (Normally managed via code constants or environment)
|
# Legacy/Reference structure (Normally managed via code constants or environment)
|
||||||
bridge:
|
bridge:
|
||||||
listenAddr: 127.0.0.1:8787
|
listenAddr: 127.0.0.1:8787
|
||||||
|
|||||||
@ -13,7 +13,7 @@ func (s *Server) Bootstrap() {
|
|||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
config, providerCatalog, providerOrder := newProductionProviderCatalog()
|
config, providerCatalog, providerOrder := newProductionProviderCatalogFromConfig(s.config)
|
||||||
s.config = config
|
s.config = config
|
||||||
s.providerOrder = providerOrder
|
s.providerOrder = providerOrder
|
||||||
|
|
||||||
@ -40,16 +40,16 @@ func (s *Server) Bootstrap() {
|
|||||||
ProviderCatalog: make([]any, 0),
|
ProviderCatalog: make([]any, 0),
|
||||||
GatewayProviders: []any{
|
GatewayProviders: []any{
|
||||||
map[string]any{
|
map[string]any{
|
||||||
"providerId": "openclaw",
|
"providerId": "openclaw",
|
||||||
"label": "OpenClaw",
|
"label": "OpenClaw",
|
||||||
"targets": []string{"gateway"},
|
"targets": []string{"gateway"},
|
||||||
"providerDisplay": map[string]any{"logoEmoji": "🦞"},
|
"providerDisplay": map[string]any{"logoEmoji": "🦞"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AvailableExecutionTargets: []any{"agent", "gateway"},
|
AvailableExecutionTargets: []any{"agent", "gateway"},
|
||||||
ProviderProbeSummary: make([]any, 0),
|
ProviderProbeSummary: make([]any, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, id := range providerOrder {
|
for _, id := range providerOrder {
|
||||||
p, ok := providerCatalog[id]
|
p, ok := providerCatalog[id]
|
||||||
if !ok || !p.Enabled {
|
if !ok || !p.Enabled {
|
||||||
|
|||||||
@ -24,6 +24,13 @@ type BridgeConfig struct {
|
|||||||
GeminiURL string `yaml:"gemini_url"`
|
GeminiURL string `yaml:"gemini_url"`
|
||||||
HermesURL string `yaml:"hermes_url"`
|
HermesURL string `yaml:"hermes_url"`
|
||||||
} `yaml:"upstream"`
|
} `yaml:"upstream"`
|
||||||
|
OpenClawGateway OpenClawGatewayConfig `yaml:"openclaw_gateway"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenClawGatewayConfig struct {
|
||||||
|
MaxActive *int `yaml:"max_active"`
|
||||||
|
MaxQueued *int `yaml:"max_queued"`
|
||||||
|
QueueTimeout string `yaml:"queue_timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadBridgeConfig() *BridgeConfig {
|
func loadBridgeConfig() *BridgeConfig {
|
||||||
@ -65,7 +72,13 @@ func bridgeSharedAuthToken() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newProductionProviderCatalog() (*BridgeConfig, map[string]syncedProvider, []string) {
|
func newProductionProviderCatalog() (*BridgeConfig, map[string]syncedProvider, []string) {
|
||||||
config := loadBridgeConfig()
|
return newProductionProviderCatalogFromConfig(loadBridgeConfig())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProductionProviderCatalogFromConfig(config *BridgeConfig) (*BridgeConfig, map[string]syncedProvider, []string) {
|
||||||
|
if config == nil {
|
||||||
|
config = &BridgeConfig{}
|
||||||
|
}
|
||||||
authorizationHeader := bridgeUpstreamAuthorizationHeader()
|
authorizationHeader := bridgeUpstreamAuthorizationHeader()
|
||||||
|
|
||||||
providers := []struct {
|
providers := []struct {
|
||||||
|
|||||||
@ -32,17 +32,24 @@ type openClawGatewayAdmissionGate struct {
|
|||||||
queued int
|
queued int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOpenClawGatewayAdmissionGateFromEnv() *openClawGatewayAdmissionGate {
|
func newOpenClawGatewayAdmissionGate(config *BridgeConfig) *openClawGatewayAdmissionGate {
|
||||||
maxActive := envInt("XWORKMATE_BRIDGE_OPENCLAW_GATEWAY_MAX_ACTIVE", defaultOpenClawGatewayMaxActive)
|
admissionConfig := OpenClawGatewayConfig{}
|
||||||
|
if config != nil {
|
||||||
|
admissionConfig = config.OpenClawGateway
|
||||||
|
}
|
||||||
|
maxActive := admissionInt(admissionConfig.MaxActive, "XWORKMATE_BRIDGE_OPENCLAW_GATEWAY_MAX_ACTIVE", defaultOpenClawGatewayMaxActive)
|
||||||
if maxActive < 1 {
|
if maxActive < 1 {
|
||||||
|
log.Printf("level=warn component=openclaw_gateway event=invalid_admission_config key=%q value=%d", "max_active", maxActive)
|
||||||
maxActive = defaultOpenClawGatewayMaxActive
|
maxActive = defaultOpenClawGatewayMaxActive
|
||||||
}
|
}
|
||||||
maxQueued := envInt("XWORKMATE_BRIDGE_OPENCLAW_GATEWAY_MAX_QUEUED", defaultOpenClawGatewayMaxQueued)
|
maxQueued := admissionInt(admissionConfig.MaxQueued, "XWORKMATE_BRIDGE_OPENCLAW_GATEWAY_MAX_QUEUED", defaultOpenClawGatewayMaxQueued)
|
||||||
if maxQueued < 0 {
|
if maxQueued < 0 {
|
||||||
|
log.Printf("level=warn component=openclaw_gateway event=invalid_admission_config key=%q value=%d", "max_queued", maxQueued)
|
||||||
maxQueued = defaultOpenClawGatewayMaxQueued
|
maxQueued = defaultOpenClawGatewayMaxQueued
|
||||||
}
|
}
|
||||||
timeout := envDuration("XWORKMATE_BRIDGE_OPENCLAW_GATEWAY_QUEUE_TIMEOUT", defaultOpenClawGatewayQueueWait)
|
timeout := admissionDuration(admissionConfig.QueueTimeout, "queue_timeout", "XWORKMATE_BRIDGE_OPENCLAW_GATEWAY_QUEUE_TIMEOUT", defaultOpenClawGatewayQueueWait)
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
|
log.Printf("level=warn component=openclaw_gateway event=invalid_admission_config key=%q value=%q", "queue_timeout", timeout.String())
|
||||||
timeout = defaultOpenClawGatewayQueueWait
|
timeout = defaultOpenClawGatewayQueueWait
|
||||||
}
|
}
|
||||||
return &openClawGatewayAdmissionGate{
|
return &openClawGatewayAdmissionGate{
|
||||||
@ -53,6 +60,26 @@ func newOpenClawGatewayAdmissionGateFromEnv() *openClawGatewayAdmissionGate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func admissionInt(configValue *int, envKey string, fallback int) int {
|
||||||
|
if configValue != nil {
|
||||||
|
return *configValue
|
||||||
|
}
|
||||||
|
return envInt(envKey, fallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
func admissionDuration(configValue string, configKey string, envKey string, fallback time.Duration) time.Duration {
|
||||||
|
raw := strings.TrimSpace(configValue)
|
||||||
|
if raw == "" {
|
||||||
|
return envDuration(envKey, fallback)
|
||||||
|
}
|
||||||
|
value, err := time.ParseDuration(raw)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("level=warn component=openclaw_gateway event=invalid_duration_config key=%q value=%q", configKey, raw)
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
func (g *openClawGatewayAdmissionGate) acquire(
|
func (g *openClawGatewayAdmissionGate) acquire(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
notifyQueued func(position int, queued int),
|
notifyQueued func(position int, queued int),
|
||||||
|
|||||||
46
internal/acp/openclaw_gateway_admission_test.go
Normal file
46
internal/acp/openclaw_gateway_admission_test.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package acp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOpenClawGatewayAdmissionGateUsesConfigValues(t *testing.T) {
|
||||||
|
maxActive := 3
|
||||||
|
maxQueued := 7
|
||||||
|
gate := newOpenClawGatewayAdmissionGate(&BridgeConfig{
|
||||||
|
OpenClawGateway: OpenClawGatewayConfig{
|
||||||
|
MaxActive: &maxActive,
|
||||||
|
MaxQueued: &maxQueued,
|
||||||
|
QueueTimeout: "45s",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if gate.maxActive != 3 {
|
||||||
|
t.Fatalf("expected max active from config, got %d", gate.maxActive)
|
||||||
|
}
|
||||||
|
if gate.maxQueued != 7 {
|
||||||
|
t.Fatalf("expected max queued from config, got %d", gate.maxQueued)
|
||||||
|
}
|
||||||
|
if gate.timeout != 45*time.Second {
|
||||||
|
t.Fatalf("expected queue timeout from config, got %s", gate.timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenClawGatewayAdmissionGateFallsBackToEnvWithoutConfig(t *testing.T) {
|
||||||
|
t.Setenv("XWORKMATE_BRIDGE_OPENCLAW_GATEWAY_MAX_ACTIVE", "4")
|
||||||
|
t.Setenv("XWORKMATE_BRIDGE_OPENCLAW_GATEWAY_MAX_QUEUED", "0")
|
||||||
|
t.Setenv("XWORKMATE_BRIDGE_OPENCLAW_GATEWAY_QUEUE_TIMEOUT", "30s")
|
||||||
|
|
||||||
|
gate := newOpenClawGatewayAdmissionGate(&BridgeConfig{})
|
||||||
|
|
||||||
|
if gate.maxActive != 4 {
|
||||||
|
t.Fatalf("expected max active from env, got %d", gate.maxActive)
|
||||||
|
}
|
||||||
|
if gate.maxQueued != 0 {
|
||||||
|
t.Fatalf("expected max queued from env, got %d", gate.maxQueued)
|
||||||
|
}
|
||||||
|
if gate.timeout != 30*time.Second {
|
||||||
|
t.Fatalf("expected queue timeout from env, got %s", gate.timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -42,11 +42,13 @@ func newHTTPServer(addr string, handler http.Handler) *http.Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewServer() *Server {
|
func NewServer() *Server {
|
||||||
|
config := loadBridgeConfig()
|
||||||
s := &Server{
|
s := &Server{
|
||||||
sessions: make(map[string]*session),
|
sessions: make(map[string]*session),
|
||||||
|
config: config,
|
||||||
allowedOrigins: shared.ParseAllowedOrigins(shared.EnvOrDefault("ACP_ALLOWED_ORIGINS", "https://xworkmate.svc.plus,http://localhost:*,http://127.0.0.1:*")),
|
allowedOrigins: shared.ParseAllowedOrigins(shared.EnvOrDefault("ACP_ALLOWED_ORIGINS", "https://xworkmate.svc.plus,http://localhost:*,http://127.0.0.1:*")),
|
||||||
authService: service.NewStaticTokenAuthService(shared.EnvOrDefault("BRIDGE_AUTH_TOKEN", "")),
|
authService: service.NewStaticTokenAuthService(shared.EnvOrDefault("BRIDGE_AUTH_TOKEN", "")),
|
||||||
openClawGate: newOpenClawGatewayAdmissionGateFromEnv(),
|
openClawGate: newOpenClawGatewayAdmissionGate(config),
|
||||||
}
|
}
|
||||||
s.Bootstrap()
|
s.Bootstrap()
|
||||||
return s
|
return s
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user