perf: 支持自定义AI个人提示词

This commit is contained in:
kuaifan 2024-12-03 08:28:13 +08:00
parent 65e75f974d
commit df917001d3
6 changed files with 264 additions and 3 deletions

View File

@ -19,6 +19,7 @@ use App\Models\AbstractModel;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
use App\Models\WebSocketDialogUser;
use App\Models\WebSocketDialogConfig;
use App\Models\WebSocketDialogMsgRead;
use App\Models\WebSocketDialogMsgTodo;
use App\Models\WebSocketDialogMsgTranslate;
@ -2771,4 +2772,86 @@ class DialogController extends AbstractController
'list' => Extranet::sticker($key)
]);
}
/**
* @api {get} api/dialog/config 57. 获取会话配置
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName config
*
* @apiParam {Number} dialog_id 对话ID
* @apiParam {String} type 配置类型
*
* @apiSuccess {String} value 配置值
*/
public function config()
{
$user = User::auth();
$dialog_id = intval(Request::input('dialog_id'));
$type = Request::input('type');
if (!$dialog_id || !$type) {
return Base::retError('参数错误');
}
WebSocketDialog::checkDialog($dialog_id);
$config = WebSocketDialogConfig::where('dialog_id', $dialog_id)
->where('userid', $user->userid)
->where('type', $type)
->first();
return Base::retSuccess('success', [
'value' => $config?->value
]);
}
/**
* @api {post} api/dialog/config/save 58. 保存会话配置
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName config__save
*
* @apiParam {Number} dialog_id 对话ID
* @apiParam {String} type 配置类型
* @apiParam {String} value 配置值
*
* @apiSuccess {String} msg 成功提示
*/
public function config__save()
{
$user = User::auth();
$dialog_id = intval(Request::input('dialog_id'));
$type = Request::input('type');
$value = Request::input('value');
if (!$dialog_id || !$type) {
return Base::retError('参数错误');
}
WebSocketDialog::checkDialog($dialog_id);
if (WebSocketDialogConfig::updateOrCreate(
[
'dialog_id' => $dialog_id,
'userid' => $user->userid,
'type' => $type,
],
[
'value' => $value,
]
)) {
WebSocketDialogMsg::sendMsg(null, $dialog_id, 'notice', [
'notice' => $value ? ("修改提示词:" . $value) : "取消提示词",
], User::userid(), true, true);
}
return Base::retSuccess('保存成功');
}
}

View File

@ -54,6 +54,7 @@ class SystemController extends AbstractController
if (env("SYSTEM_SETTING") == 'disabled') {
return Base::retError('当前环境禁止修改');
}
Base::checkClientVersion('0.41.11');
User::auth('admin');
$all = Request::input();
foreach ($all AS $key => $value) {
@ -296,7 +297,7 @@ class SystemController extends AbstractController
if (env("SYSTEM_SETTING") == 'disabled') {
return Base::retError('当前环境禁止修改');
}
Base::checkClientVersion('0.40.79');
Base::checkClientVersion('0.41.11');
$backup = $setting;
$all = Request::input();
foreach ($all as $key => $value) {

View File

@ -0,0 +1,64 @@
<?php
namespace App\Models;
/**
* App\Models\WebSocketDialogConfig
*
* @property int $id
* @property int $dialog_id 对话ID
* @property int $userid 用户ID
* @property string $type 配置类型
* @property string|null $value 配置值
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\WebSocketDialog|null $dialog
* @property-read \App\Models\User|null $user
* @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|WebSocketDialogConfig newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogConfig newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogConfig query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogConfig whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogConfig whereDialogId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogConfig whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogConfig whereType($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogConfig whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogConfig whereUserid($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogConfig whereValue($value)
* @mixin \Eloquent
*/
class WebSocketDialogConfig extends AbstractModel
{
/**
* 可以批量赋值的属性
*
* @var array
*/
protected $fillable = [
'dialog_id',
'userid',
'type',
'value',
];
/**
* 获取关联的对话
*/
public function dialog()
{
return $this->belongsTo(WebSocketDialog::class, 'dialog_id');
}
/**
* 获取关联的用户
*/
public function user()
{
return $this->belongsTo(User::class, 'userid');
}
}

View File

@ -5,6 +5,7 @@ namespace App\Tasks;
use App\Models\User;
use App\Models\UserBot;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogConfig;
use App\Models\WebSocketDialogMsg;
use App\Module\Base;
use App\Module\Doo;
@ -429,8 +430,16 @@ class BotReceiveMsgTask extends AbstractTask
if (empty($extras['api_key'])) {
$errorContent = '机器人未启用。';
}
if (in_array($this->client['platform'], ['win', 'mac', 'web']) && !Base::judgeClientVersion("0.29.11", $this->client['version'])) {
$errorContent = '当前客户端版本低所需版本≥v0.29.11)。';
if (in_array($this->client['platform'], ['win', 'mac', 'web']) && !Base::judgeClientVersion("0.41.11", $this->client['version'])) {
$errorContent = '当前客户端版本低所需版本≥v0.41.11)。';
}
$aiPrompt = WebSocketDialogConfig::where([
'dialog_id' => $dialog->id,
'userid' => $msg->userid,
'type' => 'ai_prompt',
])->value('value');
if ($aiPrompt) {
$extras['system_message'] = $aiPrompt;
}
$webhookUrl = "{$serverUrl}/ai/chat";
} else {

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateWebSocketDialogConfigsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (!Schema::hasTable('web_socket_dialog_configs')) {
Schema::create('web_socket_dialog_configs', function (Blueprint $table) {
$table->id();
$table->bigInteger('dialog_id')->unsigned()->index()->comment('对话ID');
$table->bigInteger('userid')->unsigned()->index()->comment('用户ID');
$table->string('type', 50)->default('')->comment('配置类型');
$table->text('value')->nullable()->comment('配置值');
$table->timestamps();
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('web_socket_dialog_configs');
}
}

View File

@ -86,6 +86,9 @@
<EDropdownItem v-if="isManageBot" command="modifyNormal">
<div>{{$L('修改资料')}}</div>
</EDropdownItem>
<EDropdownItem v-if="isAiBot" command="modifyAi">
<div>{{$L('修改提示词')}}</div>
</EDropdownItem>
<EDropdownItem command="openCreate">
<div>{{$L('创建群组')}}</div>
</EDropdownItem>
@ -438,6 +441,27 @@
</div>
</Modal>
<!--修改提示词-->
<Modal
v-model="modifyAiShow"
:title="$L('修改提示词')"
:mask-closable="false">
<Form :model="modifyData" @submit.native.prevent>
<FormItem prop="value" style="margin-bottom: 16px">
<Input
:maxlength="500"
type="textarea"
:autosize="{minRows:3,maxRows:5}"
v-model="modifyData.value"
:placeholder="$L('例如你是一个人开发的AI助手')"/>
</FormItem>
</Form>
<div slot="footer" class="adaption">
<Button type="default" @click="modifyAiShow=false">{{$L('取消')}}</Button>
<Button type="primary" :loading="modifyLoad > 0" @click="onAiModify">{{$L('保存')}}</Button>
</div>
</Modal>
<!-- 转发选择 -->
<UserSelect
ref="forwardSelect"
@ -759,6 +783,7 @@ export default {
searchResult: [],
modifyShow: false,
modifyAiShow: false,
modifyData: {},
modifyLoad: 0,
@ -1122,6 +1147,14 @@ export default {
return dialogData.dialog_user && dialogData.dialog_user.userid == dialogData.bot && userIsAdmin
},
isAiBot() {
const {dialogData} = this;
if (!dialogData.bot || dialogData.type !== 'user') {
return false
}
return /^ai-(.*?)@bot\.system/.test(dialogData.email)
},
isMute() {
if (this.dialogData.dialog_mute === 'close') {
return !this.userIsAdmin
@ -2543,6 +2576,23 @@ export default {
this.modifyShow = true
break;
case "modifyAi":
this.modifyData = {
dialog_id: this.dialogData.id,
type: 'ai_prompt'
}
this.modifyLoad++;
this.$store.dispatch("call", {
url: 'dialog/config',
data: this.modifyData,
}).then(({data}) => {
this.modifyData.value = data.value
}).finally(() => {
this.modifyLoad--;
})
this.modifyAiShow = true
break;
case "modifyAdmin":
this.modifyData = {
dialog_id: this.dialogData.id,
@ -2712,6 +2762,23 @@ export default {
}
},
onAiModify() {
this.modifyLoad++;
this.$store.dispatch("call", {
url: 'dialog/config/save',
data: this.modifyData
}).then(({data, msg}) => {
$A.messageSuccess(msg);
this.$store.dispatch("saveDialog", data);
this.modifyAiShow = false;
this.modifyData = {};
}).catch(({msg}) => {
$A.modalError(msg);
}).finally(_ => {
this.modifyLoad--;
});
},
onForwardBefore() {
return new Promise((resolve, reject) => {
this.forwardData = this.$refs.forwardSelect.formatSelect(this.$refs.forwardSelect.selects);