diff --git a/mateclaw-server/src/main/java/vip/mate/llm/embedding/EmbeddingModelFactory.java b/mateclaw-server/src/main/java/vip/mate/llm/embedding/EmbeddingModelFactory.java index 1b60c2b6..dc5884d8 100644 --- a/mateclaw-server/src/main/java/vip/mate/llm/embedding/EmbeddingModelFactory.java +++ b/mateclaw-server/src/main/java/vip/mate/llm/embedding/EmbeddingModelFactory.java @@ -105,6 +105,25 @@ public class EmbeddingModelFactory { cache.clear(); } + /** + * Pick the embedding protocol from the provider's {@code chatModel} column. + * Package-private for unit testing. + *
+ * dashscope-compat carries 'dashscope' in its providerId but is wired with + * OpenAIChatModel + compatible-mode base URL, so substring-matching the + * providerId routed it to the native DashScope embedding endpoint and 404'd. + * Using the {@code chatModel} column matches {@link ModelProtocol#fromChatModel} + * for the chat path — same signal, same case-insensitive trim semantics. + */ + static EmbeddingProtocol resolveEmbeddingProtocol(String chatModel) { + if (chatModel == null || chatModel.isBlank()) { + return EmbeddingProtocol.OPENAI_EMBEDDING; + } + return "DashScopeChatModel".equalsIgnoreCase(chatModel.trim()) + ? EmbeddingProtocol.DASHSCOPE_EMBEDDING + : EmbeddingProtocol.OPENAI_EMBEDDING; + } + // ==================== 内部实现 ==================== private EmbeddingModel doBuild(ModelConfigEntity modelConfig) { @@ -114,14 +133,7 @@ public class EmbeddingModelFactory { "Embedding provider '" + modelConfig.getProvider() + "' not found in mate_model_provider"); } - // Use chatModel column (same signal as ModelProtocol.fromChatModel) rather than - // providerId substring matching. dashscope-compat has "dashscope" in its id but - // uses OpenAIChatModel + compatible-mode URL — routing it to DASHSCOPE_EMBEDDING - // causes DashScopeApi to construct a native path that returns 404 against the - // compat base URL. - EmbeddingProtocol protocol = "DashScopeChatModel".equals(provider.getChatModel()) - ? EmbeddingProtocol.DASHSCOPE_EMBEDDING - : EmbeddingProtocol.OPENAI_EMBEDDING; + EmbeddingProtocol protocol = resolveEmbeddingProtocol(provider.getChatModel()); log.info("[EmbeddingFactory] Building embedding model: provider={}, chatModel={}, model={}, protocol={}", provider.getProviderId(), provider.getChatModel(), modelConfig.getModelName(), protocol); diff --git a/mateclaw-server/src/main/java/vip/mate/llm/model/EmbeddingProtocol.java b/mateclaw-server/src/main/java/vip/mate/llm/model/EmbeddingProtocol.java index a6530f88..62660697 100644 --- a/mateclaw-server/src/main/java/vip/mate/llm/model/EmbeddingProtocol.java +++ b/mateclaw-server/src/main/java/vip/mate/llm/model/EmbeddingProtocol.java @@ -6,13 +6,14 @@ package vip.mate.llm.model; * 与 {@link ModelProtocol}(Chat 协议)分离——chat 和 embedding 在同一个 Provider 下 * 可能走不同的请求格式: *
- * 注意:{@link EmbeddingModelFactory} 现在优先通过 {@code chatModel} 列判断协议 - * (与 {@link ModelProtocol#fromChatModel} 保持一致),{@link #fromProviderId} 已不再使用。 - * 保留该方法仅作参考;不要在新代码中调用它。 + * Routing happens in {@code EmbeddingModelFactory.resolveEmbeddingProtocol} + * via the {@code chatModel} column on {@link ModelProviderEntity}, matching + * {@link ModelProtocol#fromChatModel}. * * @author MateClaw Team */ @@ -30,20 +31,4 @@ public enum EmbeddingProtocol { public String getId() { return id; } - - /** - * 从 providerId 推断 embedding 协议。 - *