mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
refactor(llm): extract testable embedding protocol routing + drop dead fromProviderId
This commit is contained in:
parent
70a599e403
commit
a6b5e3b515
@ -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.
|
||||
* <p>
|
||||
* 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);
|
||||
|
||||
|
||||
@ -6,13 +6,14 @@ package vip.mate.llm.model;
|
||||
* 与 {@link ModelProtocol}(Chat 协议)分离——chat 和 embedding 在同一个 Provider 下
|
||||
* 可能走不同的请求格式:
|
||||
* <ul>
|
||||
* <li>DashScope 的 embedding endpoint 是专用 path(/api/v1/services/embeddings/text-embedding/text-embedding)</li>
|
||||
* <li>DashScope 的 embedding endpoint 是专用 path
|
||||
* (/api/v1/services/embeddings/text-embedding/text-embedding)</li>
|
||||
* <li>OpenAI 兼容协议的 embedding 统一走 /v1/embeddings</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <b>注意</b>:{@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 协议。
|
||||
* <ul>
|
||||
* <li>dashscope / 任何包含 "dashscope" / "qwen" / "aliyun" 的 → DASHSCOPE_EMBEDDING</li>
|
||||
* <li>其他(openai / deepseek / kimi / zhipu / moonshot / 任何 OpenAI 兼容) → OPENAI_EMBEDDING</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static EmbeddingProtocol fromProviderId(String providerId) {
|
||||
if (providerId == null) return OPENAI_EMBEDDING;
|
||||
String p = providerId.toLowerCase().trim();
|
||||
if (p.contains("dashscope") || p.contains("qwen") || p.contains("aliyun")) {
|
||||
return DASHSCOPE_EMBEDDING;
|
||||
}
|
||||
return OPENAI_EMBEDDING;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
package vip.mate.llm.embedding;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import vip.mate.llm.model.EmbeddingProtocol;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Routing tests for {@link EmbeddingModelFactory#resolveEmbeddingProtocol(String)}.
|
||||
* The dashscope-compat regression (#166) lives in the OpenAIChatModel branch.
|
||||
*/
|
||||
class EmbeddingModelFactoryRoutingTest {
|
||||
|
||||
@Test
|
||||
void dashscopeChatModel_routesToNativeEmbedding() {
|
||||
assertEquals(EmbeddingProtocol.DASHSCOPE_EMBEDDING,
|
||||
EmbeddingModelFactory.resolveEmbeddingProtocol("DashScopeChatModel"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void openaiChatModel_routesToOpenAiCompat() {
|
||||
// dashscope-compat carries chatModel='OpenAIChatModel' — must NOT take the native path
|
||||
assertEquals(EmbeddingProtocol.OPENAI_EMBEDDING,
|
||||
EmbeddingModelFactory.resolveEmbeddingProtocol("OpenAIChatModel"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void anthropicChatModel_routesToOpenAiCompat() {
|
||||
assertEquals(EmbeddingProtocol.OPENAI_EMBEDDING,
|
||||
EmbeddingModelFactory.resolveEmbeddingProtocol("AnthropicChatModel"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullChatModel_routesToOpenAiCompat() {
|
||||
assertEquals(EmbeddingProtocol.OPENAI_EMBEDDING,
|
||||
EmbeddingModelFactory.resolveEmbeddingProtocol(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void blankChatModel_routesToOpenAiCompat() {
|
||||
assertEquals(EmbeddingProtocol.OPENAI_EMBEDDING,
|
||||
EmbeddingModelFactory.resolveEmbeddingProtocol(""));
|
||||
assertEquals(EmbeddingProtocol.OPENAI_EMBEDDING,
|
||||
EmbeddingModelFactory.resolveEmbeddingProtocol(" "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void chatModelMatchIsCaseInsensitiveAndTrimmed() {
|
||||
// Mirror ModelProtocol.fromChatModel's normalization: equalsIgnoreCase + trim.
|
||||
assertEquals(EmbeddingProtocol.DASHSCOPE_EMBEDDING,
|
||||
EmbeddingModelFactory.resolveEmbeddingProtocol("dashscopechatmodel"));
|
||||
assertEquals(EmbeddingProtocol.DASHSCOPE_EMBEDDING,
|
||||
EmbeddingModelFactory.resolveEmbeddingProtocol(" DashScopeChatModel "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownChatModel_fallsBackToOpenAiCompat() {
|
||||
// Future / custom chatModel strings default to OpenAI-compatible (safe default).
|
||||
assertEquals(EmbeddingProtocol.OPENAI_EMBEDDING,
|
||||
EmbeddingModelFactory.resolveEmbeddingProtocol("CustomGatewayChatModel"));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user