代码编辑器添加AI助手,支持AI代码编写

This commit is contained in:
fofolee
2025-02-20 01:04:11 +08:00
parent ac19d06845
commit d82c735cfe
14 changed files with 1033 additions and 325 deletions

View File

@@ -21,7 +21,6 @@
v-if="!isRunCodePage"
v-model="commandManager.state.currentCommand"
from="quickcommand"
@update:is-expanded="isConfigExpanded = $event"
:expand-on-focus="true"
class="command-config"
/>
@@ -97,7 +96,6 @@ export default {
programLanguages: Object.keys(programs),
showComposer: false,
listener: null,
isConfigExpanded: false,
composerInfo: {
program: "quickcomposer",
},

View File

@@ -0,0 +1,90 @@
<template>
<div>
<q-select
v-if="apiOptions.length > 0"
:model-value="modelValue"
@update:model-value="updateModelValue($event)"
:options="apiOptions"
map-options
emit-value
dense
options-dense
filled
>
<template v-slot:prepend>
<q-badge color="primary" text-color="white" class="q-mr-sm q-pa-xs">
模型
</q-badge>
</template>
<template v-slot:append>
<q-btn icon="settings" dense flat @click.stop="showAIConfig = true" />
</template>
</q-select>
<q-btn
dense
color="primary"
class="full-width q-px-sm"
icon="settings"
label="配置AI接口"
unelevated
v-else
@click="showAIConfig = true"
/>
<q-dialog v-model="showAIConfig">
<AIConfig @save="onAIConfigSave" />
</q-dialog>
</div>
</template>
<script>
import AIConfig from "components/popup/AIConfig.vue";
import { dbManager } from "js/utools.js";
import { defineComponent } from "vue";
export default defineComponent({
components: { AIConfig },
emits: ["update:modelValue"],
props: {
modelValue: {
type: Object,
required: true,
},
},
data() {
return {
showAIConfig: false,
apiOptions: [],
};
},
methods: {
onAIConfigSave() {
this.apiOptions = this.getApiOptions();
const newApiConfig = this.apiOptions.find(
(option) => option.value.id === this.modelValue.id
);
const newModelValue =
newApiConfig?.value || this.apiOptions[0].value || {};
this.updateModelValue({ ...newModelValue });
},
updateModelValue(value) {
this.$emit("update:modelValue", value);
},
getApiOptions() {
const apiConfigs = dbManager.getStorage("cfg_aiConfigs");
if (!apiConfigs) return [];
return apiConfigs.map((config) => {
return {
label: config.name,
value: config,
};
});
},
},
mounted() {
this.apiOptions = this.getApiOptions();
if (!this.modelValue.id) {
this.updateModelValue(this.apiOptions[0]?.value || {});
}
},
});
</script>

View File

@@ -1,42 +1,26 @@
<template>
<div>
<q-select
v-if="apiOptions.length > 0"
:model-value="argvs.apiConfig"
@update:model-value="updateArgvs('apiConfig', $event)"
:options="apiOptions"
map-options
emit-value
dense
options-dense
filled
label="API模型"
class="q-mb-sm"
/>
<q-field filled dense v-else class="q-mb-sm">
<template #control>
<div class="flex items-center justify-center full-width text-warning">
<q-icon name="warning" class="q-mr-sm" />
<div>
未配置API模型配置方法命令配置界面-右下角菜单按钮-API配置
</div>
</div>
</template>
</q-field>
<ButtonGroup
:model-value="argvs.content.presetPrompt"
@update:modelValue="updateArgvs('content.presetPrompt', $event)"
:options="presetPromptOptions"
height="26px"
class="q-mb-sm"
/>
<VariableInput
:model-value="argvs.content.prompt"
@update:modelValue="updateArgvs('content.prompt', $event)"
label="提示词"
type="textarea"
autogrow
/>
<div class="q-pt-sm">
<AISelector
:model-value="argvs.apiConfig"
@update:modelValue="updateArgvs('apiConfig', $event)"
class="q-mb-sm"
/>
<ButtonGroup
:model-value="argvs.content.role"
@update:modelValue="updateArgvs('content.role', $event)"
:options="roleOptions"
height="26px"
class="q-mb-sm"
/>
<VariableInput
:model-value="argvs.content.prompt"
@update:modelValue="updateArgvs('content.prompt', $event)"
label="提示词"
type="textarea"
autogrow
/>
</div>
</div>
</template>
@@ -46,8 +30,7 @@ import ButtonGroup from "components/composer/common/ButtonGroup.vue";
import { newVarInputVal } from "js/composer/varInputValManager";
import VariableInput from "components/composer/common/VariableInput.vue";
import { parseFunction, stringifyArgv } from "js/composer/formatString";
import { dbManager } from "js/utools.js";
import AISelector from "components/ai/AISelector.vue";
export default defineComponent({
name: "AskAIEditor",
props: {
@@ -56,25 +39,26 @@ export default defineComponent({
components: {
VariableInput,
ButtonGroup,
AISelector,
},
emits: ["update:modelValue"],
data() {
return {
showAIConfig: false,
defaultArgvs: {
content: {
prompt: newVarInputVal("str"),
presetPrompt: "",
role: "",
},
apiConfig: {},
},
apiOptions: [],
presetPromptOptions: [
{ label: "自由问答", value: "" },
roleOptions: [
{ label: "无", value: "" },
{ label: "翻译", value: "translate" },
{ label: "总结", value: "summarize" },
{ label: "执行shell命令", value: "shell" },
{ label: "生成shell命令", value: "shell" },
],
modelTypeOptions: [
apiTypeOptions: [
{ label: "OpenAI", value: "openai" },
{ label: "Ollama", value: "ollama" },
],
@@ -125,23 +109,6 @@ export default defineComponent({
});
},
},
mounted() {
const apiConfigs = dbManager.getStorage("cfg_aiConfigs");
this.apiOptions = apiConfigs
? apiConfigs.map((config) => {
return {
label: config.name,
value: config,
};
})
: [];
this.defaultArgvs.apiConfig = apiConfigs?.[0] || {};
const argvs = this.modelValue.argvs || this.defaultArgvs;
if (!this.modelValue.code) {
this.updateModelValue(argvs);
}
},
});
</script>

View File

@@ -0,0 +1,404 @@
<template>
<q-card class="ai-dialog">
<div class="header q-px-md q-py-sm">
<q-icon name="smart_toy" size="24px" />
<div class="text-h6">AI 助手</div>
<AISelector v-model="selectedApi" />
<q-space />
<q-btn icon="close" flat round dense v-close-popup size="md" />
</div>
<!-- 聊天记录区域 -->
<q-scroll-area
ref="scrollArea"
class="chat-container"
:vertical-thumb-style="{
width: '5px',
}"
>
<div class="chat-history q-px-md">
<div
v-for="(message, index) in chatHistory"
:key="index"
class="chat-message-wrapper"
>
<div :class="['chat-message', message.role]">
<div class="avatar">
<q-avatar size="28px">
<q-icon
:name="message.role === 'user' ? 'person' : 'smart_toy'"
:color="message.role === 'user' ? 'white' : 'primary'"
size="20px"
/>
</q-avatar>
</div>
<div class="message-bubble">
<div
v-if="message.role === 'assistant'"
class="message-content markdown"
v-html="getTrimContent(message.content)"
/>
<div v-else class="message-content" v-text="message.content" />
</div>
</div>
</div>
</div>
</q-scroll-area>
<!-- 输入区域 -->
<div class="input-container q-px-md q-py-sm">
<q-input
v-model="prompt"
type="textarea"
filled
dense
autogrow
autofocus
:max-rows="3"
placeholder="请描述你的需求Enter 发送Shift+Enter 换行"
@keydown.enter.exact.prevent="handleSubmit"
@keydown.shift.enter.prevent="prompt += '\n'"
>
<template v-slot:append>
<div class="row items-center q-gutter-x-md">
<q-btn
flat
icon="delete_sweep"
size="sm"
dense
:disable="chatHistory.length === 0"
@click="clearHistory"
>
<q-tooltip>清空对话</q-tooltip>
</q-btn>
<q-btn
@click="autoUpdateCode = !autoUpdateCode"
:color="autoUpdateCode ? 'primary' : 'grey'"
icon="auto_fix_high"
size="sm"
dense
flat
>
<q-tooltip>
{{
autoUpdateCode
? "自动更新代码(已开启)"
: "自动更新代码(已关闭)"
}}
</q-tooltip>
</q-btn>
<q-btn
:color="streamingResponse ? 'negative' : 'primary'"
:icon="streamingResponse ? 'stop' : 'send'"
size="sm"
dense
flat
@click="handleSubmit"
/>
</div>
</template>
</q-input>
</div>
</q-card>
</template>
<script>
import { defineComponent } from "vue";
import AISelector from "components/ai/AISelector.vue";
import { marked } from "marked";
import DOMPurify from "dompurify";
const quickcommandApi =
require(`!raw-loader!plugins/monaco/types/quickcommand.api.d.ts`)
.default.replace(/\/\*[\s\S]*?\*\//g, "")
.replace(/\n/g, "");
const uToolsApi = require(`!raw-loader!plugins/monaco/types/utools.api.d.ts`)
.default.replace(/\/\*[\s\S]*?\*\//g, "")
.replace(/\n/g, "");
export default defineComponent({
name: "AIAssistantDialog",
components: {
AISelector,
},
data() {
return {
prompt: "",
selectedApi: {},
streamingResponse: false,
chatHistory: [],
currentRequest: null,
autoUpdateCode: true,
scrollToBottomDebounce: null,
};
},
props: {
code: {
type: String,
default: "",
},
language: {
type: String,
default: "",
},
},
emits: ["update-code"],
methods: {
scrollToBottom() {
// 清除之前的定时器
if (this.scrollToBottomDebounce) {
clearTimeout(this.scrollToBottomDebounce);
}
// 设置新的定时器,延迟执行滚动
this.scrollToBottomDebounce = setTimeout(() => {
const scrollArea = this.$refs.scrollArea;
if (scrollArea) {
const scrollTarget = scrollArea.getScrollTarget();
scrollArea.setScrollPosition(
"vertical",
scrollTarget.scrollHeight,
300
);
}
}, 100);
},
async handleSubmit() {
if (this.streamingResponse) {
this.stopStreaming();
return;
}
const promptText = this.prompt.trim();
if (!promptText || !this.selectedApi) return;
// 添加用户消息到历史记录
this.chatHistory.push(
{
role: "user",
content: promptText,
},
{
role: "assistant",
content: "",
}
);
// 添加消息后立即滚动到底部
this.$nextTick(() => {
this.scrollToBottom();
});
this.streamingResponse = true;
this.prompt = ""; // 清空输入框
try {
const response = await window.quickcommand.askAI(
{
prompt: promptText,
role: this.getRolePrompt(this.language),
context: this.chatHistory.slice(0, -2),
},
this.selectedApi,
{
showLoadingBar: false,
stream: true,
onStream: (text, controller, done) => {
this.currentRequest = controller;
if (text) {
this.chatHistory[this.chatHistory.length - 1].content += text;
this.$nextTick(() => {
this.scrollToBottom();
});
}
if (done) {
this.streamingResponse = false;
if (this.autoUpdateCode) {
const response =
this.chatHistory[this.chatHistory.length - 1].content;
const code = response.match(
/```[a-z]*\n([\s\S]*?)\n```/
)?.[1];
if (!code) return;
this.$emit("update-code", code);
}
}
},
}
);
if (!response.success && !response.cancelled) {
window.quickcommand.showMessageBox(response.error, "error");
}
} catch (error) {
window.quickcommand.showMessageBox(error.message, "error");
this.streamingResponse = false;
}
},
stopStreaming() {
this.streamingResponse = false;
if (this.currentRequest) {
this.currentRequest.abort();
this.currentRequest = null;
}
},
clearHistory() {
this.chatHistory = [];
},
getTrimContent(content) {
const markedContent = marked(content.trim());
// 解决think标签被错误地包裹在<p>标签中
const processedContent = markedContent
.replace("<p><think>", "<think><p>")
.replace("</think></p>", "</p></think>")
// 去除空的think标签
.replace("<think>\n\n</think>", "");
const purifiedContent = DOMPurify.sanitize(processedContent, {
ADD_TAGS: ["think"],
});
return purifiedContent;
},
getRolePrompt(language) {
const languageMap = {
quickcommand: "NodeJS",
javascript: "NodeJS",
};
const commonInstructions = `请作为一名专业的开发专家,根据我的需求编写${languageMap[language]}代码,并请遵循以下原则:
- 编写简洁、可读性强的代码
- 遵循${language}最佳实践和设计模式
- 使用恰当的命名规范和代码组织
- 添加必要的错误处理和边界检查
- 保持中文注释的准确性和专业性
- 提供必要的使用说明
`;
// 针对不同语言的特殊提示
let languageSpecific = {
javascript: `- 优先使用现代ES6+特性
- 使用NodeJS原生API和模块`,
python: `- 遵循PEP8规范`,
};
languageSpecific.quickcommand = `${languageSpecific.javascript}
- 支持使用以下uTools接口 ${uToolsApi}
- 支持使用以下quickcommand接口 ${quickcommandApi}`;
// 获取语言特定的提示,如果没有则使用空字符串
const specificInstructions =
languageSpecific[language.toLowerCase()] || "";
const lastInstructions =
"\n请直接生成代码任何情况下都不需要做解释和说明";
return commonInstructions + specificInstructions + lastInstructions;
},
},
});
</script>
<style scoped>
.ai-dialog {
width: 800px;
max-width: 90vw;
height: 80vh;
display: flex;
flex-direction: column;
}
.header {
display: flex;
align-items: center;
gap: 8px;
}
.chat-container {
flex: 1 1 auto;
}
.chat-message-wrapper {
margin-bottom: 1rem;
animation: fadeIn 0.3s ease-in-out;
}
.chat-message {
display: flex;
gap: 8px;
max-width: 85%;
}
.chat-message.user {
margin-left: auto;
flex-direction: row-reverse;
}
.chat-message .avatar {
background: var(--q-primary);
border-radius: 50%;
padding: 2px;
width: 32px;
height: 32px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.chat-message.assistant .avatar {
background: var(--transparent-bg-color);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.chat-message.user .avatar {
background: var(--q-primary);
}
.message-bubble {
padding: 8px 12px;
border-radius: 12px;
position: relative;
}
.message-content :deep(think) {
color: #8b8b8b;
display: block;
border-left: 4px solid #8b8b8b;
padding-left: 10px;
margin-bottom: 8px;
font-size: 12px;
}
.chat-message.user .message-bubble {
background-color: var(--q-primary);
color: white;
border-top-right-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.chat-message.assistant .message-bubble {
background-color: var(--transparent-bg-color);
border-top-left-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.message-content {
font-size: 13px;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 暗色模式适配 */
.body--dark .chat-message.assistant .message-bubble {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
</style>

View File

@@ -6,6 +6,26 @@
{{ placeholder }}
</div>
</div>
<!-- AI助手按钮 -->
<div class="ai-button-wrapper">
<q-btn
round
dense
color="primary"
icon="smart_toy"
@click="showAIDialog = true"
>
<q-tooltip>AI 助手</q-tooltip>
</q-btn>
</div>
<!-- AI对话框 -->
<q-dialog v-model="showAIDialog" position="right" seamless>
<AIAssistantDialog
:code="modelValue"
:language="language"
@update-code="setEditorValue"
/>
</q-dialog>
</div>
</template>
@@ -13,6 +33,7 @@
import * as monaco from "monaco-editor";
import importAll from "js/common/importAll.js";
import { defineComponent } from "vue";
import AIAssistantDialog from "./AIAssistantDialog.vue";
// 批量导入关键字补全
let languageCompletions = importAll(
@@ -39,6 +60,9 @@ const typeDefinitions = {
export default defineComponent({
name: "CodeEditor",
components: {
AIAssistantDialog,
},
props: {
// v-model 绑定值
modelValue: {
@@ -134,6 +158,7 @@ export default defineComponent({
// 光标样式
cursorStyle: "line",
},
showAIDialog: false,
};
},
watch: {
@@ -394,6 +419,9 @@ export default defineComponent({
formatDocument() {
editor.getAction("editor.action.formatDocument").run();
},
setEditorValue(value) {
editor.setValue(value);
},
},
computed: {
showPlaceholder() {
@@ -431,4 +459,11 @@ export default defineComponent({
user-select: none;
opacity: 0.4;
}
.ai-button-wrapper {
position: absolute;
right: 30px;
bottom: 30px;
z-index: 500;
}
</style>

View File

@@ -1,7 +1,6 @@
<template>
<q-expansion-item
v-model="isExpanded"
@update:model-value="$emit('update:is-expanded', $event)"
class="command-config"
expand-icon-toggle
>
@@ -188,7 +187,7 @@ export default defineComponent({
default: "quickcommand",
},
},
emits: ["update:modelValue", "update:is-expanded"],
emits: ["update:modelValue"],
data() {
return {
commandManager: useCommandManager(),
@@ -303,7 +302,6 @@ export default defineComponent({
},
updateExpanded(value) {
this.isExpanded = value;
this.$emit("update:is-expanded", value);
},
},
});

View File

@@ -4,7 +4,7 @@
<div>
<div class="flex q-mb-md q-px-sm" style="height: 26px">
<ButtonGroup
v-model="modelToAdd"
v-model="apiToAdd"
class="col"
:options="[
{ label: 'OPENAI', value: 'openai' },
@@ -27,93 +27,122 @@
width: '2px',
}"
>
<div class="config-list">
<div
v-for="(aiConfig, index) in aiConfigs"
:key="index"
class="config-item"
>
<div class="row q-col-gutter-sm">
<q-input
filled
dense
v-model="aiConfig.name"
class="col"
placeholder="请输入名称"
>
<template v-slot:prepend>
<q-badge
color="primary"
text-color="black"
label="名称"
class="q-pa-xs"
/>
</template>
<template v-slot:append>
<q-icon
color="grey"
name="remove_circle"
@click="deleteModel(index)"
size="16px"
class="cursor-pointer"
/>
</template>
</q-input>
<q-input
filled
dense
v-model="aiConfig.apiUrl"
class="col-8"
:placeholder="`${aiConfig.modelType} API地址`"
>
<template v-slot:prepend>
<q-badge
color="primary"
text-color="black"
label="接口"
class="q-pa-xs"
/>
</template>
</q-input>
<draggable
v-model="aiConfigs"
item-key="name"
handle=".drag-handle"
:animation="200"
class="config-list"
>
<template #item="{ element: aiConfig, index }">
<div class="config-item">
<div class="config-item-side-bar">
<q-icon
name="drag_indicator"
class="drag-handle cursor-move"
size="20px"
/>
</div>
<div class="config-item-content">
<div class="row q-col-gutter-sm">
<q-input
filled
dense
v-model="aiConfig.name"
class="col"
placeholder="请输入名称"
>
<template v-slot:prepend>
<q-badge
color="primary"
text-color="white"
label="名称"
class="q-pa-xs"
/>
</template>
<template v-slot:append>
<q-icon
color="grey"
name="remove_circle"
@click="deleteModel(index)"
size="16px"
class="cursor-pointer"
/>
</template>
</q-input>
<q-input
filled
dense
v-model="aiConfig.apiUrl"
class="col-7"
:placeholder="`${aiConfig.apiType} API地址`"
>
<template v-slot:prepend>
<q-badge
color="primary"
text-color="white"
label="接口"
class="q-pa-xs"
/>
</template>
</q-input>
</div>
<div class="row q-col-gutter-sm">
<q-input filled dense v-model="aiConfig.model" class="col">
<template v-slot:prepend>
<q-badge
color="primary"
text-color="white"
label="模型"
class="q-pa-xs"
/>
</template>
<template v-slot:append>
<q-btn-dropdown
flat
@click="getModels(aiConfig)"
dense
dropdown-icon="refresh"
>
<q-list>
<q-item
v-for="model in models"
:key="model"
clickable
v-close-popup
@click="aiConfig.model = model"
>
<q-item-section>
{{ model }}
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
<q-tooltip>获取模型</q-tooltip>
</template>
</q-input>
<q-input
filled
dense
v-model="aiConfig.apiToken"
v-if="aiConfig.apiType === 'openai'"
type="password"
class="col-7"
>
<template v-slot:prepend>
<q-badge
color="primary"
text-color="white"
label="令牌"
class="q-pa-xs"
/>
</template>
</q-input>
</div>
</div>
</div>
<div class="row q-col-gutter-sm">
<q-select
filled
dense
v-model="aiConfig.model"
:options="models"
@focus="getModels(aiConfig)"
class="col"
>
<template v-slot:prepend>
<q-badge
color="primary"
text-color="black"
label="模型"
class="q-pa-xs"
/>
</template>
</q-select>
<q-input
filled
dense
v-model="aiConfig.apiToken"
v-if="aiConfig.modelType === 'openai'"
type="password"
class="col-8"
>
<template v-slot:prepend>
<q-badge
color="primary"
text-color="black"
label="令牌"
class="q-pa-xs"
/>
</template>
</q-input>
</div>
</div>
</div>
</template>
</draggable>
</q-scroll-area>
</div>
<div class="flex justify-end q-gutter-sm q-px-sm">
@@ -133,19 +162,23 @@
import { defineComponent } from "vue";
import { dbManager } from "js/utools.js";
import ButtonGroup from "components/composer/common/ButtonGroup.vue";
import draggable from "vuedraggable";
import { getUniqueId } from "js/common/uuid.js";
export default defineComponent({
name: "AIConfig",
components: {
ButtonGroup,
draggable,
},
data() {
return {
modelToAdd: "openai",
apiToAdd: "openai",
aiConfigs: [],
models: [],
};
},
emits: ["save"],
methods: {
async getModels(aiConfig) {
const { success, result, error } = await window.getModelsFromAiApi(
@@ -153,6 +186,7 @@ export default defineComponent({
);
if (!success) {
quickcommand.showMessageBox(error, "error");
this.models = [];
return;
}
this.models = result;
@@ -162,17 +196,19 @@ export default defineComponent({
"cfg_aiConfigs",
window.lodashM.cloneDeep(this.aiConfigs)
);
this.$emit("save");
},
deleteModel(index) {
this.aiConfigs.splice(index, 1);
},
addModel() {
this.aiConfigs.push({
modelType: this.modelToAdd,
apiType: this.apiToAdd,
apiUrl: "",
apiToken: "",
model: "",
name: "",
id: getUniqueId(),
});
},
getConfigListHeight() {
@@ -188,7 +224,7 @@ export default defineComponent({
<style scoped>
.config-list,
.config-item {
.config-item-content {
display: flex;
flex-direction: column;
gap: 8px;
@@ -198,5 +234,21 @@ export default defineComponent({
border: 1px solid var(--q-primary);
border-radius: 4px;
padding: 8px;
display: flex;
}
.config-item-side-bar {
width: 20px;
padding-top: 8px;
}
.config-item-content {
flex: 1;
}
.drag-handle {
cursor: move;
color: var(--q-primary);
margin-right: 4px;
}
</style>

View File

@@ -9,6 +9,8 @@
border-radius: 6px;
overflow-x: auto;
margin: 2px 0;
overflow-x: auto;
max-width: 100%;
}
.markdown code {
@@ -74,3 +76,12 @@
border-left-color: #444;
color: #999;
}
.markdown code ::-webkit-scrollbar {
height: 5px;
}
.markdown a {
color: #007bff;
text-decoration: none;
}

View File

@@ -1,5 +1,3 @@
import { newVarInputVal } from "js/composer/varInputValManager";
export const aiCommands = {
label: "AI操作",
icon: "smart_toy",

View File

@@ -892,16 +892,23 @@ interface quickcommandApi {
/**
* 与 AI 进行问答
* @param content 对话内容
* @param content.prompt 提示词
* @param content.role 预设角色
* @param apiConfig API配置
* @param apiConfig.apiType 模型类型openai/ollama
* @param apiConfig.apiUrl API地址
* @param apiConfig.apiToken API令牌仅 OpenAI 需要)
* @param apiConfig.model 模型名称
* @param options 其他选项
* @param options.showLoadingBar 是否显示加载条
* @example
* // OpenAI 示例
* const response = await quickcommand.askAI(
* {
* prompt: "你好",
* presetPrompt: "" // 使用预设提示词translate/shell/summarize
* },
* {
* modelType: "openai",
* apiType: "openai",
* apiUrl: "https://api.openai.com/v1/chat/completions",
* apiToken: "your-api-token",
* model: "gpt-3.5-turbo"
@@ -912,10 +919,10 @@ interface quickcommandApi {
* const response = await quickcommand.askAI(
* {
* prompt: "查找进程名为chrome的进程并关闭",
* presetPrompt: "shell"
* role: "shell"
* },
* {
* modelType: "ollama",
* apiType: "ollama",
* apiUrl: "http://localhost:11434/api/generate",
* model: "qwen2.5:32b"
* }
@@ -925,18 +932,22 @@ interface quickcommandApi {
content: {
/** 提示词 */
prompt: string;
/** 预设提示词类型 */
presetPrompt?: "" | "translate" | "shell" | "summarize";
/** 预设角色 */
role?: "translate" | "shell" | "summarize";
},
apiConfig: {
/** 模型类型openai/ollama */
modelType: "openai" | "ollama";
apiType: "openai" | "ollama";
/** API地址 */
apiUrl: string;
/** API令牌仅 OpenAI 需要) */
apiToken?: string;
/** 模型名称 */
model: string;
},
options?: {
/** 是否显示加载条, 默认 true */
showLoadingBar?: boolean;
}
): Promise<{
/** 是否成功 */