feat: enhance coding harness, model security and system features

This commit is contained in:
ageerle
2026-09-08 23:33:29 +08:00
parent d4ed2b8672
commit bcff4fd9e3
259 changed files with 15183 additions and 2713 deletions

View File

@@ -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>

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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())

View File

@@ -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";