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
2 changes: 1 addition & 1 deletion agentteams-controller/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/agentscope-ai/AgentTeams/agentteams-controller

go 1.25.0
go 1.25

require (
github.com/alibabacloud-go/apig-20240327/v6 v6.0.6
Expand Down
39 changes: 39 additions & 0 deletions agentteams-controller/internal/gateway/higress.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,19 @@ func (c *HigressClient) EnsureAIProvider(ctx context.Context, req AIProviderRequ
if req.Raw != nil {
body["rawConfigs"] = req.Raw
}

_, sc, _ := c.doJSON(ctx, http.MethodGet, "/v1/ai/providers/"+req.Name, nil)
if sc == http.StatusOK {
_, putSC, err := c.doJSON(ctx, http.MethodPut, "/v1/ai/providers/"+req.Name, body)
if err != nil {
return fmt.Errorf("update AI provider %s: %w", req.Name, err)
}
if putSC != http.StatusOK && putSC != http.StatusCreated {
return fmt.Errorf("update AI provider %s: HTTP %d", req.Name, putSC)
}
return nil
}

_, sc, err := c.doJSON(ctx, http.MethodPost, "/v1/ai/providers", body)
if err != nil {
return fmt.Errorf("ensure AI provider %s: %w", req.Name, err)
Expand Down Expand Up @@ -677,6 +690,19 @@ func (c *HigressClient) ensureServiceSource(ctx context.Context, name, dnsDomain
"properties": map[string]interface{}{},
"authN": map[string]interface{}{"enabled": false},
}

_, sc, _ := c.doJSON(ctx, http.MethodGet, "/v1/service-sources/"+name, nil)
if sc == http.StatusOK {
_, putSC, err := c.doJSON(ctx, http.MethodPut, "/v1/service-sources/"+name, body)
if err != nil {
return fmt.Errorf("update service source %s: %w", name, err)
}
if putSC != http.StatusOK && putSC != http.StatusCreated {
return fmt.Errorf("update service source %s: HTTP %d", name, putSC)
}
return nil
}

_, sc, err := c.doJSON(ctx, http.MethodPost, "/v1/service-sources", body)
if err != nil {
return fmt.Errorf("ensure service source %s: %w", name, err)
Expand All @@ -694,6 +720,19 @@ func (c *HigressClient) ensureStaticServiceSource(ctx context.Context, name, add
"properties": map[string]interface{}{},
"authN": map[string]interface{}{"enabled": false},
}

_, sc, _ := c.doJSON(ctx, http.MethodGet, "/v1/service-sources/"+name, nil)
if sc == http.StatusOK {
_, putSC, err := c.doJSON(ctx, http.MethodPut, "/v1/service-sources/"+name, body)
if err != nil {
return fmt.Errorf("update static service source %s: %w", name, err)
}
if putSC != http.StatusOK && putSC != http.StatusCreated {
return fmt.Errorf("update static service source %s: HTTP %d", name, putSC)
}
return nil
}

_, sc, err := c.doJSON(ctx, http.MethodPost, "/v1/service-sources", body)
if err != nil {
return fmt.Errorf("ensure static service source %s: %w", name, err)
Expand Down
64 changes: 54 additions & 10 deletions agentteams-controller/internal/initializer/initializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package initializer
import (
"context"
"fmt"
"net"
"net/url"
"strconv"
"strings"
Expand Down Expand Up @@ -331,7 +332,7 @@ func (i *Initializer) initGatewayRoutes(ctx context.Context) error {
logger.Error(err, "failed to create LLM provider (non-fatal)")
}
} else {
// Parse URL to create DNS service source
// Parse URL to determine host type and create appropriate service source
host, port, err := parseHostPort(cfg.OpenAIBaseURL)
if err != nil {
logger.Error(err, "failed to parse AGENTTEAMS_OPENAI_BASE_URL (non-fatal)")
Expand All @@ -340,15 +341,37 @@ func (i *Initializer) initGatewayRoutes(ctx context.Context) error {
if strings.HasPrefix(cfg.OpenAIBaseURL, "http://") {
proto = "http"
}
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)")

var svcSuffix string
if net.ParseIP(host) != nil {
// IP address → static-type service source
if err := i.Gateway.EnsureStaticServiceSource(ctx, "openai-compat", host, port); err != nil {
logger.Error(err, "failed to register openai-compat static service source (non-fatal)")
}
svcSuffix = "static"
} else {
// Domain name → DNS-type service source
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)")
}
svcSuffix = "dns"
}
// Wait for DNS service source to propagate before creating provider
time.Sleep(2 * time.Second)

// Strip port from openaiCustomUrl — only keep scheme://host/path
u, err := url.Parse(cfg.OpenAIBaseURL)
customUrl := cfg.OpenAIBaseURL
if err == nil {
customUrl = fmt.Sprintf("%s://%s%s", u.Scheme, u.Hostname(), u.Path)
if u.RawQuery != "" {
customUrl += "?" + u.RawQuery
}
}

raw := map[string]interface{}{
"agentteamsMode": true,
"openaiCustomUrl": cfg.OpenAIBaseURL,
"openaiCustomServiceName": "openai-compat.dns",
"openaiCustomUrl": customUrl,
"openaiCustomServiceName": "openai-compat." + svcSuffix,
"openaiCustomServicePort": port,
}
if err := i.Gateway.EnsureAIProvider(ctx, gateway.AIProviderRequest{
Expand All @@ -375,14 +398,35 @@ func (i *Initializer) initGatewayRoutes(ctx context.Context) error {
if strings.HasPrefix(cfg.OpenAIBaseURL, "http://") {
proto = "http"
}
if err := i.Gateway.EnsureServiceSource(ctx, provider, host, port, proto); err != nil {
logger.Error(err, "failed to register service source for provider (non-fatal)")

var svcSuffix string
if net.ParseIP(host) != nil {
if err := i.Gateway.EnsureStaticServiceSource(ctx, provider, host, port); err != nil {
logger.Error(err, "failed to register static service source for provider (non-fatal)")
}
svcSuffix = "static"
} else {
if err := i.Gateway.EnsureServiceSource(ctx, provider, host, port, proto); err != nil {
logger.Error(err, "failed to register service source for provider (non-fatal)")
}
svcSuffix = "dns"
}
time.Sleep(2 * time.Second)

// Strip port from openaiCustomUrl — only keep scheme://host/path
u, err := url.Parse(cfg.OpenAIBaseURL)
customUrl := cfg.OpenAIBaseURL
if err == nil {
customUrl = fmt.Sprintf("%s://%s%s", u.Scheme, u.Hostname(), u.Path)
if u.RawQuery != "" {
customUrl += "?" + u.RawQuery
}
}

raw := map[string]interface{}{
"agentteamsMode": true,
"openaiCustomUrl": cfg.OpenAIBaseURL,
"openaiCustomServiceName": provider + ".dns",
"openaiCustomUrl": customUrl,
"openaiCustomServiceName": provider + "." + svcSuffix,
"openaiCustomServicePort": port,
}
if err := i.Gateway.EnsureAIProvider(ctx, gateway.AIProviderRequest{
Expand Down
1 change: 1 addition & 0 deletions changelog/current.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Record image-affecting changes to `manager/`, `worker/`, `copaw/`, `hermes/`, `o

**Bug Fixes**

- **openai-compat IP:port support**: Fix openai-compat provider to use static service source for IP addresses (instead of DNS), strip port from openaiCustomUrl, and use GET→PUT idempotent updates for service sources and AI providers. Fixes #1057.
- **Team Worker room boundary convergence**: Remove Manager again after standalone Worker infrastructure reconciliation restores regular Team Worker personal-room membership. ([b5b0add](https://github.com/agentscope-ai/AgentTeams/commit/b5b0add))
- **Team Worker reference enforcement**: Keep referenced Worker CRs protected during direct deletion and reject Team API members whose required role is empty. ([d96f1ed](https://github.com/agentscope-ai/AgentTeams/commit/d96f1ed))
- **Team Worker room membership**: Force Manager out of regular Team Worker personal rooms when equal Matrix power levels prevent a normal kick. ([43545c2](https://github.com/agentscope-ai/AgentTeams/commit/43545c2))
Expand Down
32 changes: 27 additions & 5 deletions manager/scripts/init/setup-higress.sh
Original file line number Diff line number Diff line change
Expand Up @@ -209,16 +209,38 @@ 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%:*}"; }

# Detect IP vs domain for service source type
OC_IS_IP=false
if echo "${OC_DOMAIN}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then
OC_IS_IP=true
fi

# Strip port from openaiCustomUrl: keep scheme://host/path only
OC_PATH="${OC_URL_STRIP#*/}"
[ "${OC_PATH}" = "/" ] && OC_PATH=""
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}"'"}'
if [ -n "${existing_svc}" ]; then
higress_api PUT /v1/service-sources/openai-compat "Updating openai-compat DNS service source" "${SVC_BODY}"
if [ "${OC_IS_IP}" = "true" ]; then
SVC_BODY='{"type":"static","name":"openai-compat","port":'"${OC_PORT}"',"protocol":"http","proxyName":"","domain":"'"${OC_DOMAIN}:${OC_PORT}"'"}'
if [ -n "${existing_svc}" ]; then
higress_api PUT /v1/service-sources/openai-compat "Updating openai-compat static service source" "${SVC_BODY}"
else
higress_api POST /v1/service-sources "Registering openai-compat static service source" "${SVC_BODY}"
fi
OC_SVC_SUFFIX="static"
else
higress_api POST /v1/service-sources "Registering openai-compat DNS service source" "${SVC_BODY}"
SVC_BODY='{"type":"dns","name":"openai-compat","port":'"${OC_PORT}"',"protocol":"'"${OC_PROTO}"'","proxyName":"","domain":"'"${OC_DOMAIN}"'"}'
if [ -n "${existing_svc}" ]; then
higress_api PUT /v1/service-sources/openai-compat "Updating openai-compat DNS service source" "${SVC_BODY}"
else
higress_api POST /v1/service-sources "Registering openai-compat DNS service source" "${SVC_BODY}"
fi
OC_SVC_SUFFIX="dns"
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}"',"agentteamsMode":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.'"${OC_SVC_SUFFIX}"'","openaiCustomServicePort":'"${OC_PORT}"',"agentteamsMode":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