1
This commit is contained in:
@@ -4,6 +4,8 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import cn.hutool.http.HttpRequest;
|
import cn.hutool.http.HttpRequest;
|
||||||
import cn.hutool.http.HttpResponse;
|
import cn.hutool.http.HttpResponse;
|
||||||
import cn.hutool.http.Method;
|
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.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -15,8 +17,8 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 社媒文案/关键词所用大模型客户端:支持本地 Ollama 或与 OpenAI 兼容的 HTTP 接口(含远程 API、Ollama /v1 等)。
|
* 社媒文案/关键词所用大模型客户端:支持本地 Ollama 或与 OpenAI 兼容的 HTTP 接口。
|
||||||
* 配置存 Redis,与若依后台「大模型接入」一致;未配置时走 {@link OllamaClientUtil} 的 yml 默认。
|
* 多套配置存 Redis(JSON),通过 activeId 选择当前使用的一套;无激活或空存储时走 {@link OllamaClientUtil} 的 yml 默认。
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class SocialMediaLlmClient {
|
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 org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SocialMediaLlmClient.class);
|
||||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
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_MODE = "social_media:llm:mode";
|
||||||
private static final String KEY_BASE_URL = "social_media:llm:base_url";
|
private static final String KEY_BASE_URL = "social_media:llm:base_url";
|
||||||
private static final String KEY_API_KEY = "social_media:llm:api_key";
|
private static final String KEY_API_KEY = "social_media:llm:api_key";
|
||||||
@@ -43,28 +46,77 @@ public class SocialMediaLlmClient {
|
|||||||
return ollamaClientUtil.getResponse(inputText);
|
return ollamaClientUtil.getResponse(inputText);
|
||||||
}
|
}
|
||||||
|
|
||||||
String mode = trimOrNull(redisTemplate.opsForValue().get(KEY_MODE));
|
ResolvedLlm cfg = resolveConfig();
|
||||||
String baseUrl = trimOrNull(redisTemplate.opsForValue().get(KEY_BASE_URL));
|
if (cfg == null) {
|
||||||
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) {
|
|
||||||
return ollamaClientUtil.getResponse(inputText);
|
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 (MODE_OPENAI.equals(effectiveMode)) {
|
||||||
if (StrUtil.isBlank(baseUrl)) {
|
if (StrUtil.isBlank(cfg.baseUrl)) {
|
||||||
throw new IOException("OpenAI 兼容模式需在后台配置完整的 Chat Completions 地址");
|
throw new IOException("OpenAI 兼容模式需在后台配置完整的 Chat Completions 地址");
|
||||||
}
|
}
|
||||||
if (StrUtil.isBlank(model)) {
|
if (StrUtil.isBlank(cfg.model)) {
|
||||||
throw new IOException("OpenAI 兼容模式需在后台配置模型名称");
|
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) {
|
private static String trimOrNull(String s) {
|
||||||
@@ -112,4 +164,11 @@ public class SocialMediaLlmClient {
|
|||||||
}
|
}
|
||||||
return choices.get(0).path("message").path("content").asText();
|
return choices.get(0).path("message").path("content").asText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class ResolvedLlm {
|
||||||
|
String mode;
|
||||||
|
String baseUrl;
|
||||||
|
String model;
|
||||||
|
String apiKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user