opt
This commit is contained in:
parent
647ef43655
commit
fe00238532
@ -1,5 +1,6 @@
|
|||||||
package com.knowledge.base.application.filter;
|
package com.knowledge.base.application.filter;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.knowledge.base.application.enums.MDCKeyEnum;
|
import com.knowledge.base.application.enums.MDCKeyEnum;
|
||||||
@ -20,79 +21,112 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description: 交易日志记录
|
* 通用交易日志过滤器
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class LogFilter extends OncePerRequestFilter {
|
public class LogFilter extends OncePerRequestFilter {
|
||||||
private final static Logger LOGGER = LoggerFactory.getLogger(LogFilter.class);
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(LogFilter.class);
|
||||||
|
private static final int MAX_LOG_BODY_LENGTH = 3000;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private DynamicConfig dynamicConfig;
|
private DynamicConfig dynamicConfig;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
protected void doFilterInternal(HttpServletRequest request,
|
||||||
ContentCachingRequestWrapper contentCachingRequestWrapper = new ContentCachingRequestWrapper(request);
|
HttpServletResponse response,
|
||||||
ContentCachingResponseWrapper contentCachingResponseWrapper = new ContentCachingResponseWrapper(response);
|
FilterChain filterChain) throws ServletException, IOException {
|
||||||
if (!"Y".equals(dynamicConfig.getRecordMsgBody())) {
|
|
||||||
// 不记录请求和响应信息
|
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
|
||||||
filterChain.doFilter(contentCachingRequestWrapper, contentCachingResponseWrapper);
|
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
|
||||||
contentCachingResponseWrapper.copyBodyToResponse();
|
|
||||||
|
if (!"Y".equalsIgnoreCase(dynamicConfig.getRecordMsgBody())) {
|
||||||
|
filterChain.doFilter(requestWrapper, responseWrapper);
|
||||||
|
responseWrapper.copyBodyToResponse();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StopWatch stopWatch = new StopWatch();
|
StopWatch stopWatch = new StopWatch();
|
||||||
|
String traceId = null;
|
||||||
try {
|
try {
|
||||||
stopWatch.start();
|
stopWatch.start();
|
||||||
//获取http header 中的traceid,没有默认生成一个,放入到mdc中,进行日志打印
|
|
||||||
String traceId = Optional.ofNullable(request.getHeader("traceId"))
|
// traceId 生成或提取
|
||||||
.orElse(UUID.randomUUID().toString().replace("-", ""));
|
traceId = Optional.ofNullable(request.getHeader("traceId"))
|
||||||
|
.orElse(IdUtil.fastSimpleUUID());
|
||||||
|
|
||||||
InetAddress addr = InetAddress.getLocalHost();
|
InetAddress addr = InetAddress.getLocalHost();
|
||||||
MDC.put(MDCKeyEnum.HOST_NAME.name, addr.getHostName());
|
MDC.put(MDCKeyEnum.HOST_NAME.name, addr.getHostName());
|
||||||
MDC.put(MDCKeyEnum.TRACE_ID.name, traceId);
|
MDC.put(MDCKeyEnum.TRACE_ID.name, traceId);
|
||||||
filterChain.doFilter(contentCachingRequestWrapper, contentCachingResponseWrapper);
|
|
||||||
|
filterChain.doFilter(requestWrapper, responseWrapper);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.warn("交易跟踪号[{}]记录交易报文信息异常:{}", MDC.get(MDCKeyEnum.TRACE_ID.name), e.getMessage());
|
LOGGER.warn("记录交易信息异常(traceId={}): {}", MDC.get(MDCKeyEnum.TRACE_ID.name), e.getMessage());
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
byte[] requestBody = contentCachingRequestWrapper.getContentAsByteArray();
|
responseWrapper.setHeader(MDCKeyEnum.TRACE_ID.name, traceId);
|
||||||
byte[] responseBody = contentCachingResponseWrapper.getContentAsByteArray();
|
stopWatch.stop();
|
||||||
try {
|
logTraceMessage(requestWrapper, responseWrapper, stopWatch.getTotalTimeMillis());
|
||||||
//获取请求路径
|
MDC.clear();
|
||||||
String url = contentCachingRequestWrapper.getRequestURL().toString();
|
responseWrapper.copyBodyToResponse();
|
||||||
//获取报文头交易码
|
|
||||||
String tranCode = Optional.ofNullable(contentCachingRequestWrapper.getHeader("TRANCODE")).orElse("UNKOWN");
|
|
||||||
//获取IP地址
|
|
||||||
String remoteIp = getRemortIP(contentCachingRequestWrapper);
|
|
||||||
//获取IP端口
|
|
||||||
int remotePort = contentCachingRequestWrapper.getRemotePort();
|
|
||||||
stopWatch.stop();
|
|
||||||
|
|
||||||
LOGGER.info("交易跟踪号[{}]\n请求路径[{}]\n交易码[{}]\n远程地址[{}]\n远程端口[{}]\n请求内容:[{}]\n响应内容:[{}]\n响应时间:[{}]毫秒",
|
|
||||||
MDC.get(MDCKeyEnum.TRACE_ID.name),
|
|
||||||
url,
|
|
||||||
tranCode,
|
|
||||||
remoteIp,
|
|
||||||
remotePort,
|
|
||||||
JSONUtil.toJsonStr(new String(requestBody)),
|
|
||||||
JSONUtil.toJsonStr(new String(responseBody)),
|
|
||||||
stopWatch.getTotalTimeMillis());
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.warn("登记交易信息异常:" + e.getMessage());
|
|
||||||
}
|
|
||||||
MDC.remove(MDCKeyEnum.HOST_NAME.name);
|
|
||||||
MDC.remove(MDCKeyEnum.TRACE_ID.name);
|
|
||||||
contentCachingResponseWrapper.copyBodyToResponse();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRemortIP(HttpServletRequest request) {
|
private void logTraceMessage(ContentCachingRequestWrapper request,
|
||||||
return StrUtil.isBlank(request.getHeader("x-forwarded-for")) ?
|
ContentCachingResponseWrapper response,
|
||||||
request.getRemoteAddr() :
|
long timeCostMs) {
|
||||||
request.getHeader("x-forwarded-for");
|
|
||||||
|
String url = request.getRequestURL().toString();
|
||||||
|
String tranCode = Optional.ofNullable(request.getHeader("TRANCODE")).orElse("UNKNOWN");
|
||||||
|
String remoteIp = getRemoteIP(request);
|
||||||
|
int remotePort = request.getRemotePort();
|
||||||
|
|
||||||
|
String reqBody = extractRequestBody(request);
|
||||||
|
String respBody = extractResponseBody(response);
|
||||||
|
|
||||||
|
Map<String, Object> logMap = new HashMap<>();
|
||||||
|
logMap.put("traceId", MDC.get(MDCKeyEnum.TRACE_ID.name));
|
||||||
|
logMap.put("url", url);
|
||||||
|
logMap.put("tranCode", tranCode);
|
||||||
|
logMap.put("ip", remoteIp);
|
||||||
|
logMap.put("port", remotePort);
|
||||||
|
logMap.put("request", reqBody);
|
||||||
|
logMap.put("response", respBody);
|
||||||
|
logMap.put("timeMs", timeCostMs);
|
||||||
|
|
||||||
|
LOGGER.info("log_trace_record: {}", JSONUtil.toJsonStr(logMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractRequestBody(ContentCachingRequestWrapper request) {
|
||||||
|
String contentType = request.getContentType();
|
||||||
|
byte[] content = request.getContentAsByteArray();
|
||||||
|
|
||||||
|
if (StrUtil.isNotBlank(contentType) && contentType.toLowerCase().contains("multipart/form-data")) {
|
||||||
|
// 表单上传只记录参数,不打印文件体
|
||||||
|
Map<String, String[]> paramMap = request.getParameterMap();
|
||||||
|
return "[multipart/form-data] " + JSONUtil.toJsonStr(paramMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.length == 0) return "<empty>";
|
||||||
|
String body = new String(content, StandardCharsets.UTF_8);
|
||||||
|
return body.length() > MAX_LOG_BODY_LENGTH ? body.substring(0, MAX_LOG_BODY_LENGTH) + "...[truncated]" : body;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractResponseBody(ContentCachingResponseWrapper response) {
|
||||||
|
byte[] content = response.getContentAsByteArray();
|
||||||
|
if (content.length == 0) return "<empty>";
|
||||||
|
String body = new String(content, StandardCharsets.UTF_8);
|
||||||
|
return body.length() > MAX_LOG_BODY_LENGTH ? body.substring(0, MAX_LOG_BODY_LENGTH) + "...[truncated]" : body;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRemoteIP(HttpServletRequest request) {
|
||||||
|
String xff = request.getHeader("x-forwarded-for");
|
||||||
|
return StrUtil.isBlank(xff) ? request.getRemoteAddr() : xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.knowledge.base.application.service;
|
||||||
|
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface LLMAppService {
|
||||||
|
String getToken(String password) throws Exception;
|
||||||
|
ResponseBodyEmitter ask(String userId, String question, Map<String, Object> params) throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
package com.knowledge.base.application.service;
|
||||||
|
|
||||||
|
import com.knowledge.base.infrastructure.south.llm.LLMService;
|
||||||
|
import com.knowledge.base.infrastructure.south.llm.LLMServiceFactory;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
|
||||||
|
|
||||||
|
import java.io.PipedInputStream;
|
||||||
|
import java.io.PipedOutputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LLMAppServiceImpl implements LLMAppService {
|
||||||
|
|
||||||
|
private final LLMServiceFactory llmServiceFactory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToken(String password) throws Exception {
|
||||||
|
return llmServiceFactory.current().fetchToken(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseBodyEmitter ask(String userId, String question, Map<String, Object> params) throws Exception {
|
||||||
|
LLMService service = llmServiceFactory.current();
|
||||||
|
|
||||||
|
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
|
||||||
|
PipedOutputStream out = new PipedOutputStream();
|
||||||
|
PipedInputStream in = new PipedInputStream(out);
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
service.streamAnswer(userId, question, params, out);
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
emitter.send("data: {\"error\": \"LLM内部异常\"}\n\n");
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
emitter.completeWithError(e);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
try (in) {
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while ((len = in.read(buf)) != -1) {
|
||||||
|
emitter.send(new String(buf, 0, len));
|
||||||
|
}
|
||||||
|
emitter.complete();
|
||||||
|
} catch (Exception e) {
|
||||||
|
emitter.completeWithError(e);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,93 +1,36 @@
|
|||||||
package com.knowledge.base.infrastructure.north.controller;
|
package com.knowledge.base.infrastructure.north.controller;
|
||||||
|
|
||||||
import com.knowledge.base.infrastructure.util.HttpHelper;
|
import com.knowledge.base.application.service.LLMAppService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.knowledge.base.infrastructure.config.LLmConfig;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import okhttp3.Response;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luke.ye
|
|
||||||
* @date 2025/6/22 08:08
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/llm")
|
@RequestMapping("/api/v1/llm")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LLMController {
|
public class LLMController {
|
||||||
|
|
||||||
private final HttpHelper okHttpHelper;
|
private final LLMAppService llmAppService;
|
||||||
private final LLmConfig llmConfig;
|
|
||||||
private final ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POST /token
|
|
||||||
* 请求:{ password: xxx }
|
|
||||||
* 返回:{ token: xxx }
|
|
||||||
*/
|
|
||||||
@PostMapping("/token")
|
@PostMapping("/token")
|
||||||
public Map<String, Object> getToken(@RequestBody Map<String, Object> body) throws Exception {
|
public Map<String, Object> getToken(@RequestHeader("Authorization") String token,
|
||||||
String url = llmConfig.getBaseUrl() + "/api/token";
|
@RequestBody Map<String, Object> body) throws Exception {
|
||||||
return okHttpHelper.postJson(url, body, null);
|
String password = String.valueOf(body.get("password"));
|
||||||
|
String resultToken = llmAppService.getToken(password);
|
||||||
|
return Map.of("token", resultToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@PostMapping(value = "/ask", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||||
* GET /workspaces
|
public ResponseBodyEmitter ask(@RequestHeader("Authorization") String token,
|
||||||
* Header: Authorization: Bearer xxx
|
@RequestBody Map<String, Object> body) throws Exception {
|
||||||
* 返回:{ workspaces: [...] }
|
String question = String.valueOf(body.get("question"));
|
||||||
*/
|
String userId = String.valueOf(body.getOrDefault("userId", "anonymous"));
|
||||||
@GetMapping("/workspaces")
|
return llmAppService.ask(userId, question, body);
|
||||||
public Map<String, Object> getWorkspaces(@RequestHeader("Authorization") String token) throws Exception {
|
|
||||||
String url = llmConfig.getBaseUrl() + "/api/workspaces";
|
|
||||||
return okHttpHelper.get(url, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POST /workspace/{slug}/stream-chat
|
|
||||||
* Header: Authorization
|
|
||||||
* Body: { message: "...", attachments: [...] }
|
|
||||||
* 返回:text/event-stream
|
|
||||||
*/
|
|
||||||
@PostMapping(value = "/workspace/{slug}/stream-chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
|
||||||
public ResponseBodyEmitter chatStream(@PathVariable String slug,
|
|
||||||
@RequestHeader("Authorization") String token,
|
|
||||||
@RequestBody Map<String, Object> body) {
|
|
||||||
String url = llmConfig.getBaseUrl() + "/api/workspace/" + slug + "/stream-chat";
|
|
||||||
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
|
|
||||||
|
|
||||||
new Thread(() -> {
|
|
||||||
try (Response resp = okHttpHelper.postStream(url, body, token);
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(resp.body().byteStream(), StandardCharsets.UTF_8))) {
|
|
||||||
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
emitter.send(line + "\n");
|
|
||||||
}
|
|
||||||
emitter.complete();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("LLM 流式回答异常", e);
|
|
||||||
try {
|
|
||||||
emitter.send("data: {\"textResponse\":\"LLM服务异常\"}\n\n");
|
|
||||||
} catch (Exception ignore) {}
|
|
||||||
emitter.completeWithError(e);
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
return emitter;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,75 @@
|
|||||||
|
package com.knowledge.base.infrastructure.south.llm;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.knowledge.base.infrastructure.util.HttpHelper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AnythingLLMServiceImpl implements LLMService {
|
||||||
|
|
||||||
|
@Value("${llm.remote.base-url}")
|
||||||
|
private String baseUrl;
|
||||||
|
|
||||||
|
private final HttpHelper httpHelper;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private String cachedToken;
|
||||||
|
private long cachedAt;
|
||||||
|
private final long cacheExpireMs = 60 * 60 * 1000;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(String type) {
|
||||||
|
return "anything".equalsIgnoreCase(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String fetchToken(String password) throws Exception {
|
||||||
|
String url = baseUrl + "/api/token";
|
||||||
|
Map<String, Object> result = httpHelper.postJson(url, Map.of("password", password), null);
|
||||||
|
String token = String.valueOf(result.get("token"));
|
||||||
|
cachedToken = token;
|
||||||
|
cachedAt = System.currentTimeMillis();
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getValidToken() throws Exception {
|
||||||
|
if (cachedToken != null && (System.currentTimeMillis() - cachedAt) < cacheExpireMs) {
|
||||||
|
return cachedToken;
|
||||||
|
}
|
||||||
|
return fetchToken("wisdom2025");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void streamAnswer(String userId, String question, Map<String, Object> params, OutputStream output) throws Exception {
|
||||||
|
String slug = String.valueOf(params.getOrDefault("slug", "kb"));
|
||||||
|
String url = baseUrl + "/api/workspace/" + slug + "/stream-chat";
|
||||||
|
|
||||||
|
Map<String, Object> body = Map.of(
|
||||||
|
"message", question,
|
||||||
|
"attachments", params.getOrDefault("attachments", List.of())
|
||||||
|
);
|
||||||
|
|
||||||
|
try (Response response = httpHelper.postStream(url, body, "Bearer " + getValidToken());
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(response.body().byteStream(), StandardCharsets.UTF_8))) {
|
||||||
|
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
output.write((line + "\n").getBytes(StandardCharsets.UTF_8));
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.knowledge.base.infrastructure.south.llm;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface LLMService {
|
||||||
|
String fetchToken(String password) throws Exception;
|
||||||
|
|
||||||
|
void streamAnswer(String userId, String question, Map<String, Object> params, OutputStream output) throws Exception;
|
||||||
|
|
||||||
|
boolean supports(String type);
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.knowledge.base.infrastructure.south.llm;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LLMServiceFactory {
|
||||||
|
|
||||||
|
@Value("${llm.provider:anything}")
|
||||||
|
private String provider;
|
||||||
|
|
||||||
|
private final List<LLMService> services;
|
||||||
|
|
||||||
|
public LLMService current() {
|
||||||
|
return services.stream()
|
||||||
|
.filter(s -> s.supports(provider))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new IllegalStateException("不支持的LLM服务类型: " + provider));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -34,4 +34,5 @@ object.storage.public-buckets[0]=kbase
|
|||||||
object.storage.public-buckets[1]=public-media
|
object.storage.public-buckets[1]=public-media
|
||||||
|
|
||||||
# 大模型相关
|
# 大模型相关
|
||||||
|
llm.provider=anything
|
||||||
llm.remote.base-url=http://llm.wisdompulse.cn
|
llm.remote.base-url=http://llm.wisdompulse.cn
|
||||||
@ -23,6 +23,7 @@ import.schedule.enabled=true
|
|||||||
import.schedule.cron=0 0 * * * *
|
import.schedule.cron=0 0 * * * *
|
||||||
|
|
||||||
# 大模型相关
|
# 大模型相关
|
||||||
|
llm.provider=anything
|
||||||
llm.remote.base-url=http://llm.wisdompulse.cn
|
llm.remote.base-url=http://llm.wisdompulse.cn
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user