mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-10 18:02:55 +00:00
feat: 添加额外数据处理,优化AI助手消息生成与发送逻辑
This commit is contained in:
parent
e801c09c0f
commit
892ad395a7
@ -1027,25 +1027,25 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/dialog/msg/ai_generate 使用 AI 助手生成消息
|
||||
* @api {post} api/dialog/msg/aiprompt AI 提示词助手
|
||||
*
|
||||
* @apiDescription 需要token身份,根据上下文自动生成拟发送的聊天消息
|
||||
* @apiDescription 需要token身份,整理当前会话的系统提示词与上下文信息
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__ai_generate
|
||||
* @apiName msg__aiprompt
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
* @apiParam {String} content 消息需求描述
|
||||
* @apiParam {String} content 消息需求描述(用于提示词整理,可为空)
|
||||
* @apiParam {String} [draft] 当前草稿内容(HTML 格式)
|
||||
* @apiParam {Number} [quote_id] 引用消息ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
* @apiSuccess {String} data.text AI 生成的消息文本(Markdown 格式)
|
||||
* @apiSuccess {String} data.html AI 生成的消息内容(HTML 格式)
|
||||
* @apiSuccess {String} data.system_prompt AI 使用的系统提示词
|
||||
* @apiSuccess {String} data.context_prompt AI 使用的上下文提示词
|
||||
*/
|
||||
public function msg__ai_generate()
|
||||
public function msg__aiprompt()
|
||||
{
|
||||
$user = User::auth();
|
||||
$user->checkChatInformation();
|
||||
@ -1055,9 +1055,6 @@ class DialogController extends AbstractController
|
||||
if ($dialog_id <= 0) {
|
||||
return Base::retError('参数错误');
|
||||
}
|
||||
if ($content === '') {
|
||||
return Base::retError('消息需求描述不能为空');
|
||||
}
|
||||
|
||||
$dialog = WebSocketDialog::checkDialog($dialog_id);
|
||||
|
||||
@ -1144,12 +1141,16 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
// 生成消息
|
||||
$result = AI::generateMessage($content, $context);
|
||||
if (Base::isError($result)) {
|
||||
return Base::retError('生成消息失败', $result);
|
||||
$systemPrompt = AI::messageSystemPrompt();
|
||||
$contextPrompt = AI::buildMessageContextPrompt($context);
|
||||
if ($content) {
|
||||
$contextPrompt .= "\n\n请根据以上信息,结合提示词生成一条待发送的消息:\n\n";
|
||||
}
|
||||
|
||||
return Base::retSuccess('生成消息成功', $result['data']);
|
||||
return Base::retSuccess('success', [
|
||||
'system_prompt' => $systemPrompt,
|
||||
'context_prompt' => $contextPrompt,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1179,6 +1180,7 @@ class DialogController extends AbstractController
|
||||
* - no: 正常发送(默认)
|
||||
* - yes: 静默发送
|
||||
* @apiParam {String} [model_name] 模型名称(仅AI机器人支持)
|
||||
* @apiParam {Object} [extra_data] 附加数据(保存到附加表)
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
@ -1200,6 +1202,7 @@ class DialogController extends AbstractController
|
||||
$text_type = strtolower(trim(Request::input('text_type')));
|
||||
$silence = in_array(strtolower(trim(Request::input('silence'))), ['yes', 'true', '1']);
|
||||
$model_name = trim(Request::input('model_name'));
|
||||
$extra_data = Request::input('extra_data');
|
||||
$markdown = in_array($text_type, ['md', 'markdown']);
|
||||
//
|
||||
$result = [];
|
||||
@ -1233,14 +1236,14 @@ class DialogController extends AbstractController
|
||||
$text = WebSocketDialogMsg::formatMsg($text, $dialog_id);
|
||||
}
|
||||
$strlen = mb_strlen($text);
|
||||
$noimglen = mb_strlen(preg_replace("/<img[^>]*?>/i", "", $text));
|
||||
$reallen = mb_strlen(preg_replace("/<img[^>]*?>/i", "", $text));
|
||||
if ($strlen < 1) {
|
||||
return Base::retError('消息内容不能为空');
|
||||
}
|
||||
if ($noimglen > 200000) {
|
||||
if ($reallen > 200000) {
|
||||
return Base::retError('消息内容最大不能超过200000字');
|
||||
}
|
||||
if ($noimglen > 5000) {
|
||||
if ($reallen > 5000) {
|
||||
// 内容过长转成文件发送
|
||||
$path = "uploads/chat/" . date("Ym") . "/" . $dialog_id . "/";
|
||||
Base::makeDir(public_path($path));
|
||||
@ -1282,7 +1285,7 @@ class DialogController extends AbstractController
|
||||
if ($model_name) {
|
||||
$msgData['model_name'] = $model_name;
|
||||
}
|
||||
$result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'longtext', $msgData, $user->userid, false, false, $silence, $key);
|
||||
$result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'longtext', $msgData, $user->userid, false, false, $silence, $key, $extra_data);
|
||||
} else {
|
||||
$msgData = ['text' => $text];
|
||||
if ($markdown) {
|
||||
@ -1291,7 +1294,7 @@ class DialogController extends AbstractController
|
||||
if ($model_name) {
|
||||
$msgData['model_name'] = $model_name;
|
||||
}
|
||||
$result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'text', $msgData, $user->userid, false, false, $silence, $key);
|
||||
$result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'text', $msgData, $user->userid, false, false, $silence, $key, $extra_data);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
@ -3134,11 +3137,11 @@ class DialogController extends AbstractController
|
||||
//
|
||||
WebSocketDialog::checkDialog($dialog_id);
|
||||
$strlen = mb_strlen($text);
|
||||
$noimglen = mb_strlen(preg_replace("/<img[^>]*?>/i", "", $text));
|
||||
$reallen = mb_strlen(preg_replace("/<img[^>]*?>/i", "", $text));
|
||||
if ($strlen < 1 || empty($list)) {
|
||||
return Base::retError('内容不能为空');
|
||||
}
|
||||
if ($noimglen > 200000) {
|
||||
if ($reallen > 200000) {
|
||||
return Base::retError('内容最大不能超过200000字');
|
||||
}
|
||||
//
|
||||
@ -3283,11 +3286,11 @@ class DialogController extends AbstractController
|
||||
});
|
||||
} else {
|
||||
$strlen = mb_strlen($text);
|
||||
$noimglen = mb_strlen(preg_replace("/<img[^>]*?>/i", "", $text));
|
||||
$reallen = mb_strlen(preg_replace("/<img[^>]*?>/i", "", $text));
|
||||
if ($strlen < 1) {
|
||||
return Base::retError('内容不能为空');
|
||||
}
|
||||
if ($noimglen > 200000) {
|
||||
if ($reallen > 200000) {
|
||||
return Base::retError('内容最大不能超过200000字');
|
||||
}
|
||||
$msgData = [
|
||||
|
||||
@ -43,6 +43,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @property-read int|mixed $percentage
|
||||
* @property-read \App\Models\User|null $user
|
||||
* @property-read \App\Models\WebSocketDialogMsgExtra|null $extra
|
||||
* @property-read \App\Models\WebSocketDialog|null $webSocketDialog
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
|
||||
@ -111,6 +112,14 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
return $this->hasOne(User::class, 'userid', 'userid');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function extra(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(WebSocketDialogMsgExtra::class, 'msg_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 阅读占比
|
||||
* @return int|mixed
|
||||
@ -1233,9 +1242,10 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
* @param bool|null $push_silence 推送-静默
|
||||
* - type = [text|file|record|meeting] 默认为:false
|
||||
* @param string|null $search_key 搜索关键词(用于搜索,留空则自动生成)
|
||||
* @param array|null $extra_data 额外数据(仅在发送消息时有效)
|
||||
* @return array
|
||||
*/
|
||||
public static function sendMsg($action, $dialog_id, $type, $msg, $sender = null, $push_self = false, $push_retry = false, $push_silence = null, $search_key = null)
|
||||
public static function sendMsg($action, $dialog_id, $type, $msg, $sender = null, $push_self = false, $push_retry = false, $push_silence = null, $search_key = null, $extra_data = null)
|
||||
{
|
||||
$link = 0;
|
||||
$mtype = $type;
|
||||
@ -1380,10 +1390,17 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
'msg' => $msg,
|
||||
'read' => 0,
|
||||
]);
|
||||
AbstractModel::transaction(function () use ($search_key, $dialogMsg) {
|
||||
AbstractModel::transaction(function () use ($search_key, $dialogMsg, $extra_data) {
|
||||
$dialogMsg->send = 1;
|
||||
$dialogMsg->generateKeyAndSave($search_key);
|
||||
//
|
||||
if ($extra_data) {
|
||||
WebSocketDialogMsgExtra::createInstance([
|
||||
'msg_id' => $dialogMsg->id,
|
||||
'data' => Base::array2json($extra_data),
|
||||
])->save();
|
||||
}
|
||||
//
|
||||
WebSocketDialogSession::updateTitle($dialogMsg->session_id, $dialogMsg);
|
||||
//
|
||||
if ($dialogMsg->type === 'meeting') {
|
||||
|
||||
56
app/Models/WebSocketDialogMsgExtra.php
Normal file
56
app/Models/WebSocketDialogMsgExtra.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* App\Models\WebSocketDialogMsgExtra
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $msg_id 消息ID
|
||||
* @property string|null $data 长内容
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \App\Models\WebSocketDialogMsg|null $webSocketDialogMsg
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgExtra newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgExtra newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgExtra query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgExtra whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgExtra whereData($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgExtra whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgExtra whereMsgId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgExtra whereUpdatedAt($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class WebSocketDialogMsgExtra extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @param $value
|
||||
* @return array
|
||||
*/
|
||||
public function getDataAttribute($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return Base::json2array($value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 关联到消息
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function webSocketDialogMsg(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
{
|
||||
return $this->belongsTo(WebSocketDialogMsg::class, 'msg_id', 'id');
|
||||
}
|
||||
}
|
||||
|
||||
@ -556,72 +556,6 @@ class AI
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 openAI 生成聊天消息
|
||||
* @param string $text 用户提供的提示词
|
||||
* @param array $context 上下文信息
|
||||
* @return array
|
||||
*/
|
||||
public static function generateMessage($text, $context = [])
|
||||
{
|
||||
$text = trim((string)$text);
|
||||
if ($text === '') {
|
||||
return Base::retError("消息提示词不能为空");
|
||||
}
|
||||
|
||||
$contextPrompt = self::buildMessageContextPrompt($context);
|
||||
|
||||
$post = json_encode([
|
||||
"model" => "gpt-5-mini",
|
||||
"reasoning_effort" => "minimal",
|
||||
"messages" => [
|
||||
[
|
||||
"role" => "system",
|
||||
"content" => <<<EOF
|
||||
你是一名专业的沟通助手,协助用户编写得体、清晰且具行动指向的即时消息。
|
||||
|
||||
写作要求:
|
||||
1. 根据用户提供的需求与上下文生成完整消息,语气需符合业务沟通场景,保持真诚、礼貌且高效
|
||||
2. 默认使用简洁的短段落,可使用 Markdown 基础格式(加粗、列表、引用)增强结构,但不要输出代码块或 JSON
|
||||
3. 如果上下文包含引用信息或草稿,请在消息中自然呼应相关要点
|
||||
4. 如无特别说明,将消息长度控制在 60-180 字;若需更短或更长,遵循用户描述
|
||||
5. 如需提出行动或问题,请明确表达,避免含糊
|
||||
|
||||
输出规范:
|
||||
- 仅返回可直接发送的消息内容
|
||||
- 禁止在内容前后添加额外说明、标签或引导语
|
||||
EOF
|
||||
],
|
||||
[
|
||||
"role" => "user",
|
||||
"content" => ($contextPrompt ? $contextPrompt . "\n\n" : "") . "请根据以上信息,并结合以下提示词生成一条待发送的消息:\n\n" . $text
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$ai = new self($post);
|
||||
$ai->setTimeout(45);
|
||||
|
||||
$res = $ai->request();
|
||||
if (Base::isError($res)) {
|
||||
return Base::retError("消息生成失败", $res);
|
||||
}
|
||||
|
||||
$content = trim($res['data']);
|
||||
$content = preg_replace('/^\s*```(?:markdown|md|text)?\s*/i', '', $content);
|
||||
$content = preg_replace('/\s*```\s*$/', '', $content);
|
||||
$content = trim($content);
|
||||
|
||||
if ($content === '') {
|
||||
return Base::retError("消息生成结果为空");
|
||||
}
|
||||
|
||||
return Base::retSuccess("success", [
|
||||
'text' => $content,
|
||||
'html' => Base::markdown2html($content),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对工作汇报内容进行分析
|
||||
* @param Report $report
|
||||
@ -836,7 +770,25 @@ class AI
|
||||
return empty($prompts) ? "" : implode("\n", $prompts);
|
||||
}
|
||||
|
||||
private static function buildMessageContextPrompt($context)
|
||||
public static function messageSystemPrompt()
|
||||
{
|
||||
return <<<EOF
|
||||
你是一名专业的沟通助手,协助用户编写得体、清晰且具行动指向的即时消息。
|
||||
|
||||
写作要求:
|
||||
1. 根据用户提供的需求与上下文生成完整消息,语气需符合业务沟通场景,保持真诚、礼貌且高效
|
||||
2. 默认使用简洁的短段落,可使用 Markdown 基础格式(加粗、列表、引用)增强结构,但不要输出代码块或 JSON
|
||||
3. 如果上下文包含引用信息或草稿,请在消息中自然呼应相关要点
|
||||
4. 如无特别说明,将消息长度控制在 60-180 字;若需更短或更长,遵循用户描述
|
||||
5. 如需提出行动或问题,请明确表达,避免含糊
|
||||
|
||||
输出规范:
|
||||
- 仅返回可直接发送的消息内容
|
||||
- 禁止在内容前后添加额外说明、标签或引导语
|
||||
EOF;
|
||||
}
|
||||
|
||||
public static function buildMessageContextPrompt($context)
|
||||
{
|
||||
$prompts = [];
|
||||
|
||||
|
||||
@ -66,7 +66,7 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
}
|
||||
|
||||
// 判断消息是否存在
|
||||
$msg = WebSocketDialogMsg::with(['user'])->find($this->msgId);
|
||||
$msg = WebSocketDialogMsg::with(['user', 'extra'])->find($this->msgId);
|
||||
if (empty($msg)) {
|
||||
return;
|
||||
}
|
||||
@ -523,6 +523,16 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
{$sendText}
|
||||
EOF;
|
||||
}
|
||||
// 处理额外数据
|
||||
if ($msg->extra && $msg->extra->data) {
|
||||
$extraData = $msg->extra->data;
|
||||
if ($extraData['system_prompt']) {
|
||||
$extras['system_message'] = $extraData['system_prompt'];
|
||||
}
|
||||
if ($extraData['context_prompt']) {
|
||||
$sendText = $extraData['context_prompt'] . $sendText;
|
||||
}
|
||||
}
|
||||
$webhookUrl = "http://nginx/ai/chat";
|
||||
} else {
|
||||
// 用户机器人
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateWebSocketDialogMsgExtrasTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('web_socket_dialog_msg_extras', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('msg_id')->nullable()->default(0)->comment('消息ID');
|
||||
$table->longText('data')->nullable()->comment('额外数据');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('msg_id')->references('id')->on('web_socket_dialog_msgs')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('web_socket_dialog_msg_extras');
|
||||
}
|
||||
}
|
||||
@ -133,6 +133,7 @@ export default {
|
||||
inputAutosize: this.defaultInputAutosize,
|
||||
inputMaxlength: this.defaultInputMaxlength,
|
||||
inputOnOk: null,
|
||||
inputOnBeforeSend: null,
|
||||
|
||||
// 模型选择
|
||||
inputModel: '',
|
||||
@ -186,6 +187,7 @@ export default {
|
||||
this.inputAutosize = params.autosize || this.defaultInputAutosize;
|
||||
this.inputMaxlength = params.maxlength || this.defaultInputMaxlength;
|
||||
this.inputOnOk = params.onOk || null;
|
||||
this.inputOnBeforeSend = params.onBeforeSend || null;
|
||||
}
|
||||
this.responses = [];
|
||||
this.pendingResponses = [];
|
||||
@ -368,7 +370,7 @@ export default {
|
||||
prompt: rawValue,
|
||||
});
|
||||
this.scrollResponsesToBottom();
|
||||
const message = await this.sendAiMessage(dialogId, this.formatPlainText(rawValue), modelOption.value);
|
||||
const message = await this.sendAiMessage(dialogId, rawValue, modelOption.value);
|
||||
if (responseEntry) {
|
||||
responseEntry.userid = userid;
|
||||
responseEntry.message = message;
|
||||
@ -476,11 +478,11 @@ export default {
|
||||
const {data} = await this.$store.dispatch("call", {
|
||||
url: 'dialog/msg/sendtext',
|
||||
method: 'post',
|
||||
data: {
|
||||
data: await this.buildPayloadData({
|
||||
dialog_id: dialogId,
|
||||
text,
|
||||
model_name: model,
|
||||
},
|
||||
}),
|
||||
});
|
||||
if (data) {
|
||||
this.$store.dispatch("saveDialogMsg", data);
|
||||
@ -494,15 +496,26 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* 将纯文本转换成HTML
|
||||
* 构建最终发送的数据
|
||||
*/
|
||||
formatPlainText(text) {
|
||||
const escaped = `${text}`
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\n/g, '<br/>');
|
||||
return `<p>${escaped}</p>`;
|
||||
async buildPayloadData(data) {
|
||||
if (typeof this.inputOnBeforeSend !== 'function') {
|
||||
return data;
|
||||
}
|
||||
try {
|
||||
const result = this.inputOnBeforeSend(data);
|
||||
if (result && typeof result.then === 'function') {
|
||||
const resolved = await result;
|
||||
if ($A.isJson(resolved)) {
|
||||
return resolved;
|
||||
}
|
||||
} else if ($A.isJson(result)) {
|
||||
return result;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[AIAssistant] onBeforeSend error:', e);
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -681,16 +694,19 @@ export default {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
max-height: calc(100vh - 344px);
|
||||
@media (height <= 900px) {
|
||||
max-height: calc(100vh - 214px);
|
||||
}
|
||||
|
||||
.ai-assistant-output {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
background: #f8f9fb;
|
||||
border: 1px solid rgba(0, 0, 0, 0.04);
|
||||
max-height: calc(100vh - 390px);
|
||||
overflow-y: auto;
|
||||
@media (height <= 900px) {
|
||||
max-height: calc(100vh - 260px);
|
||||
}
|
||||
}
|
||||
|
||||
.ai-assistant-output-item + .ai-assistant-output-item {
|
||||
|
||||
@ -342,7 +342,7 @@ import clickoutside from "../../../../directives/clickoutside";
|
||||
import longpress from "../../../../directives/longpress";
|
||||
import {inputLoadAdd, inputLoadIsLast, inputLoadRemove} from "./one";
|
||||
import {languageList, languageName} from "../../../../language";
|
||||
import {isMarkdownFormat} from "../../../../utils/markdown";
|
||||
import {isMarkdownFormat, MarkdownConver} from "../../../../utils/markdown";
|
||||
import emitter from "../../../../store/events";
|
||||
import historyMixin from "./history";
|
||||
|
||||
@ -1904,63 +1904,45 @@ export default {
|
||||
return;
|
||||
}
|
||||
if (!this.dialogId) {
|
||||
$A.messageWarning(this.$L('当前未选择会话'));
|
||||
$A.messageWarning('当前未选择会话');
|
||||
return;
|
||||
}
|
||||
let canceled = false;
|
||||
$A.modalInput({
|
||||
title: 'AI 生成',
|
||||
placeholder: '请简要描述消息的主题、语气或要点,AI 将生成完整消息',
|
||||
inputProps: {
|
||||
type: 'textarea',
|
||||
rows: 2,
|
||||
autosize: {minRows: 2, maxRows: 6},
|
||||
maxlength: 500,
|
||||
},
|
||||
onCancel: () => {
|
||||
canceled = true;
|
||||
},
|
||||
onOk: (value) => {
|
||||
if (!value) {
|
||||
return '请输入消息需求';
|
||||
emitter.emit('openAIAssistant', {
|
||||
placeholder: this.$L('请简要描述消息的主题、语气或要点,AI 将生成完整消息'),
|
||||
onBeforeSend: async (sendData) => {
|
||||
if (!sendData) {
|
||||
return sendData;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
if (canceled) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
this.$store.dispatch('call', {
|
||||
url: 'dialog/msg/ai_generate',
|
||||
try {
|
||||
const {data: extraData} = await this.$store.dispatch('call', {
|
||||
url: 'dialog/msg/aiprompt',
|
||||
data: {
|
||||
dialog_id: this.dialogId,
|
||||
content: value,
|
||||
content: sendData.text,
|
||||
draft: this.value || '',
|
||||
quote_id: this.quoteData?.id || 0,
|
||||
},
|
||||
timeout: 45 * 1000,
|
||||
}).then(({data}) => {
|
||||
const html = data && (data.html || data.text) ? (data.html || data.text) : '';
|
||||
if (canceled) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
if (!html) {
|
||||
reject(this.$L('AI 未生成内容'));
|
||||
});
|
||||
if ($A.isJson(extraData)) {
|
||||
sendData.extra_data = extraData;
|
||||
}
|
||||
return sendData;
|
||||
} catch (error) {
|
||||
const msg = error?.msg || 'AI 提示生成失败';
|
||||
$A.modalError(msg);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
onOk: ({aiContent}) => {
|
||||
if (!aiContent) {
|
||||
$A.messageWarning('AI 未生成内容');
|
||||
return;
|
||||
}
|
||||
const html = MarkdownConver(aiContent);
|
||||
this.$emit('input', html);
|
||||
this.$nextTick(() => this.focus());
|
||||
resolve();
|
||||
}).catch(({msg}) => {
|
||||
if (canceled) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
reject(msg);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onFullInput() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user