Browse Source

add copy files

master
godo 5 months ago
parent
commit
936a045c88
  1. 1
      README.md
  2. 24
      frontend/src/components/builtin/FileList.vue
  3. 9
      frontend/src/components/computer/Computer.vue
  4. 81
      frontend/src/components/localchat/AiChatMain.vue
  5. 11
      frontend/src/hook/useContextMenu.ts
  6. 48
      godo/ai/server/chat.go
  7. 5
      godo/ai/vector/vector.go
  8. 4
      godo/model/vec_doc.go

1
README.md

@ -19,6 +19,7 @@
- 修改锁屏机制,确保外网访问安全。
- 支持本地聊天ai对话文件和联网搜索。
- 支持知识库根据文件智能生成,一键生成知识库索引,一键搜索知识库。
- 新增复制/粘贴快捷键
## 🏭 第三阶段目标(一月底发布)
1. **文档处理与Markdown智能升级**:(已完成)

24
frontend/src/components/builtin/FileList.vue

@ -252,7 +252,28 @@ function startDragApp(mouse: DragEvent, item: OsFileWithoutContent) {
});
}
}
document.addEventListener('copy', function () {
const files = chosenIndexs.value.map(
(index) => props.fileList[index]
)
if (files.length > 0) {
copyFile(files);
chosenIndexs.value = [];
}
});
// document.addEventListener('keydown', function (event) {
// // Control + C
// if (event.ctrlKey && event.key === 'c') {
// //console.log('Control + C ');
// copyFile(
// chosenIndexs.value.map(
// (index) => props.fileList[index]
// )
// );
// chosenIndexs.value = [];
// }
// });
function handleRightClick(
mouse: MouseEvent,
item: OsFileWithoutContent,
@ -361,11 +382,12 @@ function handleRightClick(
title: t("tips"),
content: "添加成功",
});
props.onRefresh();
}
})
},
});
}else{
} else {
menuArr.push({
label: "对话知识库",
click: () => {

9
frontend/src/components/computer/Computer.vue

@ -92,7 +92,7 @@ const currentList = ref<Array<OsFileWithoutContent>>([]);
const system = useSystem();
const { dragFileToDrop } = useFileDrag(system);
const { createDesktopContextMenu } = useContextMenu();
const { createDesktopContextMenu,pasteFile } = useContextMenu();
//
const props = defineProps({
translateSavePath: {
@ -312,6 +312,7 @@ let chosenCallback: (rect: Rect) => void = () => {
//
};
function onChosen(callback: (rect: Rect) => void) {
console.log(callback)
chosenCallback = callback;
}
@ -380,6 +381,12 @@ async function handleNavSearch(path: string) {
setRouter("search:" + path);
refersh();
}
document.addEventListener('paste', function() {
if(router_url.value !== "/" && !router_url.value.startsWith("/B")){
pasteFile(router_url.value)
refersh();
}
});
/* ------------ 路径输入框end ---------*/
</script>
<style lang="scss" scoped>

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

@ -48,50 +48,54 @@ const requestMessages = computed(() => {
return [...promptMessage.value, ...slicedMessages];
}
});
// async function addKnowledge(){
// const knowledgeId = win?.config?.knowledgeId || 0;
// let msg = userMessage.value
// if (knowledgeId > 0) {
// const askData: any = {
// id: knowledgeId,
// input: userMessage.value,
// }
// const config = getSystemConfig()
// const postData: any = {
// method: "POST",
// body: JSON.stringify(askData),
// };
// const completion = await fetch(config.apiUrl + '/ai/askknowledge', postData);
// if (!completion.ok) {
// const errorData = await completion.json();
// //console.log(errorData)
// notifyError(errorData.message);
// isPadding.value = false;
// return;
// }
// const res = await completion.json();
// console.log(res)
// // let prompt = await chatStore.getPrompt("knowledge")
// // if (prompt == '') {
// // notifyError("prompt")
// // return
// // }
// if (res && res.data.length > 0) {
// let context: string = "";
// res.data.forEach((item: any) => {
// context += "- " + item.content + "\n";
// })
// //prompt = prompt.replace("{content}", context)
// msg = `\n${context}\n:${userMessage.value} `
// }
// }
// return msg
// }
const sendMessage = async () => {
if (chatStore.activeId < 1) {
notifyError(t("aichat.selectModel"));
return;
}
const knowledgeId = win?.config?.knowledgeId || 0;
if (knowledgeId > 0) {
const askData: any = {
id: knowledgeId,
input: userMessage.value,
}
const config = getSystemConfig()
const postData: any = {
method: "POST",
body: JSON.stringify(askData),
};
const completion = await fetch(config.apiUrl + '/ai/askknowledge', postData);
if (!completion.ok) {
const errorData = await completion.json();
//console.log(errorData)
notifyError(errorData.message);
isPadding.value = false;
return;
}
const res = await completion.json();
console.log(res)
let prompt = await chatStore.getPrompt("knowledge")
if (prompt == '') {
notifyError("知识库prompt为空")
return
}
if (res && res.data.length > 0) {
let context: string = "";
res.data.forEach((item: any) => {
context += "- " + item.content + "\n";
})
prompt = prompt.replace("{content}", context)
chatStore.chatInfo.prompt = prompt
}
}
if (userMessage.value) {
// Add the message to the list
if (isPadding.value === true) return;
const saveMessage = {
let saveMessage:any = {
content: userMessage.value,
chatId: chatStore.activeId,
role: "user",
@ -99,6 +103,7 @@ const sendMessage = async () => {
createdAt: Date.now(),
};
chatStore.messageList.push(saveMessage);
await chatStore.addMessages(chatStore.activeId, saveMessage);
// Clear the input
@ -124,6 +129,7 @@ const createCompletion = async () => {
};
const chatConfig = modelStore.chatConfig.chat;
const knowledgeId = win?.config?.knowledgeId*1 || 0;
let postMsg: any = {
messages: requestMessages.value,
model: chatStore.chatInfo.model,
@ -133,6 +139,7 @@ const createCompletion = async () => {
fileContent: fileContent.value,
fileName: fileName.value,
options: chatConfig,
knowledgeId:knowledgeId,
};
if (imageData.value != "") {
const img2txtModel = await modelStore.getModel("img2txt");
@ -151,8 +158,10 @@ const createCompletion = async () => {
images: [imageData.value],
},
],
knowledgeId:knowledgeId,
};
}
const postData: any = {
method: "POST",
body: JSON.stringify(postMsg),

11
frontend/src/hook/useContextMenu.ts

@ -45,7 +45,13 @@ function useContextMenu() {
callback?.();
});
}
// document.addEventListener('keydown', function (event) {
// // 检测 Control + V
// if (event.ctrlKey && event.key === 'v') {
// console.log('Control + V 被按下',path);
// // 在这里添加你的粘贴逻辑
// }
// });
//console.log(path)
let menuArr: any = [
{
@ -321,6 +327,7 @@ function useContextMenu() {
}
async function copyFile(files: OsFileWithoutContent[]) {
//console.log(files)
const system = useSystem();
const rootState = system._rootState;
if (!system) return;
@ -329,6 +336,7 @@ function useContextMenu() {
}
}
async function pasteFile(path: string) {
//console.log(path);
const system = useSystem();
if (!system) return;
if (["/", "/B"].includes(path)) return;
@ -353,6 +361,7 @@ function useContextMenu() {
}
return system.fs.copyFile(clipFile, fspath.join(path, tempName) + ext);
});
rootState.clipboard = [];
} else {
system.emitError('no file in clipboard');
}

48
godo/ai/server/chat.go

@ -5,6 +5,7 @@ import (
"fmt"
"godo/ai/search"
"godo/libs"
"godo/model"
"godo/office"
"log"
"net/http"
@ -20,6 +21,7 @@ type ChatRequest struct {
FileName string `json:"fileName"`
Options map[string]interface{} `json:"options"`
Messages []Message `json:"messages"`
KnowledgeId uint `json:"knowledgeId"`
}
type Message struct {
@ -39,15 +41,19 @@ func ChatHandler(w http.ResponseWriter, r *http.Request) {
if req.WebSearch {
err = ChatWithWeb(&req)
if err != nil {
libs.ErrorMsg(w, err.Error())
return
log.Printf("the chat with web error:%v", err)
}
}
if req.FileContent != "" {
err = ChatWithFile(&req)
if err != nil {
libs.ErrorMsg(w, err.Error())
return
log.Printf("the chat with file error:%v", err)
}
}
if req.KnowledgeId != 0 {
err = ChatWithKnowledge(&req)
if err != nil {
log.Printf("the chat with knowledge error:%v", err)
}
}
headers, url, err := GetHeadersAndUrl(req, "chat")
@ -70,7 +76,39 @@ func ChatWithFile(req *ChatRequest) error {
}
userQuestion := fmt.Sprintf("请对\n%s\n的内容进行分析,给出对用户输入的回答: %s", fileContent, lastMessage)
log.Printf("the search file is %v", userQuestion)
req.Messages = append(req.Messages, Message{Role: "user", Content: userQuestion})
req.Messages = append([]Message{}, Message{Role: "user", Content: userQuestion})
return nil
}
func ChatWithKnowledge(req *ChatRequest) error {
lastMessage, err := GetLastMessage(*req)
if err != nil {
return err
}
askrequest := model.AskRequest{
ID: req.KnowledgeId,
Input: lastMessage,
}
var knowData model.VecList
if err := model.Db.First(&knowData, askrequest.ID).Error; err != nil {
return fmt.Errorf("the knowledge id is not exist")
}
//var filterDocs
filterDocs := []string{askrequest.Input}
// 获取嵌入向量
resList, err := GetEmbeddings(knowData.Engine, knowData.EmbeddingModel, filterDocs)
if err != nil {
return fmt.Errorf("the embeddings get error:%v", err)
}
res, err := model.AskDocument(askrequest.ID, resList[0])
if err != nil {
return fmt.Errorf("the ask document error:%v", err)
}
msg := ""
for _, res := range res {
msg += fmt.Sprintf("- %s\n", res.Content)
}
prompt := fmt.Sprintf(`从文档\n\"\"\"\n%s\n\"\"\"\n中找问题\n\"\"\"\n%s\n\"\"\"\n的答案,找到答案就使用文档语句回答问题,找不到答案就用自身知识回答并且告诉用户该信息不是来自文档。\n不要复述问题,直接开始回答。`, msg, lastMessage)
req.Messages = append([]Message{}, Message{Role: "user", Content: prompt})
return nil
}
func ChatWithWeb(req *ChatRequest) error {

5
godo/ai/vector/vector.go

@ -82,10 +82,7 @@ func HandlerCreateKnowledge(w http.ResponseWriter, r *http.Request) {
libs.SuccessMsg(w, id, "create vector success")
}
func HandlerAskKnowledge(w http.ResponseWriter, r *http.Request) {
var req struct {
ID uint `json:"id"`
Input string `json:"input"`
}
var req model.AskRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
libs.ErrorMsg(w, "the chat request error:"+err.Error())

4
godo/model/vec_doc.go

@ -89,6 +89,10 @@ type AskDocResponse struct {
Score float32 `json:"score"`
FilePath string `json:"file_path"`
}
type AskRequest struct {
ID uint `json:"id"`
Input string `json:"input"`
}
func AskDocument(listId uint, query []float32) ([]AskDocResponse, error) {
// 序列化查询向量

Loading…
Cancel
Save