Skip to content
Open
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
23 changes: 21 additions & 2 deletions agentteams-controller/internal/initializer/initializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ func (i *Initializer) initGatewayRoutes(ctx context.Context) error {
time.Sleep(2 * time.Second)
raw := map[string]interface{}{
"hiclawMode": true,
"openaiCustomUrl": cfg.OpenAIBaseURL,
"openaiCustomUrl": customOpenAIURL(cfg.OpenAIBaseURL),
"openaiCustomServiceName": "openai-compat.dns",
"openaiCustomServicePort": port,
}
Expand Down Expand Up @@ -396,7 +396,7 @@ func (i *Initializer) initGatewayRoutes(ctx context.Context) error {
time.Sleep(2 * time.Second)
raw := map[string]interface{}{
"hiclawMode": true,
"openaiCustomUrl": cfg.OpenAIBaseURL,
"openaiCustomUrl": customOpenAIURL(cfg.OpenAIBaseURL),
"openaiCustomServiceName": provider + ".dns",
"openaiCustomServicePort": port,
}
Expand Down Expand Up @@ -448,6 +448,25 @@ func (i *Initializer) initGatewayRoutes(ctx context.Context) error {
return nil
}

// customOpenAIURL returns the base URL with any port stripped while preserving
// everything else (escaped path, query string, fragment). Higress derives the
// upstream host domain from openaiCustomUrl, so an embedded port would be
// treated as part of the host and break DNS resolution. The port is supplied
// separately via openaiCustomServicePort.
func customOpenAIURL(rawURL string) string {
u, err := url.Parse(rawURL)
if err != nil || u.Hostname() == "" {
return rawURL
}
host := u.Hostname()
if strings.Contains(host, ":") {
// Re-bracket IPv6 literals so the reconstructed URL stays valid.
host = "[" + host + "]"
}
u.Host = host
return u.String()
}

// parseHostPort extracts host and port from a URL like "http://host:port".
func parseHostPort(rawURL string) (string, int, error) {
u, err := url.Parse(rawURL)
Expand Down
28 changes: 28 additions & 0 deletions agentteams-controller/internal/initializer/initializer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package initializer

import "testing"

func TestCustomOpenAIURL(t *testing.T) {
cases := []struct {
name string
in string
want string
}{
{"ip port with path", "http://10.43.46.12:3000/v1", "http://10.43.46.12/v1"},
{"ip port no path", "http://10.43.46.12:3000", "http://10.43.46.12"},
{"https port with path", "https://example.com:8443/v1", "https://example.com/v1"},
{"no port keeps url", "https://api.openai.com/v1", "https://api.openai.com/v1"},
{"query string preserved", "http://10.43.46.12:3000/v1?api-version=2024-05", "http://10.43.46.12/v1?api-version=2024-05"},
{"escaped path preserved", "http://10.43.46.12:3000/v1/models%2Ffoo", "http://10.43.46.12/v1/models%2Ffoo"},
{"ipv6 port with path", "http://[2001:db8::1]:3000/v1", "http://[2001:db8::1]/v1"},
{"ipv6 no port keeps brackets", "http://[2001:db8::1]/v1", "http://[2001:db8::1]/v1"},
{"unparseable returns input", "://bad", "://bad"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := customOpenAIURL(tc.in); got != tc.want {
t.Fatalf("customOpenAIURL(%q) = %q, want %q", tc.in, got, tc.want)
}
})
}
}
1 change: 1 addition & 0 deletions changelog/current.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Record image-affecting changes to `manager/`, `worker/`, `copaw/`, `hermes/`, `o
**Bug Fixes**

- **CoPaw Team coordination routing**: Route Team Leader worker assignments sent through the `message` tool from Leader DM to Team Room, matching the Matrix channel send path. ([92c8145](https://github.com/agentscope-ai/AgentTeams/commit/92c8145))
- **openai-compat provider with IP:port base URL**: Strip the port from `openaiCustomUrl` in the controller initializer and `setup-higress.sh`, so Higress derives the host domain without the port. Previously a base URL like `http://10.43.46.12:3000/v1` produced host domain `10.43.46.12:3000`, breaking DNS resolution; the port is now supplied only via `openaiCustomServicePort`. (#1057)

**Branding and Compatibility**

Expand Down
11 changes: 10 additions & 1 deletion manager/scripts/init/setup-higress.sh
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,15 @@ if [ -n "${AGENTTEAMS_LLM_API_KEY}" ]; then
OC_DOMAIN="${OC_URL_STRIP%%/*}"
echo "${OC_DOMAIN}" | grep -q ':' && { OC_PORT="${OC_DOMAIN##*:}"; OC_DOMAIN="${OC_DOMAIN%:*}"; }

# Build openaiCustomUrl WITHOUT the port: Higress derives the
# host domain from this URL, so an embedded port would break
# DNS resolution. The port is supplied via openaiCustomServicePort.
OC_PATH=""
case "${OC_URL_STRIP}" in
*/*) OC_PATH="/${OC_URL_STRIP#*/}" ;;
esac
OC_CUSTOM_URL="${OC_PROTO}://${OC_DOMAIN}${OC_PATH}"

# Service source: GET → PUT if exists, POST if not
existing_svc=$(higress_get /v1/service-sources/openai-compat)
SVC_BODY='{"type":"dns","name":"openai-compat","port":'"${OC_PORT}"',"protocol":"'"${OC_PROTO}"'","proxyName":"","domain":"'"${OC_DOMAIN}"'"}'
Expand All @@ -218,7 +227,7 @@ if [ -n "${AGENTTEAMS_LLM_API_KEY}" ]; then
higress_api POST /v1/service-sources "Registering openai-compat DNS service source" "${SVC_BODY}"
fi

PROVIDER_BODY='{"type":"openai","name":"openai-compat","tokens":["'"${AGENTTEAMS_LLM_API_KEY}"'"],"version":0,"protocol":"openai/v1","tokenFailoverConfig":{"enabled":false},"rawConfigs":{"openaiCustomUrl":"'"${OPENAI_BASE_URL}"'","openaiCustomServiceName":"openai-compat.dns","openaiCustomServicePort":'"${OC_PORT}"',"hiclawMode":true}}'
PROVIDER_BODY='{"type":"openai","name":"openai-compat","tokens":["'"${AGENTTEAMS_LLM_API_KEY}"'"],"version":0,"protocol":"openai/v1","tokenFailoverConfig":{"enabled":false},"rawConfigs":{"openaiCustomUrl":"'"${OC_CUSTOM_URL}"'","openaiCustomServiceName":"openai-compat.dns","openaiCustomServicePort":'"${OC_PORT}"',"hiclawMode":true}}'
existing_provider=$(higress_get /v1/ai/providers/openai-compat)
if [ -n "${existing_provider}" ]; then
higress_api PUT /v1/ai/providers/openai-compat "Updating LLM provider (openai-compat)" "${PROVIDER_BODY}"
Expand Down
Loading