mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-11-15 07:20:40 +00:00
feat: enhance coding harness, model security and system features
This commit is contained in:
@@ -153,6 +153,12 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -60,6 +60,11 @@ public class WorkflowMessageUtil {
|
||||
* @param message 消息
|
||||
*/
|
||||
public static void saveWorkflowMessage(WfState wfState, String message) {
|
||||
// Input validation may fail before WfState is created. Do not mask the
|
||||
// original error and prevent WorkflowEngine from completing the SSE.
|
||||
if (wfState == null) {
|
||||
return;
|
||||
}
|
||||
Long sessionId = wfState.getSessionId();
|
||||
Long userId = wfState.getUserId();
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ public class WorkflowUtil{
|
||||
|
||||
public void streamingInvokeLLM(WfState wfState, WfNodeState state, WorkflowNode node, String modelName,
|
||||
String prompt, String nodeMessageTemplate) {
|
||||
log.info("stream invoke, modelName: {}", modelName);
|
||||
log.info("workflow_llm operation=STREAM_INVOKE status=STARTED");
|
||||
|
||||
// 获取用户信息和Token以及SSe连接对象(对话接口需要使用)
|
||||
Long sessionId = wfState.getSessionId();
|
||||
@@ -103,7 +103,7 @@ public class WorkflowUtil{
|
||||
StreamingChatGenerator<AgentState> streamingGenerator = StreamingChatGenerator.builder()
|
||||
.mapResult(response -> {
|
||||
String responseTxt = response.aiMessage().text();
|
||||
log.info("llm response:{}", responseTxt);
|
||||
logCompletionMetadata(responseTxt);
|
||||
// 传递所有输入数据 + 添加 LLM 输出
|
||||
wfState.getNodeStateByNodeUuid(node.getUuid()).ifPresent(item -> {
|
||||
List<NodeIOData> outputs = new ArrayList<>(item.getInputs());
|
||||
@@ -158,7 +158,7 @@ public class WorkflowUtil{
|
||||
* @return
|
||||
*/
|
||||
private UserMessage getMessage(String role, String value) {
|
||||
log.info("Creating message with role: {}, content: {}", role, value);
|
||||
logMessageMetadata(role, value);
|
||||
return new UserMessage(value);
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ public class WorkflowUtil{
|
||||
* 调用LLM 根据文字生成图片
|
||||
*/
|
||||
public String buildTextToImage(String modelName, String prompt, String size, Integer seed){
|
||||
log.info("Generate image invoke, modelName: {}", modelName);
|
||||
log.info("workflow_image operation=GENERATE status=STARTED");
|
||||
// 根据模型名称查询模型信息
|
||||
ChatModelVo chatModelVo = chatModelService.selectModelByName(modelName);
|
||||
if (chatModelVo == null) {
|
||||
@@ -197,4 +197,19 @@ public class WorkflowUtil{
|
||||
// 调用LLM 生成图片
|
||||
return imageService.generateImage(imageContext);
|
||||
}
|
||||
|
||||
static void logCompletionMetadata(String responseText) {
|
||||
log.info("workflow_llm operation=STREAM_INVOKE status=COMPLETED outputChars={}",
|
||||
textLength(responseText));
|
||||
}
|
||||
|
||||
static void logMessageMetadata(String role, String content) {
|
||||
String roleType = "user".equalsIgnoreCase(role) ? "USER" : "OTHER";
|
||||
log.info("workflow_message status=CREATED roleType={} contentChars={}",
|
||||
roleType, textLength(content));
|
||||
}
|
||||
|
||||
private static int textLength(String value) {
|
||||
return value == null ? 0 : value.length();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import ai.z.openapi.service.web_search.WebSearchResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.ruoyi.common.chat.domain.bo.chat.ChatModelBo;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.chat.security.ChatModelCredentialPolicy;
|
||||
import org.ruoyi.common.chat.service.chat.IChatModelService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -78,21 +80,25 @@ public class ZhipuWebSearchClient {
|
||||
}
|
||||
|
||||
private Credential resolveCredential() {
|
||||
if (isUsableApiKey(properties.getApiKey())) {
|
||||
return new Credential(normalizeBaseUrl(properties.getBaseUrl()), properties.getApiKey().trim());
|
||||
}
|
||||
|
||||
ChatModelBo query = new ChatModelBo();
|
||||
query.setProviderCode(ZHIPU_PROVIDER_CODE);
|
||||
return chatModelService.queryList(query).stream()
|
||||
return chatModelService.queryAvailableList(query).stream()
|
||||
.filter(model -> isUsableApiKey(model.getApiKey()))
|
||||
.findFirst()
|
||||
.map(model -> new Credential(normalizeBaseUrl(model.getApiHost()), model.getApiKey().trim()))
|
||||
.map(this::credentialForConfiguredEndpoint)
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"未配置智谱 API Key,请设置环境变量 ZAI_API_KEY,或在模型管理中配置 zhipu 厂商密钥"
|
||||
"智谱凭据未进入受信 provider/model/endpoint 注册表,当前拒绝解析"
|
||||
));
|
||||
}
|
||||
|
||||
private Credential credentialForConfiguredEndpoint(ChatModelVo model) {
|
||||
String finalBaseUrl = normalizeBaseUrl(model.getApiHost());
|
||||
String apiKey = ChatModelCredentialPolicy.resolveApiKeyForUse(
|
||||
ZHIPU_PROVIDER_CODE, model.getProviderCode(), model.getModelName(), finalBaseUrl,
|
||||
model.getApiKey());
|
||||
return new Credential(finalBaseUrl, apiKey);
|
||||
}
|
||||
|
||||
private boolean isUsableApiKey(String apiKey) {
|
||||
return StringUtils.isNotBlank(apiKey)
|
||||
&& !"sk_xx".equalsIgnoreCase(apiKey.trim())
|
||||
|
||||
@@ -236,7 +236,6 @@ public class KnowledgeRetrievalNode extends AbstractWfNode {
|
||||
? config.getSimilarityThreshold() : kb.getSimilarityThreshold());
|
||||
bo.setEmbeddingModelName(kb.getEmbeddingModel());
|
||||
bo.setVectorModelName(kb.getVectorModel());
|
||||
bo.setApiKey(embModel.getApiKey());
|
||||
bo.setBaseUrl(embModel.getApiHost());
|
||||
|
||||
String mode = config.getRetrievalMode() != null ? config.getRetrievalMode().toLowerCase() : "vector";
|
||||
|
||||
Reference in New Issue
Block a user