Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ public class AIOperationConstant {
* JSON修复操作
*/
public static final String JSON_REPAIR_OP = "JSON_REPAIR_OP";

/**
* 文本分类操作
*/
public static final String TEXT_CLASSIFICATION_OP = "TEXT_CLASSIFICATION";

/**
* 模型验证操作
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

import io.github.timemachinelab.sfchain.core.logging.AICallLog;
import io.github.timemachinelab.sfchain.core.logging.AICallLogManager;
import org.springframework.beans.factory.annotation.Autowired;
import io.github.timemachinelab.sfchain.core.logging.AICallLogSummary;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

Expand All @@ -14,42 +15,43 @@
*/
@RestController
@RequestMapping("/sf/api/ai-logs")
@CrossOrigin(origins = "*")
public class AICallLogController {

@Autowired
@Resource
private AICallLogManager logManager;

/**
* 获取所有日志
* 获取所有日志摘要(轻量级)
*/
@GetMapping
public ResponseEntity<List<AICallLog>> getAllLogs() {
return ResponseEntity.ok(logManager.getAllLogs());
public ResponseEntity<List<AICallLogSummary>> getAllLogSummaries() {
return ResponseEntity.ok(logManager.getAllLogSummaries());
}

/**
* 根据调用ID获取日志
* 根据调用ID获取完整日志详情
*/
@GetMapping("/{callId}")
public ResponseEntity<AICallLog> getLog(@PathVariable String callId) {
AICallLog log = logManager.getLog(callId);
public ResponseEntity<AICallLog> getFullLog(@PathVariable String callId) {
AICallLog log = logManager.getFullLog(callId);
return log != null ? ResponseEntity.ok(log) : ResponseEntity.notFound().build();
}

/**
* 根据操作类型获取日志
* 根据操作类型获取日志摘要(轻量级)
*/
@GetMapping("/operation/{operationType}")
public ResponseEntity<List<AICallLog>> getLogsByOperation(@PathVariable String operationType) {
return ResponseEntity.ok(logManager.getLogsByOperation(operationType));
public ResponseEntity<List<AICallLogSummary>> getLogSummariesByOperation(@PathVariable String operationType) {
return ResponseEntity.ok(logManager.getLogSummariesByOperation(operationType));
}

/**
* 根据模型名称获取日志
* 根据模型名称获取日志摘要(轻量级)
*/
@GetMapping("/model/{modelName}")
public ResponseEntity<List<AICallLog>> getLogsByModel(@PathVariable String modelName) {
return ResponseEntity.ok(logManager.getLogsByModel(modelName));
public ResponseEntity<List<AICallLogSummary>> getLogSummariesByModel(@PathVariable String modelName) {
return ResponseEntity.ok(logManager.getLogSummariesByModel(modelName));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,19 @@ public ResponseEntity<Map<String, Object>> saveModel(
Map<String, Object> result = new HashMap<>();
String modelName = config.getModelName();
try {
// 验证模型配置
boolean validationResult = validateModelConfig(modelName, config);
// 检查模型是否已存在
Map<String, ModelConfigData> existingModels = persistenceManager.getAllModelConfigs();
boolean modelExists = existingModels.containsKey(modelName);

// 验证模型配置(临时验证,不影响现有模型)
boolean validationResult = validateModelConfig(modelName, config, true);

if (!validationResult) {
result.put("success", false);
result.put("message", "模型验证失败,请检查配置参数");
return ResponseEntity.badRequest().body(result);
}

// 检查模型是否已存在
Map<String, ModelConfigData> existingModels = persistenceManager.getAllModelConfigs();
boolean modelExists = existingModels.containsKey(modelName);

// 根据模型是否存在选择添加或更新
if (modelExists) {
persistenceManager.updateModelConfig(modelName, config);
Expand All @@ -132,31 +132,12 @@ public ResponseEntity<Map<String, Object>> saveModel(
}
}

/**
* 删除模型配置
*/
@DeleteMapping("/{modelName}")
public ResponseEntity<Map<String, Object>> deleteModel(@PathVariable String modelName) {
Map<String, Object> result = new HashMap<>();
try {
persistenceManager.deleteModelConfig(modelName);
result.put("success", true);
result.put("message", "模型配置删除成功");
result.put("modelName", modelName);
return ResponseEntity.ok(result);
} catch (Exception e) {
log.error("删除模型配置失败: {} - {}", modelName, e.getMessage());
result.put("success", false);
result.put("message", "删除失败: " + e.getMessage());
return ResponseEntity.badRequest().body(result);
}
}

/**
* 测试模型连接
*/
@PostMapping("/{modelName}/test")
public ResponseEntity<Map<String, Object>> testModel(@PathVariable String modelName) {
@PostMapping("/test")
public ResponseEntity<Map<String, Object>> testModel(@RequestBody Map<String, String> request) {
String modelName = request.get("modelName");
Map<String, Object> result = new HashMap<>();
try {
Map<String, ModelConfigData> models = persistenceManager.getAllModelConfigs();
Expand All @@ -168,7 +149,8 @@ public ResponseEntity<Map<String, Object>> testModel(@PathVariable String modelN
return ResponseEntity.notFound().build();
}

boolean testResult = validateModelConfig(modelName, config);
// 测试已存在的模型,不需要临时注册
boolean testResult = validateModelConfig(modelName, config, false);
result.put("success", testResult);
result.put("message", testResult ? "模型连接测试成功" : "模型连接测试失败");
result.put("modelName", modelName);
Expand All @@ -186,21 +168,27 @@ public ResponseEntity<Map<String, Object>> testModel(@PathVariable String modelN

/**
* 验证模型配置是否可用
* @param modelName 模型名称
* @param config 模型配置
* @param isTemporaryValidation 是否为临时验证(true: 验证后移除临时模型,false: 验证已存在的模型)
*/
private boolean validateModelConfig(String modelName, ModelConfigData config) {
private boolean validateModelConfig(String modelName, ModelConfigData config, boolean isTemporaryValidation) {
boolean tempRegistered = false;
try {
log.info("开始验证模型配置: {}", modelName);
log.info("开始验证模型配置: {} (临时验证: {})", modelName, isTemporaryValidation);

ModelValidationOperation validationOp = (ModelValidationOperation) operationRegistry.getOperation(MODEL_VALIDATION_OP);
if (validationOp == null) {
log.warn("未找到模型验证操作,跳过验证");
return true;
}

OpenAIModelConfig tempConfig = convertToOpenAIConfig(config);
modelFactory.registerModel(tempConfig);
tempRegistered = true;
// 如果是临时验证,需要临时注册模型
if (isTemporaryValidation) {
OpenAIModelConfig tempConfig = convertToOpenAIConfig(config);
modelFactory.registerModel(tempConfig);
tempRegistered = true;
}

ModelValidationOperation.ValidationRequest request =
new ModelValidationOperation.ValidationRequest("请回答:2+3等于几?");
Expand All @@ -213,15 +201,38 @@ private boolean validateModelConfig(String modelName, ModelConfigData config) {
log.error("模型验证失败: {} - {}", modelName, e.getMessage());
return false;
} finally {
if (tempRegistered) {
// 只有在临时验证时才清理临时模型
if (tempRegistered && isTemporaryValidation) {
try {
modelFactory.removeModel(modelName);
log.debug("已清理临时模型配置: {}", modelName);
} catch (Exception e) {
log.warn("清理临时模型配置失败: {}", e.getMessage());
}
}
}
}


/**
* 删除模型配置
*/
@DeleteMapping("/{modelName}")
public ResponseEntity<Map<String, Object>> deleteModel(@PathVariable String modelName) {
Map<String, Object> result = new HashMap<>();
try {
persistenceManager.deleteModelConfig(modelName);
result.put("success", true);
result.put("message", "模型配置删除成功");
result.put("modelName", modelName);
return ResponseEntity.ok(result);
} catch (Exception e) {
log.error("删除模型配置失败: {} - {}", modelName, e.getMessage());
result.put("success", false);
result.put("message", "删除失败: " + e.getMessage());
return ResponseEntity.badRequest().body(result);
}
}

/**
* 转换配置格式
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,7 @@ public void registerOperation(String operationType, BaseAIOperation<?, ?> operat
*/
public String getModelForOperation(String operationType) {
// 优先从内存缓存获取
String cachedModel = modelMapping.get(operationType);
if (cachedModel != null) {
return cachedModel;
}

// 如果缓存中没有,可以从 PersistenceManager 获取操作配置中的模型名称
return null; // 或者注入 PersistenceManager 来获取
return modelMapping.get(operationType);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package io.github.timemachinelab.sfchain.core;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

/**
* AI提示词构建器
* 用于构建结构化的AI提示词,支持与AIResponseParser配合使用
* @author suifeng
* 日期: 2025/04/18
*/
public class AIPromptBuilder {

private final StringBuilder promptBuilder = new StringBuilder();
private boolean hasJsonOutput = false;

/**
* 创建一个提示词构建器
* @param title 提示词标题
*/
public AIPromptBuilder(String title) {
promptBuilder.append("# ").append(title).append("\n\n");
}

/**
* 添加角色描述
* @param roleDescription 角色描述
* @return 构建器实例
*/
public AIPromptBuilder addRole(String roleDescription) {
// 获取当前北京时间
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
String formattedTime = now.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"));
promptBuilder.append("# 当前的时间是北京时间:").append(formattedTime).append("\n");
promptBuilder.append(roleDescription).append("\n\n");
return this;
}

/**
* 添加章节
* @param sectionTitle 章节标题
* @param content 章节内容
* @return 构建器实例
*/
public AIPromptBuilder addSection(String sectionTitle, String content) {
promptBuilder.append("## ").append(sectionTitle).append("\n");
promptBuilder.append(content).append("\n\n");
return this;
}

/**
* 添加子章节
* @param subSectionTitle 子章节标题
* @param content 子章节内容
* @return 构建器实例
*/
public AIPromptBuilder addSubSection(String subSectionTitle, String content) {
promptBuilder.append("### ").append(subSectionTitle).append("\n");
promptBuilder.append(content).append("\n\n");
return this;
}

/**
* 添加JSON输出格式
* @param jsonExample JSON示例
* @return 构建器实例
*/
public AIPromptBuilder addJsonOutput(String jsonExample) {
promptBuilder.append("## 输出格式要求\n");
promptBuilder.append("所有字符串中的引用部分只能用英文单引号 ' 包裹,内容中禁止出现英文双引号, 但是你要注意json格式规范是要加\"的,保证我能正常解析JSON 。\n");
promptBuilder.append("```json\n");
promptBuilder.append(jsonExample).append("\n");
promptBuilder.append("```\n");
promptBuilder.append("不需要给我任何额外的信息,我只需要最终的json结果,以保证生成和响应的速度\n");
hasJsonOutput = true;
return this;
}

/**
* 添加表格输出格式
* @param headers 表头
* @param example 示例行
* @return 构建器实例
*/
public AIPromptBuilder addTableOutput(String[] headers, String[] example) {
promptBuilder.append("## 输出格式要求\n");
promptBuilder.append("你必须严格按照以下表格格式返回结果:\n");
promptBuilder.append("```\n");

// 构建表头
promptBuilder.append("| ");
for (String header : headers) {
promptBuilder.append(header).append(" | ");
}
promptBuilder.append("\n");

// 构建分隔行
promptBuilder.append("| ");
for (int i = 0; i < headers.length; i++) {
promptBuilder.append("--- | ");
}
promptBuilder.append("\n");

// 构建示例行
if (example != null) {
promptBuilder.append("| ");
for (String cell : example) {
promptBuilder.append(cell).append(" | ");
}
promptBuilder.append("\n");
}

promptBuilder.append("```\n\n");
return this;
}

/**
* 添加自定义输出格式
* @param formatDescription 格式描述
* @param example 示例
* @param formatType 格式类型(用于AIResponseParser识别)
* @return 构建器实例
*/
public AIPromptBuilder addCustomOutput(String formatDescription, String example, String formatType) {
promptBuilder.append("## 输出格式要求\n");
promptBuilder.append(formatDescription).append("\n");
promptBuilder.append("```").append(formatType).append("\n");
promptBuilder.append(example).append("\n");
promptBuilder.append("```\n\n");
return this;
}

/**
* 添加原始文本(不添加任何格式)
* @param text 原始文本
* @return 构建器实例
*/
public AIPromptBuilder addRawText(String text) {
promptBuilder.append(text).append("\n\n");
return this;
}

/**
* 构建提示词
* @return 完整的提示词字符串
*/
public String build() {
if (!hasJsonOutput) {
// 如果没有指定输出格式,添加默认提示
promptBuilder.append("请确保你的回答简洁明了,直接提供所需信息。\n");
}
return promptBuilder.toString();
}
}
Loading
Loading