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
22 changes: 21 additions & 1 deletion README.ja-JP.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,34 @@ helm install agentteams higress.io/agentteams \

</details>

<details>
<summary>OrcaRouter を使用する場合</summary>

[OrcaRouter](https://www.orcarouter.ai) は `https://api.orcarouter.ai/v1` で OpenAI 互換 API を提供する統合 AI ゲートウェイです。provider 名を `orcarouter` に設定すると、単一の API キーで複数のモデルを利用できます:

```bash
helm install agentteams higress.io/agentteams \
-n agentteams-system --create-namespace \
--render-subchart-notes \
--set credentials.llmApiKey=<your-orcarouter-api-key> \
--set credentials.llmProvider=orcarouter \
--set credentials.defaultModel=orcarouter/auto \
--set credentials.adminPassword=<your-admin-password> \
--set gateway.publicURL=http://localhost:18080
```

It also runs gateway-level, zero-trust security for AI agents on the same endpoint — screening every prompt/response and governing every tool call on a default-deny basis, with no application code changes.

</details>

| 値 | 必須 | 説明 |
|---|---|---|
| `credentials.llmApiKey` | 必須 | LLM プロバイダーの API キー |
| `gateway.publicURL` | 必須 | ユーザーが Element Web にアクセスする公開 URL(port-forward 環境では `http://localhost:18080`、本番では `https://agentteams.example.com` 等) |
| `credentials.adminPassword` | 推奨 | Matrix 管理者パスワード。空のままだと自動生成(後で Secret から読み出す必要あり) |
| `credentials.llmProvider` | 任意 | LLM プロバイダー名、デフォルトは `openai-compat` |
| `credentials.defaultModel` | 任意 | デフォルトモデル、デフォルトは `gpt-5.4` |
| `credentials.llmBaseUrl` | 任意 | OpenAI 互換のベース URL(例: `https://api.deepseek.com/v1`)。公式 OpenAI API を使用する場合は空のまま |
| `credentials.llmBaseUrl` | 任意 | OpenAI 互換のベース URL(例: `https://api.deepseek.com/v1`)。公式 OpenAI API または内蔵の `orcarouter` provider を使用する場合は空のまま |
| `manager.runtime` | 任意 | Manager エージェントランタイム: `openclaw`(デフォルト)、`copaw`、または `hermes` |
| `worker.defaultRuntime` | 任意 | Worker デフォルトランタイム: `openclaw`(デフォルト)、`copaw`、または `hermes` |

Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,34 @@ helm install agentteams higress.io/agentteams \

</details>

<details>
<summary>Using OrcaRouter instead</summary>

[OrcaRouter](https://www.orcarouter.ai) is a unified AI gateway that exposes an OpenAI-compatible API at `https://api.orcarouter.ai/v1`. Set the provider name to `orcarouter` to use it with a single API key across models:

```bash
helm install agentteams higress.io/agentteams \
-n agentteams-system --create-namespace \
--render-subchart-notes \
--set credentials.llmApiKey=<your-orcarouter-api-key> \
--set credentials.llmProvider=orcarouter \
--set credentials.defaultModel=orcarouter/auto \
--set credentials.adminPassword=<your-admin-password> \
--set gateway.publicURL=http://localhost:18080
```

It also runs gateway-level, zero-trust security for AI agents on the same endpoint — screening every prompt/response and governing every tool call on a default-deny basis, with no application code changes.

</details>

| Value | Required | Description |
|---|---|---|
| `credentials.llmApiKey` | yes | API key for your LLM provider |
| `gateway.publicURL` | yes | Public URL where users will reach Element Web (e.g. `http://localhost:18080` for port-forward, or `https://agentteams.example.com` for an Ingress) |
| `credentials.adminPassword` | recommended | Matrix admin password; auto-generated if left empty (you'll have to read it back from the Secret) |
| `credentials.llmProvider` | no | LLM provider name, defaults to `openai-compat` |
| `credentials.defaultModel` | no | Default model, defaults to `gpt-5.4` |
| `credentials.llmBaseUrl` | no | OpenAI-compatible base URL (e.g. `https://api.deepseek.com/v1`). Leave empty for official OpenAI API |
| `credentials.llmBaseUrl` | no | OpenAI-compatible base URL (e.g. `https://api.deepseek.com/v1`). Leave empty for official OpenAI API or the built-in `orcarouter` provider |
| `preflight.llm.enabled` | no | Run an install/upgrade hook that validates the LLM API key, base URL, and model before the controller starts. Defaults to `true` |
| `preflight.llm.strict` | no | Fail the Helm install/upgrade when the LLM preflight fails. Defaults to `true`; set to `false` to emit a warning and continue |
| `preflight.llm.timeoutSeconds` | no | Per-request timeout for the LLM preflight HTTP probe. Defaults to `30` |
Expand Down
22 changes: 21 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,34 @@ helm install agentteams higress.io/agentteams \

</details>

<details>
<summary>使用 OrcaRouter</summary>

[OrcaRouter](https://www.orcarouter.ai) 是一个统一的 AI 网关,在 `https://api.orcarouter.ai/v1` 提供 OpenAI 兼容 API。将 provider 名设为 `orcarouter` 即可用一个 API Key 接入多种模型:

```bash
helm install agentteams higress.io/agentteams \
-n agentteams-system --create-namespace \
--render-subchart-notes \
--set credentials.llmApiKey=<你的-OrcaRouter-API-Key> \
--set credentials.llmProvider=orcarouter \
--set credentials.defaultModel=orcarouter/auto \
--set credentials.adminPassword=<你的-管理员密码> \
--set gateway.publicURL=http://localhost:18080
```

It also runs gateway-level, zero-trust security for AI agents on the same endpoint — screening every prompt/response and governing every tool call on a default-deny basis, with no application code changes.

</details>

| 参数 | 是否必填 | 说明 |
|---|---|---|
| `credentials.llmApiKey` | 必填 | LLM 服务商 API Key |
| `gateway.publicURL` | 必填 | 用户访问 Element Web 的对外地址(端口转发场景填 `http://localhost:18080`,正式环境填 `https://agentteams.example.com` 等) |
| `credentials.adminPassword` | 推荐 | Matrix 管理员密码;留空时会自动生成(之后需要从 Secret 中读取) |
| `credentials.llmProvider` | 可选 | LLM 服务商名,默认 `openai-compat` |
| `credentials.defaultModel` | 可选 | 默认模型,默认 `gpt-5.4` |
| `credentials.llmBaseUrl` | 可选 | OpenAI 兼容的 Base URL(例如 `https://api.deepseek.com/v1`)。使用官方 OpenAI API 时留空 |
| `credentials.llmBaseUrl` | 可选 | OpenAI 兼容的 Base URL(例如 `https://api.deepseek.com/v1`)。使用官方 OpenAI API 或内置的 `orcarouter` provider 时留空 |
| `preflight.llm.enabled` | 可选 | 安装或升级前通过 hook 验证 LLM API Key、Base URL 和模型;默认 `true` |
| `preflight.llm.strict` | 可选 | LLM 探测失败时终止安装或升级;默认 `true`,设为 `false` 时仅告警并继续 |
| `preflight.llm.timeoutSeconds` | 可选 | LLM 探测单次 HTTP 请求的超时时间;默认 `30` 秒 |
Expand Down
5 changes: 4 additions & 1 deletion agentteams-controller/cmd/agt/llm_preflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
const (
defaultOpenAICompatibleBaseURL = "https://api.openai.com/v1"
defaultQwenCompatibleBaseURL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
defaultOrcaRouterBaseURL = "https://api.orcarouter.ai/v1"
defaultLLMPreflightTimeout = 30 * time.Second
defaultLLMPreflightRetries = 2
defaultLLMPreflightRetryBackoff = 500 * time.Millisecond
Expand Down Expand Up @@ -97,7 +98,7 @@ func llmPreflightCmd() *cobra.Command {
},
}

cmd.Flags().StringVar(&opts.Provider, "provider", opts.Provider, "LLM provider (openai-compat|qwen|custom)")
cmd.Flags().StringVar(&opts.Provider, "provider", opts.Provider, "LLM provider (openai-compat|qwen|orcarouter|custom)")
cmd.Flags().StringVar(&opts.APIKey, "api-key", opts.APIKey, "LLM API key")
cmd.Flags().StringVar(&opts.BaseURL, "base-url", opts.BaseURL, "OpenAI-compatible base URL")
cmd.Flags().StringVar(&opts.Model, "model", opts.Model, "Model name to probe")
Expand Down Expand Up @@ -171,6 +172,8 @@ func resolveLLMPreflightBaseURL(provider, baseURL string) (string, error) {
baseURL = defaultOpenAICompatibleBaseURL
case "qwen":
baseURL = defaultQwenCompatibleBaseURL
case "orcarouter":
baseURL = defaultOrcaRouterBaseURL
default:
return "", fmt.Errorf("LLM base URL is required for provider %q (set AGENTTEAMS_OPENAI_BASE_URL or --base-url)", provider)
}
Expand Down
14 changes: 14 additions & 0 deletions agentteams-controller/cmd/agt/llm_preflight_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ func TestResolveLLMPreflightConfigDefaultsQwen(t *testing.T) {
}
}

func TestResolveLLMPreflightConfigDefaultsOrcaRouter(t *testing.T) {
cfg, err := resolveLLMPreflightConfig(llmPreflightOptions{
Provider: "orcarouter",
APIKey: "sk-orca-test",
Model: "orcarouter/auto",
})
if err != nil {
t.Fatalf("resolveLLMPreflightConfig: %v", err)
}
if cfg.BaseURL != "https://api.orcarouter.ai/v1" {
t.Fatalf("BaseURL=%q, want OrcaRouter gateway endpoint", cfg.BaseURL)
}
}

func TestResolveLLMPreflightConfigCustomProviderRequiresBaseURL(t *testing.T) {
_, err := resolveLLMPreflightConfig(llmPreflightOptions{
Provider: "custom-vendor",
Expand Down
22 changes: 22 additions & 0 deletions agentteams-controller/internal/initializer/initializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,28 @@ func (i *Initializer) initGatewayRoutes(ctx context.Context) error {
logger.Error(err, "failed to create LLM provider (non-fatal)")
}

case "orcarouter":
// OrcaRouter: OpenAI-compatible gateway (https://www.orcarouter.ai)
if err := i.Gateway.EnsureServiceSource(ctx, "orcarouter", "api.orcarouter.ai", 443, "https"); err != nil {
logger.Error(err, "failed to register orcarouter service source (non-fatal)")
}
time.Sleep(2 * time.Second)
raw := map[string]interface{}{
"agentteamsMode": true,
"openaiCustomUrl": "https://api.orcarouter.ai/v1",
"openaiCustomServiceName": "orcarouter.dns",
"openaiCustomServicePort": 443,
}
if err := i.Gateway.EnsureAIProvider(ctx, gateway.AIProviderRequest{
Name: "orcarouter",
Type: "openai",
Tokens: []string{cfg.LLMAPIKey},
Protocol: "openai/v1",
Raw: raw,
}); err != nil {
logger.Error(err, "failed to create LLM provider (non-fatal)")
}

case "openai-compat":
if cfg.OpenAIBaseURL == "" {
// No custom base URL — fall back to official OpenAI endpoint
Expand Down
2 changes: 1 addition & 1 deletion helm/agentteams/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ credentials:
adminUser: "admin"
adminPassword: "" # Matrix admin password (auto-generated if empty)
llmApiKey: "" # LLM API key (required)
llmProvider: "openai-compat"
llmProvider: "openai-compat" # openai-compat, qwen, orcarouter, or a custom name
defaultModel: "gpt-5.4"
llmBaseUrl: "" # OpenAI-compatible base URL (e.g. https://api.openai.com/v1)

Expand Down
2 changes: 1 addition & 1 deletion install/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ $env:AGENTTEAMS_LLM_API_KEY = "your-api-key"
| Variable | Description | Default |
|----------|-------------|---------|
| `AGENTTEAMS_NON_INTERACTIVE` | Skip all prompts | `0` |
| `AGENTTEAMS_LLM_PROVIDER` | LLM provider (`qwen` or `openai-compat`) | `qwen` |
| `AGENTTEAMS_LLM_PROVIDER` | LLM provider (`qwen`, `orcarouter` or `openai-compat`) | `qwen` |
| `AGENTTEAMS_DEFAULT_MODEL` | Default model ID | `qwen3.5-plus` |
| `AGENTTEAMS_LLM_API_KEY` | LLM API key | *(required)* |
| `AGENTTEAMS_ADMIN_USER` | Admin username | `admin` |
Expand Down
53 changes: 47 additions & 6 deletions install/agentteams-install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,8 @@ $script:Messages = @{
"llm.providers_title" = @{ zh = "可用 LLM 提供商:"; en = "Available LLM Providers:" }
"llm.provider.alibaba" = @{ zh = " 1) 阿里云通义 Token 套餐 - 推荐中国用户使用"; en = " 1) Qwen Cloud - International (OpenAI-compatible API, recommended)" }
"llm.provider.openai_compat" = @{ zh = " 2) OpenAI 兼容 API - 自定义 Base URL(OpenAI、DeepSeek 等)"; en = " 2) OpenAI-compatible API - Custom Base URL (OpenAI, DeepSeek, etc.)" }
"llm.provider.select" = @{ zh = "选择提供商 [1/2]"; en = "Select provider [1/2]" }
"llm.provider.orcarouter" = @{ zh = " 3) OrcaRouter - AI 网关(https://www.orcarouter.ai)"; en = " 3) OrcaRouter - AI gateway (https://www.orcarouter.ai)" }
"llm.provider.select" = @{ zh = "选择提供商 [1/2/3]"; en = "Select provider [1/2/3]" }
"llm.alibaba.models_title" = @{ zh = "选择阿里云模型接入方式:"; en = "Select Alibaba Cloud model access:" }
"llm.alibaba.model.tokenplan" = @{ zh = " 1) 阿里云通义 Token 套餐 - 兼容模式(推荐)"; en = " 1) Alibaba Cloud Token Plan - compatible-mode (recommended)" }
"llm.alibaba.model.bailian" = @{ zh = " 2) 阿里云百炼 - DashScope 通用兼容接口"; en = " 2) Alibaba Cloud Bailian - DashScope compatible mode" }
Expand All @@ -335,7 +336,7 @@ $script:Messages = @{
"llm.provider.selected_codingplan_legacy" = @{ zh = " 提供商: 阿里云 Coding 套餐(coding.dashscope)"; en = " Provider: Alibaba Cloud Coding Plan (coding.dashscope)" }
"llm.provider.selected_qwen" = @{ zh = " 提供商: 阿里云百炼"; en = " Provider: Alibaba Cloud Bailian" }
"llm.provider.selected_openai" = @{ zh = " 提供商: {0}(OpenAI 兼容)"; en = " Provider: {0} (OpenAI-compatible)" }
"llm.provider.invalid" = @{ zh = "无效选择: {0}(请输入 1 或 2)"; en = "Invalid choice: {0} (please enter 1 or 2)" }
"llm.provider.invalid" = @{ zh = "无效选择: {0}(请输入 1、23)"; en = "Invalid choice: {0} (please enter 1, 2, or 3)" }
"llm.qwen.model_prompt" = @{ zh = "默认模型 ID"; en = "Default Model ID" }
"llm.openai.base_url_prompt" = @{ zh = "Base URL"; en = "Base URL" }
"llm.openai.model_prompt" = @{ zh = "默认模型 ID"; en = "Default Model ID" }
Expand Down Expand Up @@ -1340,7 +1341,8 @@ function New-OpenAICompatProvider {
param(
[string]$BaseUrl,
[string]$ApiKey,
[int]$ConsolePort = 18001
[int]$ConsolePort = 18001,
[string]$ProviderName = "openai-compat"
)

if (-not $BaseUrl -or -not $ApiKey) {
Expand Down Expand Up @@ -1372,7 +1374,7 @@ function New-OpenAICompatProvider {
Write-Log (Get-Msg "install.openai_compat.port" -f $port)
Write-Log (Get-Msg "install.openai_compat.protocol" -f $protocol)

$serviceName = "openai-compat"
$serviceName = $ProviderName

# Create DNS service source
$serviceBody = @{
Expand All @@ -1396,7 +1398,7 @@ function New-OpenAICompatProvider {
# Create AI provider
$providerBody = @{
type = "openai"
name = "openai-compat"
name = $ProviderName
tokens = @($ApiKey)
version = 0
protocol = "openai/v1"
Expand Down Expand Up @@ -1772,11 +1774,16 @@ function Step-Llm {
Write-Host (Get-Msg "llm.providers_title")
Write-Host (Get-Msg "llm.provider.alibaba")
Write-Host (Get-Msg "llm.provider.openai_compat")
Write-Host (Get-Msg "llm.provider.orcarouter")
Write-Host ""

# If upgrade mode with loaded provider, show current as default
if ($script:AGENTTEAMS_UPGRADE -and $script:config.LLM_PROVIDER) {
$defaultProvider = if ($script:config.LLM_PROVIDER -eq "openai-compat") { "2" } else { "1" }
$defaultProvider = switch ($script:config.LLM_PROVIDER) {
"openai-compat" { "2" }
"orcarouter" { "3" }
default { "1" }
}
$providerChoice = Read-Host "$(Get-Msg 'llm.provider.select') [${defaultProvider}]"
$providerChoice = if ($providerChoice) { $providerChoice } else { $defaultProvider }
} elseif ($script:AGENTTEAMS_QUICKSTART) {
Expand Down Expand Up @@ -1994,6 +2001,38 @@ function Step-Llm {
Test-LlmConnectivity -BaseUrl $script:config.OPENAI_BASE_URL -ApiKey $script:config.LLM_API_KEY -Model $script:config.DEFAULT_MODEL
if ($script:StepResult -eq "back") { return }
}
"^(3|orcarouter)$" {
$script:config.LLM_PROVIDER = "orcarouter"
$script:config.OPENAI_BASE_URL = "https://api.orcarouter.ai/v1"
Write-Log (Get-Msg "llm.provider.selected_openai" -f "OrcaRouter")
Write-Log (Get-Msg "llm.openai.base_url_label" -f $script:config.OPENAI_BASE_URL)
if ($script:AGENTTEAMS_UPGRADE -and $env:AGENTTEAMS_UPGRADE_KEEP_ALL -eq "1") {
Write-Log (Get-Msg "prompt.upgrade_keep" -f (Get-Msg "llm.openai.model_prompt"), $script:config.DEFAULT_MODEL)
} else {
$currentModel = $script:config.DEFAULT_MODEL
if ($currentModel) {
$modelInput = Read-Host "$(Get-Msg 'llm.openai.model_prompt') [${currentModel}]"
} else {
$modelInput = Read-Host (Get-Msg 'llm.openai.model_prompt')
}
if ($modelInput -eq "b") { $script:StepResult = "back"; return }
if ($modelInput) {
$script:config.DEFAULT_MODEL = $modelInput
} elseif ($currentModel) {
$script:config.DEFAULT_MODEL = $currentModel
} else {
$script:config.DEFAULT_MODEL = "orcarouter/auto"
}
}
Write-Log (Get-Msg "llm.model.label" -f $script:config.DEFAULT_MODEL)
Request-CustomModelParams $script:config.DEFAULT_MODEL
if ($script:StepResult -eq "back") { return }
Write-Log ""
$script:config.LLM_API_KEY = Read-Prompt -VarName "AGENTTEAMS_LLM_API_KEY" -PromptText (Get-Msg "llm.apikey_prompt") -Secret
if ($script:StepResult -eq "back") { return }
Test-LlmConnectivity -BaseUrl $script:config.OPENAI_BASE_URL -ApiKey $script:config.LLM_API_KEY -Model $script:config.DEFAULT_MODEL
if ($script:StepResult -eq "back") { return }
}
default {
Write-Error (Get-Msg "llm.provider.invalid" -f $providerChoice)
}
Expand Down Expand Up @@ -3348,6 +3387,8 @@ function Install-Manager {
# Create OpenAI-compatible provider if needed
if ($config.LLM_PROVIDER -eq "openai-compat") {
New-OpenAICompatProvider -BaseUrl $config.OPENAI_BASE_URL -ApiKey $config.LLM_API_KEY -ConsolePort ([int]$config.PORT_CONSOLE)
} elseif ($config.LLM_PROVIDER -eq "orcarouter") {
New-OpenAICompatProvider -BaseUrl $config.OPENAI_BASE_URL -ApiKey $config.LLM_API_KEY -ConsolePort ([int]$config.PORT_CONSOLE) -ProviderName "orcarouter"
}

# Print success message
Expand Down
Loading
Loading