fix(initializer): use static service source for IP-based openai-compat base URLs - #1171
Conversation
…t base URLs When AGENTTEAMS_OPENAI_BASE_URL uses a bare IP address (e.g. http://192.168.1.10:3000/v1), the initializer unconditionally registers a DNS-type service source. Higress console rejects bare IPs in ValidateUtil.checkDomain (DOMAIN_PATTERN requires a letter TLD), so the service source is never created and the provider references a non-existent openai-compat.dns cluster, causing every LLM call to fail with HTTP 503 cluster_not_found. Detect IP hosts with net.ParseIP and register a static-type service source instead (which supports ip:port domains), and reference it as <provider>.static in rawConfigs.openaiCustomServiceName. Fixes agentscope-ai#1057 (verified end-to-end on Embedded v1.2.2: provider restored by manually creating the static service source and pointing openaiCustomServiceName to openai-compat.static). Also applies the same fix to the default provider branch (unrecognized provider names with a custom base URL).
503 复发机制深挖(2026-08-14,PR #1171 补充证据)1. 复发事实
2. 决定性实验(复现)
结论:controller 重启即复发——启动路径无条件覆盖 provider 配置。 3. 根因(二进制证据)
4. 完整故障链5. 上游修复建议(PR #1171 补充)
6. 现场处置(缓解)PUT provider 503 Deep exploration of the recurrence mechanism (2026-08-14, PR #1171 additional evidence)1. Recurrence facts
2. Decisive experiment (reproduction)
Conclusion: Relapse occurs when the controller is restarted - the startup path unconditionally overrides the provider configuration. 3. Root cause (binary evidence)
4. Complete fault chain5. Upstream fix suggestions (Added by PR #1171)
6. On-site disposal (mitigation)PUT provider |
oss-maintainer
left a comment
There was a problem hiding this comment.
Summary
Fixes #1057: when AGENTTEAMS_OPENAI_BASE_URL is a bare IP, the initializer now registers a static service source (via net.ParseIP detection in both the openai-compat and default-provider branches) and points openaiCustomServiceName at <name>.static; domain-based hosts keep the DNS path unchanged. The Go-side fix is correct, minimal, and backward compatible — we verified EnsureStaticServiceSource exists on current main with a compatible signature, and end-to-end verification against Embedded v1.2.2 is documented in the PR description. Overall verdict: the change is good to merge, but there is one companion overwrite path this PR does not cover (see notes below), which matches the 503 recurrence the author documented in the follow-up comment.
Findings
- [Critical]
manager/scripts/init/setup-higress.sh:221— hardcodedopenai-compat.dns+ unconditional PUT on every Manager boot overwrites the.staticprovider config (the documented recurrence). Companion fix needed. - [Warning]
install/agentteams-install.ps1:1406— also hardcodes$serviceName.dns; fresh IP-based installs hit the same 503. - [Warning] IPv6:
ensureStaticServiceSourceformatsip:portwithout brackets;net.ParseIPaccepts IPv6, so the new branch can reach this path. - [Info]
protounused in static branch (static sources hardcode http); no unit tests for the new branching.
Suggestions
- Follow-up (or extend this PR): in
setup-higress.sh, detect an IP host and register a static service source +openai-compat.static(mirror the Go logic); same for the default provider section andagentteams-install.ps1. - Alternatively, change the shell script's LLM provider section from unconditional PUT to read-modify-write so an existing service-name suffix set by the controller is preserved.
- Mitigating factor for the Go path:
EnsureAIProvideruses POST and treats 409 (already exists) as success without updating, so controller restarts do not overwrite the provider — the overwrite vector is specifically the shell script's PUT.
Automated review by github-manager-bot
Additional notes (not anchored to a changed line)
- [CRITICAL]
manager/scripts/init/setup-higress.sh:221— Recurrence gap: this script hardcodesopenaiCustomServiceName: openai-compat.dnsand the idempotent LLM Provider section unconditionally PUTs the provider on every Manager container boot. After the controller initializer correctly sets.static, the next Manager restart overwrites it back to.dns, reproducing the exact 503 recurrence documented in this PR's comments. This PR does not touch setup-higress.sh; a companion fix is needed (detect IP host here too and use.static, or switch to read-modify-write so the existing service-name suffix is preserved). (line outside diff) - [WARNING]
install/agentteams-install.ps1:1406— The PowerShell install script also hardcodesopenaiCustomServiceName = "$serviceName.dns". Fresh Windows/embedded installs using an IP-based OpenAI base URL will hit the same 503 cluster_not_found. Not addressed by this PR. (line outside diff) - [WARNING]
agentteams-controller/internal/gateway/higress.go:692— IPv6 formatting concern: ensureStaticServiceSource builds the domain as fmt.Sprintf("%s:%d", address, port). For an IPv6 host this yields::1:8080rather than[::1]:8080. net.ParseIP accepts IPv6 literals, so the new static branch can route IPv6 hosts into this path; low probability today but a latent bug for IPv6 deployments. (line outside diff)
| logger.Error(err, "failed to register openai-compat service source (non-fatal)") | ||
| svcName := "openai-compat" | ||
| svcSuffix := "dns" | ||
| if net.ParseIP(host) != nil { |
There was a problem hiding this comment.
proto is computed but unused in the static branch: EnsureStaticServiceSource hardcodes protocol: "http" internally. An https:// base URL would silently register an http static source. Consistent with existing static-source usage (Tuwunel, Element Web), but worth a short comment or log line documenting the limitation.
| } | ||
| if err := i.Gateway.EnsureServiceSource(ctx, "openai-compat", host, port, proto); err != nil { | ||
| logger.Error(err, "failed to register openai-compat service source (non-fatal)") | ||
| svcName := "openai-compat" |
There was a problem hiding this comment.
No unit tests exist for initializer.go yet. The IP-vs-domain branching is easy to test in isolation — consider a table-driven test covering bare IPv4, bracketed IPv6, plain domain, domain with port, localhost, and empty host.
PR 描述(英文版,用于 GitHub PR)
Title:
fix(initializer): use static service source for IP-based openai-compat base URLsSummary
Fixes #1057. When
AGENTTEAMS_OPENAI_BASE_URLuses a bare IP address (e.g.http://192.168.1.10:3000/v1), the initializer unconditionally registers a DNS-type service source. Two independent failures result:ValidateUtil.checkDomain()usesDOMAIN_PATTERN = ^(?:(?!-)[a-z0-9]...\.)+[a-z]{2,63}$which requires a letter TLD;checkDomain("192.168.1.10")fails →ValidationException: serviceSource body is not valid→ the service source is never created.rawConfigs.openaiCustomServiceNamereferencesopenai-compat.dns, a cluster that does not exist.Result: every LLM call fails with
HTTP 503 cluster_not_found(verified end-to-end on Embedded v1.2.2).Changes
agentteams-controller/internal/initializer/initializer.go:net.ParseIP(host) != nilin both theopenai-compatbranch and the default provider branch.EnsureStaticServiceSource, which supportsip:portdomains and passescheckIpAddressvalidation) and setrawConfigs.openaiCustomServiceNameto<provider>.static.<provider>.dns), so existing domain-based setups are fully backward compatible.Verification
go build ./internal/initializer/passes (Go 1.22)openaiCustomServiceName=openai-compat.staticvia console API), all LLM calls return 200. This confirms both the root cause and the fix direction.Note on prior PRs
main. Port-stripping is tracked in fix(gateway): strip port from openai-compat openaiCustomUrl (#1057) #1061 (open).