优化样式生成逻辑,支持流式传输并更新配置文件中的密钥

master
joefalmko 6 days ago
parent 0461499502
commit 3c0646ea92

@ -12,9 +12,20 @@ type StyleGenerate struct {
// ai生成样式
func (s *StyleGenerate) StyleGenerate(c *gin.Context) {
if res, err := ai_model_cli.RequestStyle(c); err==nil {
response.Success(c, consts.CurdStatusOkMsg, res.(string))
} else {
// 非流式传输
// if res, err := ai_model_cli.RequestStyle(c); err==nil {
// response.Success(c, consts.CurdStatusOkMsg, res.(string))
// } else {
// response.Fail(c, consts.StyleGenerateFailCode, consts.StyleGenerateFailMsg, err)
// }
// 设置 HTTP 头部为 SSE
c.Writer.Header().Set("Content-Type", "text/event-stream")
c.Writer.Header().Set("Cache-Control", "no-cache")
c.Writer.Header().Set("Connection", "keep-alive")
if err := ai_model_cli.RequestStyleStream(c); err != nil {
response.Fail(c, consts.StyleGenerateFailCode, consts.StyleGenerateFailMsg, err)
}
}

@ -74,7 +74,6 @@ func RequestStyle(c *gin.Context) (interface{}, error) {
// add user input to chat history
chatHistory = append(chatHistory, qianfan.ChatCompletionUserMessage(userMsg))
// define a stream chat client
response, err := chat.Do(context.TODO(), &qianfan.ChatCompletionRequest{System: string(prompt), Messages: chatHistory})
if err != nil {
variable.ZapLog.Error(fmt.Sprintf("对话失败: %v", err))
@ -83,3 +82,87 @@ func RequestStyle(c *gin.Context) (interface{}, error) {
return response.Result, nil
}
func RequestStyleStream(c *gin.Context) error {
userMsg := c.GetString(consts.ValidatorPrefix + "user_input")
qianfan.GetConfig().AccessKey = variable.ConfigYml.GetString("BaiduCE.QianFanAccessKey")
qianfan.GetConfig().SecretKey = variable.ConfigYml.GetString("BaiduCE.QianFanSecretKey")
chat := qianfan.NewChatCompletion(
qianfan.WithModel("ERNIE-4.0-8K"),
)
chatHistory := []qianfan.ChatCompletionMessage{}
systemMsgPath := variable.ConfigYml.GetString("BaiduCE.StyleGeneratePromptPath")
prompt, err := os.ReadFile(variable.BasePath + systemMsgPath)
if err != nil || len(prompt) == 0 {
variable.ZapLog.Error(fmt.Sprintf("读取提示词文件失败: %v", err))
return err
}
userHistory, exist := c.Get(consts.ValidatorPrefix + "chat_history")
if exist && userHistory != nil {
historySlice, ok := userHistory.([]interface{})
if !ok || len(historySlice)%2 != 0 {
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v", userHistory))
return fmt.Errorf("用户历史对话格式错误")
}
var chatHistoryConverted []qianfan.ChatCompletionMessage
for _, item := range historySlice {
if itemMap, ok := item.(map[string]interface{}); ok {
role, roleOk := itemMap["role"].(string)
content, contentOk := itemMap["content"].(string)
if roleOk && contentOk {
chatHistoryConverted = append(chatHistoryConverted, qianfan.ChatCompletionMessage{
Role: role,
Content: content,
})
} else {
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v\nrole 或 content 类型断言失败", userHistory))
return fmt.Errorf("用户历史对话格式错误")
}
} else {
variable.ZapLog.Error(fmt.Sprintf("用户历史对话格式错误: %v\n无法将 item 转换为 map[string]interface{}", userHistory))
return fmt.Errorf("用户历史对话格式错误")
}
}
if len(chatHistoryConverted) > 0 && len(chatHistoryConverted)%2 == 0 {
chatHistory = append(chatHistory, chatHistoryConverted...)
}
}
chatHistory = append(chatHistory, qianfan.ChatCompletionUserMessage(userMsg))
stream, err := chat.Stream(context.TODO(), &qianfan.ChatCompletionRequest{System: string(prompt), Messages: chatHistory})
if err != nil {
variable.ZapLog.Error(fmt.Sprintf("对话失败: %v", err))
return err
}
defer stream.Close()
c.Writer.Flush()
defer c.Writer.Flush()
for {
response, err := stream.Recv()
if response.IsEnd {
break // 流结束,退出循环
}
if err != nil {
variable.ZapLog.Error(fmt.Sprintf("接收流失败: %v", err))
return err
}
// 将结果写入到响应体
if _,err:=fmt.Fprintf(c.Writer,"%s",response.Result);err!=nil{
variable.ZapLog.Error(fmt.Sprintf("写入流失败: %v", err))
return err
}
// 立即刷新缓冲区,以确保数据立即发送到客户端
c.Writer.Flush()
}
return nil // 正常结束,返回 nil
}

@ -148,6 +148,9 @@ Captcha:
BaiduCE:
ApiKey: "AR1SUIjaKSsCcDjj11QzHDOc" # 生成鉴权签名时使用的 API_KEY
SecretKey: "zvEb5CzpuGCZNdQC1TPmDh3IOWn5aWDT" # 生成鉴权签名时使用的 SECRET_KEY
QianFanAccessKey: "ALTAKOxb5YvHncyFr7Qbuv1cK0" # 访问千帆sdk 时用的 AccessKey
QianFanSecretKey: "1edf17c358574e75b9913ebff7d95b61" # 访问千帆sdk 时用的 SecretKey
# QianFanAccessKey: "ALTAKOxb5YvHncyFr7Qbuv1cK0" # 访问千帆sdk 时用的 AccessKey
QianFanAccessKey: "ALTAK0utWNCwEoQtGHvHYf46yj"
# QianFanSecretKey: "1edf17c358574e75b9913ebff7d95b61" # 访问千帆sdk 时用的 SecretKey
QianFanSecretKey: "cb812e1b6e56420ea858d160e1351869"
StyleGeneratePromptPath: "/storage/app/prompt/style_generate.prompt" # 生成样式的提示词保存路径

@ -5,7 +5,7 @@
标题:<hi>i为标题级别如 h1, h2 等)
文本块:<p>
块引用:<blockquote>
文本:<span>
文本:<span>
代码块:<pre>
所有样式都需在 .ck-content 中定义,格式为:
``` css
@ -17,7 +17,7 @@
只需要精确输出用户需要生成的样式,不要生成用户未指定的样式。
### 示例
#### 示例一
用户输入:生成一个文本块样式
用户输入:生成一个文本块(文本框)样式
输出:
``` css
.ck-content p.info-box {

@ -297,7 +297,6 @@ import { setConfig } from '../components/plugins'
// import {getUserConfigFromBackend,saveData,getPageContent,getAndApplyUserStyles} from './components/utils';
import { useStore } from 'vuex';
import router from '../router/index.js';
import axios from 'axios';
export default {
name: 'CkeditorView',
@ -407,24 +406,24 @@ export default {
// TODO css
preElement.textContent = `文心一言:\n` + preElement.textContent;
messageDiv.style.backgroundColor = '#bdc3c7';
//
this.previewStyleAtMessages(text);
}
// messagesDiv.scrollTop = messagesDiv.scrollHeight;
return preElement;
},
//
sendMessage() {
async sendMessage() {
const userInput = document.getElementById('userInput');
const messageText = userInput.value;
if (messageText.trim() === '') return;
//
let chatHistory = [];
const messages = document.getElementById('messages').children;
for (let i = 0; i < messages.length; i++) {
if (i%4==0){
if (i % 4 == 0) {
chatHistory.push({ Role: 'user', Content: messages[i].textContent });
}else if (i%4==1){
} else if (i % 4 == 1) {
const assistantResponse = messages[i].textContent.replaceAll('文心一言:\n', '');
chatHistory.push({ Role: 'assistant', Content: assistantResponse });
}
@ -438,22 +437,49 @@ export default {
// messages
// TODO
// const chat_history = []
try {
const response = await fetch('http://localhost:14514/admin/ai_layout/style_generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
user_input: userInput.value,
...(chatHistory.length > 0 && { chat_history: chatHistory })
})
});
axios({
url: 'http://localhost:14514/admin/ai_layout/style_generate',
method: 'POST',
data: {
user_input: userInput.value,
...(chatHistory.length > 0 && { chat_history: chatHistory }),
if (!response.body) {
// TODO
throw new Error('No response body');
}
})
.then(response => {
console.log(response);
this.displayMessage(response.data.data, 'ai')
})
.catch(error => {
console.error('Error:', error);
});
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let result = '';
var messageStream;
/* eslint-disable no-constant-condition */
while (true) {
const { done, value } = await reader.read();
if (done) break;
const slice = decoder.decode(value, { stream: true });
result += slice;
if (!messageStream) {
messageStream = this.displayMessage(slice, 'ai');
} else {
messageStream.textContent += slice;
}
}
console.log('生成的样式:', result);
//
this.previewStyleAtMessages(result);
/* eslint-enable no-constant-condition */
} catch (error) {
console.error('Error:', error);
this.displayError(error);
}
//
userInput.value = '';
},
//
@ -495,15 +521,18 @@ export default {
while ((match = styleRegex.exec(cssText)) !== null) {
if (!previewElement) {
previewElement = document.createElement(match[1]);
// previewElement.textContent = 'AaBbCcDdEeFf';
const previewText = document.createElement('p');
previewText.textContent = 'AaBbCcDd';
previewElement.appendChild(previewText);
if (match[1] === 'span') {
previewElement.textContent = 'AaBbCcDdEeFf';
} else {
const previewText = document.createElement('p');
previewText.textContent = 'AaBbCcDd';
previewElement.appendChild(previewText);
}
previewWrapper.appendChild(previewElement);
}
// classNameclass
const newClassName = match[2]+"_"+Math.floor(Math.random()*1000);
const newClassName = match[2] + "_" + Math.floor(Math.random() * 1000);
previewStyle.textContent = previewStyle.textContent.replaceAll(match[2], newClassName);
classNames.push(newClassName);
}
@ -539,6 +568,37 @@ export default {
console.log(styleDefinition);
console.log(cssText);// css
};
//
buttonsMessageDiv.appendChild(saveButton);
buttonsMessageDiv.appendChild(this.createClearButton());
messagesDiv.appendChild(buttonsMessageDiv);
},
//
displayError(error) {
const messagesDiv = document.getElementById('messages');
const messageDiv = document.createElement('div');
messageDiv.className = 'message';
//
messageDiv.style.backgroundColor = 'mistyrose'; // mistyrose
// 使 pre text
// css htmlpre set language
const preElement = document.createElement('pre');
//
preElement.textContent = error;
preElement.style = "color:red";
messageDiv.appendChild(preElement);
messagesDiv.appendChild(messageDiv);
//
const buttonsMessageDiv = document.createElement('div');
buttonsMessageDiv.className = 'preview-buttons';
buttonsMessageDiv.style.display = 'flex';
buttonsMessageDiv.style.justifyContent = 'flex-end';
buttonsMessageDiv.style.marginTop = '10px';
buttonsMessageDiv.appendChild(this.createClearButton());
messagesDiv.appendChild(buttonsMessageDiv);
},
createClearButton() {
// clear
const clearButton = document.createElement('el-button');
clearButton.innerHTML = '<svg t="1731509926355" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8154" width="20" height="20"><path d="M1011.43552 981.92384l-68.4032-394.40384h23.10144c18.5856 0 33.54624-14.97088 33.54624-33.55648V306.16576c0-18.5856-14.97088-33.55648-33.54624-33.55648H648.6528V37.71392c0-18.5856-14.97088-33.55648-33.55648-33.55648H408.59648c-18.5856 0-33.55648 14.97088-33.55648 33.55648v234.88512H57.5488c-18.5856 0-33.54624 14.97088-33.54624 33.55648v247.79776c0 18.5856 14.97088 33.55648 33.54624 33.55648h23.10144L12.24704 981.9136c-0.38912 1.9456-0.512 3.87072-0.512 5.6832 0 18.5856 14.97088 33.54624 33.55648 33.54624h933.10976c1.93536 0 3.88096-0.12288 5.6832-0.512 18.31936-3.08224 30.57664-20.51072 27.35104-38.7072zM114.33984 362.94656h351.03744V94.50496h92.928v268.4416h351.03744v134.22592H114.33984V362.94656zM718.336 930.816V729.48736c0-5.6832-4.64896-10.33216-10.32192-10.33216h-61.952c-5.67296 0-10.32192 4.64896-10.32192 10.33216V930.816H387.9424V729.48736c0-5.6832-4.64896-10.33216-10.32192-10.33216h-61.952c-5.67296 0-10.32192 4.64896-10.32192 10.33216V930.816H112.78336l58.20416-335.55456h681.5744L910.76608 930.816H718.336z m0 0" fill="#2C2C2C" p-id="8155"></path></svg>';
@ -552,10 +612,7 @@ export default {
messagesDiv.removeChild(messagesDiv.firstChild);
}
};
//
buttonsMessageDiv.appendChild(saveButton);
buttonsMessageDiv.appendChild(clearButton);
messagesDiv.appendChild(buttonsMessageDiv);
return clearButton;
}
},
components: {

Loading…
Cancel
Save