mirror of
https://github.com/ccmjga/zhilu-admin
synced 2026-03-15 22:23:41 +08:00
init llm config
This commit is contained in:
@@ -1,15 +1,29 @@
|
||||
package com.zl.mjga.config.ai;
|
||||
|
||||
import com.zl.mjga.service.LlmService;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jooq.generated.default_schema.enums.LlmCodeEnum;
|
||||
import org.jooq.generated.mjga.tables.pojos.AiLlmConfig;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "deep-seek")
|
||||
@RequiredArgsConstructor
|
||||
public class DeepSeekConfiguration {
|
||||
|
||||
private String baseUrl;
|
||||
private String apiKey;
|
||||
private String modelName;
|
||||
|
||||
private final LlmService llmService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
AiLlmConfig aiLlmConfig = llmService.loadConfig(LlmCodeEnum.DEEP_SEEK);
|
||||
baseUrl = aiLlmConfig.getUrl();
|
||||
apiKey = aiLlmConfig.getApiKey();
|
||||
modelName = aiLlmConfig.getModelName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
package com.zl.mjga.config.ai;
|
||||
|
||||
import com.zl.mjga.service.LlmService;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.jooq.generated.default_schema.enums.LlmCodeEnum;
|
||||
import org.jooq.generated.mjga.tables.pojos.AiLlmConfig;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "zhipu")
|
||||
public class ZhiPuConfiguration {
|
||||
|
||||
private String baseUrl;
|
||||
private String apiKey;
|
||||
private String modelName;
|
||||
|
||||
private final LlmService llmService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
AiLlmConfig aiLlmConfig = llmService.loadConfig(LlmCodeEnum.ZHI_PU);
|
||||
baseUrl = aiLlmConfig.getUrl();
|
||||
apiKey = aiLlmConfig.getApiKey();
|
||||
modelName = aiLlmConfig.getModelName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.zl.mjga.controller;
|
||||
|
||||
import com.zl.mjga.dto.ai.LlmUpdateDto;
|
||||
import com.zl.mjga.service.AiChatService;
|
||||
import com.zl.mjga.service.LlmService;
|
||||
import dev.langchain4j.service.TokenStream;
|
||||
import jakarta.validation.Valid;
|
||||
import java.security.Principal;
|
||||
import java.time.Duration;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -18,11 +21,12 @@ import reactor.core.publisher.Sinks;
|
||||
public class AiController {
|
||||
|
||||
private final AiChatService aiChatService;
|
||||
private final LlmService llmService;
|
||||
|
||||
@PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public Flux<String> chat(Principal principal, @RequestBody String userMessage) {
|
||||
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();
|
||||
TokenStream chat = aiChatService.chatWithZhiPu(principal.getName(), userMessage);
|
||||
TokenStream chat = aiChatService.chatPrecedenceLlmWith(principal.getName(), userMessage);
|
||||
chat.onPartialResponse(text -> sink.tryEmitNext(text.replace(" ", "␣").replace("\t", "⇥")))
|
||||
.onCompleteResponse(
|
||||
r -> {
|
||||
@@ -33,4 +37,9 @@ public class AiController {
|
||||
.start();
|
||||
return sink.asFlux().timeout(Duration.ofSeconds(120));
|
||||
}
|
||||
|
||||
@PutMapping(value = "/llm")
|
||||
public void updateLlm(@RequestBody @Valid LlmUpdateDto llmUpdateDto) {
|
||||
llmService.update(llmUpdateDto);
|
||||
}
|
||||
}
|
||||
|
||||
16
backend/src/main/java/com/zl/mjga/dto/ai/LlmUpdateDto.java
Normal file
16
backend/src/main/java/com/zl/mjga/dto/ai/LlmUpdateDto.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.zl.mjga.dto.ai;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class LlmUpdateDto {
|
||||
@NotNull private Long id;
|
||||
@NotEmpty private String name;
|
||||
@NotEmpty private String modelName;
|
||||
@NotEmpty private String apiKey;
|
||||
@NotEmpty private String url;
|
||||
@NotNull private Boolean enable;
|
||||
@NotNull private Short priority;
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import com.zl.mjga.config.ai.AiChatAssistant;
|
||||
import dev.langchain4j.service.TokenStream;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jooq.generated.default_schema.enums.LlmCodeEnum;
|
||||
import org.jooq.generated.mjga.tables.pojos.AiLlmConfig;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@@ -13,6 +15,7 @@ public class AiChatService {
|
||||
|
||||
private final AiChatAssistant deepSeekChatAssistant;
|
||||
private final AiChatAssistant zhiPuChatAssistant;
|
||||
private final LlmService llmService;
|
||||
|
||||
public TokenStream chatWithDeepSeek(String sessionIdentifier, String userMessage) {
|
||||
return deepSeekChatAssistant.chat(sessionIdentifier, userMessage);
|
||||
@@ -21,4 +24,13 @@ public class AiChatService {
|
||||
public TokenStream chatWithZhiPu(String sessionIdentifier, String userMessage) {
|
||||
return zhiPuChatAssistant.chat(sessionIdentifier, userMessage);
|
||||
}
|
||||
|
||||
public TokenStream chatPrecedenceLlmWith(String sessionIdentifier, String userMessage) {
|
||||
AiLlmConfig precedenceLlmBy = llmService.getPrecedenceLlmBy(true);
|
||||
LlmCodeEnum code = precedenceLlmBy.getCode();
|
||||
return switch (code) {
|
||||
case ZHI_PU -> zhiPuChatAssistant.chat(sessionIdentifier, userMessage);
|
||||
case DEEP_SEEK -> deepSeekChatAssistant.chat(sessionIdentifier, userMessage);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
41
backend/src/main/java/com/zl/mjga/service/LlmService.java
Normal file
41
backend/src/main/java/com/zl/mjga/service/LlmService.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package com.zl.mjga.service;
|
||||
|
||||
import com.zl.mjga.dto.ai.LlmUpdateDto;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jooq.generated.default_schema.enums.LlmCodeEnum;
|
||||
import org.jooq.generated.mjga.tables.daos.AiLlmConfigDao;
|
||||
import org.jooq.generated.mjga.tables.pojos.AiLlmConfig;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class LlmService {
|
||||
|
||||
private final AiLlmConfigDao aiLlmConfigDao;
|
||||
|
||||
public AiLlmConfig loadConfig(LlmCodeEnum llmCodeEnum) {
|
||||
return aiLlmConfigDao.fetchOneByCode(llmCodeEnum);
|
||||
}
|
||||
|
||||
public AiLlmConfig getPrecedenceLlmBy(Boolean enable) {
|
||||
List<AiLlmConfig> aiLlmConfigs = aiLlmConfigDao.fetchByEnable(enable);
|
||||
//noinspection OptionalGetWithoutIsPresent
|
||||
return aiLlmConfigs.stream()
|
||||
.max((o1, o2) -> o2.getPriority().compareTo(o1.getPriority()))
|
||||
.get();
|
||||
}
|
||||
|
||||
public void update(LlmUpdateDto llmUpdateDto) {
|
||||
AiLlmConfig aiLlmConfig = new AiLlmConfig();
|
||||
BeanUtils.copyProperties(llmUpdateDto, aiLlmConfig);
|
||||
AiLlmConfig byId = aiLlmConfigDao.findById(llmUpdateDto.getId());
|
||||
aiLlmConfig.setCode(Objects.requireNonNull(byId).getCode());
|
||||
aiLlmConfigDao.merge(aiLlmConfig);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
deep-seek:
|
||||
base-url: "https://api.deepseek.com"
|
||||
api-key: ""
|
||||
model-name: "deepseek-chat"
|
||||
zhipu:
|
||||
base-url: "https://open.bigmodel.cn/"
|
||||
api-key: ""
|
||||
model-name: "glm-4-flash"
|
||||
@@ -15,8 +15,6 @@ cors:
|
||||
allowedHeaders: ${ALLOWED_HEADERS}
|
||||
allowedExposeHeaders: ${ALLOWED_EXPOSE_HEADERS}
|
||||
spring:
|
||||
config:
|
||||
import: classpath:ai.yml
|
||||
datasource:
|
||||
url: jdbc:postgresql://${DATABASE_HOST_PORT}/${DATABASE_DB}
|
||||
username: ${DATABASE_USER}
|
||||
|
||||
@@ -64,4 +64,22 @@ CREATE TABLE mjga.user_position_map (
|
||||
PRIMARY KEY (user_id, position_id),
|
||||
FOREIGN KEY (user_id) REFERENCES mjga.user(id) ON UPDATE NO ACTION ON DELETE RESTRICT,
|
||||
FOREIGN KEY (position_id) REFERENCES mjga.position(id) ON UPDATE NO ACTION ON DELETE RESTRICT
|
||||
);
|
||||
);
|
||||
|
||||
CREATE TYPE "llm_code_enum" AS ENUM (
|
||||
'DEEP_SEEK',
|
||||
'ZHI_PU'
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE mjga.ai_llm_config (
|
||||
id BIGSERIAL NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL UNIQUE,
|
||||
code LLM_CODE_ENUM NOT NULL UNIQUE,
|
||||
model_name VARCHAR(255) NOT NULL,
|
||||
api_key VARCHAR(255) NOT NULL,
|
||||
url VARCHAR(255) NOT NULL,
|
||||
enable BOOLEAN NOT NULL DEFAULT true,
|
||||
priority SMALLINT NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
@@ -27,3 +27,8 @@ VALUES (1, 1),
|
||||
(1, 6),
|
||||
(1, 7),
|
||||
(1, 8);
|
||||
|
||||
INSERT INTO mjga.ai_llm_config (name,code,model_name, api_key, url, enable, priority)
|
||||
VALUES
|
||||
('DeepSeek','DEEP_SEEK','deepseek-chat','', 'https://api.deepseek.com', false, 0),
|
||||
('智谱清言','ZHI_PU','glm-4-flash', '', 'https://open.bigmodel.cn/', false, 1);
|
||||
@@ -64,4 +64,14 @@ CREATE TABLE mjga.user_position_map (
|
||||
PRIMARY KEY (user_id, position_id),
|
||||
FOREIGN KEY (user_id) REFERENCES mjga.user(id) ON UPDATE NO ACTION ON DELETE RESTRICT,
|
||||
FOREIGN KEY (position_id) REFERENCES mjga.position(id) ON UPDATE NO ACTION ON DELETE RESTRICT
|
||||
);
|
||||
);
|
||||
|
||||
CREATE TABLE mjga.ai_llm_config (
|
||||
id BIGSERIAL NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL UNIQUE,
|
||||
api_key VARCHAR(255) NOT NULL,
|
||||
url VARCHAR(255) NOT NULL,
|
||||
enable BOOLEAN NOT NULL DEFAULT true,
|
||||
priority SMALLINT NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user