diff --git a/src/main/java/cn/van/business/util/ds/SocialMediaLlmClient.java b/src/main/java/cn/van/business/util/ds/SocialMediaLlmClient.java index 0aa225f..d9481d5 100644 --- a/src/main/java/cn/van/business/util/ds/SocialMediaLlmClient.java +++ b/src/main/java/cn/van/business/util/ds/SocialMediaLlmClient.java @@ -4,6 +4,8 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.http.Method; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; @@ -15,8 +17,8 @@ import java.util.HashMap; import java.util.Map; /** - * 社媒文案/关键词所用大模型客户端:支持本地 Ollama 或与 OpenAI 兼容的 HTTP 接口(含远程 API、Ollama /v1 等)。 - * 配置存 Redis,与若依后台「大模型接入」一致;未配置时走 {@link OllamaClientUtil} 的 yml 默认。 + * 社媒文案/关键词所用大模型客户端:支持本地 Ollama 或与 OpenAI 兼容的 HTTP 接口。 + * 多套配置存 Redis(JSON),通过 activeId 选择当前使用的一套;无激活或空存储时走 {@link OllamaClientUtil} 的 yml 默认。 */ @Component public class SocialMediaLlmClient { @@ -24,6 +26,7 @@ public class SocialMediaLlmClient { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SocialMediaLlmClient.class); private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final String LLM_PROFILES_STORE_KEY = "social_media:llm:profiles_store"; private static final String KEY_MODE = "social_media:llm:mode"; private static final String KEY_BASE_URL = "social_media:llm:base_url"; private static final String KEY_API_KEY = "social_media:llm:api_key"; @@ -43,28 +46,77 @@ public class SocialMediaLlmClient { return ollamaClientUtil.getResponse(inputText); } - String mode = trimOrNull(redisTemplate.opsForValue().get(KEY_MODE)); - String baseUrl = trimOrNull(redisTemplate.opsForValue().get(KEY_BASE_URL)); - String apiKey = trimOrNull(redisTemplate.opsForValue().get(KEY_API_KEY)); - String model = trimOrNull(redisTemplate.opsForValue().get(KEY_MODEL)); - - if (mode == null && baseUrl == null && model == null && apiKey == null) { + ResolvedLlm cfg = resolveConfig(); + if (cfg == null) { return ollamaClientUtil.getResponse(inputText); } - String effectiveMode = mode != null ? mode.toLowerCase() : MODE_OLLAMA; + String effectiveMode = cfg.mode != null ? cfg.mode.toLowerCase() : MODE_OLLAMA; if (MODE_OPENAI.equals(effectiveMode)) { - if (StrUtil.isBlank(baseUrl)) { + if (StrUtil.isBlank(cfg.baseUrl)) { throw new IOException("OpenAI 兼容模式需在后台配置完整的 Chat Completions 地址"); } - if (StrUtil.isBlank(model)) { + if (StrUtil.isBlank(cfg.model)) { throw new IOException("OpenAI 兼容模式需在后台配置模型名称"); } - return callOpenAiCompatible(baseUrl, apiKey, model, inputText); + return callOpenAiCompatible(cfg.baseUrl, cfg.apiKey, cfg.model, inputText); } - return ollamaClientUtil.getResponse(inputText, baseUrl, model); + return ollamaClientUtil.getResponse(inputText, cfg.baseUrl, cfg.model); + } + + private ResolvedLlm resolveConfig() { + try { + String raw = redisTemplate.opsForValue().get(LLM_PROFILES_STORE_KEY); + if (StrUtil.isNotBlank(raw)) { + JSONObject store = JSON.parseObject(raw); + if (store != null) { + String activeId = store.getString("activeId"); + if (StrUtil.isBlank(activeId)) { + return null; + } + JSONObject profiles = store.getJSONObject("profiles"); + if (profiles == null || profiles.isEmpty()) { + return null; + } + JSONObject p = profiles.getJSONObject(activeId); + if (p == null) { + log.warn("大模型 activeId={} 在 profiles 中不存在", activeId); + return null; + } + ResolvedLlm r = new ResolvedLlm(); + r.mode = trimOrNull(p.getString("mode")); + if (r.mode == null) { + r.mode = MODE_OLLAMA; + } + r.baseUrl = trimOrNull(p.getString("baseUrl")); + r.model = trimOrNull(p.getString("model")); + r.apiKey = trimOrNull(p.getString("apiKey")); + return r; + } + } + } catch (Exception e) { + log.warn("解析多套 LLM 配置失败,尝试旧版单键: {}", e.getMessage()); + } + return resolveLegacySingleKeys(); + } + + /** 兼容尚未迁移的旧版 Redis 四键 */ + private ResolvedLlm resolveLegacySingleKeys() { + String mode = trimOrNull(redisTemplate.opsForValue().get(KEY_MODE)); + String baseUrl = trimOrNull(redisTemplate.opsForValue().get(KEY_BASE_URL)); + String model = trimOrNull(redisTemplate.opsForValue().get(KEY_MODEL)); + String apiKey = trimOrNull(redisTemplate.opsForValue().get(KEY_API_KEY)); + if (mode == null && baseUrl == null && model == null && apiKey == null) { + return null; + } + ResolvedLlm r = new ResolvedLlm(); + r.mode = mode != null ? mode : MODE_OLLAMA; + r.baseUrl = baseUrl; + r.model = model; + r.apiKey = apiKey; + return r; } private static String trimOrNull(String s) { @@ -112,4 +164,11 @@ public class SocialMediaLlmClient { } return choices.get(0).path("message").path("content").asText(); } + + private static final class ResolvedLlm { + String mode; + String baseUrl; + String model; + String apiKey; + } }