support ai-mook
This commit is contained in:
parent
1ce5b3a60a
commit
34a6001455
@ -58,7 +58,7 @@ export interface EnableToolItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatSetting {
|
export interface ChatSetting {
|
||||||
modelIndex: number
|
modelIndex?: number
|
||||||
systemPrompt: string
|
systemPrompt: string
|
||||||
enableTools: EnableToolItem[]
|
enableTools: EnableToolItem[]
|
||||||
temperature: number
|
temperature: number
|
||||||
|
@ -162,16 +162,15 @@ provide('tabStorage', tabStorage);
|
|||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools-dialog-container .el-switch__core {
|
.el-switch__core {
|
||||||
border: 1px solid var(--main-color) !important;
|
border: 1px solid var(--main-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-switch .el-switch__action {
|
||||||
.tools-dialog-container .el-switch .el-switch__action {
|
|
||||||
background-color: var(--main-color);
|
background-color: var(--main-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools-dialog-container .el-switch.is-checked .el-switch__action {
|
.el-switch.is-checked .el-switch__action {
|
||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ export class TaskLoop {
|
|||||||
private bridge: MessageBridge;
|
private bridge: MessageBridge;
|
||||||
private streamingContent: Ref<string>;
|
private streamingContent: Ref<string>;
|
||||||
private streamingToolCalls: Ref<ToolCall[]>;
|
private streamingToolCalls: Ref<ToolCall[]>;
|
||||||
|
private aborted = false;
|
||||||
|
|
||||||
private currentChatId = '';
|
private currentChatId = '';
|
||||||
private onError: (error: IErrorMssage) => void = (msg) => { };
|
private onError: (error: IErrorMssage) => void = (msg) => { };
|
||||||
@ -318,6 +319,7 @@ export class TaskLoop {
|
|||||||
});
|
});
|
||||||
this.streamingContent.value = '';
|
this.streamingContent.value = '';
|
||||||
this.streamingToolCalls.value = [];
|
this.streamingToolCalls.value = [];
|
||||||
|
this.aborted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -545,6 +547,7 @@ export class TaskLoop {
|
|||||||
maxEpochs = 50,
|
maxEpochs = 50,
|
||||||
verbose = 0
|
verbose = 0
|
||||||
} = this.taskOptions || {};
|
} = this.taskOptions || {};
|
||||||
|
this.aborted = false;
|
||||||
|
|
||||||
for (let i = 0; i < maxEpochs; ++i) {
|
for (let i = 0; i < maxEpochs; ++i) {
|
||||||
|
|
||||||
@ -570,6 +573,12 @@ export class TaskLoop {
|
|||||||
// 发送请求
|
// 发送请求
|
||||||
const doConverationResult = await this.doConversation(chatData, toolcallIndexAdapter);
|
const doConverationResult = await this.doConversation(chatData, toolcallIndexAdapter);
|
||||||
|
|
||||||
|
// 如果在调用过程中出发了 abort,则直接中断
|
||||||
|
if (this.aborted) {
|
||||||
|
this.aborted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// 如果存在需要调度的工具
|
// 如果存在需要调度的工具
|
||||||
if (this.streamingToolCalls.value.length > 0) {
|
if (this.streamingToolCalls.value.length > 0) {
|
||||||
|
|
||||||
@ -597,8 +606,19 @@ export class TaskLoop {
|
|||||||
|
|
||||||
// ready to call tools
|
// ready to call tools
|
||||||
toolCall = this.consumeToolCalls(toolCall);
|
toolCall = this.consumeToolCalls(toolCall);
|
||||||
|
|
||||||
|
if (this.aborted) {
|
||||||
|
this.aborted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
let toolCallResult = await handleToolCalls(toolCall);
|
let toolCallResult = await handleToolCalls(toolCall);
|
||||||
|
|
||||||
|
if (this.aborted) {
|
||||||
|
this.aborted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// hook : finish call tools
|
// hook : finish call tools
|
||||||
toolCallResult = this.consumeToolCalleds(toolCallResult);
|
toolCallResult = this.consumeToolCalleds(toolCallResult);
|
||||||
|
|
||||||
@ -656,6 +676,11 @@ export class TaskLoop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.aborted) {
|
||||||
|
this.aborted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (this.streamingContent.value) {
|
} else if (this.streamingContent.value) {
|
||||||
tabStorage.messages.push({
|
tabStorage.messages.push({
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
|
@ -33,9 +33,36 @@
|
|||||||
<el-button @click="resetForm">
|
<el-button @click="resetForm">
|
||||||
{{ t('reset') }}
|
{{ t('reset') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="generateMockData">
|
<el-button @click="generateMockData" :loading="mockLoading"
|
||||||
|
:disabled="loading || aiMockLoading || mockLoading">
|
||||||
{{ 'mook' }}
|
{{ 'mook' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
|
|
||||||
|
<el-popover placement="top" width="350" trigger="click" v-model:visible="aiPromptVisible">
|
||||||
|
<template #reference>
|
||||||
|
<el-button :loading="aiMockLoading" :disabled="loading || aiMockLoading || mockLoading">
|
||||||
|
{{ 'ai-mook' }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<div style="margin-bottom: 8px; font-weight: bold;">
|
||||||
|
{{ t('edit-ai-mook-prompt') }}
|
||||||
|
</div>
|
||||||
|
<el-input type="textarea" v-model="aiMookPrompt" :rows="2" style="margin-bottom: 8px;" />
|
||||||
|
<div style="display: flex; align-items: center; margin-bottom: 8px;">
|
||||||
|
<el-switch
|
||||||
|
v-model="enableXmlWrapper"
|
||||||
|
style="margin-right: 8px;"
|
||||||
|
/>
|
||||||
|
<span style="opacity: 0.7;">XML</span>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: right;">
|
||||||
|
<el-button size="small" @click="aiPromptVisible = false">{{ t('cancel') }}</el-button>
|
||||||
|
<el-button size="small" type="primary" :loading="aiMockLoading" @click="onAIMookConfirm">
|
||||||
|
{{ t('confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -44,7 +71,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, defineProps, watch, ref, computed } from 'vue';
|
import { defineComponent, defineProps, watch, ref, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import type { FormInstance, FormRules } from 'element-plus';
|
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||||
import { tabs } from '../panel';
|
import { tabs } from '../panel';
|
||||||
import type { ToolStorage } from './tools';
|
import type { ToolStorage } from './tools';
|
||||||
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';
|
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';
|
||||||
@ -54,6 +81,10 @@ import { mcpClientAdapter } from '@/views/connect/core';
|
|||||||
import { JSONSchemaFaker } from 'json-schema-faker';
|
import { JSONSchemaFaker } from 'json-schema-faker';
|
||||||
|
|
||||||
defineComponent({ name: 'tool-executor' });
|
defineComponent({ name: 'tool-executor' });
|
||||||
|
const mockLoading = ref(false);
|
||||||
|
const aiMockLoading = ref(false);
|
||||||
|
const aiPromptVisible = ref(false);
|
||||||
|
const enableXmlWrapper = ref(false);
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@ -85,6 +116,7 @@ const currentTool = computed(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const aiMookPrompt = ref(`please call the tool ${currentTool.value?.name || ''} to make some test`);
|
||||||
|
|
||||||
const formRules = computed<FormRules>(() => {
|
const formRules = computed<FormRules>(() => {
|
||||||
const rules: FormRules = {};
|
const rules: FormRules = {};
|
||||||
@ -132,27 +164,89 @@ const resetForm = () => {
|
|||||||
formRef.value?.resetFields();
|
formRef.value?.resetFields();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
import { TaskLoop } from '@/components/main-panel/chat/core/task-loop';
|
||||||
|
import type { ChatStorage } from '../chat/chat-box/chat';
|
||||||
|
|
||||||
|
const onAIMookConfirm = async () => {
|
||||||
|
aiPromptVisible.value = false;
|
||||||
|
await generateAIMockData(aiMookPrompt.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateAIMockData = async (prompt?: string) => {
|
||||||
|
if (!currentTool.value?.inputSchema) return;
|
||||||
|
aiMockLoading.value = true;
|
||||||
|
try {
|
||||||
|
const loop = new TaskLoop({ maxEpochs: 1 });
|
||||||
|
const usePrompt = prompt || `please call the tool ${currentTool.value.name} to make some test`;
|
||||||
|
const chatStorage = {
|
||||||
|
messages: [],
|
||||||
|
settings: {
|
||||||
|
temperature: 0.6,
|
||||||
|
systemPrompt: '',
|
||||||
|
enableTools: [{
|
||||||
|
name: currentTool.value.name,
|
||||||
|
description: currentTool.value.description,
|
||||||
|
inputSchema: currentTool.value.inputSchema,
|
||||||
|
enabled: true
|
||||||
|
}],
|
||||||
|
enableWebSearch: false,
|
||||||
|
contextLength: 5,
|
||||||
|
enableXmlWrapper: enableXmlWrapper.value,
|
||||||
|
parallelToolCalls: false
|
||||||
|
}
|
||||||
|
} as ChatStorage;
|
||||||
|
|
||||||
|
loop.setMaxEpochs(1);
|
||||||
|
|
||||||
|
let aiMockJson: any = undefined;
|
||||||
|
|
||||||
|
loop.registerOnToolCall(toolCall => {
|
||||||
|
if (toolCall.function?.name === currentTool.value?.name) {
|
||||||
|
try {
|
||||||
|
const toolArgs = JSON.parse(toolCall.function?.arguments || '{}');
|
||||||
|
aiMockJson = toolArgs;
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('AI 生成的 JSON 解析错误');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ElMessage.error('AI 调用了未知的工具');
|
||||||
|
}
|
||||||
|
loop.abort();
|
||||||
|
return toolCall;
|
||||||
|
});
|
||||||
|
|
||||||
|
loop.registerOnError(error => {
|
||||||
|
ElMessage.error(error + '');
|
||||||
|
});
|
||||||
|
|
||||||
|
await loop.start(chatStorage, usePrompt);
|
||||||
|
|
||||||
|
if (aiMockJson && typeof aiMockJson === 'object') {
|
||||||
|
Object.keys(aiMockJson).forEach(key => {
|
||||||
|
tabStorage.formData[key] = aiMockJson[key];
|
||||||
|
});
|
||||||
|
formRef.value?.clearValidate?.();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
aiMockLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
const generateMockData = async () => {
|
const generateMockData = async () => {
|
||||||
if (!currentTool.value?.inputSchema) return;
|
if (!currentTool.value?.inputSchema) return;
|
||||||
|
mockLoading.value = true;
|
||||||
// 注册faker到json-schema-faker
|
try {
|
||||||
JSONSchemaFaker.option({
|
JSONSchemaFaker.option({
|
||||||
useDefaultValue: true,
|
useDefaultValue: true,
|
||||||
alwaysFakeOptionals: true
|
alwaysFakeOptionals: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// 生成mock数据
|
|
||||||
// TODO: as any ?
|
|
||||||
const mockData = await JSONSchemaFaker.resolve(currentTool.value.inputSchema as any) as any;
|
const mockData = await JSONSchemaFaker.resolve(currentTool.value.inputSchema as any) as any;
|
||||||
|
|
||||||
// 将 mock 数据绑定到表单
|
|
||||||
Object.keys(mockData).forEach(key => {
|
Object.keys(mockData).forEach(key => {
|
||||||
tabStorage.formData[key] = mockData[key];
|
tabStorage.formData[key] = mockData[key];
|
||||||
console.log(mockData[key]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 如果需要刷新表单校验,可以加上
|
|
||||||
formRef.value?.clearValidate?.();
|
formRef.value?.clearValidate?.();
|
||||||
|
} finally {
|
||||||
|
mockLoading.value = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function handleExecute() {
|
async function handleExecute() {
|
||||||
@ -167,10 +261,15 @@ async function handleExecute() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(currentTool, (tool) => {
|
||||||
|
aiMookPrompt.value = `please call the tool ${tool?.name || ''} to make some test`;
|
||||||
|
});
|
||||||
|
|
||||||
watch(() => tabStorage.currentToolName, () => {
|
watch(() => tabStorage.currentToolName, () => {
|
||||||
initFormData();
|
initFormData();
|
||||||
resetForm();
|
resetForm();
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -20,8 +20,14 @@
|
|||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<!-- 展示原本的信息 -->
|
<!-- 展示原本的信息 -->
|
||||||
<template v-if="!showRawJson">
|
<template v-if="!showRawJson && tabStorage.lastToolCallResponse">
|
||||||
{{tabStorage.lastToolCallResponse?.content.map(c => c.text).join('\n')}}
|
<div
|
||||||
|
v-for="(c, idx) in tabStorage.lastToolCallResponse!.content"
|
||||||
|
:key="idx"
|
||||||
|
class="tool-call-block"
|
||||||
|
>
|
||||||
|
<pre class="tool-call-text">{{ c.text }}</pre>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 展示 json -->
|
<!-- 展示 json -->
|
||||||
@ -105,4 +111,21 @@ const showRawJson = ref(false);
|
|||||||
padding: 5px 9px;
|
padding: 5px 9px;
|
||||||
border-radius: .5em;
|
border-radius: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tool-call-block {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: rgba(0,0,0,0.04);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-call-text {
|
||||||
|
font-family: var(--code-font-family, monospace);
|
||||||
|
font-size: 15px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--el-text-color-primary, #222);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -183,5 +183,6 @@
|
|||||||
"export": "تصدير",
|
"export": "تصدير",
|
||||||
"export-filename": "اسم ملف التصدير",
|
"export-filename": "اسم ملف التصدير",
|
||||||
"how-to-use": "كيفية الاستخدام؟",
|
"how-to-use": "كيفية الاستخدام؟",
|
||||||
"is-required": "هو حقل مطلوب"
|
"is-required": "هو حقل مطلوب",
|
||||||
|
"edit-ai-mook-prompt": "تحرير إشارات AI Mook"
|
||||||
}
|
}
|
@ -183,5 +183,6 @@
|
|||||||
"export": "Exportieren",
|
"export": "Exportieren",
|
||||||
"export-filename": "Exportdateiname",
|
"export-filename": "Exportdateiname",
|
||||||
"how-to-use": "Wie benutzt man?",
|
"how-to-use": "Wie benutzt man?",
|
||||||
"is-required": "ist ein Pflichtfeld"
|
"is-required": "ist ein Pflichtfeld",
|
||||||
|
"edit-ai-mook-prompt": "AI Mook-Prompts bearbeiten"
|
||||||
}
|
}
|
@ -183,5 +183,6 @@
|
|||||||
"export": "Export",
|
"export": "Export",
|
||||||
"export-filename": "Export filename",
|
"export-filename": "Export filename",
|
||||||
"how-to-use": "How to use?",
|
"how-to-use": "How to use?",
|
||||||
"is-required": "is a required field"
|
"is-required": "is a required field",
|
||||||
|
"edit-ai-mook-prompt": "Edit AI Mook prompts"
|
||||||
}
|
}
|
@ -183,5 +183,6 @@
|
|||||||
"export": "Exporter",
|
"export": "Exporter",
|
||||||
"export-filename": "Nom du fichier d'exportation",
|
"export-filename": "Nom du fichier d'exportation",
|
||||||
"how-to-use": "Comment utiliser ?",
|
"how-to-use": "Comment utiliser ?",
|
||||||
"is-required": "est un champ obligatoire"
|
"is-required": "est un champ obligatoire",
|
||||||
|
"edit-ai-mook-prompt": "Modifier les invites AI Mook"
|
||||||
}
|
}
|
@ -183,5 +183,6 @@
|
|||||||
"export": "エクスポート",
|
"export": "エクスポート",
|
||||||
"export-filename": "エクスポートファイル名",
|
"export-filename": "エクスポートファイル名",
|
||||||
"how-to-use": "使用方法",
|
"how-to-use": "使用方法",
|
||||||
"is-required": "は必須フィールドです"
|
"is-required": "は必須フィールドです",
|
||||||
|
"edit-ai-mook-prompt": "AI Mookプロンプトを編集"
|
||||||
}
|
}
|
@ -183,5 +183,6 @@
|
|||||||
"export": "내보내기",
|
"export": "내보내기",
|
||||||
"export-filename": "내보내기 파일 이름",
|
"export-filename": "내보내기 파일 이름",
|
||||||
"how-to-use": "사용 방법?",
|
"how-to-use": "사용 방법?",
|
||||||
"is-required": "는 필수 필드입니다"
|
"is-required": "는 필수 필드입니다",
|
||||||
|
"edit-ai-mook-prompt": "AI Mook 프롬프트 편집"
|
||||||
}
|
}
|
@ -183,5 +183,6 @@
|
|||||||
"export": "Экспорт",
|
"export": "Экспорт",
|
||||||
"export-filename": "Имя файла экспорта",
|
"export-filename": "Имя файла экспорта",
|
||||||
"how-to-use": "Как использовать?",
|
"how-to-use": "Как использовать?",
|
||||||
"is-required": "является обязательным полем"
|
"is-required": "является обязательным полем",
|
||||||
|
"edit-ai-mook-prompt": "Редактировать подсказки AI Mook"
|
||||||
}
|
}
|
@ -183,5 +183,6 @@
|
|||||||
"export": "导出",
|
"export": "导出",
|
||||||
"export-filename": "导出文件名",
|
"export-filename": "导出文件名",
|
||||||
"how-to-use": "如何使用?",
|
"how-to-use": "如何使用?",
|
||||||
"is-required": "是必填字段"
|
"is-required": "是必填字段",
|
||||||
|
"edit-ai-mook-prompt": "编辑 AI Mook 提示词"
|
||||||
}
|
}
|
@ -183,5 +183,6 @@
|
|||||||
"export": "導出",
|
"export": "導出",
|
||||||
"export-filename": "導出文件名",
|
"export-filename": "導出文件名",
|
||||||
"how-to-use": "如何使用?",
|
"how-to-use": "如何使用?",
|
||||||
"is-required": "是必填欄位"
|
"is-required": "是必填欄位",
|
||||||
|
"edit-ai-mook-prompt": "編輯AI Mook提示詞"
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user