Browse Source

add ai engine

master
godo 6 months ago
parent
commit
312d4c2179
  1. 16
      frontend/components.d.ts
  2. 310
      frontend/src/components/ai/AiSettingApi.vue
  3. 1
      frontend/src/components/ai/AiSettingDef.vue
  4. 62
      frontend/src/components/ai/DownAddbox.vue
  5. 31
      frontend/src/components/ai/aimodel.vue
  6. 2
      frontend/src/components/ai/aisetting.vue
  7. 5
      frontend/src/components/localchat/AiChatInfo.vue
  8. 2
      frontend/src/components/localchat/AiChatMain.vue
  9. 2
      frontend/src/i18n/lang/en.json
  10. 2
      frontend/src/i18n/lang/zh.json
  11. 7
      frontend/src/stores/aichat.ts
  12. 2
      frontend/src/stores/db.ts
  13. 14
      frontend/src/stores/model.ts
  14. 75
      frontend/src/stores/modelconfig.ts
  15. 60
      frontend/src/system/aiconfig.ts
  16. 23
      frontend/src/system/config.ts
  17. 26
      frontend/src/util/goutil.ts
  18. 3
      godo/ai/server/chat.go
  19. 8
      godo/ai/server/down.go
  20. 31
      godo/ai/server/llms.go
  21. 12
      godo/libs/dir.go

16
frontend/components.d.ts

@ -63,17 +63,29 @@ declare module 'vue' {
DownModelInfo: typeof import('./src/components/ai/DownModelInfo.vue')['default']
EditFileName: typeof import('./src/components/builtin/EditFileName.vue')['default']
EditType: typeof import('./src/components/builtin/EditType.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBadge: typeof import('element-plus/es')['ElBadge']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
ElCol: typeof import('element-plus/es')['ElCol']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElOption: typeof import('element-plus/es')['ElOption']
ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
@ -81,8 +93,12 @@ declare module 'vue' {
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElSpace: typeof import('element-plus/es')['ElSpace']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElText: typeof import('element-plus/es')['ElText']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
Error: typeof import('./src/components/taskbar/Error.vue')['default']
FileIcon: typeof import('./src/components/builtin/FileIcon.vue')['default']
FileIconImg: typeof import('./src/components/builtin/FileIconImg.vue')['default']

310
frontend/src/components/ai/AiSettingApi.vue

@ -5,55 +5,78 @@ import {
getSystemConfig,
setSystemConfig,
} from "@/system/config";
import { OpenDirDialog } from "@/util/goutil";
const config: any = ref({});
import { OpenDirDialog, checkUrl } from "@/util/goutil";
const activeNames = ref([])
const hoverTxt = {
dataDir: t('aisetting.tips_dataDir'),
apiUrl: t('aisetting.tips_apiUrl'),
};
const formData: any = ref({
dataDir: "",
aiUrl: "",
ollamaUrl: "",
ollamaDir: "",
openaiUrl: "",
openaiSecret: "",
giteeSecret: "",
cloudflareUserId: "",
cloudflareSecret: "",
deepseekSecret: "",
bigmodelSecret: "",
volcesSecret:"",
alibabaSecret: "",
groqSecret: "",
mistralSecret: "",
anthropicSecret: "",
llamafamilySecret: "",
siliconflowSecret: ""
})
onMounted(() => {
config.value = getSystemConfig();
const config = getSystemConfig();
for (const key in config) {
if (formData.value.hasOwnProperty(key)) {
formData.value[key] = config[key];
}
}
});
async function changeDir() {
async function changeDir(name: any) {
const path: any = await OpenDirDialog();
//console.log(path)
config.value.dataDir = path;
formData.value[name] = path;
}
const saveConfig = async () => {
try {
await fetch(config.value.apiUrl, {
method: "GET",
mode: "no-cors",
});
} catch (error) {
notifyError(t('common.urlError'));
return;
}
const config = getSystemConfig();
const saveData = toRaw(formData.value)
let postData: any = []
if (config.value.dataDir.trim() != "") {
postData.push({
name: "aiDir",
value: config.value.dataDir.trim(),
})
for (const key in saveData) {
saveData[key] = saveData[key].trim()
config[key] = saveData[key];
if (saveData[key] != "") {
postData.push({
name: key,
value: saveData[key],
})
}
}
if (config.value.ollamaUrl.trim() != "") {
postData.push({
name: "ollamaUrl",
value: config.value.ollamaUrl.trim(),
})
if (saveData.aiUrl != "") {
if (!await checkUrl(saveData.aiUrl)) {
notifyError("ai服务端地址有误");
return;
}
}
if (config.value.openaiUrl.trim() != "") {
postData.push({
name: "openaiUrl",
value: config.value.openaiUrl.trim(),
})
if (saveData.ollamaUrl != "") {
if (!await checkUrl(saveData.ollamaUrl)) {
notifyError("ai服务端地址有误");
return;
}
}
if (postData.length > 0) {
const postDatas = {
method: "POST",
body: JSON.stringify(postData),
};
const res: any = await fetch(config.value.apiUrl + "/system/setting", postDatas);
const res: any = await fetch(config.apiUrl + "/system/setting", postDatas);
//console.log(res)
if (!res || !res.ok) {
notifyError(t('common.saveError'));
@ -66,63 +89,182 @@ const saveConfig = async () => {
}
}
setSystemConfig(config.value);
setSystemConfig(config);
notifySuccess(t('common.saveSuccess'));
};
</script>
<template>
<el-scrollbar class="scrollbarSettingHeight">
<el-form label-width="150px" style="padding: 0 30px 50px 0">
<el-form-item :label="t('aisetting.dataDir')">
<div class="slider-container">
<el-input v-model="config.dataDir" :placeholder="t('aisetting.localDirHolder')" prefix-icon="Menu"
@click="changeDir()" clearable></el-input>
<el-popover placement="left" :width="400" trigger="click">
<template #reference>
<el-icon :size="22">
<InfoFilled />
</el-icon>
</template>
<template #default>
<div v-html="hoverTxt.dataDir"></div>
</template>
</el-popover>
</div>
</el-form-item>
<el-form-item :label="t('aisetting.serverUrl')">
<div class="slider-container">
<el-input v-model="config.aiUrl" :placeholder="t('aisetting.serverUrl')" prefix-icon="Notification"
clearable></el-input>
<el-popover placement="left" :width="400" trigger="click">
<template #reference>
<el-icon :size="22">
<InfoFilled />
</el-icon>
</template>
<template #default>
<div v-html="hoverTxt.apiUrl"></div>
</template>
</el-popover>
</div>
</el-form-item>
<el-form-item :label="t('aisetting.ollamaUrl')">
<div class="slider-container">
<el-input v-model="config.ollamaUrl" :placeholder="t('aisetting.ollamaUrl')" prefix-icon="Notification"
clearable></el-input>
</div>
</el-form-item>
<el-form-item label="OpenAI URL">
<div class="slider-container">
<el-input v-model="config.openaiUrl" placeholder="OpenAI URL" prefix-icon="Notification"
clearable></el-input>
</div>
</el-form-item>
<el-form-item>
<el-button @click="saveConfig" type="info" plain>
{{ t("common.confim") }}
</el-button>
</el-form-item>
</el-form>
</el-scrollbar>
<el-form label-width="150px" style="padding: 0 30px 50px 0">
<el-collapse v-model="activeNames">
<el-collapse-item title="系统配置" name="system">
<el-form-item :label="t('aisetting.dataDir')">
<div class="slider-container">
<el-input v-model="formData.dataDir" placeholder="系统模型下载本地地址" prefix-icon="Menu"
@click="changeDir('dataDir')" clearable></el-input>
<el-popover placement="left" :width="400" trigger="click">
<template #reference>
<el-icon :size="22">
<InfoFilled />
</el-icon>
</template>
<template #default>
<div v-html="hoverTxt.dataDir"></div>
</template>
</el-popover>
</div>
</el-form-item>
<el-form-item label="AI服务端地址">
<div class="slider-container">
<el-input v-model="formData.aiUrl" placeholder="AI服务端地址" prefix-icon="Notification" clearable></el-input>
<el-popover placement="left" :width="400" trigger="click">
<template #reference>
<el-icon :size="22">
<InfoFilled />
</el-icon>
</template>
<template #default>
<div v-html="hoverTxt.apiUrl"></div>
</template>
</el-popover>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="Ollama配置" name="ollama">
<el-form-item :label="t('aisetting.ollamaUrl')">
<div class="slider-container">
<el-input v-model="formData.ollamaUrl" placeholder="ollama模型访问地址,设置后ollama需重启,为空则使用http://localhost:11434"
prefix-icon="Notification" clearable></el-input>
</div>
</el-form-item>
<el-form-item label="模型存储地址">
<div class="slider-container">
<el-input v-model="formData.ollamaDir" placeholder="ollama模型存储地址,设置后ollama需重启,为空则使用默认地址"
prefix-icon="Menu" @click="changeDir('ollamaDir')" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="OpenAI配置" name="openai">
<el-form-item label="OpenAI地址">
<div class="slider-container">
<el-input v-model="formData.openaiUrl" placeholder="OpenAI地址,可设置为代理地址" prefix-icon="Notification"
clearable></el-input>
</div>
</el-form-item>
<el-form-item label="OpenAI私钥">
<div class="slider-container">
<el-input v-model="formData.openaiSecret" placeholder="OpenAI私钥" prefix-icon="Lock" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="GiteeAI配置" name="gitee">
<el-form-item label="GiteeAI私钥">
<div class="slider-container">
<el-input v-model="formData.giteeSecret" placeholder="GiteeAI私钥" prefix-icon="Lock" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="Cloudflare Workers AI配置" name="cloudflare">
<el-form-item label="Cloudflare用户ID">
<div class="slider-container">
<el-input v-model="formData.cloudflareUserId" placeholder="Cloudflare用户ID" prefix-icon="Notification"
clearable></el-input>
</div>
</el-form-item>
<el-form-item label="Cloudflare私钥">
<div class="slider-container">
<el-input v-model="formData.cloudflareSecret" placeholder="Cloudflare私钥" prefix-icon="Lock"
clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="DeepSeek配置" name="deepseek">
<el-form-item label="DeepSeek私钥">
<div class="slider-container">
<el-input v-model="formData.deepseekSecret" placeholder="DeepSeek私钥" prefix-icon="Lock" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="智谱清言语BigModel配置" name="bigmodel">
<el-form-item label="智谱清言语私钥">
<div class="slider-container">
<el-input v-model="formData.bigmodelSecret" placeholder="智谱清言语私钥" prefix-icon="Lock" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="火山方舟配置" name="volces">
<el-form-item label="火山方舟私钥">
<div class="slider-container">
<el-input v-model="formData.volcesSecret" placeholder="火山方舟私钥" prefix-icon="Lock" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="阿里通义DashScope配置" name="alibaba">
<el-form-item label="阿里通义私钥">
<div class="slider-container">
<el-input v-model="formData.alibabaSecret" placeholder="阿里通义DashScope私钥" prefix-icon="Lock" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="Groq配置" name="groq">
<el-form-item label="Groq私钥">
<div class="slider-container">
<el-input v-model="formData.groqSecret" placeholder="Groq私钥" prefix-icon="Lock" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="Mistral配置" name="mistral">
<el-form-item label="mistral私钥">
<div class="slider-container">
<el-input v-model="formData.mistralSecret" placeholder="mistral私钥" prefix-icon="Lock" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="Anthropic配置" name="anthropic">
<el-form-item label="anthropic私钥">
<div class="slider-container">
<el-input v-model="formData.anthropicSecret" placeholder="anthropic私钥" prefix-icon="Lock" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="llama.family配置" name="llamafamily">
<el-form-item label="llamafamily私钥">
<div class="slider-container">
<el-input v-model="formData.llamafamilySecret" placeholder="llamafamily私钥" prefix-icon="Lock" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
<el-collapse-item title="硅基流动配置" name="siliconflow">
<el-form-item label="硅基流动私钥">
<div class="slider-container">
<el-input v-model="formData.siliconflowSecret" placeholder="硅基流动siliconflow私钥" prefix-icon="Lock" clearable></el-input>
</div>
</el-form-item>
</el-collapse-item>
</el-collapse>
<el-form-item style="margin-top: 10px;">
<el-button @click="saveConfig" type="info" plain>
{{ t("common.confim") }}
</el-button>
</el-form-item>
</el-form>
</el-scrollbar>
</template>

1
frontend/src/components/ai/AiSettingDef.vue

@ -6,7 +6,6 @@ const currentsModel: any = ref({});
onMounted(async () => {
await modelStore.getModelList();
updateCurrentsModel();
});
function updateCurrentsModel() {
modelStore.cateList.forEach((item:any) => {

62
frontend/src/components/ai/DownAddbox.vue

@ -20,8 +20,18 @@ const formInit = {
quant: "q4_K_M",
pb: "",
},
type: "",
type: "local",
};
const engineType = [
{
name: "local",
label: "本地引擎",
},
{
name: "net",
label: "网络引擎",
},
]
const formData = ref(formInit);
const fromSource = computed(() => {
if (formData.value.info.engine == "ollama") {
@ -59,6 +69,15 @@ function setFrom(val: string) {
formData.value.info.from = "network"
}
}
function setType(val: string) {
if (val == "local") {
formData.value.info.from = "ollama"
formData.value.info.engine = "ollama"
} else {
formData.value.info.from = "network"
formData.value.info.engine = "openai"
}
}
const emit = defineEmits(["closeFn", "saveFn"]);
const localModels: any = ref([]);
async function getLocalModel() {
@ -99,13 +118,11 @@ function setLocalInfo() {
urls.push(url + item);
});
//formData.value.info.url = urls;
modelData.info.url = urls;
modelData.info.url = urls.join("\n");
modelData.info.engine = "ollama";
modelData.info.from = "local";
modelData.info.model = formData.value.info.model;
formData.value.info = modelData.info;
//formData.value.file_name = modelData.file_name;
//formData.value.engine = modelData.engine;
// if (modelData.engine == "ollama") {
// formData.value.type = "local";
// }
}
async function download() {
const saveData: any = toRaw(formData.value);
@ -115,12 +132,17 @@ async function download() {
notifyError(t('model.selectLabel'));
return;
}
if(saveData.info.model == ""){
notifyError(t('model.labelNameEmpty'));
return;
}
if(saveData.type == "net"){
saveData.info.url = [];
saveData.info.context_length = 1024;
emit("saveFn", saveData);
return;
}
if (saveData.info.from == "ollama") {
if (saveData.info.model == "") {
notifyError(t('model.labelNameEmpty'));
return;
}
if (saveData.info.model.indexOf(":") === -1) {
saveData.info.model = saveData.info.model + ":latest";
}
@ -204,12 +226,20 @@ async function download() {
<el-option v-for="(item, key) in modelStore.labelList" :key="key" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="引擎类型">
<el-select v-model="formData.type" placeholder="选择引擎类型" @change="setType">
<el-option v-for="item, key in engineType" :key="key" :label="item.label" :value="item.name" />
</el-select>
</el-form-item>
<el-form-item :label="t('model.selectEngine')">
<el-select v-model="formData.info.engine" :placeholder="t('model.selectEngine')" @change="setFrom">
<el-select v-model="formData.info.engine" v-if="formData.type == 'local'" :placeholder="t('model.selectEngine')" @change="setFrom">
<el-option v-for="item, key in modelStore.modelEngines" :key="key" :label="item.name" :value="item.name" />
</el-select>
<el-select v-model="formData.info.engine" v-else :placeholder="t('model.selectEngine')" @change="setFrom">
<el-option v-for="item, key in modelStore.netEngines" :key="key" :label="item.name" :value="item.cpp" />
</el-select>
</el-form-item>
<el-form-item :label="t('model.selectSource')">
<el-form-item :label="t('model.selectSource')" v-if="formData.type == 'local'">
<el-select v-model="formData.info.from">
<el-option v-for="(item, key) in fromSource" :key="key" :label="item.label" :value="item.value" />
</el-select>
@ -219,7 +249,7 @@ async function download() {
:placeholder="t('model.enterModelName')"></el-input>
</el-form-item>
<template v-if="formData.info.from === 'local'">
<template v-if="formData.info.from === 'local' && formData.type == 'local'">
<el-form-item :label="t('model.oppositeIpAddress')">
<el-input v-model="formData.ip" prefix-icon="Key" clearable placeholder="192.168.1.66"
@blur="getLocalModel"></el-input>
@ -230,7 +260,7 @@ async function download() {
</el-select>
</el-form-item>
</template>
<template v-if="formData.info.from === 'network'">
<template v-if="formData.info.from === 'network' && formData.type == 'local'">
<el-form-item :label="t('model.modelUrl')">
<el-input type="textarea" :row="3" v-model="formData.info.url"
:placeholder="t('model.enterModelUrl')"></el-input>

31
frontend/src/components/ai/aimodel.vue

@ -38,7 +38,7 @@ async function downLabel(modelData: any, labelData: any) {
labelData = toRaw(labelData);
modelData = toRaw(modelData);
//console.log(modelData, labelData)
if(modelData.model){
if (modelData.model) {
modelData.info.model = modelData.model
}
const saveData = {
@ -62,6 +62,13 @@ async function saveBox(modelData: any) {
notifyError(t('model.chooseLabel'));
return;
}
if (modelData.type == "net") {
const engine = modelData.info.engine;
if (config[engine + "Secret"] == "") {
notifyError(engine + " secret is empty");
return;
}
}
//console.log(modelData)
downLabel(modelData, labelData);
}
@ -77,7 +84,8 @@ async function download(saveData: any) {
notifyError(t('model.labelDown'));
return;
}
//console.log(saveData);
const downUrl = config.aiUrl + "/ai/download";
try {
@ -91,11 +99,22 @@ async function download(saveData: any) {
notifyError(errorData.message);
return;
}
if (saveData.type == "net") {
//console.log(saveData)
const res = await completion.json();
if(res.code == -1){
notifyError(res.message);
return
}
await modelStore.checkModelList(saveData)
notifySuccess(t('model.labelDown'));
} else {
saveData.status = "loading";
saveData.progress = 0;
modelStore.addDownload(saveData);
await handleDown(saveData, completion);
}
saveData.status = "loading";
saveData.progress = 0;
modelStore.addDownload(saveData);
await handleDown(saveData, completion);
} catch (error: any) {
notifyError(error.message);
}

2
frontend/src/components/ai/aisetting.vue

@ -27,7 +27,7 @@ const activeName = ref("system");
}
.scrollbarSettingHeight {
height: 85vh;
height: 75vh;
padding-bottom: 30px;
}

5
frontend/src/components/localchat/AiChatInfo.vue

@ -36,7 +36,10 @@ const changeInfo = async () => {
prompt: info.prompt,
promptId: info.promptId,
};
await chatStore.addChat(info.title, info.model, promptData, "");
const modelData = chatStore.modelList.find((item:any) => {
return item.model == info.model;
});
await chatStore.addChat(info.title, modelData, promptData, "");
notifySuccess(t("aichat.addsuccess"));
}
await chatStore.getActiveChat();

2
frontend/src/components/localchat/AiChatMain.vue

@ -83,6 +83,7 @@ const createCompletion = async () => {
let postMsg: any = {
messages: requestMessages.value,
model: chatStore.chatInfo.model,
engine: chatStore.chatInfo.engine,
stream: false,
options: chatConfig,
};
@ -92,6 +93,7 @@ const createCompletion = async () => {
postMsg = {
model: img2txtModel.model,
//"prompt":userMessage.value,
engine: img2txtModel.info.engine,
stream: false,
options: chatConfig,
messages: [

2
frontend/src/i18n/lang/en.json

@ -269,7 +269,7 @@
"noDown": "Incomplete",
"downloading": "Downloading",
"modelLabel": "ModelLabel",
"modelDown": "Download",
"modelDown": "Add Model",
"help_label": "Choose category",
"help_labelDesc": "Select the model category you want to download",
"help_showdown": "View Download",

2
frontend/src/i18n/lang/zh.json

@ -272,7 +272,7 @@
"noDown": "未下载",
"downloading": "下载中",
"modelLabel": "模型标签",
"modelDown": "模型下载",
"modelDown": "添加模型",
"labelName": "标签名称",
"family": "家族",
"category": "分类",

7
frontend/src/stores/aichat.ts

@ -26,7 +26,7 @@ export const useAiChatStore = defineStore('aichat', () => {
return false
}
const promptData = await promptStore.getPrompt('chat')
return await addChat(t('aichat.newchat'), currentModel.model, promptData, "")
return await addChat(t('aichat.newchat'), currentModel, promptData, "")
}
const initChat = async () => {
if (activeId.value === 0) {
@ -53,12 +53,13 @@ export const useAiChatStore = defineStore('aichat', () => {
return list
}
// 添加聊天
async function addChat(title: string, model: any, promptData: any, knowledgeId: string) {
async function addChat(title: string, modelData: any, promptData: any, knowledgeId: string) {
const newChat = {
title,
prompt: promptData.prompt,
promptId: promptData.id,
model,
model: modelData.model,
engine: modelData.info.engine,
createdAt: Date.now(),
knowledgeId
}

2
frontend/src/stores/db.ts

@ -11,7 +11,7 @@ dbInit.version(1).stores({
// 模型列表
modelslist: '++id,model,label,action,status,params,type,isdef,info,created_at',
// ai对话列表
aichatlist: '++id,title,model,promptId,prompt,knowledgeId,createdAt',
aichatlist: '++id,title,model,engine,promptId,prompt,knowledgeId,createdAt',
// ai对话消息
aichatmsg: '++id,chatId,role,content,createdAt',
// 用户列表

14
frontend/src/stores/model.ts

@ -76,7 +76,7 @@ export const useModelStore = defineStore('modelStore', () => {
if (existingModels.includes(d.model)) {
d.isdef = 1
}
if(d.action == ""){
if (d.action == "") {
d.action = "chat"
}
});
@ -104,7 +104,7 @@ export const useModelStore = defineStore('modelStore', () => {
return model
}
}
async function getModelAction(action:string) {
async function getModelAction(action: string) {
return await db.rows("modelslist", { action })
}
async function getList() {
@ -203,12 +203,15 @@ export const useModelStore = defineStore('modelStore', () => {
modelData.isLoading = 0;
modelData.progress = 0;
deleteDownload(modelData.model);
await getModelList();
await checkLabelData(modelData);
await setCurrentModel(modelData.action, modelData.model);
await checkModelList(modelData);
}
}
}
async function checkModelList(modelData: any) {
await getModelList();
await checkLabelData(modelData);
await setCurrentModel(modelData.action, modelData.model);
}
function parseJson(str: string): any {
try {
return JSON.parse(str);
@ -274,6 +277,7 @@ export const useModelStore = defineStore('modelStore', () => {
deleteDownload,
updateDownload,
checkLabelData,
checkModelList,
getLabelCate,
getLabelSearch,
getLabelList,

75
frontend/src/stores/modelconfig.ts

@ -38,52 +38,59 @@ export const netEngines = [
cpp: "openai",
needID: false,
},
// {
// name: "Google",
// cpp: "gemini"
// },
{
name: "GiteeAI",
cpp: "giteeAI",
cpp: "gitee",
needID: false,
},
// {
// name: "Baidu",
// cpp: "baidu"
// },
{
name: "Alibaba",
cpp: "alibaba",
name: "CloudflareWorkersAI",
cpp: "cloudflare",
needID: true,
},
{
name: "DeepSeek",
cpp: "deepseek",
needID: false,
},
// {
// name: "Tencent",
// cpp: "tencent"
// },
// {
// name: "Kimi",
// cpp: "Moonshot"
// },
{
name: "BigModel",
cpp: "BigModel",
name: "智谱清言语BigModel",
cpp: "bigmodel",
needID: false,
},
{
name: "火山方舟",
cpp: "volces",
needID: false,
},
{
name: "阿里通义",
cpp: "alibaba",
needID: false,
},
// {
// name: "xAI",
// cpp: "xAI"
// },
// {
// name: "Stability",
// cpp: "stability"
// },
// {
// name: "Anthropic",
// cpp: "claude"
// },
{
name: "Groq",
cpp: "groqcloud",
cpp: "groq",
needID: false,
},
{
name: "Mistral",
cpp: "mistral",
needID: false,
},
{
name: "Anthropic",
cpp: "anthropic",
needID: false,
},
{
name: "llama.family",
cpp: "llamafamily",
needID: false,
},
{
name: "硅基流动",
cpp: "siliconflow",
needID: false,
},
]

60
frontend/src/system/aiconfig.ts

@ -0,0 +1,60 @@
export const parseAiConfig = (config: any) => {
if (!config.ollamaUrl) {
config.ollamaUrl = `${window.location.protocol}//${window.location.hostname}:11434`
}
if(!config.ollamaDir) {
config.ollamaDir = ''
}
if (!config.dataDir) {
config.dataDir = ''
}
if (!config.aiUrl) {
config.aiUrl = config.apiUrl
}
//openai
if (!config.openaiUrl) {
config.openaiUrl = 'https://api.openai.com/v1'
}
if (!config.openaiSecret) {
config.openaiSecret = ""
}
//gitee
if (!config.giteeSecret) {
config.giteeSecret = ""
}
//cloudflare
if(!config.cloudflareUserId){
config.cloudflareUserId = ""
}
if(!config.cloudflareSecret){
config.cloudflareSecret = ""
}
if(!config.deepseekSecret) {
config.deepseekSecret = ""
}
if(!config.bigmodelSecret) {
config.bigmodelSecret = ""
}
if(!config.volcesSecret){
config.volcesSecret = ""
}
if(!config.alibabaSecret) {
config.alibabaSecret = ""
}
if(!config.groqSecret) {
config.groqSecret = ""
}
if(!config.mistralSecret) {
config.mistralSecret = ""
}
if(!config.anthropicSecret) {
config.anthropicSecret = ""
}
if(!config.llamafamilySecret) {
config.llamafamilySecret = ""
}
if(!config.siliconflowSecret) {
config.siliconflowSecret = ""
}
return config
};

23
frontend/src/system/config.ts

@ -1,5 +1,6 @@
import { generateRandomString } from "../util/common.ts";
export const configStoreType = localStorage.getItem('GodoOS-storeType') || 'local';
import { parseAiConfig } from "./aiconfig.ts";
/**
*
*
@ -10,7 +11,7 @@ export const getSystemConfig = (ifset = false) => {
// 从本地存储中尝试获取配置信息,若不存在则使用默认空对象
const configSetting = localStorage.getItem('GodoOS-config') || '{}';
// 解析配置信息为JSON对象
const config = JSON.parse(configSetting);
let config:any = JSON.parse(configSetting);
// 初始化配置对象的各项属性,若本地存储中已存在则不进行覆盖
if (!config.version) {
@ -148,24 +149,7 @@ export const getSystemConfig = (ifset = false) => {
'fourthEnd': '254'
}
}
if (!config.ollamaUrl) {
config.ollamaUrl = `${window.location.protocol}//${window.location.hostname}:11434`
}
if (!config.dataDir) {
config.dataDir = ''
}
if (!config.aiUrl) {
config.aiUrl = config.apiUrl
}
if (!config.openaiUrl) {
config.openaiUrl = 'https://api.openai.com/v1'
}
if (!config.openaiSecret) {
config.openaiSecret = ""
}
if(!config.giteeSecret){
config.giteeSecret = ""
}
// 初始化桌面快捷方式列表,若本地存储中已存在则不进行覆盖
if (!config.desktopList) {
config.desktopList = [];
@ -178,6 +162,7 @@ export const getSystemConfig = (ifset = false) => {
if (!config.token) {
config.token = generateRandomString(16);
}
config = parseAiConfig(config);
// 根据参数决定是否更新本地存储中的配置信息
if (ifset) {
setSystemConfig(config)

26
frontend/src/util/goutil.ts

@ -1,16 +1,26 @@
export async function OpenDirDialog(){
if((window as any).go) {
export async function OpenDirDialog() {
if ((window as any).go) {
return (window as any)['go']['app']['App']['OpenDirDialog']();
}else {
} else {
return ""
}
}
export function RestartApp(){
if(!(window as any).go){
export async function checkUrl(url: string) {
try {
await fetch(url, {
method: "GET",
mode: "no-cors",
});
return true;
} catch (error) {
return false;
}
}
export function RestartApp() {
if (!(window as any).go) {
window.location.reload();
}else{
} else {
return (window as any)['go']['app']['App']['RestartApp']();
}
}

3
godo/ai/server/chat.go

@ -3,6 +3,7 @@ package server
import (
"encoding/json"
"godo/libs"
"log"
"net/http"
)
@ -16,6 +17,8 @@ func ChatHandler(w http.ResponseWriter, r *http.Request) {
return
}
headers, url, err := GetHeadersAndUrl(req, "chat")
log.Printf("url: %s", url)
log.Printf("headers: %v", headers)
if err != nil {
libs.ErrorMsg(w, err.Error())
return

8
godo/ai/server/down.go

@ -54,6 +54,14 @@ func Download(w http.ResponseWriter, r *http.Request) {
return
}
if reqBody.Type == "net" {
if err := config.SetModel(reqBody); err != nil {
libs.ErrorMsg(w, "Set model error")
return
}
noticeSuccess(w)
return
}
if reqBody.Info.From == "ollama" && reqBody.Info.Engine == "ollama" {
setOllamaInfo(w, r, reqBody)
return

31
godo/ai/server/llms.go

@ -12,6 +12,7 @@ var OpenAIApiMaps = map[string]string{
"gitee": "",
"cloudflare": "",
"deepseek": "https://api.deepseek.com/v1",
"volces": "https://ark.cn-beijing.volces.com/api/v3",
"bigmodel": "https://open.bigmodel.cn/api/paas/v4",
"alibaba": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"01ai": "https://api.lingyiwanwu.com/v1",
@ -19,6 +20,7 @@ var OpenAIApiMaps = map[string]string{
"mistral": "https://api.mistral.ai/v1",
"anthropic": "https://api.anthropic.com/v1",
"llamafamily": "https://api.atomecho.cn/v1",
"siliconflow": "https://api.siliconflow.cn/v1",
}
func GetHeadersAndUrl(req map[string]interface{}, chattype string) (map[string]string, string, error) {
@ -59,14 +61,31 @@ func GetHeadersAndUrl(req map[string]interface{}, chattype string) (map[string]s
if chattype == "embeddings" {
typeUrl = "/embeddings"
} else if chattype == "text2img" {
typeUrl = "/images/generations"
if engine == "gitee" {
typeUrl = "/text-to-image"
} else {
typeUrl = "/images/generations"
}
}
return headers, url + typeUrl, nil
}
func GetOpenAIHeaders(types string) (map[string]string, error) {
if types == "ollama" {
return map[string]string{
"Content-Type": "application/json",
}, nil
}
secret, err := GetOpenAISecret(types)
if types == "gitee" {
return map[string]string{
"Content-Type": "application/json",
"Authorization": "Bearer " + secret,
"X-Package": "1910",
}, nil
}
if err != nil {
return map[string]string{
"Content-Type": "application/json",
@ -109,13 +128,5 @@ func GetCloudflareUrl() (string, error) {
return "https://api.cloudflare.com/client/v4/accounts/" + userId + "/ai/v1", nil
}
func GetGiteeUrl(model string, chatType string) string {
if chatType == "chat" {
return "https://ai.gitee.com/api/serverless/" + model + "/chat/completions"
} else if chatType == "embeddings" {
return "https://ai.gitee.com/api/serverless/" + model + "/embeddings"
} else if chatType == "text2img" {
return "https://ai.gitee.com/api/serverless/" + model + "/text-to-image"
} else {
return "https://ai.gitee.com/api/serverless/" + model + "/chat/completions"
}
return "https://ai.gitee.com/api/serverless/" + model
}

12
godo/libs/dir.go

@ -55,7 +55,7 @@ func InitServer() error {
}
SetConfig(ipSetting)
}
InitOllamaModelPath()
return nil
}
func InitOsDir() (string, error) {
@ -69,6 +69,16 @@ func InitOsDir() (string, error) {
}
return osDir, nil
}
func InitOllamaModelPath() {
ollamaDir, ok := GetConfig("ollamaDir")
if ok && ollamaDir.(string) != "" {
os.Setenv("OLLAMA_MODELS", ollamaDir.(string))
}
ollamaUrl, ok := GetConfig("ollamaUrl")
if ok && ollamaUrl.(string) != "" {
os.Setenv("OLLAMA_HOST", ollamaUrl.(string))
}
}
func GetOsDir() (string, error) {
osDir, ok := GetConfig("osPath")
if !ok {

Loading…
Cancel
Save