Skip to content

Commit ca2c24b

Browse files
authored
Merge pull request #339 from erupts/develop
1.13.3
2 parents 5e5f1f3 + 9152883 commit ca2c24b

File tree

168 files changed

+5110
-3061
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

168 files changed

+5110
-3061
lines changed

deploy/erupt-cloud-server-docker/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# 默认使用阿里云镜像
2-
FROM openjdk:11-jre-slim
2+
FROM eclipse-temurin:17-jre
33

44
# 设置工作目录
55
WORKDIR /app

deploy/erupt-cloud-server-docker/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>xyz.erupt</groupId>
88
<artifactId>erupt</artifactId>
9-
<version>1.13.2</version>
9+
<version>1.13.3</version>
1010
<relativePath>../../pom.xml</relativePath>
1111
</parent>
1212

erupt-admin/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<parent>
1111
<groupId>xyz.erupt</groupId>
1212
<artifactId>erupt</artifactId>
13-
<version>1.13.2</version>
13+
<version>1.13.3</version>
1414
<relativePath>../pom.xml</relativePath>
1515
</parent>
1616

erupt-ai-web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"markdown-it-footnote": "^4.0.0",
2424
"markdown-it-highlightjs": "^4.2.0",
2525
"markdown-it-ins": "^4.0.0",
26+
"markdown-it-katex": "^2.0.3",
2627
"markdown-it-link-attributes": "^4.0.1",
2728
"markdown-it-mark": "^4.0.0",
2829
"markdown-it-mermaid": "^0.2.5",

erupt-ai-web/src/App.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ const send = (message: string) => {
127127
eventSource.onmessage = (event) => {
128128
sending.value = false;
129129
sendDisabled.value = true;
130-
console.log(JSON.parse(event.data).text)
131130
accumulatedMarkdown.value += JSON.parse(event.data).text;
132131
let msg = messages.value[messages.value.length - 1];
133132
if (msg.loading) {

erupt-ai-web/src/app.less

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,9 @@
148148
background: #ddd;
149149
margin: @base-margin * 2 0;
150150
}
151+
152+
/* 修复 KaTeX 上下标显示问题 */
153+
.katex .vlist {
154+
vertical-align: baseline !important;
155+
}
151156
}

erupt-ai-web/src/components/markdown.ts

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,37 @@ import abbr from 'markdown-it-abbr';
1111
import deflist from 'markdown-it-deflist';
1212
import linkAttributes from 'markdown-it-link-attributes';
1313
import taskLists from 'markdown-it-task-lists';
14-
import katex from 'katex';
14+
import markdownItKatex from 'markdown-it-katex';
1515
import 'katex/dist/katex.min.css';
16+
import mermaid from 'mermaid';
1617

18+
// 初始化 mermaid
19+
// if (typeof window !== 'undefined') {
20+
// mermaid.initialize({
21+
// startOnLoad: true,
22+
// theme: 'default',
23+
// securityLevel: 'loose'
24+
// });
25+
// }
1726

18-
// 自定义数学公式渲染
19-
// @ts-ignore
20-
const renderMath = (md) => {
21-
const defaultRender = md.renderer.rules.text;
22-
md.renderer.rules.text = (tokens: {
23-
[x: string]: any;
24-
}, idx: string | number, options: any, env: any, self: any) => {
25-
const token = tokens[idx];
26-
const match = token.content.match(/(\$\$?)([\s\S]*?)\1/g);
27-
if (match) {
28-
match.forEach((m: string) => {
29-
const isBlock = m.startsWith('$$');
30-
const tex = m.slice(2, -2);
31-
try {
32-
const html = katex.renderToString(tex, {displayMode: isBlock});
33-
token.content = token.content.replace(m, html);
34-
} catch (e: any) {
35-
token.content = token.content.replace(m, `<span class="error">Error rendering math: ${e.message}</span>`);
36-
}
37-
});
38-
}
39-
return defaultRender(tokens, idx, options, env, self);
40-
};
41-
};
27+
function preprocessLatex(text: string): string {
28+
// 1. 先将 ```latex 代码块的内容提取出来(保留原始内容)
29+
text = text.replace(/```latex\s*([\s\S]*?)```/g, (_match, content) => {
30+
return '\n' + content.trim() + '\n';
31+
});
32+
33+
// 2. 统一将 \[ ... \] 转换为 $$...$$(LaTeX 块级公式)
34+
text = text.replace(/\\\[([\s\S]*?)\\\]/g, (_match, formula) => {
35+
return '$$' + formula.trim() + '$$';
36+
});
37+
38+
// 3. 将 \( ... \) 转换为 $...$(LaTeX 行内公式)
39+
text = text.replace(/\\\(([\s\S]*?)\\\)/g, (_match, formula) => {
40+
return '$' + formula.trim() + '$';
41+
});
42+
43+
return text;
44+
}
4245

4346
// 创建 markdown-it 实例并加载插件
4447
const md = new MarkdownIt({
@@ -47,6 +50,12 @@ const md = new MarkdownIt({
4750
linkify: true,
4851
typographer: true,
4952
highlight: (str: string, lang: string) => {
53+
// 处理 mermaid 图表
54+
if (lang === 'mermaid') {
55+
return `<div class="mermaid">${str}</div>`;
56+
}
57+
58+
// 处理代码高亮
5059
if (lang && hljs.getLanguage(lang)) {
5160
return `<pre class="hljs"><code>${hljs.highlight(str, {language: lang}).value}</code></pre>`;
5261
}
@@ -63,6 +72,26 @@ const md = new MarkdownIt({
6372
.use(deflist)
6473
.use(linkAttributes, {attrs: {target: '_blank', rel: 'noopener'}})
6574
.use(taskLists)
66-
.use(renderMath);
75+
.use(markdownItKatex, {
76+
throwOnError: false,
77+
errorColor: '#cc0000'
78+
});
79+
80+
// 重写 render 方法,添加预处理
81+
const originalRender = md.render.bind(md);
82+
md.render = function (text: string, env?: any): string {
83+
const preprocessedText = preprocessLatex(text);
84+
const html = originalRender(preprocessedText, env);
85+
86+
if (html.indexOf('<div class="mermaid">') !== -1) {
87+
setTimeout(() => {
88+
mermaid.run({
89+
querySelector: '.mermaid'
90+
});
91+
}, 0);
92+
}
93+
94+
return html;
95+
};
6796

6897
export default md;

erupt-ai/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<parent>
1111
<groupId>xyz.erupt</groupId>
1212
<artifactId>erupt</artifactId>
13-
<version>1.13.2</version>
13+
<version>1.13.3</version>
1414
<relativePath>../pom.xml</relativePath>
1515
</parent>
1616

erupt-ai/src/main/java/xyz/erupt/ai/config/AiProp.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ public class AiProp {
1919

2020
private boolean devMode = false;
2121

22+
private boolean enableFunctionCall = true;
23+
2224
}

erupt-ai/src/main/java/xyz/erupt/ai/core/OpenAi.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.io.IOException;
2121
import java.util.HashMap;
2222
import java.util.List;
23+
import java.util.concurrent.TimeUnit;
2324
import java.util.function.Consumer;
2425

2526
/**
@@ -30,6 +31,14 @@
3031
@Slf4j
3132
public abstract class OpenAi extends LlmCore {
3233

34+
private final OkHttpClient client = new OkHttpClient().newBuilder()
35+
.connectTimeout(30, TimeUnit.SECONDS)
36+
.readTimeout(5, TimeUnit.MINUTES) // 流式需要很长!
37+
.writeTimeout(30, TimeUnit.SECONDS)
38+
.pingInterval(10, TimeUnit.SECONDS) // 保持连接心跳
39+
.build();
40+
;
41+
3342
private final Gson gson = new GsonBuilder().create();
3443

3544
public String chatApiPath() {
@@ -50,7 +59,6 @@ public LlmConfig config() {
5059
public ChatCompletionResponse chat(LlmRequest llmRequest, String userPrompt, List<ChatCompletionMessage> assistantPrompt) {
5160
assistantPrompt.add(new ChatCompletionMessage(MessageRole.user, userPrompt));
5261
ChatCompletion completion = ChatCompletion.builder().model(llmRequest.getModel()).stream(false).messages(assistantPrompt).build();
53-
OkHttpClient client = new OkHttpClient();
5462
RequestBody body = RequestBody.create(
5563
gson.toJson(completion),
5664
MediaType.parse("application/json; charset=utf-8")
@@ -77,6 +85,7 @@ public ChatCompletionResponse chat(LlmRequest llmRequest, String userPrompt, Lis
7785
@Override
7886
@SneakyThrows
7987
public void chatSse(LlmRequest llmRequest, String userPrompt, List<ChatCompletionMessage> assistantPrompt, Consumer<SseListener> listener) {
88+
assistantPrompt.removeIf(it -> StringUtils.isBlank(it.getContent()));
8089
ChatCompletion completion = ChatCompletion.builder().model(llmRequest.getModel()).messages(assistantPrompt).stream(true).build();
8190
completion.setResponse_format(new HashMap<>() {{
8291
this.put("type", String.valueOf(llmRequest.getResponseFormat()));
@@ -118,8 +127,8 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO
118127
String line = source.readUtf8Line();
119128
if (StringUtils.isNotBlank(line)) {
120129
if (!response.isSuccessful()) {
121-
this.onFailure(call, new IOException(response.body().string()));
122-
log.error("Failed to get llm response from server: {}", response.body());
130+
this.onFailure(call, new IOException(response.body().string() + line));
131+
log.error("Failed to get llm response from server: {}", response.body() + " → " + line);
123132
return;
124133
} else {
125134
try {
@@ -146,7 +155,8 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO
146155
listener.accept(sseListener);
147156
}
148157
} catch (Exception e) {
149-
this.onFailure(call, new IOException(e));
158+
this.onFailure(call, new IOException(e + "→" + line));
159+
break;
150160
}
151161
}
152162
}

0 commit comments

Comments
 (0)