Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
7ef115b
update readme
erupts May 27, 2025
c745006
update readme
erupts May 27, 2025
33ac9ce
excel export show condition
erupts Jun 3, 2025
07fcee6
新增德语、葡萄牙语、印尼语、阿拉伯语支持
erupts Jun 4, 2025
b92afdf
support Deutsch/Português/Bahasa/العربية Indonesia config
erupts Jun 4, 2025
cd18fc7
fix export condition bug
erupts Jun 4, 2025
4b6ddd6
调整redisCache销毁时间参数传递参数
erupts Jun 4, 2025
324989b
excel export name show datetime
erupts Jun 4, 2025
a0240be
update erupt-web
erupts Jun 5, 2025
88b0996
update linq.j to 0.0.8
erupts Jun 8, 2025
799a543
upgrade version to 1.12.21
erupts Jun 9, 2025
23fd299
update /pom.xml
erupts Jun 9, 2025
a0816a1
fix ipRegion error
erupts Jun 9, 2025
5651413
upgrade magic-api and fix path
erupts Jun 14, 2025
8222385
upgrade magic-api and fix path
erupts Jun 14, 2025
7e5b237
调整erupt基类时间和人字段的填充机制,可同时实现页面级别自动填充与eruptDao.persist 与 eruptDao.merge自动填充
erupts Jun 14, 2025
8f5cb1d
调整erupt基类时间和人字段的填充机制,可同时实现页面级别自动填充与eruptDao.persist 与 eruptDao.merge自动填充
erupts Jun 14, 2025
0975bbc
解决view tpl配置前端支持不完整的问题
erupts Jun 16, 2025
7255c5f
update erupt-web
erupts Jun 22, 2025
9b0fde5
add markdown component
erupts Jun 24, 2025
377ef98
add markdown component
erupts Jun 24, 2025
6f0e092
update depend
erupts Jun 25, 2025
1739c30
add markdown component
erupts Jun 25, 2025
51d87f5
add markdown component
erupts Jun 25, 2025
c7a2752
add markdown component
erupts Jun 25, 2025
0e9875d
update depend
erupts Jun 29, 2025
e6572af
update depend
erupts Jun 29, 2025
de34f51
update i18n
erupts Jun 29, 2025
e226e80
update i18n
erupts Jun 30, 2025
9d05a0b
update i18n
erupts Jun 30, 2025
1bf3cf0
Merge remote-tracking branch 'origin/develop' into develop
erupts Jul 1, 2025
cb72247
update i18n
erupts Jul 2, 2025
a90235c
Merge remote-tracking branch 'origin/develop' into develop
erupts Jul 2, 2025
d1ccdcf
erupt+ai → 智能表单填充
erupts Jul 14, 2025
d03a16f
update erupt-web
erupts Jul 15, 2025
5d773dc
Merge remote-tracking branch 'origin/develop' into develop
erupts Jul 15, 2025
6f445d4
修复EruptEditEvent传递修改前数据对象时,数据内容不准确的bug
erupts Jul 17, 2025
a21071d
error page use english
erupts Jul 17, 2025
af11c8e
remove unuseful class
erupts Jul 20, 2025
6daeef5
简化HyperModelCreatorVo.java与HyperModelUpdateVo.java的继承层级
erupts Jul 20, 2025
7b62507
数据库注释信息支持多语言
erupts Jul 20, 2025
9024574
optimize erupt-flow
erupts Jul 20, 2025
b9716de
optimize erupt-flow
erupts Jul 22, 2025
28d9e33
update erupt-web
erupts Jul 23, 2025
4a43879
Merge remote-tracking branch 'origin/develop' into develop
erupts Jul 23, 2025
f380848
MCP
erupts Jul 23, 2025
b13bc31
Merge remote-tracking branch 'origin/develop' into develop
erupts Jul 23, 2025
628949d
support mcp
erupts Jul 23, 2025
759fb3e
remove mcp
erupts Jul 24, 2025
98c25f4
optimize erupt-flow
erupts Jul 24, 2025
e93d04c
optimize erupt-job code
erupts Jul 25, 2025
bde4b9c
optimize 40x page
erupts Jul 25, 2025
430b71b
update erupt-web
erupts Jul 31, 2025
b36544e
update erupt-web
erupts Jul 31, 2025
3aac5a2
update erupt-web
erupts Jul 31, 2025
4ffb7e8
init erupt-flow
erupts Jul 31, 2025
b1be6d3
add delete and deleteAndFlush for eruptDao
luoben137 Aug 1, 2025
291230b
Merge pull request #314 from luoben137/develop
erupts Aug 1, 2025
482a8d0
init erupt-flow
erupts Aug 1, 2025
21088f8
optimize lambdaQuery delete()
erupts Aug 4, 2025
01637c8
optimize flow
erupts Aug 5, 2025
f39166c
optimize flow
erupts Aug 6, 2025
8084ff2
upgrade to 1.12.23
erupts Aug 6, 2025
8f960a9
解决 CHOICE 组件模板excel导入255长度限制,下拉框长度不限
erupts Aug 6, 2025
74ebbb5
Merge remote-tracking branch 'origin/develop' into develop
erupts Aug 6, 2025
6393ab6
optimize excel import
erupts Aug 6, 2025
93aa786
upgrade to 1.12.23
erupts Aug 6, 2025
b161740
update operator expr
erupts Aug 6, 2025
b60c5ca
update erupt-web
erupts Aug 7, 2025
600c353
update erupt-web
erupts Aug 7, 2025
4727287
pom add name tag
erupts Aug 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
14 changes: 7 additions & 7 deletions deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ pnpm run build
cd ../erupt/erupt-web
git add src/main/resources/public

cd ../erupt-ai-web
pnpm run build
cd ../erupt-ai
git add src/main/resources/static

cd ../deploy/erupt-cloud-server-docker
source deploy.sh
#cd ../erupt-ai-web
#pnpm run build
#cd ../erupt-ai
#git add src/main/resources/static
#
#cd ../deploy/erupt-cloud-server-docker
#source deploy.sh
12 changes: 10 additions & 2 deletions deploy/erupt-cloud-server-docker/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
<parent>
<groupId>xyz.erupt</groupId>
<artifactId>erupt</artifactId>
<version>1.12.21</version>
<version>1.12.23</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<artifactId>erupt-cloud-server-docker</artifactId>
<name>erupt-cloud-server-docker</name>

<properties>
<maven.deploy.skip>true</maven.deploy.skip>
Expand Down Expand Up @@ -72,7 +73,14 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.8.0</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>

</project>
3 changes: 2 additions & 1 deletion erupt-admin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>erupt-admin</artifactId>
<name>erupt-admin</name>

<parent>
<groupId>xyz.erupt</groupId>
<artifactId>erupt</artifactId>
<version>1.12.21</version>
<version>1.12.23</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
7 changes: 6 additions & 1 deletion erupt-ai/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<parent>
<groupId>xyz.erupt</groupId>
<artifactId>erupt</artifactId>
<version>1.12.21</version>
<version>1.12.23</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down Expand Up @@ -45,6 +45,11 @@
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.13.0</version>
</dependency>
</dependencies>

</project>
12 changes: 7 additions & 5 deletions erupt-ai/src/main/java/xyz/erupt/ai/call/AiFunctionManager.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package xyz.erupt.ai.call;

import com.google.gson.reflect.TypeToken;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
Expand Down Expand Up @@ -36,31 +37,32 @@
@Slf4j
public class AiFunctionManager implements ApplicationRunner {

private final Map<String, AiFunctionCall> aiFunctionMap = new HashMap<>();
@Getter
private static final Map<String, AiFunctionCall> aiFunctions = new HashMap<>();

@Override
public void run(ApplicationArguments args) {
EruptSpringUtil.scannerPackage(EruptApplication.getScanPackage(),
new TypeFilter[]{new AssignableTypeFilter(AiFunctionCall.class)}, clazz ->
aiFunctionMap.put(clazz.getSimpleName(), (AiFunctionCall) EruptSpringUtil.getBean(clazz))
aiFunctions.put(clazz.getSimpleName(), (AiFunctionCall) EruptSpringUtil.getBean(clazz))
);
}

public String getFunctionCallPrompt() {
StringBuilder sb = new StringBuilder("下面是一组 Function Call 的映射,根据情况决定是否调用,否则忽略这段提示词\n");
for (Map.Entry<String, AiFunctionCall> entry : aiFunctionMap.entrySet()) {
for (Map.Entry<String, AiFunctionCall> entry : aiFunctions.entrySet()) {
sb.append("- 如果用户问:").append(entry.getValue().description()).append(",就只回复:").append(entry.getKey()).append("\n");
}
return sb.toString();
}

public boolean exist(String key) {
return aiFunctionMap.containsKey(key);
return aiFunctions.containsKey(key);
}

@SneakyThrows
public String call(String key, LLM llm, String userMessage, List<ChatCompletionMessage> userContext) {
AiFunctionCall aiFunctionCall = aiFunctionMap.get(key);
AiFunctionCall aiFunctionCall = aiFunctions.get(key);
Map<String, Field> params = new HashMap<>();
for (Field field : aiFunctionCall.getClass().getDeclaredFields()) {
Optional.ofNullable(field.getAnnotation(AiParam.class)).ifPresent(it -> {
Expand Down
25 changes: 25 additions & 0 deletions erupt-ai/src/main/java/xyz/erupt/ai/config/AiMCPProp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package xyz.erupt.ai.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.stereotype.Component;

/**
* @author YuePeng
* date 2025/2/25 22:19
*/
@Getter
@Setter
@Component
//@ConfigurationProperties("erupt.ai.mcp")
public class AiMCPProp {

private boolean enable = false;

private String apiDomain;

private String name;

private String description;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package xyz.erupt.ai.controller;

import com.google.gson.reflect.TypeToken;
import org.springframework.web.bind.annotation.*;
import xyz.erupt.ai.service.LLMService;
import xyz.erupt.ai.util.ClassTemplateUtil;
import xyz.erupt.ai.util.MarkDownUtil;
import xyz.erupt.core.config.GsonFactory;
import xyz.erupt.core.constant.EruptRestPath;
import xyz.erupt.core.service.EruptCoreService;
import xyz.erupt.core.view.EruptModel;
import xyz.erupt.core.view.R;

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

/**
* @author YuePeng
* date 2025/6/4 22:10
*/
@RestController
@RequestMapping(EruptRestPath.ERUPT_API + "/ai/intellect")
public class IntellectController {

@Resource
private LLMService llmService;

@GetMapping("/form/fill/{erupt}")
public R<Map<String, Object>> formFill(@PathVariable("erupt") String eruptName,
@RequestParam(required = false) String prompt) {
EruptModel eruptModel = EruptCoreService.getErupt(eruptName);
String promptBuilder = "根据如下面的class类结构,生成内容随机的json,注意只返回纯json\n" +
ClassTemplateUtil.geneEruptFormClass(eruptModel.getClazz());
String result = llmService.send(promptBuilder);
return R.ok(GsonFactory.getGson().fromJson(MarkDownUtil.extractCodeBlock(result), new TypeToken<Map<String, Object>>() {
}.getType()));
}

}
101 changes: 101 additions & 0 deletions erupt-ai/src/main/java/xyz/erupt/ai/controller/McpController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package xyz.erupt.ai.controller;

import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.*;
import xyz.erupt.ai.annotation.AiParam;
import xyz.erupt.ai.call.AiFunctionCall;
import xyz.erupt.ai.call.AiFunctionManager;
import xyz.erupt.ai.config.AiMCPProp;
import xyz.erupt.ai.util.McpUtil;
import xyz.erupt.ai.vo.OpenAiVo;
import xyz.erupt.core.util.EruptInformation;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
public class McpController {

@Resource
private AiMCPProp mcpProp;

private static final String MCP_CALL_API = "/mcp/call";

@GetMapping("/openapi.json")
public OpenAiVo eruptAi() {
OpenAiVo openAiVo = new OpenAiVo();
OpenAiVo.Info info = new OpenAiVo.Info();
info.setTitle(mcpProp.getName());
info.setVersion(EruptInformation.getEruptVersion());
info.setDescription(mcpProp.getDescription());
openAiVo.setInfo(info);
openAiVo.setServers(new OpenAiVo.Server[]{new OpenAiVo.Server(mcpProp.getApiDomain())});
for (Map.Entry<String, AiFunctionCall> entry : AiFunctionManager.getAiFunctions().entrySet()) {
OpenAiVo.Path path = new OpenAiVo.Path();
openAiVo.getPaths().put(MCP_CALL_API + "/" + entry.getKey(), path);
OpenAiVo.PathPost post = new OpenAiVo.PathPost();
path.setPost(post);
post.setSummary(entry.getValue().description());
post.setOperationId(entry.getKey());
{
List<String> required = new ArrayList<>();
Map<String, OpenAiVo.SchemaProperties> properties = new HashMap<>();
for (Field field : entry.getValue().getClass().getDeclaredFields()) {
AiParam aiParam = field.getDeclaredAnnotation(AiParam.class);
if (null != aiParam) {
if (aiParam.required()) {
required.add(field.getName());
}
OpenAiVo.SchemaProperties schemaProperties = new OpenAiVo.SchemaProperties();
schemaProperties.setType(McpUtil.toMcp(field.getType()));
schemaProperties.setDescription(aiParam.description());
properties.put(field.getName(), schemaProperties);
}
}

OpenAiVo.RequestBody requestBody = new OpenAiVo.RequestBody();
post.setRequestBody(requestBody);
requestBody.setRequired(!properties.isEmpty());
OpenAiVo.ApplicationType applicationType = new OpenAiVo.ApplicationType();
requestBody.getContent().put("application/json", applicationType);
OpenAiVo.Schema schema = new OpenAiVo.Schema();
applicationType.setSchema(schema);
schema.setType("object");
schema.setRequired(required);
schema.setProperties(properties);
}
{
OpenAiVo.Response response = new OpenAiVo.Response();
post.getResponses().put(200, response);
response.setDescription("Success");
OpenAiVo.ApplicationType applicationType = new OpenAiVo.ApplicationType();
OpenAiVo.Schema schema = new OpenAiVo.Schema();
schema.setType(McpUtil.toMcp(String.class));
schema.setRequired(new ArrayList<>());
applicationType.setSchema(schema);
response.getContent().put("text/plain", applicationType);
}
}
return openAiVo;
}

@PostMapping(MCP_CALL_API + "/{code}")
@SneakyThrows
public String mcpCall(@PathVariable String code, @RequestBody(required = false) Map<String, Object> params) {
AiFunctionCall aiFunctionCall = AiFunctionManager.getAiFunctions().get(code);
if (null != params) {
for (Map.Entry<String, Object> entry : params.entrySet()) {
Field field = aiFunctionCall.getClass().getDeclaredField(entry.getKey());
field.setAccessible(true);
field.set(aiFunctionCall, entry.getValue());
field.setAccessible(false);
}
}
return aiFunctionCall.call(null);
}

}
24 changes: 22 additions & 2 deletions erupt-ai/src/main/java/xyz/erupt/ai/service/LLMService.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import xyz.erupt.ai.vo.SseBody;
import xyz.erupt.core.config.GsonFactory;
import xyz.erupt.core.context.MetaContext;
import xyz.erupt.core.exception.EruptWebApiRuntimeException;
import xyz.erupt.core.util.EruptSpringUtil;
import xyz.erupt.jpa.dao.EruptDao;

Expand All @@ -47,7 +48,26 @@ public class LLMService {
@Resource
private AiFunctionManager aiFunctionManager;

public static final int MESSAGE_TS = 100;
private static final int MESSAGE_TS = 100;

public String send(String prompt) {
return send(prompt, Collections.emptyList());
}

public String send(String prompt, List<ChatCompletionMessage> assistantPrompt) {
return send(eruptDao.lambdaQuery(LLM.class).eq(LLM::getDefaultLLM, true).eq(LLM::getEnable, true).limit(1).one(), prompt, assistantPrompt);
}

public String send(LLM llm, String prompt) {
return send(llm, prompt, Collections.emptyList());
}

public String send(LLM llmConfig, String prompt, List<ChatCompletionMessage> assistantPrompt) {
if (null == llmConfig) {
throw new EruptWebApiRuntimeException("Not found LLM config");
}
return LlmCore.getLLM(llmConfig.getLlm()).chat(llmConfig.toLlmRequest(), prompt, assistantPrompt).getMessageStr();
}

@SneakyThrows
public List<ChatCompletionMessage> geneCompletionPrompt(Chat chat, LLMAgent llmAgent, Integer contextTurn) {
Expand All @@ -67,7 +87,7 @@ public List<ChatCompletionMessage> geneCompletionPrompt(Chat chat, LLMAgent llmA
.eq(ChatMessage::getChatId, chat.getId())
.isNotNull(ChatMessage::getContent)
.orderByDesc(ChatMessage::getCreatedAt)
.limit(contextTurn).list();
.limit(contextTurn + 1).list();
Collections.reverse(chatMessages);
chatMessages.forEach(it -> chatCompletionMessages.add(
new ChatCompletionMessage(it.getSenderType() == ChatSenderType.USER ? MessageRole.user : MessageRole.assistant, it.getContent()))
Expand Down
32 changes: 32 additions & 0 deletions erupt-ai/src/main/java/xyz/erupt/ai/util/ClassTemplateUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package xyz.erupt.ai.util;

import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.TypeSpec;
import xyz.erupt.annotation.EruptField;
import xyz.erupt.core.util.ReflectUtil;

import javax.lang.model.element.Modifier;
import java.util.Optional;

/**
* @author YuePeng
* date 2025/7/14 22:13
*/
public class ClassTemplateUtil {

public static String geneEruptFormClass(Class<?> erupt) {
// 构建类
TypeSpec.Builder builder = TypeSpec.classBuilder(erupt.getSimpleName()).addModifiers(Modifier.PUBLIC);
ReflectUtil.findClassAllFields(erupt, field -> {
Optional.ofNullable(field.getDeclaredAnnotation(EruptField.class)).ifPresent(eruptField -> {
if (!"".equals(eruptField.edit().title())) {
FieldSpec.Builder fieldSpec = FieldSpec.builder(field.getType(), field.getName(), Modifier.PUBLIC);
fieldSpec.addJavadoc(eruptField.edit().title());
builder.addField(fieldSpec.build());
}
});
});
return builder.build().toString();
}

}
22 changes: 22 additions & 0 deletions erupt-ai/src/main/java/xyz/erupt/ai/util/McpUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package xyz.erupt.ai.util;

/**
* @author YuePeng
* date 2025/7/24 00:13
*/

public final class McpUtil {

public static String toMcp(Class<?> c) {
if (c.isPrimitive()) {
if (c == boolean.class) return "boolean";
if (c == float.class || c == double.class) return "number";
return "integer"; // byte/short/int/long
}
if (Number.class.isAssignableFrom(c)) {
if (c == Float.class || c == Double.class) return "number";
return "integer"; // Byte/Short/Integer/Long
}
return "string";
}
}
Loading
Loading