fix(deploy): drop DASHSCOPE_API_KEY requirement so Docker boots with no LLM key configured (#99)

This commit is contained in:
matevip 2026-05-11 20:33:44 +08:00
parent a1ca7ab534
commit 08fcab12d1
7 changed files with 38 additions and 10 deletions

View File

@ -77,7 +77,10 @@ services:
DB_NAME: ${DB_NAME:-mateclaw} DB_NAME: ${DB_NAME:-mateclaw}
DB_USERNAME: ${DB_USERNAME:-mateclaw} DB_USERNAME: ${DB_USERNAME:-mateclaw}
DB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required in .env} DB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required in .env}
DASHSCOPE_API_KEY: ${DASHSCOPE_API_KEY:-} # LLM provider keys (DashScope / OpenAI / Anthropic / DeepSeek / Kimi / …) are
# NOT configured via env vars. After startup, add providers in the admin UI:
# Settings → Models → Add Provider
# Keys are stored in mate_model_provider and hot-reloaded.
SERPER_API_KEY: ${SERPER_API_KEY:-} SERPER_API_KEY: ${SERPER_API_KEY:-}
JWT_SECRET: ${JWT_SECRET:-} JWT_SECRET: ${JWT_SECRET:-}
MATECLAW_CORS_ALLOWED_ORIGINS: ${MATECLAW_CORS_ALLOWED_ORIGINS:-} MATECLAW_CORS_ALLOWED_ORIGINS: ${MATECLAW_CORS_ALLOWED_ORIGINS:-}

View File

@ -15,13 +15,19 @@ import org.springframework.scheduling.annotation.EnableScheduling;
* @author MateClaw Team * @author MateClaw Team
*/ */
@SpringBootApplication(exclude = { @SpringBootApplication(exclude = {
// 禁用 Spring AI MCP Client 自动配置 McpClientManager 自行管理生命周期 // Disable Spring AI MCP Client auto-configuration (lifecycle owned by McpClientManager).
org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration.class, org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration.class,
org.springframework.ai.mcp.client.common.autoconfigure.McpToolCallbackAutoConfiguration.class, org.springframework.ai.mcp.client.common.autoconfigure.McpToolCallbackAutoConfiguration.class,
org.springframework.ai.mcp.client.common.autoconfigure.StdioTransportAutoConfiguration.class, org.springframework.ai.mcp.client.common.autoconfigure.StdioTransportAutoConfiguration.class,
org.springframework.ai.mcp.client.common.autoconfigure.annotations.McpClientAnnotationScannerAutoConfiguration.class, org.springframework.ai.mcp.client.common.autoconfigure.annotations.McpClientAnnotationScannerAutoConfiguration.class,
org.springframework.ai.mcp.client.httpclient.autoconfigure.SseHttpClientTransportAutoConfiguration.class, org.springframework.ai.mcp.client.httpclient.autoconfigure.SseHttpClientTransportAutoConfiguration.class,
org.springframework.ai.mcp.client.httpclient.autoconfigure.StreamableHttpHttpClientTransportAutoConfiguration.class, org.springframework.ai.mcp.client.httpclient.autoconfigure.StreamableHttpHttpClientTransportAutoConfiguration.class,
// DashScopeAgent is the Bailian "Application Agent" (Bailian-hosted prompt+tool app),
// not the chat model. We don't use it model configuration is admin-UI driven and
// built by AgentDashScopeChatModelBuilder. Its auto-config strictly requires
// spring.ai.dashscope.api-key to be non-empty at startup, which makes the whole
// ApplicationContext fail when users deploy via Docker without setting the key.
com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAgentAutoConfiguration.class,
}) })
@EnableScheduling @EnableScheduling
@MapperScan("vip.mate.**.repository") @MapperScan("vip.mate.**.repository")

View File

@ -172,7 +172,7 @@ public class AgentDashScopeChatModelBuilder implements ChatModelBuilder {
} }
if (!modelProviderService.hasUsableApiKey(apiKey)) { if (!modelProviderService.hasUsableApiKey(apiKey)) {
throw new MateClawException("err.agent.dashscope_key_missing", throw new MateClawException("err.agent.dashscope_key_missing",
"DashScope API Key 未配置,请在模型设置中填写 dashscope 的 API Key或设置 DASHSCOPE_API_KEY 环境变量"); "DashScope API Key 未配置,请在「设置 → 模型 → 添加供应商」中为 dashscope 填写 API Key");
} }
builder.apiKey(apiKey.trim()); builder.apiKey(apiKey.trim());

View File

@ -551,7 +551,16 @@ public class ModelProviderService {
return false; return false;
} }
String normalized = apiKey.trim(); String normalized = apiKey.trim();
// Reject masked display values (the UI sends "********" when the user
// didn't re-type the key) and known placeholder sentinels without this
// check, the chat / embedding fallback chain happily forwards the
// placeholder to the LLM endpoint, which then returns a 401 at request
// time. "configure-in-admin-ui" is the application.yml default that
// keeps DashScopeChatAutoConfiguration happy at startup when no env var
// is set; "your-*-api-key-here" are legacy sentinels from earlier
// .env.example / application.yml versions.
return !normalized.contains("*") return !normalized.contains("*")
&& !"configure-in-admin-ui".equalsIgnoreCase(normalized)
&& !"your-dashscope-api-key-here".equalsIgnoreCase(normalized) && !"your-dashscope-api-key-here".equalsIgnoreCase(normalized)
&& !"your-api-key-here".equalsIgnoreCase(normalized); && !"your-api-key-here".equalsIgnoreCase(normalized);
} }

View File

@ -8,10 +8,11 @@ import vip.mate.llm.service.ModelProviderService;
* DashScope vision provider uses {@code qwen-vl-max} via the * DashScope vision provider uses {@code qwen-vl-max} via the
* OpenAI-compatible endpoint at {@code /compatible-mode/v1/chat/completions}. * OpenAI-compatible endpoint at {@code /compatible-mode/v1/chat/completions}.
* *
* <p>Default for the Chinese cloud rollout: API keys are typically * <p>Sits at the front of the auto-detect chain when a DashScope provider row
* available (DASHSCOPE_API_KEY is mandatory for the rest of the * is configured in the admin UI: per-image cost is the lowest of the supported
* platform) and per-image cost is the lowest of the supported vendors, * vendors, and DashScope is the most common first provider added on the
* so this provider sits at the front of the auto-detect chain. * Chinese cloud rollout. Falls back to the next provider in the chain when no
* DashScope API key is available.
*/ */
@Component @Component
public class DashScopeVisionProvider extends OpenAiCompatibleVisionProvider { public class DashScopeVisionProvider extends OpenAiCompatibleVisionProvider {

View File

@ -67,10 +67,19 @@ spring:
enabled: ${H2_CONSOLE_ENABLED:false} enabled: ${H2_CONSOLE_ENABLED:false}
path: /h2-console path: /h2-console
# Spring AI Alibaba (DashScope) - Spring AI Alibaba 1.1.x 配置路径 # Spring AI Alibaba (DashScope) — Spring AI Alibaba 1.1.x configuration path.
#
# The api-key here is only consumed by Spring AI Alibaba's auto-configured beans
# as a *fallback*. The real source of truth for every provider/key/model is the
# admin UI ("Settings → Models", persisted in mate_model_provider /
# mate_model_config); AgentDashScopeChatModelBuilder resolves the key per-request
# from the provider row first, only falling back to this property when the row
# is incomplete. The placeholder default keeps DashScopeChatAutoConfiguration
# happy at startup when no env var is set (Docker / fresh install) — leave it
# alone unless you know what you're doing.
ai: ai:
dashscope: dashscope:
api-key: ${DASHSCOPE_API_KEY:your-dashscope-api-key-here} api-key: ${DASHSCOPE_API_KEY:configure-in-admin-ui}
chat: chat:
options: options:
model: qwen-max model: qwen-max

View File

@ -7,7 +7,7 @@
## 前置条件 ## 前置条件
1. 后端启动:`cd mateclaw-server && mvn spring-boot:run`(需设置 `DASHSCOPE_API_KEY` 1. 后端启动:`cd mateclaw-server && mvn spring-boot:run`(零环境变量,启动后到「设置 → 模型」加 LLM 供应商
2. 前端启动:`cd mateclaw-ui && pnpm dev` 2. 前端启动:`cd mateclaw-ui && pnpm dev`
3. 访问 http://localhost:5173登录 3. 访问 http://localhost:5173登录