From a22b869a8723572f083c258015cd344c2ffdc0db Mon Sep 17 00:00:00 2001 From: johnlanni Date: Thu, 29 Jan 2026 08:28:12 +0800 Subject: [PATCH 1/4] feat: integrate with Clawdbot for auto-routing and model provider --- .../plugin/clawdbot.plugin.json | 31 +++ .../clawdbot-integration/plugin/index.ts | 260 ++++++++++++++++++ .../clawdbot-integration/plugin/package.json | 22 ++ .../skill/higress-auto-router/SKILL.md | 175 ++++++++++++ all-in-one/get-ai-gateway.sh | 214 +++++++++++++- 5 files changed, 701 insertions(+), 1 deletion(-) create mode 100644 all-in-one/clawdbot-integration/plugin/clawdbot.plugin.json create mode 100644 all-in-one/clawdbot-integration/plugin/index.ts create mode 100644 all-in-one/clawdbot-integration/plugin/package.json create mode 100644 all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md diff --git a/all-in-one/clawdbot-integration/plugin/clawdbot.plugin.json b/all-in-one/clawdbot-integration/plugin/clawdbot.plugin.json new file mode 100644 index 0000000..a54afd3 --- /dev/null +++ b/all-in-one/clawdbot-integration/plugin/clawdbot.plugin.json @@ -0,0 +1,31 @@ +{ + "id": "higress-ai-gateway", + "name": "Higress AI Gateway", + "description": "Model provider plugin for Higress AI Gateway with auto-routing support", + "providers": ["higress"], + "skills": ["higress-auto-router"], + "configSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "gatewayUrl": { + "type": "string", + "description": "Higress AI Gateway URL (e.g., http://localhost:8080)" + }, + "consoleUrl": { + "type": "string", + "description": "Higress Console URL for configuration (e.g., http://localhost:8001)" + } + } + }, + "uiHints": { + "gatewayUrl": { + "label": "Gateway URL", + "placeholder": "http://localhost:8080" + }, + "consoleUrl": { + "label": "Console URL", + "placeholder": "http://localhost:8001" + } + } +} diff --git a/all-in-one/clawdbot-integration/plugin/index.ts b/all-in-one/clawdbot-integration/plugin/index.ts new file mode 100644 index 0000000..bbab912 --- /dev/null +++ b/all-in-one/clawdbot-integration/plugin/index.ts @@ -0,0 +1,260 @@ +import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk"; + +const DEFAULT_GATEWAY_URL = "http://localhost:8080"; +const DEFAULT_CONSOLE_URL = "http://localhost:8001"; +const DEFAULT_CONTEXT_WINDOW = 128_000; +const DEFAULT_MAX_TOKENS = 8192; + +// Common models that Higress AI Gateway typically supports +const DEFAULT_MODEL_IDS = [ + // Auto-routing special model + "higress/auto", + // OpenAI models + "gpt-4o", + "gpt-4o-mini", + "gpt-4-turbo", + // Anthropic models + "claude-opus-4.5", + "claude-sonnet-4.5", + "claude-haiku-4.5", + // Qwen models + "qwen-turbo", + "qwen-plus", + "qwen-max", + "qwen-coder", + // DeepSeek models + "deepseek-chat", + "deepseek-coder", + // Other common models + "moonshot-v1-8k", + "glm-4", +] as const; + +function normalizeGatewayUrl(value: string): string { + const trimmed = value.trim(); + if (!trimmed) return DEFAULT_GATEWAY_URL; + let normalized = trimmed; + while (normalized.endsWith("/")) normalized = normalized.slice(0, -1); + return normalized; +} + +function validateUrl(value: string): string | undefined { + const normalized = normalizeGatewayUrl(value); + try { + new URL(normalized); + } catch { + return "Enter a valid URL"; + } + return undefined; +} + +function parseModelIds(input: string): string[] { + const parsed = input + .split(/[\n,]/) + .map((model) => model.trim()) + .filter(Boolean); + return Array.from(new Set(parsed)); +} + +function buildModelDefinition(modelId: string) { + const isAutoModel = modelId === "higress/auto"; + return { + id: modelId, + name: isAutoModel ? "Higress Auto Router" : modelId, + api: "openai-completions", + reasoning: false, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: DEFAULT_CONTEXT_WINDOW, + maxTokens: DEFAULT_MAX_TOKENS, + }; +} + +async function testGatewayConnection(gatewayUrl: string): Promise { + try { + const response = await fetch(`${gatewayUrl}/v1/models`, { + method: "GET", + headers: { "Content-Type": "application/json" }, + signal: AbortSignal.timeout(5000), + }); + return response.ok || response.status === 401; // 401 means gateway is up but needs auth + } catch { + return false; + } +} + +async function fetchAvailableModels(consoleUrl: string): Promise { + try { + // Try to get models from Higress Console API + const response = await fetch(`${consoleUrl}/v1/ai/routes`, { + method: "GET", + headers: { "Content-Type": "application/json" }, + signal: AbortSignal.timeout(5000), + }); + if (response.ok) { + const data = await response.json() as { data?: { model?: string }[] }; + if (data.data && Array.isArray(data.data)) { + return data.data + .map((route: { model?: string }) => route.model) + .filter((m): m is string => typeof m === "string"); + } + } + } catch { + // Ignore errors, use defaults + } + return []; +} + +const higressPlugin = { + id: "higress-ai-gateway", + name: "Higress AI Gateway", + description: "Model provider plugin for Higress AI Gateway with auto-routing support", + configSchema: emptyPluginConfigSchema(), + register(api) { + api.registerProvider({ + id: "higress", + label: "Higress AI Gateway", + docsPath: "/providers/models", + aliases: ["higress-gateway", "higress-ai"], + auth: [ + { + id: "api-key", + label: "API Key", + hint: "Configure Higress AI Gateway endpoint with optional API key", + kind: "custom", + run: async (ctx) => { + // Step 1: Get Gateway URL + const gatewayUrlInput = await ctx.prompter.text({ + message: "Higress AI Gateway URL", + initialValue: DEFAULT_GATEWAY_URL, + validate: validateUrl, + }); + const gatewayUrl = normalizeGatewayUrl(gatewayUrlInput); + + // Step 2: Get Console URL (for auto-router configuration) + const consoleUrlInput = await ctx.prompter.text({ + message: "Higress Console URL (for auto-router config)", + initialValue: DEFAULT_CONSOLE_URL, + validate: validateUrl, + }); + const consoleUrl = normalizeGatewayUrl(consoleUrlInput); + + // Step 3: Test connection + const spin = ctx.prompter.progress("Testing gateway connection…"); + const isConnected = await testGatewayConnection(gatewayUrl); + if (!isConnected) { + spin.stop("Gateway connection failed"); + await ctx.prompter.note( + [ + "Could not connect to Higress AI Gateway.", + "Make sure the gateway is running and the URL is correct.", + "", + `Tried: ${gatewayUrl}/v1/models`, + ].join("\n"), + "Connection Warning", + ); + } else { + spin.stop("Gateway connected"); + } + + // Step 4: Get API Key (optional for local gateway) + const apiKeyInput = await ctx.prompter.text({ + message: "API Key (leave empty if not required)", + initialValue: "", + }); + const apiKey = apiKeyInput.trim() || "higress-local"; + + // Step 5: Fetch available models or use defaults + spin.update("Fetching available models…"); + const fetchedModels = await fetchAvailableModels(consoleUrl); + const defaultModels = fetchedModels.length > 0 + ? ["higress/auto", ...fetchedModels] + : DEFAULT_MODEL_IDS; + spin.stop(); + + // Step 6: Let user customize model list + const modelInput = await ctx.prompter.text({ + message: "Model IDs (comma-separated, higress/auto enables auto-routing)", + initialValue: defaultModels.slice(0, 10).join(", "), + validate: (value) => + parseModelIds(value).length > 0 ? undefined : "Enter at least one model id", + }); + + const modelIds = parseModelIds(modelInput); + const hasAutoModel = modelIds.includes("higress/auto"); + const defaultModelId = hasAutoModel ? "higress/auto" : (modelIds[0] ?? "qwen-turbo"); + const defaultModelRef = `higress/${defaultModelId}`; + + // Step 7: Configure default model for auto-routing + let autoRoutingDefaultModel = "qwen-turbo"; + if (hasAutoModel) { + autoRoutingDefaultModel = await ctx.prompter.text({ + message: "Default model for auto-routing (when no rule matches)", + initialValue: "qwen-turbo", + }); + } + + return { + profiles: [ + { + profileId: `higress:${apiKey === "higress-local" ? "local" : "default"}`, + credential: { + type: "token", + provider: "higress", + token: apiKey, + }, + }, + ], + configPatch: { + models: { + providers: { + higress: { + baseUrl: `${gatewayUrl}/v1`, + apiKey: apiKey, + api: "openai-completions", + authHeader: apiKey !== "higress-local", + models: modelIds.map((modelId) => buildModelDefinition(modelId)), + }, + }, + }, + agents: { + defaults: { + models: Object.fromEntries( + modelIds.map((modelId) => [`higress/${modelId}`, {}]), + ), + }, + }, + plugins: { + entries: { + "higress-ai-gateway": { + enabled: true, + config: { + gatewayUrl, + consoleUrl, + autoRoutingDefaultModel, + }, + }, + }, + }, + }, + defaultModel: defaultModelRef, + notes: [ + "Higress AI Gateway is now configured as a model provider.", + hasAutoModel + ? `Auto-routing enabled: use model "higress/auto" to route based on message content.` + : "Add 'higress/auto' to models to enable auto-routing.", + `Gateway endpoint: ${gatewayUrl}/v1/chat/completions`, + `Console: ${consoleUrl}`, + "", + "To configure auto-routing rules, use the higress-auto-router skill:", + ' Say: "route to claude-opus-4.5 when solving difficult problems"', + ], + }; + }, + }, + ], + }); + }, +}; + +export default higressPlugin; diff --git a/all-in-one/clawdbot-integration/plugin/package.json b/all-in-one/clawdbot-integration/plugin/package.json new file mode 100644 index 0000000..7c15a41 --- /dev/null +++ b/all-in-one/clawdbot-integration/plugin/package.json @@ -0,0 +1,22 @@ +{ + "name": "@higress/clawdbot-ai-gateway", + "version": "1.0.0", + "description": "Higress AI Gateway model provider plugin for Clawdbot with auto-routing support", + "main": "index.ts", + "clawdbot": { + "extensions": ["./index.ts"] + }, + "keywords": [ + "clawdbot", + "higress", + "ai-gateway", + "model-router", + "auto-routing" + ], + "author": "Higress Team", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/higress-group/higress-standalone" + } +} diff --git a/all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md b/all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md new file mode 100644 index 0000000..425aa6f --- /dev/null +++ b/all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md @@ -0,0 +1,175 @@ +# Higress Auto Router SKILL + +Configure automatic model routing based on user's natural language requests. + +## Description + +This skill enables users to configure Higress AI Gateway's model-router plugin through natural language commands. When a user describes their routing preference (e.g., "route to claude-opus-4.5 when solving difficult problems"), this skill will: + +1. Analyze the user's request to understand the routing intent +2. Generate appropriate regex patterns that are distinctive and easy to remember +3. Configure the model-router plugin via Higress Console API +4. Confirm the configuration and explain how to trigger the route + +## When to Use + +Use this skill when: +- User wants to configure automatic model routing +- User mentions keywords like "route to", "switch model", "use model when", "auto routing" +- User describes scenarios that should trigger specific models + +## Prerequisites + +- Higress AI Gateway running (default: http://localhost:8001 for console, http://localhost:8080 for gateway) +- model-router plugin enabled with autoRouting feature + +## Configuration + +The skill reads Higress configuration from environment or defaults: +- `HIGRESS_CONSOLE_URL`: Higress Console URL (default: http://localhost:8001) +- `HIGRESS_GATEWAY_URL`: Higress Gateway URL (default: http://localhost:8080) + +## Workflow + +### Step 1: Parse User Intent + +Extract from the user's request: +- **Target model**: The model they want to route to (e.g., `claude-opus-4.5`, `qwen-coder`) +- **Trigger scenario**: When they want this routing to happen (e.g., "difficult problems", "coding tasks") + +### Step 2: Generate Pattern and Trigger Phrase + +Based on the scenario, generate: +- A memorable **trigger phrase** users can use (Chinese and English options) +- A **regex pattern** to match the trigger phrase + +Common mappings: +| Scenario | Trigger Phrases | Pattern | +|----------|-----------------|---------| +| Complex/difficult reasoning | `深入思考`, `deep thinking` | `(?i)^(深入思考\|deep thinking)` | +| Coding tasks | `写代码`, `code:`, `coding:` | `(?i)^(写代码\|code:\|coding:)` | +| Creative writing | `创意写作`, `creative:` | `(?i)^(创意写作\|creative:)` | +| Translation | `翻译:`, `translate:` | `(?i)^(翻译:\|translate:)` | +| Math problems | `数学题`, `math:` | `(?i)^(数学题\|math:)` | +| Image generation | `画图:`, `draw:`, `image:` | `(?i)^(画图:\|draw:\|image:)` | +| Quick answers | `快速回答`, `quick:` | `(?i)^(快速回答\|quick:)` | + +### Step 3: Check Existing Rules + +Before adding a new rule, fetch existing rules from model-router configuration to: +- Avoid duplicate patterns +- Check for conflicts +- Suggest alternative trigger phrases if needed + +### Step 4: Configure via Higress Console API + +Use curl to update the model-router plugin configuration: + +```bash +# Get current plugin config +curl -s "${HIGRESS_CONSOLE_URL}/v1/plugins/model-router" | jq . + +# Update plugin config with new rule +curl -X PUT "${HIGRESS_CONSOLE_URL}/v1/plugins/model-router" \ + -H "Content-Type: application/json" \ + -d '{ + "enabled": true, + "config": { + "modelToHeader": "x-higress-llm-model", + "autoRouting": { + "enable": true, + "defaultModel": "qwen-turbo", + "rules": [ + { + "pattern": "(?i)^(深入思考|deep thinking)", + "model": "claude-opus-4.5" + } + ] + } + } + }' +``` + +### Step 5: Confirm to User + +After successful configuration, inform the user: + +**Example response:** +``` +✅ 自动路由配置完成! + +已添加规则:当消息以 "深入思考" 或 "deep thinking" 开头时,自动路由到 claude-opus-4.5 模型。 + +使用方式: +- 中文:深入思考 如何优化这段代码的性能? +- English: deep thinking How to optimize this code's performance? + +提示:请确保您的请求中 model 参数设置为 `higress/auto` 才能触发自动路由。 +``` + +## API Reference + +### Higress Console Plugin API + +**Get plugin config:** +``` +GET /v1/plugins/{pluginName} +``` + +**Update plugin config:** +``` +PUT /v1/plugins/{pluginName} +Content-Type: application/json +{ + "enabled": true, + "config": { ... } +} +``` + +### model-router Plugin Schema + +```yaml +modelToHeader: x-higress-llm-model +autoRouting: + enable: true + defaultModel: string # fallback model + rules: + - pattern: string # regex pattern + model: string # target model name +``` + +## Error Handling + +- If Higress Console is unreachable, inform user to check if the gateway is running +- If pattern conflicts with existing rule, suggest alternative trigger phrases +- If model name is invalid, list available models from configured providers + +## Examples + +**User:** 我希望在解决困难问题时路由到claude-opus-4.5的模型 + +**Response:** +``` +已为您配置自动路由规则: + +触发方式:以 "深入思考" 或 "deep thinking" 开头发起请求 +目标模型:claude-opus-4.5 + +配置已生效。现在您可以这样使用: +- 深入思考 这道算法题应该怎么解? +- deep thinking What's the best architecture for this system? +``` + +**User:** 写代码的时候用qwen-coder + +**Response:** +``` +已为您配置自动路由规则: + +触发方式:以 "写代码" 或 "code:" 开头发起请求 +目标模型:qwen-coder + +配置已生效。使用示例: +- 写代码 实现一个快速排序算法 +- code: implement a binary search tree in Python +``` diff --git a/all-in-one/get-ai-gateway.sh b/all-in-one/get-ai-gateway.sh index 99ec287..69a73f3 100755 --- a/all-in-one/get-ai-gateway.sh +++ b/all-in-one/get-ai-gateway.sh @@ -41,10 +41,21 @@ KNOWN_COMMANDS=($COMMAND_START, $COMMAND_STOP, $COMMAND_DELETE) cd "$(dirname -- "$0")" ROOT="$(pwd -P)/higress" +SCRIPT_DIR="$(pwd -P)" cd - >/dev/null CONFIGURED_MARK="$ROOT/.configured" +# Clawdbot integration paths +CLAWDBOT_WORKSPACE="$HOME/clawd" +CLAWDBOT_EXTENSIONS_DIR="$HOME/.clawdbot/extensions" +CLAWDBOT_SKILLS_DIR="$CLAWDBOT_WORKSPACE/skills/public" +CLAWDBOT_INTEGRATION_DIR="$SCRIPT_DIR/clawdbot-integration" + +# Auto-routing configuration +ENABLE_AUTO_ROUTING="false" +AUTO_ROUTING_DEFAULT_MODEL="" + initArch() { ARCH=$(uname -m) case $ARCH in @@ -139,6 +150,150 @@ resetEnv() { LLM_ENVS=() } +# Check if Clawdbot is installed +checkClawdbot() { + if [ -d "$CLAWDBOT_WORKSPACE" ]; then + return 0 + fi + return 1 +} + +# Configure Clawdbot integration +configureClawdbotIntegration() { + if ! checkClawdbot; then + return 0 + fi + + echo + echo "=======================================================" + echo " Clawdbot Integration Detected " + echo "=======================================================" + echo + echo "Clawdbot workspace found at: $CLAWDBOT_WORKSPACE" + echo + echo "Higress AI Gateway can integrate with Clawdbot to provide:" + echo " 1. Auto-routing: Automatically route requests to different models" + echo " based on message content (e.g., 'deep thinking' → claude-opus-4.5)" + echo " 2. Model provider: Use Higress as a unified model provider in Clawdbot" + echo + + read -r -u 3 -p "Enable auto-routing feature? (y/N): " enableAutoRouting + case "$enableAutoRouting" in + [yY]|[yY][eE][sS]) + ENABLE_AUTO_ROUTING="true" + + echo + echo "Auto-routing allows you to route requests to different models based on" + echo "keywords in your message. For example:" + echo " - '深入思考 ...' or 'deep thinking ...' → reasoning model" + echo " - '写代码 ...' or 'code: ...' → coding model" + echo + + # Get default model for auto-routing + read -r -u 3 -p "Default model when no routing rule matches (default: qwen-turbo): " defaultModel + if [ -z "$defaultModel" ]; then + AUTO_ROUTING_DEFAULT_MODEL="qwen-turbo" + else + AUTO_ROUTING_DEFAULT_MODEL="$defaultModel" + fi + + echo + echo "You can configure routing rules later using natural language in Clawdbot." + echo "For example, say: 'route to claude-opus-4.5 when solving difficult problems'" + echo + ;; + *) + ENABLE_AUTO_ROUTING="false" + echo "Auto-routing disabled. You can enable it later via Higress Console." + ;; + esac + + # Install Clawdbot plugin and skill + installClawdbotPlugin + installClawdbotSkill +} + +# Install Clawdbot plugin +installClawdbotPlugin() { + if [ ! -d "$CLAWDBOT_INTEGRATION_DIR/plugin" ]; then + echo "Warning: Plugin source not found at $CLAWDBOT_INTEGRATION_DIR/plugin" + return 1 + fi + + echo "Installing Higress AI Gateway plugin for Clawdbot..." + + # Create extensions directory if not exists + mkdir -p "$CLAWDBOT_EXTENSIONS_DIR" + + # Copy plugin files + local PLUGIN_DEST="$CLAWDBOT_EXTENSIONS_DIR/higress-ai-gateway" + if [ -d "$PLUGIN_DEST" ]; then + echo "Plugin already exists, updating..." + rm -rf "$PLUGIN_DEST" + fi + + cp -r "$CLAWDBOT_INTEGRATION_DIR/plugin" "$PLUGIN_DEST" + + echo "✓ Plugin installed at: $PLUGIN_DEST" + + # Generate plugin configuration snippet + local GATEWAY_URL="http://localhost:$GATEWAY_HTTP_PORT" + local CONSOLE_URL="http://localhost:$CONSOLE_PORT" + + echo + echo "To complete plugin setup, run:" + echo " clawdbot models auth login --provider higress" + echo + echo "Or add to your Clawdbot config (config.yaml):" + echo + cat <$DATA_FOLDER/$CONFIG_FILENAME MODE=full O11Y=on @@ -458,7 +625,7 @@ CONFIG_TEMPLATE=ai-gateway GATEWAY_HTTP_PORT=${GATEWAY_HTTP_PORT} GATEWAY_HTTPS_PORT=${GATEWAY_HTTPS_PORT} CONSOLE_PORT=${CONSOLE_PORT} -${LLM_CONFIGS} +${LLM_CONFIGS}${AUTO_ROUTING_CONFIG} EOF } @@ -493,6 +660,34 @@ outputWelcomeMessage() { echo "Higress AI Gateway chat completion endpoint:" echo " http://localhost:$GATEWAY_HTTP_PORT/v1/chat/completions" echo + + # Show auto-routing info if enabled + if [ "$ENABLE_AUTO_ROUTING" == "true" ]; then + echo "=======================================================" + echo " Auto-Routing Mode " + echo "=======================================================" + echo + echo "Auto-routing is enabled! Use model 'higress/auto' to automatically" + echo "route requests based on message content." + echo + echo "Default model: $AUTO_ROUTING_DEFAULT_MODEL" + echo + echo "Example with auto-routing:" + echo + echo " curl 'http://localhost:$GATEWAY_HTTP_PORT/v1/chat/completions' \\" + echo " -H 'Content-Type: application/json' \\" + echo " -d '{" + echo " \"model\": \"higress/auto\"," + echo ' "messages": [' + echo " {" + echo ' "role": "user",' + echo ' "content": "深入思考 如何设计一个高并发系统?"' + echo " }" + echo " ]" + echo " }'" + echo + fi + echo "You can try it with cURL directly:" echo echo " curl 'http://localhost:$GATEWAY_HTTP_PORT/v1/chat/completions' \\" @@ -515,6 +710,23 @@ outputWelcomeMessage() { echo "Higress Console URL (open with browser):" echo " http://localhost:$CONSOLE_PORT" + # Show Clawdbot integration info if detected + if checkClawdbot; then + echo + echo "=======================================================" + echo " Clawdbot Integration " + echo "=======================================================" + echo + echo "Higress AI Gateway plugin and skill have been installed." + echo + echo "To complete setup, run:" + echo " clawdbot models auth login --provider higress" + echo + echo "To configure auto-routing rules, tell Clawdbot:" + echo " '我希望在解决困难问题时路由到claude-opus-4.5的模型'" + echo + fi + echo echo "To stop the gateway run:" echo From 10647bf8b40af53abc59b9ff2cda0853de55601f Mon Sep 17 00:00:00 2001 From: johnlanni Date: Thu, 29 Jan 2026 08:53:24 +0800 Subject: [PATCH 2/4] fix: update auto-routing to modify config file directly instead of Console API - Remove Console API calls from SKILL.md - Add configureAutoRouting function to modify model-router.yaml directly - Wait for container to generate config files before modification - Use sed to inject auto-routing configuration into model-router.internal.yaml - Backup original config file before modification - Remove unused ai-gateway-with-autorouting.sh script --- .../skill/higress-auto-router/SKILL.md | 192 +++++++++++------- all-in-one/get-ai-gateway.sh | 65 +++++- 2 files changed, 169 insertions(+), 88 deletions(-) diff --git a/all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md b/all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md index 425aa6f..accf8ba 100644 --- a/all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md +++ b/all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md @@ -8,8 +8,9 @@ This skill enables users to configure Higress AI Gateway's model-router plugin t 1. Analyze the user's request to understand the routing intent 2. Generate appropriate regex patterns that are distinctive and easy to remember -3. Configure the model-router plugin via Higress Console API -4. Confirm the configuration and explain how to trigger the route +3. Modify the model-router configuration file directly at `/data/wasmplugins/model-router.internal.yaml` +4. Restart Higress Gateway to apply the changes +5. Confirm the configuration and explain how to trigger the route ## When to Use @@ -20,14 +21,14 @@ Use this skill when: ## Prerequisites -- Higress AI Gateway running (default: http://localhost:8001 for console, http://localhost:8080 for gateway) -- model-router plugin enabled with autoRouting feature +- Higress AI Gateway running +- Access to modify `/data/wasmplugins/model-router.internal.yaml` +- Ability to restart Higress container -## Configuration +## Configuration Files -The skill reads Higress configuration from environment or defaults: -- `HIGRESS_CONSOLE_URL`: Higress Console URL (default: http://localhost:8001) -- `HIGRESS_GATEWAY_URL`: Higress Gateway URL (default: http://localhost:8080) +**Higress Gateway Data Directory:** Usually the directory where the container mounts data (default: `/root/higress/`) +**Model Router Config:** `/wasmplugins/model-router.internal.yaml` ## Workflow @@ -46,51 +47,94 @@ Based on the scenario, generate: Common mappings: | Scenario | Trigger Phrases | Pattern | |----------|-----------------|---------| -| Complex/difficult reasoning | `深入思考`, `deep thinking` | `(?i)^(深入思考\|deep thinking)` | -| Coding tasks | `写代码`, `code:`, `coding:` | `(?i)^(写代码\|code:\|coding:)` | -| Creative writing | `创意写作`, `creative:` | `(?i)^(创意写作\|creative:)` | -| Translation | `翻译:`, `translate:` | `(?i)^(翻译:\|translate:)` | -| Math problems | `数学题`, `math:` | `(?i)^(数学题\|math:)` | -| Image generation | `画图:`, `draw:`, `image:` | `(?i)^(画图:\|draw:\|image:)` | -| Quick answers | `快速回答`, `quick:` | `(?i)^(快速回答\|quick:)` | +| Complex/difficult reasoning | `深入思考`, `deep thinking` | `(?i)^(深入思考|deep thinking)` | +| Coding tasks | `写代码`, `code:`, `coding:` | `(?i)^(写代码|code:|coding:)` | +| Creative writing | `创意写作`, `creative:` | `(?i)^(创意写作|creative:)` | +| Translation | `翻译:`, `translate:` | `(?i)^(翻译:|translate:)` | +| Math problems | `数学题`, `math:` | `(?i)^(数学题|math:)` | +| Image generation | `画图:`, `draw:`, `image:` | `(?i)^(画图:|draw:|image:)` | +| Quick answers | `快速回答`, `quick:` | `(?i)^(快速回答|quick:)` | -### Step 3: Check Existing Rules +### Step 3: Find Configuration File -Before adding a new rule, fetch existing rules from model-router configuration to: -- Avoid duplicate patterns -- Check for conflicts -- Suggest alternative trigger phrases if needed +The model-router configuration file is typically at one of these locations: +- `/root/higress/wasmplugins/model-router.internal.yaml` (default) +- `/data/wasmplugins/model-router.internal.yaml` (inside container) -### Step 4: Configure via Higress Console API +Check these locations and use the first one that exists. -Use curl to update the model-router plugin configuration: +### Step 4: Read Existing Configuration + +Read the current `model-router.internal.yaml` file to understand: +- Current `autoRouting` configuration (if any) +- Existing rules to avoid conflicts +- Default model setting + +Example configuration structure: +```yaml +apiVersion: extensions.higress.io/v1alpha1 +kind: WasmPlugin +metadata: + name: model-router.internal + namespace: higress-system +spec: + defaultConfig: + modelToHeader: x-higress-llm-model + autoRouting: + enable: true + defaultModel: qwen-turbo + rules: + - pattern: (?i)^(深入思考|deep thinking) + model: claude-opus-4.5 +``` + +### Step 5: Check for Conflicts + +Before adding a new rule: +- Parse existing rules from the config +- Check if the new pattern conflicts with existing ones +- If conflict detected, suggest alternative trigger phrases + +### Step 6: Update Configuration File + +Modify the YAML file to add/update the autoRouting configuration. Use a YAML parser or carefully structured text replacement. + +**Structure:** +```yaml +spec: + defaultConfig: + modelToHeader: x-higress-llm-model + autoRouting: + enable: true + defaultModel: qwen-turbo + rules: + - pattern: (?i)^(深入思考|deep thinking) + model: claude-opus-4.5 + - pattern: (?i)^(写代码|code:) + model: qwen-coder +``` + +**Important:** +- Ensure proper YAML indentation +- Keep the existing `modelToHeader` configuration +- Use `(?i)` for case-insensitive patterns +- Use `^` to anchor pattern to message start (prevents false matches) + +### Step 7: Restart Higress Gateway + +After modifying the configuration, restart the Higress container to apply changes: ```bash -# Get current plugin config -curl -s "${HIGRESS_CONSOLE_URL}/v1/plugins/model-router" | jq . - -# Update plugin config with new rule -curl -X PUT "${HIGRESS_CONSOLE_URL}/v1/plugins/model-router" \ - -H "Content-Type: application/json" \ - -d '{ - "enabled": true, - "config": { - "modelToHeader": "x-higress-llm-model", - "autoRouting": { - "enable": true, - "defaultModel": "qwen-turbo", - "rules": [ - { - "pattern": "(?i)^(深入思考|deep thinking)", - "model": "claude-opus-4.5" - } - ] - } - } - }' +# If running via Docker +docker restart higress-ai-gateway + +# Or via systemd/systemd-managed container +systemctl restart higress-gateway ``` -### Step 5: Confirm to User +Wait a few seconds for the gateway to be ready. + +### Step 8: Confirm to User After successful configuration, inform the user: @@ -100,6 +144,9 @@ After successful configuration, inform the user: 已添加规则:当消息以 "深入思考" 或 "deep thinking" 开头时,自动路由到 claude-opus-4.5 模型。 +配置文件已更新:/root/higress/wasmplugins/model-router.internal.yaml +Higress Gateway 已重启以应用新配置。 + 使用方式: - 中文:深入思考 如何优化这段代码的性能? - English: deep thinking How to optimize this code's performance? @@ -107,42 +154,13 @@ After successful configuration, inform the user: 提示:请确保您的请求中 model 参数设置为 `higress/auto` 才能触发自动路由。 ``` -## API Reference - -### Higress Console Plugin API - -**Get plugin config:** -``` -GET /v1/plugins/{pluginName} -``` - -**Update plugin config:** -``` -PUT /v1/plugins/{pluginName} -Content-Type: application/json -{ - "enabled": true, - "config": { ... } -} -``` - -### model-router Plugin Schema - -```yaml -modelToHeader: x-higress-llm-model -autoRouting: - enable: true - defaultModel: string # fallback model - rules: - - pattern: string # regex pattern - model: string # target model name -``` - ## Error Handling -- If Higress Console is unreachable, inform user to check if the gateway is running -- If pattern conflicts with existing rule, suggest alternative trigger phrases -- If model name is invalid, list available models from configured providers +- **Configuration file not found**: Inform user to check Higress installation directory +- **Pattern conflicts**: Suggest alternative trigger phrases +- **Invalid model name**: List available models from configured providers +- **YAML parsing errors**: Report the specific line/issue and suggest manual review +- **Restart failed**: Provide manual restart instructions ## Examples @@ -155,6 +173,9 @@ autoRouting: 触发方式:以 "深入思考" 或 "deep thinking" 开头发起请求 目标模型:claude-opus-4.5 +配置文件:/root/higress/wasmplugins/model-router.internal.yaml +Higress Gateway 已重启 + 配置已生效。现在您可以这样使用: - 深入思考 这道算法题应该怎么解? - deep thinking What's the best architecture for this system? @@ -169,7 +190,20 @@ autoRouting: 触发方式:以 "写代码" 或 "code:" 开头发起请求 目标模型:qwen-coder +配置文件:/root/higress/wasmplugins/model-router.internal.yaml +Higress Gateway 已重启 + 配置已生效。使用示例: - 写代码 实现一个快速排序算法 - code: implement a binary search tree in Python ``` + +## Implementation Notes + +When implementing this skill: + +1. **Use a YAML library** (like `yaml` in Python or `js-yaml` in Node.js) for safe configuration manipulation +2. **Backup before modification**: Always create a backup of the original config file +3. **Validate YAML**: After modification, validate the YAML syntax before restarting +4. **Wait for restart**: Ensure the gateway is fully started before confirming to user +5. **Handle permissions**: Ensure the process has write access to the config file diff --git a/all-in-one/get-ai-gateway.sh b/all-in-one/get-ai-gateway.sh index 69a73f3..9343b7a 100755 --- a/all-in-one/get-ai-gateway.sh +++ b/all-in-one/get-ai-gateway.sh @@ -294,6 +294,54 @@ installClawdbotSkill() { echo } +# Configure auto-routing in model-router plugin +configureAutoRouting() { + if [ "$ENABLE_AUTO_ROUTING" != "true" ]; then + return 0 + fi + + echo "Configuring auto-routing in model-router plugin..." + + local MODEL_ROUTER_FILE="$ROOT/wasmplugins/model-router.internal.yaml" + + # Wait for the file to be created (it's created when the container starts) + local MAX_WAIT=30 + local WAIT_COUNT=0 + while [ ! -f "$MODEL_ROUTER_FILE" ] && [ $WAIT_COUNT -lt $MAX_WAIT ]; do + sleep 1 + WAIT_COUNT=$((WAIT_COUNT + 1)) + done + + if [ ! -f "$MODEL_ROUTER_FILE" ]; then + echo "Warning: Could not find model-router configuration file at $MODEL_ROUTER_FILE" + echo "Auto-routing will be configured manually later." + return 1 + fi + + # Backup the original file + cp "$MODEL_ROUTER_FILE" "${MODEL_ROUTER_FILE}.backup" + + # Add auto-routing configuration to the defaultConfig section + # The file has this structure: + # spec: + # defaultConfig: + # modelToHeader: x-higress-llm-model + + # We need to add: + # autoRouting: + # enable: true + # defaultModel: qwen-turbo + + # Use sed to insert the auto-routing configuration after modelToHeader line + sed -i "/modelToHeader: x-higress-llm-model/a\\ + autoRouting:\\ + enable: true\\ + defaultModel: $AUTO_ROUTING_DEFAULT_MODEL" "$MODEL_ROUTER_FILE" + + echo "✓ Auto-routing configured with default model: $AUTO_ROUTING_DEFAULT_MODEL" + echo " Configuration file: $MODEL_ROUTER_FILE" +} + # Configuration wizard runConfigWizard() { echo "Provide a key for each LLM provider you want to enable, then press Enter." @@ -610,14 +658,6 @@ writeConfiguration() { ${env}=${!env}" done - # Add auto-routing configuration if enabled - local AUTO_ROUTING_CONFIG="" - if [ "$ENABLE_AUTO_ROUTING" == "true" ]; then - AUTO_ROUTING_CONFIG=" -ENABLE_AUTO_ROUTING=true -AUTO_ROUTING_DEFAULT_MODEL=${AUTO_ROUTING_DEFAULT_MODEL}" - fi - cat <$DATA_FOLDER/$CONFIG_FILENAME MODE=full O11Y=on @@ -625,7 +665,7 @@ CONFIG_TEMPLATE=ai-gateway GATEWAY_HTTP_PORT=${GATEWAY_HTTP_PORT} GATEWAY_HTTPS_PORT=${GATEWAY_HTTPS_PORT} CONSOLE_PORT=${CONSOLE_PORT} -${LLM_CONFIGS}${AUTO_ROUTING_CONFIG} +${LLM_CONFIGS} EOF } @@ -772,6 +812,13 @@ start() { --mount "type=bind,source=$NORMALIZED_DATA_FOLDER_PATH,target=/data" "$IMAGE_REPO:$IMAGE_TAG" >/dev/null if [ $? -eq 0 ]; then + # Wait a moment for the container to generate initial config files + echo "Waiting for gateway to initialize..." + sleep 5 + + # Configure auto-routing if enabled + configureAutoRouting + outputWelcomeMessage fi } From 26bd67bd09f92210d53f753e3b859928b8791e6b Mon Sep 17 00:00:00 2001 From: johnlanni Date: Thu, 29 Jan 2026 09:09:10 +0800 Subject: [PATCH 3/4] fix: update skill to modify config inside container with hot-reload - Use docker exec to modify model-router.internal.yaml inside container - Remove container restart requirement - Use docker cp for safer file modification (copy out, modify, copy back) - Trigger configuration reload by touching the file - Higress hot-reloads config from /data/wasmplugins/ automatically - Add example Python implementation for skill --- .../skill/higress-auto-router/SKILL.md | 170 ++++++++++++------ 1 file changed, 112 insertions(+), 58 deletions(-) diff --git a/all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md b/all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md index accf8ba..7fb580b 100644 --- a/all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md +++ b/all-in-one/clawdbot-integration/skill/higress-auto-router/SKILL.md @@ -6,10 +6,10 @@ Configure automatic model routing based on user's natural language requests. This skill enables users to configure Higress AI Gateway's model-router plugin through natural language commands. When a user describes their routing preference (e.g., "route to claude-opus-4.5 when solving difficult problems"), this skill will: -1. Analyze the user's request to understand the routing intent +1. Analyze user's request to understand the routing intent 2. Generate appropriate regex patterns that are distinctive and easy to remember -3. Modify the model-router configuration file directly at `/data/wasmplugins/model-router.internal.yaml` -4. Restart Higress Gateway to apply the changes +3. Modify model-router configuration file **inside the container** at `/data/wasmplugins/model-router.internal.yaml` +4. Trigger Higress configuration reload (no container restart needed) 5. Confirm the configuration and explain how to trigger the route ## When to Use @@ -21,14 +21,14 @@ Use this skill when: ## Prerequisites -- Higress AI Gateway running -- Access to modify `/data/wasmplugins/model-router.internal.yaml` -- Ability to restart Higress container +- Higress AI Gateway container running +- Container name: `higress-ai-gateway` (default) or user-specified +- Ability to execute `docker exec` commands -## Configuration Files +## Configuration Location -**Higress Gateway Data Directory:** Usually the directory where the container mounts data (default: `/root/higress/`) -**Model Router Config:** `/wasmplugins/model-router.internal.yaml` +**Inside Container:** `/data/wasmplugins/model-router.internal.yaml` +**Default Container Name:** `higress-ai-gateway` ## Workflow @@ -55,20 +55,20 @@ Common mappings: | Image generation | `画图:`, `draw:`, `image:` | `(?i)^(画图:|draw:|image:)` | | Quick answers | `快速回答`, `quick:` | `(?i)^(快速回答|quick:)` | -### Step 3: Find Configuration File +### Step 3: Determine Container Name -The model-router configuration file is typically at one of these locations: -- `/root/higress/wasmplugins/model-router.internal.yaml` (default) -- `/data/wasmplugins/model-router.internal.yaml` (inside container) - -Check these locations and use the first one that exists. +Try to find the Higress container: +1. Default: `higress-ai-gateway` +2. If not found, list running containers and ask user to specify +3. Use `docker ps --filter "name=higress"` to find containers ### Step 4: Read Existing Configuration -Read the current `model-router.internal.yaml` file to understand: -- Current `autoRouting` configuration (if any) -- Existing rules to avoid conflicts -- Default model setting +Read the current `model-router.internal.yaml` file from inside the container: + +```bash +docker exec cat /data/wasmplugins/model-router.internal.yaml +``` Example configuration structure: ```yaml @@ -91,48 +91,48 @@ spec: ### Step 5: Check for Conflicts Before adding a new rule: -- Parse existing rules from the config +- Parse existing rules from config - Check if the new pattern conflicts with existing ones - If conflict detected, suggest alternative trigger phrases -### Step 6: Update Configuration File +### Step 6: Modify Configuration File Inside Container -Modify the YAML file to add/update the autoRouting configuration. Use a YAML parser or carefully structured text replacement. +Use `docker exec` to modify the YAML file directly inside the container: -**Structure:** -```yaml -spec: - defaultConfig: - modelToHeader: x-higress-llm-model - autoRouting: - enable: true - defaultModel: qwen-turbo - rules: - - pattern: (?i)^(深入思考|deep thinking) - model: claude-opus-4.5 - - pattern: (?i)^(写代码|code:) - model: qwen-coder +```bash +# Option 1: Use sed to add rule +docker exec sed -i '/rules:/a\ - pattern: (?i)^(深入思考|deep thinking)\n model: claude-opus-4.5' /data/wasmplugins/model-router.internal.yaml + +# Option 2: Copy file out, modify, copy back (safer) +docker cp :/data/wasmplugins/model-router.internal.yaml /tmp/model-router.yaml +# Edit /tmp/model-router.yaml with new rule +docker cp /tmp/model-router.yaml :/data/wasmplugins/model-router.internal.yaml ``` +**Recommended approach: Copy out, modify, copy in** for safety and proper YAML formatting. + **Important:** -- Ensure proper YAML indentation -- Keep the existing `modelToHeader` configuration +- Ensure proper YAML indentation (2 spaces per level) +- Keep existing `modelToHeader` configuration - Use `(?i)` for case-insensitive patterns - Use `^` to anchor pattern to message start (prevents false matches) -### Step 7: Restart Higress Gateway +### Step 7: Trigger Configuration Reload -After modifying the configuration, restart the Higress container to apply changes: +After modifying the configuration, trigger Higress to reload the plugin configuration **without restarting the container**: ```bash -# If running via Docker -docker restart higress-ai-gateway +# Method 1: Touch the configuration file to trigger reload +docker exec touch /data/wasmplugins/model-router.internal.yaml -# Or via systemd/systemd-managed container -systemctl restart higress-gateway +# Method 2: Use Higress's configuration reload endpoint (if available) +curl -X POST http://localhost:8001/api/v1/plugins/reload + +# Method 3: Send SIGHUP to the Higress process (if supported) +docker exec kill -HUP 1 ``` -Wait a few seconds for the gateway to be ready. +**Note:** Higress watches configuration files in `/data/wasmplugins/` and automatically reloads when they are modified. The modification itself should trigger a reload. If not, use Method 1 (touch) to ensure the file's timestamp is updated. ### Step 8: Confirm to User @@ -144,8 +144,8 @@ After successful configuration, inform the user: 已添加规则:当消息以 "深入思考" 或 "deep thinking" 开头时,自动路由到 claude-opus-4.5 模型。 -配置文件已更新:/root/higress/wasmplugins/model-router.internal.yaml -Higress Gateway 已重启以应用新配置。 +配置文件已更新(容器内):/data/wasmplugins/model-router.internal.yaml +配置已自动重载,无需重启容器。 使用方式: - 中文:深入思考 如何优化这段代码的性能? @@ -156,11 +156,11 @@ Higress Gateway 已重启以应用新配置。 ## Error Handling -- **Configuration file not found**: Inform user to check Higress installation directory +- **Container not found**: List running containers and ask user to specify the correct container name - **Pattern conflicts**: Suggest alternative trigger phrases - **Invalid model name**: List available models from configured providers -- **YAML parsing errors**: Report the specific line/issue and suggest manual review -- **Restart failed**: Provide manual restart instructions +- **YAML parsing errors**: Report the issue and suggest reviewing the file manually +- **Permission errors**: Ensure the Docker socket is accessible and the user has necessary permissions ## Examples @@ -173,8 +173,8 @@ Higress Gateway 已重启以应用新配置。 触发方式:以 "深入思考" 或 "deep thinking" 开头发起请求 目标模型:claude-opus-4.5 -配置文件:/root/higress/wasmplugins/model-router.internal.yaml -Higress Gateway 已重启 +配置文件已更新(容器内):/data/wasmplugins/model-router.internal.yaml +配置已自动重载 配置已生效。现在您可以这样使用: - 深入思考 这道算法题应该怎么解? @@ -190,8 +190,8 @@ Higress Gateway 已重启 触发方式:以 "写代码" 或 "code:" 开头发起请求 目标模型:qwen-coder -配置文件:/root/higress/wasmplugins/model-router.internal.yaml -Higress Gateway 已重启 +配置文件已更新(容器内):/data/wasmplugins/model-router.internal.yaml +配置已自动重载 配置已生效。使用示例: - 写代码 实现一个快速排序算法 @@ -202,8 +202,62 @@ Higress Gateway 已重启 When implementing this skill: -1. **Use a YAML library** (like `yaml` in Python or `js-yaml` in Node.js) for safe configuration manipulation -2. **Backup before modification**: Always create a backup of the original config file -3. **Validate YAML**: After modification, validate the YAML syntax before restarting -4. **Wait for restart**: Ensure the gateway is fully started before confirming to user -5. **Handle permissions**: Ensure the process has write access to the config file +1. **Determine container name**: Use `docker ps` to find the running Higress container +2. **Use docker cp for safety**: Copy the file out, modify with a YAML library, then copy it back +3. **Validate YAML**: Before copying back, validate the YAML syntax +4. **Trigger reload**: Higress automatically watches for file changes in `/data/wasmplugins/` +5. **No restart needed**: Configuration changes are hot-reloaded by Higress +6. **Handle timestamps**: Touch the file after modification to ensure Higress detects the change + +### Example Python Implementation + +```python +import subprocess +import yaml +import tempfile +import os + +CONTAINER_NAME = "higress-ai-gateway" +CONFIG_PATH = "/data/wasmplugins/model-router.internal.yaml" + +def read_container_config(): + result = subprocess.run( + ["docker", "exec", CONTAINER_NAME, "cat", CONFIG_PATH], + capture_output=True, + text=True + ) + return yaml.safe_load(result.stdout) + +def write_container_config(config): + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(config, f, default_flow_style=False) + temp_path = f.name + + # Copy back to container + subprocess.run(["docker", "cp", temp_path, f"{CONTAINER_NAME}:{CONFIG_PATH}"]) + + # Clean up + os.unlink(temp_path) + + # Touch file to trigger reload + subprocess.run(["docker", "exec", CONTAINER_NAME, "touch", CONFIG_PATH]) + +def add_routing_rule(pattern, model): + config = read_container_config() + + # Ensure autoRouting exists + if 'autoRouting' not in config['spec']['defaultConfig']: + config['spec']['defaultConfig']['autoRouting'] = { + 'enable': True, + 'defaultModel': 'qwen-turbo', + 'rules': [] + } + + # Add rule + config['spec']['defaultConfig']['autoRouting']['rules'].append({ + 'pattern': pattern, + 'model': model + }) + + write_container_config(config) +``` From 9649654092f1988b34df75178851e0bdb2e749ae Mon Sep 17 00:00:00 2001 From: johnlanni Date: Thu, 29 Jan 2026 09:47:12 +0800 Subject: [PATCH 4/4] fix: address cursor bot review issues Plugin fixes (index.ts): - Fix double prefix issue: check if modelId already starts with 'higress/' - Fix spinner reuse: create separate spinner (spin2) for fetching models - Add .trim() to autoRoutingDefaultModel input Plugin manifest fix (clawdbot.plugin.json): - Remove duplicate configSchema to avoid inconsistency with emptyPluginConfigSchema() Shell script fixes (get-ai-gateway.sh): - Add loadSavedConfig() to read ENABLE_AUTO_ROUTING from config on restart - Use awk instead of sed for cross-platform compatibility (macOS/Linux) - Add docker exec touch to trigger hot-reload without container restart - Create generic installFiles() to reduce duplication - Simplify user instructions in outputWelcomeMessage() - Save AUTO_ROUTING config to config file for persistence --- .../plugin/clawdbot.plugin.json | 26 +-- .../clawdbot-integration/plugin/index.ts | 40 ++-- all-in-one/get-ai-gateway.sh | 200 +++++++++--------- 3 files changed, 124 insertions(+), 142 deletions(-) diff --git a/all-in-one/clawdbot-integration/plugin/clawdbot.plugin.json b/all-in-one/clawdbot-integration/plugin/clawdbot.plugin.json index a54afd3..1dca596 100644 --- a/all-in-one/clawdbot-integration/plugin/clawdbot.plugin.json +++ b/all-in-one/clawdbot-integration/plugin/clawdbot.plugin.json @@ -3,29 +3,5 @@ "name": "Higress AI Gateway", "description": "Model provider plugin for Higress AI Gateway with auto-routing support", "providers": ["higress"], - "skills": ["higress-auto-router"], - "configSchema": { - "type": "object", - "additionalProperties": false, - "properties": { - "gatewayUrl": { - "type": "string", - "description": "Higress AI Gateway URL (e.g., http://localhost:8080)" - }, - "consoleUrl": { - "type": "string", - "description": "Higress Console URL for configuration (e.g., http://localhost:8001)" - } - } - }, - "uiHints": { - "gatewayUrl": { - "label": "Gateway URL", - "placeholder": "http://localhost:8080" - }, - "consoleUrl": { - "label": "Console URL", - "placeholder": "http://localhost:8001" - } - } + "skills": ["higress-auto-router"] } diff --git a/all-in-one/clawdbot-integration/plugin/index.ts b/all-in-one/clawdbot-integration/plugin/index.ts index bbab912..5ffb8a9 100644 --- a/all-in-one/clawdbot-integration/plugin/index.ts +++ b/all-in-one/clawdbot-integration/plugin/index.ts @@ -30,16 +30,17 @@ const DEFAULT_MODEL_IDS = [ "glm-4", ] as const; -function normalizeGatewayUrl(value: string): string { +function normalizeBaseUrl(value: string): string { const trimmed = value.trim(); if (!trimmed) return DEFAULT_GATEWAY_URL; let normalized = trimmed; while (normalized.endsWith("/")) normalized = normalized.slice(0, -1); + if (!normalized.endsWith("/v1")) normalized = `${normalized}/v1`; return normalized; } function validateUrl(value: string): string | undefined { - const normalized = normalizeGatewayUrl(value); + const normalized = normalizeBaseUrl(value); try { new URL(normalized); } catch { @@ -92,7 +93,7 @@ async function fetchAvailableModels(consoleUrl: string): Promise { signal: AbortSignal.timeout(5000), }); if (response.ok) { - const data = await response.json() as { data?: { model?: string }[] }; + const data = (await response.json()) as { data?: { model?: string }[] }; if (data.data && Array.isArray(data.data)) { return data.data .map((route: { model?: string }) => route.model) @@ -129,7 +130,7 @@ const higressPlugin = { initialValue: DEFAULT_GATEWAY_URL, validate: validateUrl, }); - const gatewayUrl = normalizeGatewayUrl(gatewayUrlInput); + const gatewayUrl = normalizeBaseUrl(gatewayUrlInput); // Step 2: Get Console URL (for auto-router configuration) const consoleUrlInput = await ctx.prompter.text({ @@ -137,9 +138,9 @@ const higressPlugin = { initialValue: DEFAULT_CONSOLE_URL, validate: validateUrl, }); - const consoleUrl = normalizeGatewayUrl(consoleUrlInput); + const consoleUrl = normalizeBaseUrl(consoleUrlInput); - // Step 3: Test connection + // Step 3: Test connection (create a new spinner) const spin = ctx.prompter.progress("Testing gateway connection…"); const isConnected = await testGatewayConnection(gatewayUrl); if (!isConnected) { @@ -164,13 +165,13 @@ const higressPlugin = { }); const apiKey = apiKeyInput.trim() || "higress-local"; - // Step 5: Fetch available models or use defaults - spin.update("Fetching available models…"); + // Step 5: Fetch available models (create a new spinner) + const spin2 = ctx.prompter.progress("Fetching available models…"); const fetchedModels = await fetchAvailableModels(consoleUrl); const defaultModels = fetchedModels.length > 0 ? ["higress/auto", ...fetchedModels] : DEFAULT_MODEL_IDS; - spin.stop(); + spin2.stop(); // Step 6: Let user customize model list const modelInput = await ctx.prompter.text({ @@ -182,16 +183,23 @@ const higressPlugin = { const modelIds = parseModelIds(modelInput); const hasAutoModel = modelIds.includes("higress/auto"); - const defaultModelId = hasAutoModel ? "higress/auto" : (modelIds[0] ?? "qwen-turbo"); - const defaultModelRef = `higress/${defaultModelId}`; + + // FIX: Avoid double prefix - if modelId already starts with provider, don't add prefix again + const defaultModelId = hasAutoModel + ? "higress/auto" + : (modelIds[0] ?? "qwen-turbo"); + const defaultModelRef = defaultModelId.startsWith("higress/") + ? defaultModelId + : `higress/${defaultModelId}`; // Step 7: Configure default model for auto-routing let autoRoutingDefaultModel = "qwen-turbo"; if (hasAutoModel) { - autoRoutingDefaultModel = await ctx.prompter.text({ + const autoRoutingModelInput = await ctx.prompter.text({ message: "Default model for auto-routing (when no rule matches)", initialValue: "qwen-turbo", }); + autoRoutingDefaultModel = autoRoutingModelInput.trim(); // FIX: Add trim() here } return { @@ -220,7 +228,13 @@ const higressPlugin = { agents: { defaults: { models: Object.fromEntries( - modelIds.map((modelId) => [`higress/${modelId}`, {}]), + modelIds.map((modelId) => { + // FIX: Avoid double prefix - only add provider prefix if not already present + const modelRef = modelId.startsWith("higress/") + ? modelId + : `higress/${modelId}`; + return [modelRef, {}]; + }), ), }, }, diff --git a/all-in-one/get-ai-gateway.sh b/all-in-one/get-ai-gateway.sh index 9343b7a..a429ff2 100755 --- a/all-in-one/get-ai-gateway.sh +++ b/all-in-one/get-ai-gateway.sh @@ -86,6 +86,15 @@ normalizePath() { echo "$(cygpath -m "$1")" } +# Cross-platform sed in-place edit (macOS vs Linux) +sedInPlace() { + if [ "$OS" == "darwin" ]; then + sed -i '' "$@" + else + sed -i "$@" + fi +} + parseArgs() { resetEnv @@ -150,6 +159,22 @@ resetEnv() { LLM_ENVS=() } +# Load saved configuration from config file +loadSavedConfig() { + local CONFIG_FILE="$ROOT/$CONFIG_FILENAME" + if [ -f "$CONFIG_FILE" ]; then + # Read ENABLE_AUTO_ROUTING and AUTO_ROUTING_DEFAULT_MODEL from config + local saved_auto_routing=$(grep "^ENABLE_AUTO_ROUTING=" "$CONFIG_FILE" | cut -d'=' -f2) + local saved_default_model=$(grep "^AUTO_ROUTING_DEFAULT_MODEL=" "$CONFIG_FILE" | cut -d'=' -f2) + if [ -n "$saved_auto_routing" ]; then + ENABLE_AUTO_ROUTING="$saved_auto_routing" + fi + if [ -n "$saved_default_model" ]; then + AUTO_ROUTING_DEFAULT_MODEL="$saved_default_model" + fi + fi +} + # Check if Clawdbot is installed checkClawdbot() { if [ -d "$CLAWDBOT_WORKSPACE" ]; then @@ -158,6 +183,34 @@ checkClawdbot() { return 1 } +# Generic function to install files to a destination +installFiles() { + local SRC_DIR="$1" + local DEST_DIR="$2" + local NAME="$3" + + if [ ! -d "$SRC_DIR" ]; then + echo "Warning: $NAME source not found at $SRC_DIR" + return 1 + fi + + echo "Installing $NAME..." + + # Create parent directory if not exists + mkdir -p "$(dirname "$DEST_DIR")" + + # Remove existing if present + if [ -d "$DEST_DIR" ]; then + echo "$NAME already exists, updating..." + rm -rf "$DEST_DIR" + fi + + cp -r "$SRC_DIR" "$DEST_DIR" + + echo "✓ $NAME installed at: $DEST_DIR" + return 0 +} + # Configure Clawdbot integration configureClawdbotIntegration() { if ! checkClawdbot; then @@ -208,93 +261,17 @@ configureClawdbotIntegration() { ;; esac - # Install Clawdbot plugin and skill - installClawdbotPlugin - installClawdbotSkill -} - -# Install Clawdbot plugin -installClawdbotPlugin() { - if [ ! -d "$CLAWDBOT_INTEGRATION_DIR/plugin" ]; then - echo "Warning: Plugin source not found at $CLAWDBOT_INTEGRATION_DIR/plugin" - return 1 - fi - - echo "Installing Higress AI Gateway plugin for Clawdbot..." - - # Create extensions directory if not exists - mkdir -p "$CLAWDBOT_EXTENSIONS_DIR" - - # Copy plugin files - local PLUGIN_DEST="$CLAWDBOT_EXTENSIONS_DIR/higress-ai-gateway" - if [ -d "$PLUGIN_DEST" ]; then - echo "Plugin already exists, updating..." - rm -rf "$PLUGIN_DEST" - fi - - cp -r "$CLAWDBOT_INTEGRATION_DIR/plugin" "$PLUGIN_DEST" - - echo "✓ Plugin installed at: $PLUGIN_DEST" - - # Generate plugin configuration snippet - local GATEWAY_URL="http://localhost:$GATEWAY_HTTP_PORT" - local CONSOLE_URL="http://localhost:$CONSOLE_PORT" + # Install Clawdbot plugin and skill using generic function + installFiles "$CLAWDBOT_INTEGRATION_DIR/plugin" "$CLAWDBOT_EXTENSIONS_DIR/higress-ai-gateway" "Higress AI Gateway plugin" + installFiles "$CLAWDBOT_INTEGRATION_DIR/skill/higress-auto-router" "$CLAWDBOT_SKILLS_DIR/higress-auto-router" "Higress Auto Router skill" echo - echo "To complete plugin setup, run:" + echo "To complete Clawdbot setup, run:" echo " clawdbot models auth login --provider higress" echo - echo "Or add to your Clawdbot config (config.yaml):" - echo - cat < "$TEMP_FILE" + + # Replace original with modified version + mv "$TEMP_FILE" "$MODEL_ROUTER_FILE" + + # Trigger config reload inside container by touching the file + $DOCKER_COMMAND exec "$CONTAINER_NAME" touch /data/wasmplugins/model-router.internal.yaml 2>/dev/null || true echo "✓ Auto-routing configured with default model: $AUTO_ROUTING_DEFAULT_MODEL" echo " Configuration file: $MODEL_ROUTER_FILE" @@ -658,6 +639,14 @@ writeConfiguration() { ${env}=${!env}" done + # Save auto-routing configuration + local AUTO_ROUTING_CONFIG="" + if [ "$ENABLE_AUTO_ROUTING" == "true" ]; then + AUTO_ROUTING_CONFIG=" +ENABLE_AUTO_ROUTING=true +AUTO_ROUTING_DEFAULT_MODEL=${AUTO_ROUTING_DEFAULT_MODEL}" + fi + cat <$DATA_FOLDER/$CONFIG_FILENAME MODE=full O11Y=on @@ -665,7 +654,7 @@ CONFIG_TEMPLATE=ai-gateway GATEWAY_HTTP_PORT=${GATEWAY_HTTP_PORT} GATEWAY_HTTPS_PORT=${GATEWAY_HTTPS_PORT} CONSOLE_PORT=${CONSOLE_PORT} -${LLM_CONFIGS} +${LLM_CONFIGS}${AUTO_ROUTING_CONFIG} EOF } @@ -701,7 +690,7 @@ outputWelcomeMessage() { echo " http://localhost:$GATEWAY_HTTP_PORT/v1/chat/completions" echo - # Show auto-routing info if enabled + # Show auto-routing info if enabled (read from saved config) if [ "$ENABLE_AUTO_ROUTING" == "true" ]; then echo "=======================================================" echo " Auto-Routing Mode " @@ -757,14 +746,14 @@ outputWelcomeMessage() { echo " Clawdbot Integration " echo "=======================================================" echo - echo "Higress AI Gateway plugin and skill have been installed." - echo - echo "To complete setup, run:" + echo "To configure Clawdbot, run:" echo " clawdbot models auth login --provider higress" echo - echo "To configure auto-routing rules, tell Clawdbot:" - echo " '我希望在解决困难问题时路由到claude-opus-4.5的模型'" - echo + if [ "$ENABLE_AUTO_ROUTING" == "true" ]; then + echo "To configure auto-routing rules, tell Clawdbot:" + echo " '我希望在解决困难问题时路由到claude-opus-4.5的模型'" + echo + fi fi echo @@ -792,6 +781,9 @@ tryAwake() { fi fi + # Load saved config to show auto-routing info correctly on restart + loadSavedConfig + outputWelcomeMessage exit 0 }