perf: AI机器人支持多会话

This commit is contained in:
kuaifan 2025-02-07 16:42:42 +09:00
parent 6338a44cc1
commit 6bcc7b6c49
8 changed files with 276 additions and 67 deletions

View File

@ -189,10 +189,22 @@ class UserBot extends AbstractModel
default:
if (preg_match('/^ai-(.*?)@bot\.system$/', $email)) {
return [
[
if (!Base::judgeClientVersion('0.42.62')) {
return [
'key' => '%3A.clear',
'label' => Doo::translate('清空上下文')
];
}
return [
[
'key' => 'ai-newchat',
'label' => Doo::translate('开启新对话'),
'config' => []
],
[
'key' => 'ai-historychat',
'label' => Doo::translate('历史对话'),
'config' => []
]
];
}

View File

@ -27,6 +27,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int|null $read 已阅数量
* @property int|null $send 发送数量
* @property int|null $tag 标注会员ID
* @property string|null $session 会话标识符
* @property int|null $todo 设为待办会员ID
* @property int|null $link 是否存在链接
* @property int|null $modify 是否编辑
@ -69,6 +70,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereReplyId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereReplyNum($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereSend($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereSession($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereTag($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereTodo($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereType($value)

View File

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

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class WebSocketDialogMsgsAddSession extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) {
if (!Schema::hasColumn('web_socket_dialog_msgs', 'session')) {
$table->string('session', 36)->nullable()->default('')->after('tag')->comment('会话标识符');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) {
$table->dropColumn('session');
});
}
}

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateWebSocketDialogSessionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (!Schema::hasTable('web_socket_dialog_sessions')) {
Schema::create('web_socket_dialog_sessions', function (Blueprint $table) {
$table->id();
$table->bigInteger('dialog_id')->unsigned()->index()->comment('对话ID');
$table->bigInteger('userid')->unsigned()->index()->comment('用户ID');
$table->string('title', 255)->default('')->comment('会话标题');
$table->timestamps();
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('web_socket_dialog_sessions');
}
}

View File

@ -255,7 +255,7 @@
</div>
<div v-else-if="quickShow" class="chat-bottom-menu">
<ul class="scrollbar-hidden">
<li v-for="item in quickMsgs" @click.stop="sendQuick(item)">
<li v-for="item in quickMsgs" @click.stop="sendQuick(item, $event)">
<div class="bottom-menu-desc no-dark-content" :style="item.style || null">{{item.label}}</div>
</li>
</ul>
@ -707,6 +707,7 @@ import touchclick from "../../../directives/touchclick";
import {languageList} from "../../../language";
import {isLocalResourcePath} from "../../../components/Replace/utils";
import emitter from "../../../store/events";
import {AIModelList} from "../../../store/utils";
export default {
name: "DialogWrapper",
@ -1801,8 +1802,9 @@ export default {
/**
* 发送快捷消息
* @param item
* @param event
*/
sendQuick(item) {
sendQuick(item, event = undefined) {
switch (item.key) {
//
case "locat-checkin":
@ -1854,6 +1856,28 @@ export default {
});
break;
//
case "ai-newchat":
if (!this.isAiBot) {
return
}
this.$store.state.menuOperation = {
event,
list: AIModelList(this.dialogData.email).map(value => ({label: value, value: value})),
scrollHide: true,
onUpdate: async (model) => {
}
}
break;
//
case "ai-historychat":
if (!this.isAiBot) {
return
}
break;
//
default:
this.sendMsg(`<p><span data-quick-key="${item.key}">${item.label}</span></p>`)

View File

@ -65,6 +65,7 @@
<script>
import {mapState} from "vuex";
import {AIModelList} from "../../../../store/utils";
export default {
name: "SystemAibot",
@ -93,16 +94,7 @@ export default {
label: '模型',
prop: 'openai_model',
type: 'auto-complete',
data: [
'gpt-4',
'gpt-4-turbo',
'gpt-4o',
'gpt-4o-mini',
'gpt-3.5-turbo',
'gpt-3.5-turbo-16k',
'gpt-3.5-turbo-0125',
'gpt-3.5-turbo-1106'
],
data: AIModelList('openai'),
noFilter: true,
placeholder: '请输入模型名称',
tipPrefix: '查看说明',
@ -143,17 +135,7 @@ export default {
label: '模型',
prop: 'claude_model',
type: 'auto-complete',
data: [
'claude-3-5-sonnet-latest',
'claude-3-5-sonnet-20241022',
'claude-3-5-haiku-latest',
'claude-3-5-haiku-20241022',
'claude-3-opus-latest',
'claude-3-opus-20240229',
'claude-3-haiku-20240307',
'claude-2.1',
'claude-2.0'
],
data: AIModelList('claude'),
noFilter: true,
placeholder: '请输入模型名称',
tipPrefix: '查看说明',
@ -188,15 +170,19 @@ export default {
label: '模型',
prop: 'deepseek_model',
type: 'auto-complete',
data: [
'deepseek-chat',
'deepseek-reasoner'
],
data: AIModelList('deepseek'),
noFilter: true,
placeholder: '请输入模型名称',
tipPrefix: '查看说明',
link: 'https://api-docs.deepseek.com/zh-cn/quick_start/pricing'
},
{
label: 'Base URL',
prop: 'deepseek_base_url',
type: 'input',
placeholder: 'Enter base URL...',
tip: 'API请求的基础URL路径如果没有请留空'
},
{
label: '使用代理',
prop: 'deepseek_agency',
@ -225,12 +211,7 @@ export default {
label: '模型',
prop: 'gemini_model',
type: 'auto-complete',
data: [
'gemini-1.5-flash',
'gemini-1.5-flash-8b',
'gemini-1.5-pro',
'gemini-1.0-pro',
],
data: AIModelList('gemini'),
noFilter: true,
placeholder: '请输入模型名称',
tipPrefix: '查看说明',
@ -264,17 +245,7 @@ export default {
label: '模型',
prop: 'zhipu_model',
type: 'auto-complete',
data: [
'glm-4',
'glm-4-plus',
'glm-4-air',
'glm-4-airx',
'glm-4-long',
'glm-4-flash',
'glm-4v',
'glm-4v-plus',
'glm-3-turbo'
],
data: AIModelList('zhipu'),
noFilter: true,
placeholder: '请输入模型名称',
tipPrefix: '查看说明',
@ -308,15 +279,7 @@ export default {
label: '模型',
prop: 'qianwen_model',
type: 'auto-complete',
data: [
'qwen-turbo',
'qwen-turbo-latest',
'qwen-plus',
'qwen-plus-latest',
'qwen-max',
'qwen-max-latest',
'qwen-long',
],
data: AIModelList('qianwen'),
noFilter: true,
placeholder: '请输入模型名称',
tipPrefix: '查看说明',
@ -357,18 +320,7 @@ export default {
label: '模型',
prop: 'wenxin_model',
type: 'auto-complete',
data: [
'ernie-4.0-8k',
'ernie-4.0-8k-latest',
'ernie-4.0-turbo-128k',
'ernie-4.0-turbo-8k',
'ernie-3.5-128k',
'ernie-3.5-8k',
'ernie-speed-128k',
'ernie-speed-8k',
'ernie-lite-8k',
'ernie-tiny-8k',
],
data: AIModelList('wenxin'),
noFilter: true,
placeholder: '请输入模型名称',
tipPrefix: '查看说明',

View File

@ -162,3 +162,91 @@ export class SSEClient {
}
}
}
export function AIModelList(email) {
switch (email) {
case "openai":
case "ai-openai@bot.system":
return [
'gpt-4',
'gpt-4-turbo',
'gpt-4o',
'gpt-4o-mini',
'gpt-3.5-turbo',
'gpt-3.5-turbo-16k',
'gpt-3.5-turbo-0125',
'gpt-3.5-turbo-1106'
]
case "claude":
case "ai-claude@bot.system":
return [
'claude-3-5-sonnet-latest',
'claude-3-5-sonnet-20241022',
'claude-3-5-haiku-latest',
'claude-3-5-haiku-20241022',
'claude-3-opus-latest',
'claude-3-opus-20240229',
'claude-3-haiku-20240307',
'claude-2.1',
'claude-2.0'
]
case "deepseek":
case "ai-deepseek@bot.system":
return [
'deepseek-chat',
'deepseek-reasoner'
]
case "wenxin":
case "ai-wenxin@bot.system":
return [
'gemini-1.5-flash',
'gemini-1.5-flash-8b',
'gemini-1.5-pro',
'gemini-1.0-pro',
]
case "qianwen":
case "ai-qianwen@bot.system":
return [
'glm-4',
'glm-4-plus',
'glm-4-air',
'glm-4-airx',
'glm-4-long',
'glm-4-flash',
'glm-4v',
'glm-4v-plus',
'glm-3-turbo'
]
case "gemini":
case "ai-gemini@bot.system":
return [
'qwen-turbo',
'qwen-turbo-latest',
'qwen-plus',
'qwen-plus-latest',
'qwen-max',
'qwen-max-latest',
'qwen-long',
]
case "zhipu":
case "ai-zhipu@bot.system":
return [
'ernie-4.0-8k',
'ernie-4.0-8k-latest',
'ernie-4.0-turbo-128k',
'ernie-4.0-turbo-8k',
'ernie-3.5-128k',
'ernie-3.5-8k',
'ernie-speed-128k',
'ernie-speed-8k',
'ernie-lite-8k',
'ernie-tiny-8k',
]
}
}