Commit bd287386 authored by Wade's avatar Wade

summary ok

parent afaa731b
{"level":"info","pid":48106,"time":1749131682,"caller":"/Users/wade/project/wuban/agentchat/log.go:69","message":"This message appears when log level set to Debug or Info"}
{"level":"info","pid":48106,"method":"DeepSeek.Init","time":1749131682,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:91","message":"Initializing DeepSeek plugin"}
{"level":"info","pid":48106,"method":"DeepSeek.Init","time":1749131682,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:104","message":"Initialization successful"}
{"level":"info","pid":48106,"method":"Milvus.Init","time":1749131682,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:75","message":"Initializing Milvus plugin"}
{"level":"info","pid":48106,"method":"Milvus.Init","time":1749131683,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:87","message":"Initialization successful"}
{"level":"info","pid":48106,"method":"GraphKnowledge.Init","time":1749131683,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:299","message":"Initializing GraphKnowledge plugin"}
{"level":"info","pid":48106,"method":"NewClient","ip":"54.92.111.204","port":5670,"time":1749131683,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:93","message":"Creating new GraphRAG client"}
{"level":"info","pid":48106,"method":"GraphKnowledge.Init","time":1749131683,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:311","message":"Initialization successful"}
{"level":"info","pid":48106,"method":"DefineIndexerAndRetriever","collection":"chatRag1","dimension":768,"time":1749131683,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:152","message":"Defining indexer and retriever"}
{"level":"info","pid":48106,"method":"Milvus.newDocStore","collection":"chatRag1","dimension":768,"time":1749131683,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:201","message":"Creating new doc store"}
{"level":"info","pid":48106,"method":"Milvus.newDocStore","collection":"chatRag1","time":1749131684,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:302","message":"Doc store created successfully"}
{"level":"info","pid":48106,"method":"DefineIndexerAndRetriever","collection":"chatRag1","time":1749131684,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:182","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":48106,"method":"DefineIndexerAndRetriever","time":1749131684,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:357","message":"Defining indexer and retriever"}
{"level":"info","pid":48106,"method":"GraphKnowledge.newDocStore","space_id":"","model_name":"Qwen/Qwen2.5-Coder-32B-Instruct","time":1749131684,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:393","message":"Creating new doc store"}
{"level":"info","pid":48106,"method":"GraphKnowledge.newDocStore","time":1749131684,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:399","message":"Doc store created successfully"}
{"level":"info","pid":48106,"method":"DefineIndexerAndRetriever","time":1749131684,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:376","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":48106,"method":"NewKnowledgeClient","endpoint":"lkeap.tencentcloudapi.com","region":"ap-guangzhou","secret_id":"","token":"","time":1749131685,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:43","message":"Creating new KnowledgeClient"}
{"level":"info","pid":48106,"method":"KnowledgeClient.Init","time":1749131685,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:51","message":"Initializing KnowledgeClient"}
{"level":"error","pid":48106,"method":"KnowledgeClient.Init","time":1749131685,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:79","message":"SecretID and SecretKey are required"}
{"level":"fatal","pid":48106,"time":1749131685,"caller":"/Users/wade/project/wuban/agentchat/main.go:261","message":"Failed to initialize KnowledgeClient: knowledge: SecretID and SecretKey are required"}
{"level":"info","pid":48510,"time":1749131759,"caller":"/Users/wade/project/wuban/agentchat/log.go:69","message":"This message appears when log level set to Debug or Info"}
{"level":"info","pid":48510,"method":"DeepSeek.Init","time":1749131759,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:91","message":"Initializing DeepSeek plugin"}
{"level":"info","pid":48510,"method":"DeepSeek.Init","time":1749131759,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:104","message":"Initialization successful"}
{"level":"info","pid":48510,"method":"Milvus.Init","time":1749131759,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:75","message":"Initializing Milvus plugin"}
{"level":"info","pid":48510,"method":"Milvus.Init","time":1749131760,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:87","message":"Initialization successful"}
{"level":"info","pid":48510,"method":"GraphKnowledge.Init","time":1749131760,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:299","message":"Initializing GraphKnowledge plugin"}
{"level":"info","pid":48510,"method":"NewClient","ip":"54.92.111.204","port":5670,"time":1749131760,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:93","message":"Creating new GraphRAG client"}
{"level":"info","pid":48510,"method":"GraphKnowledge.Init","time":1749131760,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:311","message":"Initialization successful"}
{"level":"info","pid":48510,"method":"DefineIndexerAndRetriever","collection":"chatRag1","dimension":768,"time":1749131760,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:152","message":"Defining indexer and retriever"}
{"level":"info","pid":48510,"method":"Milvus.newDocStore","collection":"chatRag1","dimension":768,"time":1749131760,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:201","message":"Creating new doc store"}
{"level":"info","pid":48510,"method":"Milvus.newDocStore","collection":"chatRag1","time":1749131761,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:302","message":"Doc store created successfully"}
{"level":"info","pid":48510,"method":"DefineIndexerAndRetriever","collection":"chatRag1","time":1749131761,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:182","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":48510,"method":"DefineIndexerAndRetriever","time":1749131761,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:357","message":"Defining indexer and retriever"}
{"level":"info","pid":48510,"method":"GraphKnowledge.newDocStore","space_id":"","model_name":"Qwen/Qwen2.5-Coder-32B-Instruct","time":1749131761,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:393","message":"Creating new doc store"}
{"level":"info","pid":48510,"method":"GraphKnowledge.newDocStore","time":1749131761,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:399","message":"Doc store created successfully"}
{"level":"info","pid":48510,"method":"DefineIndexerAndRetriever","time":1749131761,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:376","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":48510,"method":"NewKnowledgeClient","endpoint":"lkeap.tencentcloudapi.com","region":"ap-guangzhou","secret_id":"","token":"","time":1749131763,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:43","message":"Creating new KnowledgeClient"}
{"level":"info","pid":48510,"method":"KnowledgeClient.Init","time":1749131763,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:51","message":"Initializing KnowledgeClient"}
{"level":"info","pid":48510,"method":"KnowledgeClient.Init","time":1749131763,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:109","message":"Initialization successful"}
{"level":"info","pid":48510,"time":1749131763,"caller":"/Users/wade/project/wuban/agentchat/main.go:267","message":"KnowledgeClient initialized successfully"}
{"level":"info","pid":48510,"time":1749131772,"caller":"/Users/wade/project/wuban/agentchat/main.go:281","message":"input--------{\"content\":\"What is the capital of UK?\",\"model\":\"gpt-3.5-turbo\",\"apiKey\":\"sk-1234567890abcdef\",\"from\":\"Alice\",\"from_id\":\"user123\",\"to\":\"Bob\",\"to_id\":\"user456\"}"}
{"level":"info","pid":48510,"time":1749131772,"caller":"/Users/wade/project/wuban/agentchat/main.go:307","message":"qaAsJson--------{\"ID\":35,\"CreatedAt\":\"2025-06-05T10:48:41.391675Z\",\"FromID\":\"user123\",\"From\":\"Alice\",\"Question\":\"What is the capital of UK?\",\"Answer\":null,\"Summary\":null,\"To\":\"Bob\",\"ToID\":\"user456\"}"}
{"level":"info","pid":48510,"method":"docStore.Retrieve","collection":"chatRag1","time":1749131772,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:450","message":"Starting retrieve operation"}
{"level":"info","pid":48510,"method":"docStore.Retrieve","collection":"chatRag1","documents":2,"time":1749131774,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:640","message":"Retrieve operation completed successfully"}
{"level":"info","pid":48510,"time":1749131774,"caller":"/Users/wade/project/wuban/agentchat/main.go:333","message":"promptInput.Context: Paris is the capital of France?\nUSA is the largest importer of coffee?\n"}
{"level":"info","pid":48510,"method":"docStore.Retrieve","space_id":"","time":1749131774,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:755","message":"Starting retrieve operation"}
{"level":"info","pid":48510,"method":"docStore.Retrieve","space_id":"","documents":1,"time":1749131782,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:892","message":"Retrieve operation completed successfully"}
{"level":"info","pid":48510,"time":1749131782,"caller":"/Users/wade/project/wuban/agentchat/main.go:349","message":"promptInput.Graph : 知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n"}
{"level":"info","pid":48980,"time":1749131845,"caller":"/Users/wade/project/wuban/agentchat/log.go:69","message":"This message appears when log level set to Debug or Info"}
{"level":"info","pid":48980,"method":"DeepSeek.Init","time":1749131845,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:91","message":"Initializing DeepSeek plugin"}
{"level":"info","pid":48980,"method":"DeepSeek.Init","time":1749131845,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:104","message":"Initialization successful"}
{"level":"info","pid":48980,"method":"Milvus.Init","time":1749131845,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:75","message":"Initializing Milvus plugin"}
{"level":"info","pid":48980,"method":"Milvus.Init","time":1749131846,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:87","message":"Initialization successful"}
{"level":"info","pid":48980,"method":"GraphKnowledge.Init","time":1749131846,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:299","message":"Initializing GraphKnowledge plugin"}
{"level":"info","pid":48980,"method":"NewClient","ip":"54.92.111.204","port":5670,"time":1749131846,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:93","message":"Creating new GraphRAG client"}
{"level":"info","pid":48980,"method":"GraphKnowledge.Init","time":1749131846,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:311","message":"Initialization successful"}
{"level":"info","pid":48980,"method":"DefineIndexerAndRetriever","collection":"chatRag1","dimension":768,"time":1749131846,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:152","message":"Defining indexer and retriever"}
{"level":"info","pid":48980,"method":"Milvus.newDocStore","collection":"chatRag1","dimension":768,"time":1749131846,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:201","message":"Creating new doc store"}
{"level":"info","pid":48980,"method":"Milvus.newDocStore","collection":"chatRag1","time":1749131847,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:302","message":"Doc store created successfully"}
{"level":"info","pid":48980,"method":"DefineIndexerAndRetriever","collection":"chatRag1","time":1749131847,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:182","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":48980,"method":"DefineIndexerAndRetriever","time":1749131847,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:357","message":"Defining indexer and retriever"}
{"level":"info","pid":48980,"method":"GraphKnowledge.newDocStore","space_id":"","model_name":"Qwen/Qwen2.5-Coder-32B-Instruct","time":1749131847,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:393","message":"Creating new doc store"}
{"level":"info","pid":48980,"method":"GraphKnowledge.newDocStore","time":1749131847,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:399","message":"Doc store created successfully"}
{"level":"info","pid":48980,"method":"DefineIndexerAndRetriever","time":1749131847,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:376","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":48980,"method":"NewKnowledgeClient","endpoint":"lkeap.tencentcloudapi.com","region":"ap-guangzhou","secret_id":"","token":"","time":1749131848,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:43","message":"Creating new KnowledgeClient"}
{"level":"info","pid":48980,"method":"KnowledgeClient.Init","time":1749131848,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:51","message":"Initializing KnowledgeClient"}
{"level":"info","pid":48980,"method":"KnowledgeClient.Init","time":1749131848,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:109","message":"Initialization successful"}
{"level":"info","pid":48980,"time":1749131848,"caller":"/Users/wade/project/wuban/agentchat/main.go:267","message":"KnowledgeClient initialized successfully"}
{"level":"info","pid":48980,"time":1749131855,"caller":"/Users/wade/project/wuban/agentchat/main.go:281","message":"input--------{\"content\":\"What is the capital of UK?\",\"model\":\"gpt-3.5-turbo\",\"apiKey\":\"sk-1234567890abcdef\",\"from\":\"Alice\",\"from_id\":\"user123\",\"to\":\"Bob\",\"to_id\":\"user456\"}"}
{"level":"info","pid":48980,"time":1749131855,"caller":"/Users/wade/project/wuban/agentchat/main.go:307","message":"qaAsJson--------{\"ID\":36,\"CreatedAt\":\"2025-06-05T13:56:12.838893Z\",\"FromID\":\"user123\",\"From\":\"Alice\",\"Question\":\"What is the capital of UK?\",\"Answer\":null,\"Summary\":null,\"To\":\"Bob\",\"ToID\":\"user456\"}"}
{"level":"info","pid":48980,"method":"docStore.Retrieve","collection":"chatRag1","time":1749131855,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:450","message":"Starting retrieve operation"}
{"level":"info","pid":48980,"method":"docStore.Retrieve","collection":"chatRag1","documents":2,"time":1749131858,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:640","message":"Retrieve operation completed successfully"}
{"level":"info","pid":48980,"time":1749131858,"caller":"/Users/wade/project/wuban/agentchat/main.go:333","message":"promptInput.Context: Paris is the capital of France?\nUSA is the largest importer of coffee?\n"}
{"level":"info","pid":48980,"method":"docStore.Retrieve","space_id":"","time":1749131858,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:755","message":"Starting retrieve operation"}
{"level":"info","pid":48980,"method":"docStore.Retrieve","space_id":"","documents":1,"time":1749131868,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:892","message":"Retrieve operation completed successfully"}
{"level":"info","pid":48980,"time":1749131868,"caller":"/Users/wade/project/wuban/agentchat/main.go:349","message":"promptInput.Graph : 知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n"}
{"level":"info","pid":49330,"time":1749131919,"caller":"/Users/wade/project/wuban/agentchat/log.go:68","message":"This message appears when log level set to Debug or Info"}
{"level":"info","pid":49330,"method":"DeepSeek.Init","time":1749131919,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:91","message":"Initializing DeepSeek plugin"}
{"level":"info","pid":49330,"method":"DeepSeek.Init","time":1749131919,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:104","message":"Initialization successful"}
{"level":"info","pid":49330,"method":"Milvus.Init","time":1749131919,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:75","message":"Initializing Milvus plugin"}
{"level":"info","pid":49330,"method":"Milvus.Init","time":1749131920,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:87","message":"Initialization successful"}
{"level":"info","pid":49330,"method":"GraphKnowledge.Init","time":1749131920,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:299","message":"Initializing GraphKnowledge plugin"}
{"level":"info","pid":49330,"method":"NewClient","ip":"54.92.111.204","port":5670,"time":1749131920,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:93","message":"Creating new GraphRAG client"}
{"level":"info","pid":49330,"method":"GraphKnowledge.Init","time":1749131920,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:311","message":"Initialization successful"}
{"level":"info","pid":49330,"method":"DefineIndexerAndRetriever","collection":"chatRag1","dimension":768,"time":1749131920,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:152","message":"Defining indexer and retriever"}
{"level":"info","pid":49330,"method":"Milvus.newDocStore","collection":"chatRag1","dimension":768,"time":1749131920,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:201","message":"Creating new doc store"}
{"level":"info","pid":49330,"method":"Milvus.newDocStore","collection":"chatRag1","time":1749131921,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:302","message":"Doc store created successfully"}
{"level":"info","pid":49330,"method":"DefineIndexerAndRetriever","collection":"chatRag1","time":1749131921,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:182","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":49330,"method":"DefineIndexerAndRetriever","time":1749131921,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:357","message":"Defining indexer and retriever"}
{"level":"info","pid":49330,"method":"GraphKnowledge.newDocStore","space_id":"","model_name":"Qwen/Qwen2.5-Coder-32B-Instruct","time":1749131921,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:393","message":"Creating new doc store"}
{"level":"info","pid":49330,"method":"GraphKnowledge.newDocStore","time":1749131921,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:399","message":"Doc store created successfully"}
{"level":"info","pid":49330,"method":"DefineIndexerAndRetriever","time":1749131921,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:376","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":49330,"method":"NewKnowledgeClient","endpoint":"lkeap.tencentcloudapi.com","region":"ap-guangzhou","secret_id":"","token":"","time":1749131922,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:43","message":"Creating new KnowledgeClient"}
{"level":"info","pid":49330,"method":"KnowledgeClient.Init","time":1749131922,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:51","message":"Initializing KnowledgeClient"}
{"level":"info","pid":49330,"method":"KnowledgeClient.Init","time":1749131922,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:109","message":"Initialization successful"}
{"level":"info","pid":49330,"time":1749131922,"caller":"/Users/wade/project/wuban/agentchat/main.go:263","message":"KnowledgeClient initialized successfully"}
{"level":"info","pid":49330,"time":1749131926,"caller":"/Users/wade/project/wuban/agentchat/main.go:277","message":"input--------{\"content\":\"What is the capital of UK?\",\"model\":\"gpt-3.5-turbo\",\"apiKey\":\"sk-1234567890abcdef\",\"from\":\"Alice\",\"from_id\":\"user123\",\"to\":\"Bob\",\"to_id\":\"user456\"}"}
{"level":"info","pid":49330,"time":1749131926,"caller":"/Users/wade/project/wuban/agentchat/main.go:303","message":"qaAsJson--------{\"ID\":37,\"CreatedAt\":\"2025-06-05T13:57:36.176828Z\",\"FromID\":\"user123\",\"From\":\"Alice\",\"Question\":\"What is the capital of UK?\",\"Answer\":null,\"Summary\":null,\"To\":\"Bob\",\"ToID\":\"user456\"}"}
{"level":"info","pid":49330,"method":"docStore.Retrieve","collection":"chatRag1","time":1749131926,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:450","message":"Starting retrieve operation"}
{"level":"info","pid":49330,"method":"docStore.Retrieve","collection":"chatRag1","documents":2,"time":1749131929,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:640","message":"Retrieve operation completed successfully"}
{"level":"info","pid":49330,"time":1749131929,"caller":"/Users/wade/project/wuban/agentchat/main.go:329","message":"promptInput.Context: Paris is the capital of France?\nUSA is the largest importer of coffee?\n"}
{"level":"info","pid":49330,"method":"docStore.Retrieve","space_id":"","time":1749131929,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:755","message":"Starting retrieve operation"}
{"level":"info","pid":49330,"method":"docStore.Retrieve","space_id":"","documents":1,"time":1749131935,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:892","message":"Retrieve operation completed successfully"}
{"level":"info","pid":49330,"time":1749131935,"caller":"/Users/wade/project/wuban/agentchat/main.go:344","message":"promptInput.Graph : 知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n"}
{"level":"info","pid":49330,"time":1749135401,"caller":"/Users/wade/project/wuban/agentchat/main.go:277","message":"input--------{\"content\":\"What is the capital of UK?\",\"model\":\"gpt-3.5-turbo\",\"apiKey\":\"sk-1234567890abcdef\",\"from\":\"Alice\",\"from_id\":\"user123\",\"to\":\"Bob\",\"to_id\":\"user456\"}"}
{"level":"info","pid":49330,"time":1749135428,"caller":"/Users/wade/project/wuban/agentchat/main.go:303","message":"qaAsJson--------{\"ID\":38,\"CreatedAt\":\"2025-06-05T13:58:47.372064Z\",\"FromID\":\"user123\",\"From\":\"Alice\",\"Question\":\"What is the capital of UK?\",\"Answer\":null,\"Summary\":null,\"To\":\"Bob\",\"ToID\":\"user456\"}"}
{"level":"info","pid":49330,"method":"docStore.Retrieve","collection":"chatRag1","time":1749135428,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:450","message":"Starting retrieve operation"}
{"level":"info","pid":49330,"method":"docStore.Retrieve","collection":"chatRag1","documents":2,"time":1749135433,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:640","message":"Retrieve operation completed successfully"}
{"level":"info","pid":49330,"time":1749135433,"caller":"/Users/wade/project/wuban/agentchat/main.go:329","message":"promptInput.Context: Paris is the capital of France?\nUSA is the largest importer of coffee?\n"}
{"level":"info","pid":49330,"method":"docStore.Retrieve","space_id":"","time":1749135433,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:755","message":"Starting retrieve operation"}
{"level":"info","pid":49330,"method":"docStore.Retrieve","space_id":"","documents":1,"time":1749135440,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:892","message":"Retrieve operation completed successfully"}
{"level":"info","pid":49330,"time":1749135440,"caller":"/Users/wade/project/wuban/agentchat/main.go:344","message":"promptInput.Graph : 知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n"}
{"level":"info","pid":3634,"time":1749179323,"caller":"/Users/wade/project/wuban/agentchat/log.go:68","message":"This message appears when log level set to Debug or Info"}
{"level":"info","pid":3634,"method":"DeepSeek.Init","time":1749179323,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:91","message":"Initializing DeepSeek plugin"}
{"level":"info","pid":3634,"method":"DeepSeek.Init","time":1749179323,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:104","message":"Initialization successful"}
{"level":"info","pid":3634,"method":"Milvus.Init","time":1749179323,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:75","message":"Initializing Milvus plugin"}
{"level":"info","pid":3634,"method":"Milvus.Init","time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:87","message":"Initialization successful"}
{"level":"info","pid":3634,"method":"GraphKnowledge.Init","time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:299","message":"Initializing GraphKnowledge plugin"}
{"level":"info","pid":3634,"method":"NewClient","ip":"54.92.111.204","port":5670,"time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:93","message":"Creating new GraphRAG client"}
{"level":"info","pid":3634,"method":"GraphKnowledge.Init","time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:311","message":"Initialization successful"}
{"level":"info","pid":3634,"method":"DefineIndexerAndRetriever","collection":"chatRag1","dimension":768,"time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:152","message":"Defining indexer and retriever"}
{"level":"info","pid":3634,"method":"Milvus.newDocStore","collection":"chatRag1","dimension":768,"time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:201","message":"Creating new doc store"}
{"level":"info","pid":3634,"method":"Milvus.newDocStore","collection":"chatRag1","time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:302","message":"Doc store created successfully"}
{"level":"info","pid":3634,"method":"DefineIndexerAndRetriever","collection":"chatRag1","time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:182","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":3634,"method":"DefineIndexerAndRetriever","time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:357","message":"Defining indexer and retriever"}
{"level":"info","pid":3634,"method":"GraphKnowledge.newDocStore","space_id":"","model_name":"Qwen/Qwen2.5-Coder-32B-Instruct","time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:393","message":"Creating new doc store"}
{"level":"info","pid":3634,"method":"GraphKnowledge.newDocStore","time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:399","message":"Doc store created successfully"}
{"level":"info","pid":3634,"method":"DefineIndexerAndRetriever","time":1749179324,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:376","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":3634,"method":"NewKnowledgeClient","endpoint":"lkeap.tencentcloudapi.com","region":"ap-guangzhou","secret_id":"","token":"","time":1749179325,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:43","message":"Creating new KnowledgeClient"}
{"level":"info","pid":3634,"method":"KnowledgeClient.Init","time":1749179325,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:51","message":"Initializing KnowledgeClient"}
{"level":"info","pid":3634,"method":"KnowledgeClient.Init","time":1749179325,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:109","message":"Initialization successful"}
{"level":"info","pid":3634,"time":1749179325,"caller":"/Users/wade/project/wuban/agentchat/main.go:263","message":"KnowledgeClient initialized successfully"}
{"level":"info","pid":3634,"time":1749179345,"caller":"/Users/wade/project/wuban/agentchat/main.go:277","message":"input--------{\"content\":\"What is the capital of UK?\",\"model\":\"gpt-3.5-turbo\",\"apiKey\":\"sk-1234567890abcdef\",\"from\":\"Alice\",\"from_id\":\"user123\",\"to\":\"Bob\",\"to_id\":\"user456\"}"}
{"level":"info","pid":3634,"time":1749179346,"caller":"/Users/wade/project/wuban/agentchat/main.go:303","message":"qaAsJson--------{\"ID\":39,\"CreatedAt\":\"2025-06-05T14:57:08.59507Z\",\"FromID\":\"user123\",\"From\":\"Alice\",\"Question\":\"What is the capital of UK?\",\"Answer\":null,\"Summary\":null,\"To\":\"Bob\",\"ToID\":\"user456\"}"}
{"level":"info","pid":3634,"method":"docStore.Retrieve","collection":"chatRag1","time":1749179346,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:450","message":"Starting retrieve operation"}
{"level":"error","pid":3634,"error":"doRequest: error sending request: Post \"https://generativelanguage.googleapis.com//v1beta/models/embedding-001:batchEmbedContents\": dial tcp 74.125.135.95:443: i/o timeout","method":"docStore.Retrieve","collection":"chatRag1","time":1749179376,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:508","message":"Embedding failed"}
{"level":"error","pid":3634,"time":1749179376,"caller":"/Users/wade/project/wuban/agentchat/main.go:320","message":"milvus Retrieve err.Error() milvus retrieve embedding failed: doRequest: error sending request: Post \"https://generativelanguage.googleapis.com//v1beta/models/embedding-001:batchEmbedContents\": dial tcp 74.125.135.95:443: i/o timeout"}
{"level":"info","pid":3634,"method":"docStore.Retrieve","space_id":"","time":1749179376,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:755","message":"Starting retrieve operation"}
{"level":"info","pid":3634,"method":"docStore.Retrieve","space_id":"","documents":1,"time":1749179384,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:892","message":"Retrieve operation completed successfully"}
{"level":"info","pid":3634,"time":1749179384,"caller":"/Users/wade/project/wuban/agentchat/main.go:344","message":"promptInput.Graph : 知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n"}
{"level":"info","pid":3634,"method":"KnowledgeClient.QueryRewriteWithSummary","user_question":"What is the capital of UK?","assistant_answer":"The graph context indicates that the knowledge base does not have the answer to your question. I do not have the information to answer your question about the capital of the UK.\n","history_summary":"","time":1749179395,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:256","message":"Starting query rewrite with summary operation"}
{"level":"info","pid":3634,"method":"KnowledgeClient.QueryRewrite","message_count":2,"model":"lke-query-rewrite-base","time":1749179395,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:131","message":"Starting query rewrite operation"}
{"level":"error","pid":3634,"error":"[TencentCloudSDKError] Code=InvalidParameter, Message=20024-invalid params, RequestId=bca5a1f8-7d20-4313-a59a-66f8e25c2825","method":"KnowledgeClient.QueryRewrite","time":1749179400,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:206","message":"Tencent Cloud API error"}
{"level":"error","pid":3634,"time":1749179400,"caller":"/Users/wade/project/wuban/agentchat/main.go:364","message":"tencent cloud api error: [TencentCloudSDKError] Code=InvalidParameter, Message=20024-invalid params, RequestId=bca5a1f8-7d20-4313-a59a-66f8e25c2825"}
{"level":"info","pid":3634,"question":"What is the capital of UK?","context":"","graph":"知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n","last summary":"","answer":"The graph context indicates that the knowledge base does not have the answer to your question. I do not have the information to answer your question about the capital of the UK.\n","time":1749179400,"caller":"/Users/wade/project/wuban/agentchat/main.go:380","message":"Question and answer pair recorded"}
{"level":"info","pid":3634,"time":1749179415,"caller":"/Users/wade/project/wuban/agentchat/main.go:277","message":"input--------{\"content\":\"What is the capital of UK?\",\"model\":\"gpt-3.5-turbo\",\"apiKey\":\"sk-1234567890abcdef\",\"from\":\"Alice\",\"from_id\":\"user123\",\"to\":\"Bob\",\"to_id\":\"user456\"}"}
{"level":"info","pid":3634,"time":1749179415,"caller":"/Users/wade/project/wuban/agentchat/main.go:303","message":"qaAsJson--------{\"ID\":40,\"CreatedAt\":\"2025-06-06T03:09:06.196118Z\",\"FromID\":\"user123\",\"From\":\"Alice\",\"Question\":\"What is the capital of UK?\",\"Answer\":null,\"Summary\":null,\"To\":\"Bob\",\"ToID\":\"user456\"}"}
{"level":"info","pid":3634,"method":"docStore.Retrieve","collection":"chatRag1","time":1749179415,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:450","message":"Starting retrieve operation"}
{"level":"info","pid":3634,"method":"docStore.Retrieve","collection":"chatRag1","documents":2,"time":1749179417,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:640","message":"Retrieve operation completed successfully"}
{"level":"info","pid":3634,"time":1749179417,"caller":"/Users/wade/project/wuban/agentchat/main.go:329","message":"promptInput.Context: Paris is the capital of France?\nUSA is the largest importer of coffee?\n"}
{"level":"info","pid":3634,"method":"docStore.Retrieve","space_id":"","time":1749179417,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:755","message":"Starting retrieve operation"}
{"level":"info","pid":3634,"method":"docStore.Retrieve","space_id":"","documents":1,"time":1749179422,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:892","message":"Retrieve operation completed successfully"}
{"level":"info","pid":3634,"time":1749179422,"caller":"/Users/wade/project/wuban/agentchat/main.go:344","message":"promptInput.Graph : 知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n"}
{"level":"info","pid":3634,"method":"KnowledgeClient.QueryRewriteWithSummary","user_question":"What is the capital of UK?","assistant_answer":"The provided context doesn't contain the answer to your question. The capital of the UK is London.\n","history_summary":"","time":1749179423,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:256","message":"Starting query rewrite with summary operation"}
{"level":"info","pid":3634,"method":"KnowledgeClient.QueryRewrite","message_count":2,"model":"lke-query-rewrite-base","time":1749179423,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:131","message":"Starting query rewrite operation"}
{"level":"error","pid":3634,"error":"[TencentCloudSDKError] Code=InvalidParameter, Message=20024-invalid params, RequestId=5063326c-2f74-4f7d-9890-771587cde628","method":"KnowledgeClient.QueryRewrite","time":1749179423,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:206","message":"Tencent Cloud API error"}
{"level":"error","pid":3634,"time":1749179423,"caller":"/Users/wade/project/wuban/agentchat/main.go:364","message":"tencent cloud api error: [TencentCloudSDKError] Code=InvalidParameter, Message=20024-invalid params, RequestId=5063326c-2f74-4f7d-9890-771587cde628"}
{"level":"info","pid":3634,"question":"What is the capital of UK?","context":"Paris is the capital of France?\nUSA is the largest importer of coffee?\n","graph":"知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n","last summary":"","answer":"The provided context doesn't contain the answer to your question. The capital of the UK is London.\n","time":1749179423,"caller":"/Users/wade/project/wuban/agentchat/main.go:380","message":"Question and answer pair recorded"}
{"level":"info","pid":4733,"time":1749180547,"caller":"/Users/wade/project/wuban/agentchat/log.go:68","message":"This message appears when log level set to Debug or Info"}
{"level":"info","pid":4733,"method":"DeepSeek.Init","time":1749180547,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:91","message":"Initializing DeepSeek plugin"}
{"level":"info","pid":4733,"method":"DeepSeek.Init","time":1749180547,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:104","message":"Initialization successful"}
{"level":"info","pid":4733,"method":"Milvus.Init","time":1749180547,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:75","message":"Initializing Milvus plugin"}
{"level":"info","pid":4733,"method":"Milvus.Init","time":1749180548,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:87","message":"Initialization successful"}
{"level":"info","pid":4733,"method":"GraphKnowledge.Init","time":1749180548,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:299","message":"Initializing GraphKnowledge plugin"}
{"level":"info","pid":4733,"method":"NewClient","ip":"54.92.111.204","port":5670,"time":1749180548,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:93","message":"Creating new GraphRAG client"}
{"level":"info","pid":4733,"method":"GraphKnowledge.Init","time":1749180548,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:311","message":"Initialization successful"}
{"level":"info","pid":4733,"method":"DefineIndexerAndRetriever","collection":"chatRag1","dimension":768,"time":1749180548,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:152","message":"Defining indexer and retriever"}
{"level":"info","pid":4733,"method":"Milvus.newDocStore","collection":"chatRag1","dimension":768,"time":1749180548,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:201","message":"Creating new doc store"}
{"level":"info","pid":4733,"method":"Milvus.newDocStore","collection":"chatRag1","time":1749180549,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:302","message":"Doc store created successfully"}
{"level":"info","pid":4733,"method":"DefineIndexerAndRetriever","collection":"chatRag1","time":1749180549,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:182","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":4733,"method":"DefineIndexerAndRetriever","time":1749180549,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:357","message":"Defining indexer and retriever"}
{"level":"info","pid":4733,"method":"GraphKnowledge.newDocStore","space_id":"","model_name":"Qwen/Qwen2.5-Coder-32B-Instruct","time":1749180549,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:393","message":"Creating new doc store"}
{"level":"info","pid":4733,"method":"GraphKnowledge.newDocStore","time":1749180549,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:399","message":"Doc store created successfully"}
{"level":"info","pid":4733,"method":"DefineIndexerAndRetriever","time":1749180549,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:376","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":4733,"method":"NewKnowledgeClient","endpoint":"lkeap.tencentcloudapi.com","region":"ap-guangzhou","secret_id":"","token":"","time":1749180550,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:43","message":"Creating new KnowledgeClient"}
{"level":"info","pid":4733,"method":"KnowledgeClient.Init","time":1749180550,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:51","message":"Initializing KnowledgeClient"}
{"level":"info","pid":4733,"method":"KnowledgeClient.Init","time":1749180550,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:109","message":"Initialization successful"}
{"level":"info","pid":4733,"time":1749180550,"caller":"/Users/wade/project/wuban/agentchat/main.go:263","message":"KnowledgeClient initialized successfully"}
{"level":"info","pid":4733,"time":1749180554,"caller":"/Users/wade/project/wuban/agentchat/main.go:277","message":"input--------{\"content\":\"What is the capital of UK?\",\"model\":\"gpt-3.5-turbo\",\"apiKey\":\"sk-1234567890abcdef\",\"from\":\"Alice\",\"from_id\":\"user123\",\"to\":\"Bob\",\"to_id\":\"user456\"}"}
{"level":"info","pid":4733,"time":1749180554,"caller":"/Users/wade/project/wuban/agentchat/main.go:303","message":"qaAsJson--------{\"ID\":41,\"CreatedAt\":\"2025-06-06T03:10:15.510778Z\",\"FromID\":\"user123\",\"From\":\"Alice\",\"Question\":\"What is the capital of UK?\",\"Answer\":null,\"Summary\":null,\"To\":\"Bob\",\"ToID\":\"user456\"}"}
{"level":"info","pid":4733,"method":"docStore.Retrieve","collection":"chatRag1","time":1749180554,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:450","message":"Starting retrieve operation"}
{"level":"info","pid":4733,"method":"docStore.Retrieve","collection":"chatRag1","documents":2,"time":1749180556,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:640","message":"Retrieve operation completed successfully"}
{"level":"info","pid":4733,"time":1749180556,"caller":"/Users/wade/project/wuban/agentchat/main.go:329","message":"promptInput.Context: Paris is the capital of France?\nUSA is the largest importer of coffee?\n"}
{"level":"info","pid":4733,"method":"docStore.Retrieve","space_id":"","time":1749180556,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:755","message":"Starting retrieve operation"}
{"level":"info","pid":4733,"method":"docStore.Retrieve","space_id":"","documents":1,"time":1749180561,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:892","message":"Retrieve operation completed successfully"}
{"level":"info","pid":4733,"time":1749180561,"caller":"/Users/wade/project/wuban/agentchat/main.go:344","message":"promptInput.Graph : 知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n"}
{"level":"info","pid":4733,"method":"KnowledgeClient.QueryRewriteWithSummary","user_question":"What is the capital of UK?","assistant_answer":"I am sorry, but the provided context does not contain the answer to your question about the capital of the UK.\n","history_summary":"","time":1749180562,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:256","message":"Starting query rewrite with summary operation"}
{"level":"info","pid":4733,"method":"KnowledgeClient.QueryRewrite","message_count":2,"model":"lke-query-rewrite-base","time":1749180562,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:131","message":"Starting query rewrite operation"}
{"level":"error","pid":4733,"error":"[TencentCloudSDKError] Code=InvalidParameter, Message=20024-invalid params, RequestId=51b1a188-a8c9-4956-839a-bc3f83ace3e5","method":"KnowledgeClient.QueryRewrite","time":1749180563,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:206","message":"Tencent Cloud API error"}
{"level":"error","pid":4733,"time":1749180563,"caller":"/Users/wade/project/wuban/agentchat/main.go:367","message":"tencent cloud api error: [TencentCloudSDKError] Code=InvalidParameter, Message=20024-invalid params, RequestId=51b1a188-a8c9-4956-839a-bc3f83ace3e5"}
{"level":"info","pid":4733,"question":"What is the capital of UK?","context":"Paris is the capital of France?\nUSA is the largest importer of coffee?\n","graph":"知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n","last summary":"","answer":"I am sorry, but the provided context does not contain the answer to your question about the capital of the UK.\n","time":1749180563,"caller":"/Users/wade/project/wuban/agentchat/main.go:383","message":"Question and answer pair recorded"}
{"level":"info","pid":6105,"time":1749180845,"caller":"/Users/wade/project/wuban/agentchat/log.go:68","message":"This message appears when log level set to Debug or Info"}
{"level":"info","pid":6105,"method":"DeepSeek.Init","time":1749180845,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:91","message":"Initializing DeepSeek plugin"}
{"level":"info","pid":6105,"method":"DeepSeek.Init","time":1749180845,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:104","message":"Initialization successful"}
{"level":"info","pid":6105,"method":"Milvus.Init","time":1749180845,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:75","message":"Initializing Milvus plugin"}
{"level":"info","pid":6105,"method":"Milvus.Init","time":1749180846,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:87","message":"Initialization successful"}
{"level":"info","pid":6105,"method":"GraphKnowledge.Init","time":1749180846,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:299","message":"Initializing GraphKnowledge plugin"}
{"level":"info","pid":6105,"method":"NewClient","ip":"54.92.111.204","port":5670,"time":1749180846,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:93","message":"Creating new GraphRAG client"}
{"level":"info","pid":6105,"method":"GraphKnowledge.Init","time":1749180846,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:311","message":"Initialization successful"}
{"level":"info","pid":6105,"method":"DefineIndexerAndRetriever","collection":"chatRag1","dimension":768,"time":1749180846,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:152","message":"Defining indexer and retriever"}
{"level":"info","pid":6105,"method":"Milvus.newDocStore","collection":"chatRag1","dimension":768,"time":1749180846,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:201","message":"Creating new doc store"}
{"level":"info","pid":6105,"method":"Milvus.newDocStore","collection":"chatRag1","time":1749180848,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:302","message":"Doc store created successfully"}
{"level":"info","pid":6105,"method":"DefineIndexerAndRetriever","collection":"chatRag1","time":1749180848,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:182","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":6105,"method":"DefineIndexerAndRetriever","time":1749180848,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:357","message":"Defining indexer and retriever"}
{"level":"info","pid":6105,"method":"GraphKnowledge.newDocStore","space_id":"","model_name":"Qwen/Qwen2.5-Coder-32B-Instruct","time":1749180848,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:393","message":"Creating new doc store"}
{"level":"info","pid":6105,"method":"GraphKnowledge.newDocStore","time":1749180848,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:399","message":"Doc store created successfully"}
{"level":"info","pid":6105,"method":"DefineIndexerAndRetriever","time":1749180848,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:376","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":6105,"method":"NewKnowledgeClient","endpoint":"lkeap.tencentcloudapi.com","region":"ap-guangzhou","secret_id":"","token":"","time":1749180849,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:43","message":"Creating new KnowledgeClient"}
{"level":"info","pid":6105,"method":"KnowledgeClient.Init","time":1749180849,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:51","message":"Initializing KnowledgeClient"}
{"level":"info","pid":6105,"method":"KnowledgeClient.Init","time":1749180849,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:109","message":"Initialization successful"}
{"level":"info","pid":6105,"time":1749180849,"caller":"/Users/wade/project/wuban/agentchat/main.go:263","message":"KnowledgeClient initialized successfully"}
{"level":"info","pid":6105,"time":1749180858,"caller":"/Users/wade/project/wuban/agentchat/main.go:277","message":"input--------{\"content\":\"What is the capital of UK?\",\"model\":\"gpt-3.5-turbo\",\"apiKey\":\"sk-1234567890abcdef\",\"from\":\"Alice\",\"from_id\":\"user123\",\"to\":\"Bob\",\"to_id\":\"user456\"}"}
{"level":"info","pid":6105,"time":1749180859,"caller":"/Users/wade/project/wuban/agentchat/main.go:303","message":"qaAsJson--------{\"ID\":42,\"CreatedAt\":\"2025-06-06T03:29:14.509175Z\",\"FromID\":\"user123\",\"From\":\"Alice\",\"Question\":\"What is the capital of UK?\",\"Answer\":null,\"Summary\":null,\"To\":\"Bob\",\"ToID\":\"user456\"}"}
{"level":"info","pid":6105,"method":"docStore.Retrieve","collection":"chatRag1","time":1749180859,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:450","message":"Starting retrieve operation"}
{"level":"info","pid":6105,"method":"docStore.Retrieve","collection":"chatRag1","documents":2,"time":1749180861,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:640","message":"Retrieve operation completed successfully"}
{"level":"info","pid":6105,"time":1749180861,"caller":"/Users/wade/project/wuban/agentchat/main.go:329","message":"promptInput.Context: Paris is the capital of France?\nUSA is the largest importer of coffee?\n"}
{"level":"info","pid":6105,"method":"docStore.Retrieve","space_id":"","time":1749180861,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:755","message":"Starting retrieve operation"}
{"level":"info","pid":6105,"method":"docStore.Retrieve","space_id":"","documents":1,"time":1749180867,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:892","message":"Retrieve operation completed successfully"}
{"level":"info","pid":6105,"time":1749180867,"caller":"/Users/wade/project/wuban/agentchat/main.go:344","message":"promptInput.Graph : 知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n"}
{"level":"info","pid":6105,"method":"KnowledgeClient.QueryRewriteWithSummary","user_question":"What is the capital of UK?","assistant_answer":"The available knowledge base does not contain information about the capital of the UK.\n","history_summary":"The available knowledge base does not contain information about the capital of the UK.\n","time":1749180868,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:256","message":"Starting query rewrite with summary operation"}
{"level":"info","pid":6105,"method":"KnowledgeClient.QueryRewrite","message_count":3,"model":"lke-query-rewrite-base","time":1749180868,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:131","message":"Starting query rewrite operation"}
{"level":"info","pid":6105,"method":"KnowledgeClient.QueryRewrite","rewritten_query":"Conversation summary: The available knowledge base does not contain information about the capital of the UK.","request_id":"15f1ce0c-a83f-4d95-af22-33a3bd829e8d","usage":{"InputTokens":74,"OutputTokens":19,"TotalTokens":93},"raw_response":"{\"Response\":{\"Content\":\"Conversation summary: The available knowledge base does not contain information about the capital of the UK.\",\"Usage\":{\"InputTokens\":74,\"OutputTokens\":19,\"TotalTokens\":93},\"RequestId\":\"15f1ce0c-a83f-4d95-af22-33a3bd829e8d\"}}","time":1749180870,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:237","message":"Query rewrite operation completed successfully"}
{"level":"info","pid":6105,"question":"What is the capital of UK?","context":"Paris is the capital of France?\nUSA is the largest importer of coffee?\n","graph":"知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n","last summary":"The available knowledge base does not contain information about the capital of the UK.\n","answer":"The available knowledge base does not contain information about the capital of the UK.\n","time":1749180870,"caller":"/Users/wade/project/wuban/agentchat/main.go:387","message":"Question and answer pair recorded"}
{"level":"info","pid":6250,"time":1749181044,"caller":"/Users/wade/project/wuban/agentchat/log.go:68","message":"This message appears when log level set to Debug or Info"}
{"level":"info","pid":6250,"method":"DeepSeek.Init","time":1749181044,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:91","message":"Initializing DeepSeek plugin"}
{"level":"info","pid":6250,"method":"DeepSeek.Init","time":1749181044,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:104","message":"Initialization successful"}
{"level":"info","pid":6250,"method":"Milvus.Init","time":1749181044,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:75","message":"Initializing Milvus plugin"}
{"level":"info","pid":6250,"method":"Milvus.Init","time":1749181045,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:87","message":"Initialization successful"}
{"level":"info","pid":6250,"method":"GraphKnowledge.Init","time":1749181045,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:299","message":"Initializing GraphKnowledge plugin"}
{"level":"info","pid":6250,"method":"NewClient","ip":"54.92.111.204","port":5670,"time":1749181045,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:93","message":"Creating new GraphRAG client"}
{"level":"info","pid":6250,"method":"GraphKnowledge.Init","time":1749181045,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:311","message":"Initialization successful"}
{"level":"info","pid":6250,"method":"DefineIndexerAndRetriever","collection":"chatRag1","dimension":768,"time":1749181045,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:152","message":"Defining indexer and retriever"}
{"level":"info","pid":6250,"method":"Milvus.newDocStore","collection":"chatRag1","dimension":768,"time":1749181045,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:201","message":"Creating new doc store"}
{"level":"info","pid":6250,"method":"Milvus.newDocStore","collection":"chatRag1","time":1749181046,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:302","message":"Doc store created successfully"}
{"level":"info","pid":6250,"method":"DefineIndexerAndRetriever","collection":"chatRag1","time":1749181046,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:182","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":6250,"method":"DefineIndexerAndRetriever","time":1749181046,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:357","message":"Defining indexer and retriever"}
{"level":"info","pid":6250,"method":"GraphKnowledge.newDocStore","space_id":"","model_name":"Qwen/Qwen2.5-Coder-32B-Instruct","time":1749181046,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:393","message":"Creating new doc store"}
{"level":"info","pid":6250,"method":"GraphKnowledge.newDocStore","time":1749181046,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:399","message":"Doc store created successfully"}
{"level":"info","pid":6250,"method":"DefineIndexerAndRetriever","time":1749181046,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:376","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":6250,"method":"NewKnowledgeClient","endpoint":"lkeap.tencentcloudapi.com","region":"ap-guangzhou","secret_id":"","token":"","time":1749181047,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:43","message":"Creating new KnowledgeClient"}
{"level":"info","pid":6250,"method":"KnowledgeClient.Init","time":1749181047,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:51","message":"Initializing KnowledgeClient"}
{"level":"info","pid":6250,"method":"KnowledgeClient.Init","time":1749181047,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:109","message":"Initialization successful"}
{"level":"info","pid":6250,"time":1749181047,"caller":"/Users/wade/project/wuban/agentchat/main.go:263","message":"KnowledgeClient initialized successfully"}
{"level":"info","pid":6250,"time":1749181055,"caller":"/Users/wade/project/wuban/agentchat/main.go:277","message":"input--------{\"content\":\"What is the capital of UK?\",\"model\":\"gpt-3.5-turbo\",\"apiKey\":\"sk-1234567890abcdef\",\"from\":\"Alice\",\"from_id\":\"user123\",\"to\":\"Bob\",\"to_id\":\"user456\"}"}
{"level":"info","pid":6250,"time":1749181055,"caller":"/Users/wade/project/wuban/agentchat/main.go:303","message":"qaAsJson--------{\"ID\":43,\"CreatedAt\":\"2025-06-06T03:34:19.041174Z\",\"FromID\":\"user123\",\"From\":\"Alice\",\"Question\":\"What is the capital of UK?\",\"Answer\":\"The available knowledge base does not contain information about the capital of the UK.\\n\",\"Summary\":\"\",\"To\":\"Bob\",\"ToID\":\"user456\"}"}
{"level":"info","pid":6250,"method":"docStore.Retrieve","collection":"chatRag1","time":1749181055,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:450","message":"Starting retrieve operation"}
{"level":"info","pid":6250,"method":"docStore.Retrieve","collection":"chatRag1","documents":2,"time":1749181058,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:640","message":"Retrieve operation completed successfully"}
{"level":"info","pid":6250,"time":1749181058,"caller":"/Users/wade/project/wuban/agentchat/main.go:329","message":"promptInput.Context: Paris is the capital of France?\nUSA is the largest importer of coffee?\n"}
{"level":"info","pid":6250,"method":"docStore.Retrieve","space_id":"","time":1749181058,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:755","message":"Starting retrieve operation"}
{"level":"info","pid":6250,"method":"docStore.Retrieve","space_id":"","documents":1,"time":1749181065,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:892","message":"Retrieve operation completed successfully"}
{"level":"info","pid":6250,"time":1749181065,"caller":"/Users/wade/project/wuban/agentchat/main.go:344","message":"promptInput.Graph : 知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n"}
{"level":"info","pid":6250,"method":"KnowledgeClient.QueryRewriteWithSummary","user_question":"What is the capital of UK?","assistant_answer":"The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.\n","history_summary":"The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.\n","time":1749181066,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:256","message":"Starting query rewrite with summary operation"}
{"level":"info","pid":6250,"method":"KnowledgeClient.QueryRewrite","message_count":3,"model":"lke-query-rewrite-base","time":1749181066,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:131","message":"Starting query rewrite operation"}
{"level":"info","pid":6250,"method":"KnowledgeClient.QueryRewrite","rewritten_query":"Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.","request_id":"e8ef75a3-6f35-4766-8eea-b8a7d3506c84","usage":{"InputTokens":154,"OutputTokens":59,"TotalTokens":213},"raw_response":"{\"Response\":{\"Content\":\"Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.\",\"Usage\":{\"InputTokens\":154,\"OutputTokens\":59,\"TotalTokens\":213},\"RequestId\":\"e8ef75a3-6f35-4766-8eea-b8a7d3506c84\"}}","time":1749181069,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:237","message":"Query rewrite operation completed successfully"}
{"level":"info","pid":6250,"question":"What is the capital of UK?","context":"Paris is the capital of France?\nUSA is the largest importer of coffee?\n","graph":"知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n","last summary":"The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.\n","answer":"The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.\n","time":1749181069,"caller":"/Users/wade/project/wuban/agentchat/main.go:398","message":"Question and answer pair recorded"}
{"level":"info","pid":6250,"time":1749181117,"caller":"/Users/wade/project/wuban/agentchat/main.go:277","message":"input--------{\"content\":\"你是谁?\",\"model\":\"gpt-3.5-turbo\",\"apiKey\":\"sk-1234567890abcdef\",\"from\":\"Alice\",\"from_id\":\"user123\",\"to\":\"Bob\",\"to_id\":\"user456\"}"}
{"level":"info","pid":6250,"time":1749181118,"caller":"/Users/wade/project/wuban/agentchat/main.go:303","message":"qaAsJson--------{\"ID\":44,\"CreatedAt\":\"2025-06-06T03:37:35.757771Z\",\"FromID\":\"user123\",\"From\":\"Alice\",\"Question\":\"What is the capital of UK?\",\"Answer\":\"The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.\\n\",\"Summary\":\"Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.\",\"To\":\"Bob\",\"ToID\":\"user456\"}"}
{"level":"info","pid":6250,"method":"docStore.Retrieve","collection":"chatRag1","time":1749181118,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:450","message":"Starting retrieve operation"}
{"level":"info","pid":6250,"method":"docStore.Retrieve","collection":"chatRag1","documents":2,"time":1749181118,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:640","message":"Retrieve operation completed successfully"}
{"level":"info","pid":6250,"time":1749181118,"caller":"/Users/wade/project/wuban/agentchat/main.go:329","message":"promptInput.Context: Paris is the capital of France?\nUSA is the largest importer of coffee?\n"}
{"level":"info","pid":6250,"method":"docStore.Retrieve","space_id":"","time":1749181118,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:755","message":"Starting retrieve operation"}
{"level":"info","pid":6250,"method":"docStore.Retrieve","space_id":"","documents":1,"time":1749181123,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:892","message":"Retrieve operation completed successfully"}
{"level":"info","pid":6250,"time":1749181123,"caller":"/Users/wade/project/wuban/agentchat/main.go:344","message":"promptInput.Graph : 知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n"}
{"level":"info","pid":6250,"method":"KnowledgeClient.QueryRewriteWithSummary","user_question":"你是谁?","assistant_answer":"我是一个基于Milvus的AI助手。知识库中提供的内容不足以回答你是谁这个问题。\n","history_summary":"Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.","time":1749181124,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:256","message":"Starting query rewrite with summary operation"}
{"level":"info","pid":6250,"method":"KnowledgeClient.QueryRewrite","message_count":3,"model":"lke-query-rewrite-base","time":1749181124,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:131","message":"Starting query rewrite operation"}
{"level":"info","pid":6250,"method":"KnowledgeClient.QueryRewrite","rewritten_query":"Conversation summary: Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.","request_id":"6b4d918c-5e65-4fd8-b1b3-52b961b8170a","usage":{"InputTokens":119,"OutputTokens":62,"TotalTokens":181},"raw_response":"{\"Response\":{\"Content\":\"Conversation summary: Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.\",\"Usage\":{\"InputTokens\":119,\"OutputTokens\":62,\"TotalTokens\":181},\"RequestId\":\"6b4d918c-5e65-4fd8-b1b3-52b961b8170a\"}}","time":1749181126,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:237","message":"Query rewrite operation completed successfully"}
{"level":"info","pid":6250,"question":"你是谁?","context":"Paris is the capital of France?\nUSA is the largest importer of coffee?\n","graph":"知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n","last summary":"Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.","answer":"我是一个基于Milvus的AI助手。知识库中提供的内容不足以回答你是谁这个问题。\n","time":1749181126,"caller":"/Users/wade/project/wuban/agentchat/main.go:398","message":"Question and answer pair recorded"}
{"level":"info","pid":6592,"time":1749181440,"caller":"/Users/wade/project/wuban/agentchat/log.go:68","message":"This message appears when log level set to Debug or Info"}
{"level":"info","pid":6592,"method":"DeepSeek.Init","time":1749181440,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:91","message":"Initializing DeepSeek plugin"}
{"level":"info","pid":6592,"method":"DeepSeek.Init","time":1749181440,"caller":"/Users/wade/project/wuban/agentchat/plugins/deepseek/deepseek.go:104","message":"Initialization successful"}
{"level":"info","pid":6592,"method":"Milvus.Init","time":1749181440,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:75","message":"Initializing Milvus plugin"}
{"level":"info","pid":6592,"method":"Milvus.Init","time":1749181441,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:87","message":"Initialization successful"}
{"level":"info","pid":6592,"method":"GraphKnowledge.Init","time":1749181441,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:299","message":"Initializing GraphKnowledge plugin"}
{"level":"info","pid":6592,"method":"NewClient","ip":"54.92.111.204","port":5670,"time":1749181441,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:93","message":"Creating new GraphRAG client"}
{"level":"info","pid":6592,"method":"GraphKnowledge.Init","time":1749181441,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:311","message":"Initialization successful"}
{"level":"info","pid":6592,"method":"DefineIndexerAndRetriever","collection":"chatRag1","dimension":768,"time":1749181441,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:152","message":"Defining indexer and retriever"}
{"level":"info","pid":6592,"method":"Milvus.newDocStore","collection":"chatRag1","dimension":768,"time":1749181441,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:201","message":"Creating new doc store"}
{"level":"info","pid":6592,"method":"Milvus.newDocStore","collection":"chatRag1","time":1749181442,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:302","message":"Doc store created successfully"}
{"level":"info","pid":6592,"method":"DefineIndexerAndRetriever","collection":"chatRag1","time":1749181442,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:182","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":6592,"method":"DefineIndexerAndRetriever","time":1749181442,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:357","message":"Defining indexer and retriever"}
{"level":"info","pid":6592,"method":"GraphKnowledge.newDocStore","space_id":"","model_name":"Qwen/Qwen2.5-Coder-32B-Instruct","time":1749181442,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:393","message":"Creating new doc store"}
{"level":"info","pid":6592,"method":"GraphKnowledge.newDocStore","time":1749181442,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:399","message":"Doc store created successfully"}
{"level":"info","pid":6592,"method":"DefineIndexerAndRetriever","time":1749181442,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:376","message":"Indexer and retriever defined successfully"}
{"level":"info","pid":6592,"method":"NewKnowledgeClient","endpoint":"lkeap.tencentcloudapi.com","region":"ap-guangzhou","secret_id":"","token":"","time":1749181443,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:43","message":"Creating new KnowledgeClient"}
{"level":"info","pid":6592,"method":"KnowledgeClient.Init","time":1749181443,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:51","message":"Initializing KnowledgeClient"}
{"level":"info","pid":6592,"method":"KnowledgeClient.Init","time":1749181443,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:109","message":"Initialization successful"}
{"level":"info","pid":6592,"time":1749181443,"caller":"/Users/wade/project/wuban/agentchat/main.go:263","message":"KnowledgeClient initialized successfully"}
{"level":"info","pid":6592,"time":1749181451,"caller":"/Users/wade/project/wuban/agentchat/main.go:277","message":"input--------{\"content\":\"你是谁?\",\"model\":\"gpt-3.5-turbo\",\"apiKey\":\"sk-1234567890abcdef\",\"from\":\"Alice\",\"from_id\":\"user123\",\"to\":\"Bob\",\"to_id\":\"user456\"}"}
{"level":"info","pid":6592,"time":1749181451,"caller":"/Users/wade/project/wuban/agentchat/main.go:303","message":"qaAsJson--------{\"ID\":45,\"CreatedAt\":\"2025-06-06T03:38:38.177331Z\",\"FromID\":\"user123\",\"From\":\"Alice\",\"Question\":\"你是谁?\",\"Answer\":\"我是一个基于Milvus的AI助手。知识库中提供的内容不足以回答你是谁这个问题。\\n\",\"Summary\":\"Conversation summary: Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.\",\"To\":\"Bob\",\"ToID\":\"user456\"}"}
{"level":"info","pid":6592,"method":"docStore.Retrieve","collection":"chatRag1","time":1749181451,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:450","message":"Starting retrieve operation"}
{"level":"info","pid":6592,"method":"docStore.Retrieve","collection":"chatRag1","documents":2,"time":1749181454,"caller":"/Users/wade/project/wuban/agentchat/plugins/milvus/milvus.go:640","message":"Retrieve operation completed successfully"}
{"level":"info","pid":6592,"time":1749181454,"caller":"/Users/wade/project/wuban/agentchat/main.go:329","message":"promptInput.Context: Paris is the capital of France?\nUSA is the largest importer of coffee?\n"}
{"level":"info","pid":6592,"method":"docStore.Retrieve","space_id":"","time":1749181454,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:755","message":"Starting retrieve operation"}
{"level":"info","pid":6592,"method":"docStore.Retrieve","space_id":"","documents":1,"time":1749181460,"caller":"/Users/wade/project/wuban/agentchat/plugins/graphrag/graph.go:892","message":"Retrieve operation completed successfully"}
{"level":"info","pid":6592,"time":1749181460,"caller":"/Users/wade/project/wuban/agentchat/main.go:344","message":"promptInput.Graph : 知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n"}
{"level":"info","pid":6592,"from":"Alice","from_id":"user123","to":"Bob","to_id":"user456","promptInput.Query":"你是谁?","resp.Text()":"我是一个基于Milvus的助手,但我能提供的信息有限。目前我只知道巴黎是法国的首都,美国是最大的咖啡进口国。知识库的信息不足以回答“你是谁”这个问题。\n","promptInput.Summary":"Conversation summary: Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.","time":1749181461,"caller":"/Users/wade/project/wuban/agentchat/main.go:371","message":"QueryRewriteWithSummary"}
{"level":"info","pid":6592,"method":"KnowledgeClient.QueryRewriteWithSummary","user_question":"你是谁?","assistant_answer":"我是一个基于Milvus的助手,但我能提供的信息有限。目前我只知道巴黎是法国的首都,美国是最大的咖啡进口国。知识库的信息不足以回答“你是谁”这个问题。\n","history_summary":"Conversation summary: Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.","time":1749181461,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:256","message":"Starting query rewrite with summary operation"}
{"level":"info","pid":6592,"method":"KnowledgeClient.QueryRewrite","message_count":3,"model":"lke-query-rewrite-base","time":1749181461,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:131","message":"Starting query rewrite operation"}
{"level":"info","pid":6592,"method":"KnowledgeClient.QueryRewrite","rewritten_query":"Conversation summary: Conversation summary: Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.","request_id":"01a825ea-5033-4bba-b62b-8597c780167d","usage":{"InputTokens":143,"OutputTokens":65,"TotalTokens":208},"raw_response":"{\"Response\":{\"Content\":\"Conversation summary: Conversation summary: Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.\",\"Usage\":{\"InputTokens\":143,\"OutputTokens\":65,\"TotalTokens\":208},\"RequestId\":\"01a825ea-5033-4bba-b62b-8597c780167d\"}}","time":1749181464,"caller":"/Users/wade/project/wuban/agentchat/plugins/knowledge/knowledge.go:237","message":"Query rewrite operation completed successfully"}
{"level":"info","pid":6592,"from":"Alice","from_id":"user123","to":"Bob","to_id":"user456","question":"你是谁?","context":"Paris is the capital of France?\nUSA is the largest importer of coffee?\n","graph":"知识库中提供的内容不足以回答此问题\n\n<references title=\"References\" references=\"[]\" />\n","last summary":"Conversation summary: Conversation summary: The provided context does not contain the answer to your question. The context mentions Paris is the capital of France and the USA is the largest importer of coffee. The graph context also indicates a lack of information. Therefore, I cannot answer your question about the capital of the UK.","answer":"我是一个基于Milvus的助手,但我能提供的信息有限。目前我只知道巴黎是法国的首都,美国是最大的咖啡进口国。知识库的信息不足以回答“你是谁”这个问题。\n","time":1749181464,"caller":"/Users/wade/project/wuban/agentchat/main.go:397","message":"Question and answer pair recorded"}
...@@ -23,7 +23,6 @@ func getPackageName() string { ...@@ -23,7 +23,6 @@ func getPackageName() string {
return pkg return pkg
} }
func loggingInit() { func loggingInit() {
// debug := flag.Bool("debug", false, "sets log level to debug") // debug := flag.Bool("debug", false, "sets log level to debug")
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"net/http" "net/http"
"os"
"strings" "strings"
"time" "time"
...@@ -14,6 +15,7 @@ import ( ...@@ -14,6 +15,7 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/wade-liwei/agentchat/plugins/deepseek" "github.com/wade-liwei/agentchat/plugins/deepseek"
"github.com/wade-liwei/agentchat/plugins/graphrag" "github.com/wade-liwei/agentchat/plugins/graphrag"
"github.com/wade-liwei/agentchat/plugins/knowledge" // Import knowledge package
"github.com/wade-liwei/agentchat/plugins/milvus" "github.com/wade-liwei/agentchat/plugins/milvus"
"github.com/firebase/genkit/go/plugins/evaluators" "github.com/firebase/genkit/go/plugins/evaluators"
...@@ -76,7 +78,6 @@ type simpleQaPromptInput struct { ...@@ -76,7 +78,6 @@ type simpleQaPromptInput struct {
// Please provide a response that aligns with the given personality while leveraging the provided context, graph, and conversation summary. // Please provide a response that aligns with the given personality while leveraging the provided context, graph, and conversation summary.
// ` // `
const simpleQaPromptTemplate = ` const simpleQaPromptTemplate = `
You're a helpful agent that answers the user's questions based on the provided context. You're a helpful agent that answers the user's questions based on the provided context.
...@@ -94,18 +95,16 @@ Instructions: ...@@ -94,18 +95,16 @@ Instructions:
- Ensure responses leverage the Previous conversation summary when relevant. - Ensure responses leverage the Previous conversation summary when relevant.
` `
func main() { func main() {
// Define command-line flags with hardcoded values as defaults
// Define command-line flags with hardcoded values as defaults deepseekAPIKey := flag.String("deepseek-api-key", "sk-9f70df871a7c4b8aa566a3c7a0603706", "DeepSeek API key")
deepseekAPIKey := flag.String("deepseek-api-key", "sk-9f70df871a7c4b8aa566a3c7a0603706", "DeepSeek API key") milvusAddr := flag.String("milvus-addr", "54.92.111.204:19530", "Milvus server address")
milvusAddr := flag.String("milvus-addr", "54.92.111.204:19530", "Milvus server address") graphragAddr := flag.String("graphrag-addr", "54.92.111.204:5670", "GraphRAG server address")
graphragAddr := flag.String("graphrag-addr", "54.92.111.204:5670", "GraphRAG server address") googleAIApiKey := flag.String("googleai-api-key", "AIzaSyCoYBOmnwRWlH_-nT25lpn8pMg3T18Q0uI", "Google AI API key")
googleAIApiKey := flag.String("googleai-api-key", "AIzaSyCoYBOmnwRWlH_-nT25lpn8pMg3T18Q0uI", "Google AI API key")
pgConnString := flag.String("pg-conn-string", "postgresql://postgres.awcfgdodiuqnlsobcivq:P99IU9NEoDRPsBfb@aws-0-ap-southeast-1.pooler.supabase.com:5432/postgres", "PostgreSQL connection string") pgConnString := flag.String("pg-conn-string", "postgresql://postgres.awcfgdodiuqnlsobcivq:P99IU9NEoDRPsBfb@aws-0-ap-southeast-1.pooler.supabase.com:5432/postgres", "PostgreSQL connection string")
debug := flag.Bool("debug", false, "sets log level to debug") debug := flag.Bool("debug", false, "sets log level to debug")
flag.Parse() flag.Parse()
loggingInit() loggingInit()
...@@ -113,6 +112,9 @@ func main() { ...@@ -113,6 +112,9 @@ func main() {
zerolog.SetGlobalLevel(zerolog.DebugLevel) zerolog.SetGlobalLevel(zerolog.DebugLevel)
} }
os.Setenv("TENCENTCLOUD_SECRET_ID", "AKID64oLfmfLtESUJ6i8LPSM4gCVbiniQuBF")
os.Setenv("TENCENTCLOUD_SECRET_KEY", "rX2JMBnBMJ2YqulOo37xa5OUMSN4Xnpd")
ctx := context.Background() ctx := context.Background()
metrics := []evaluators.MetricConfig{ metrics := []evaluators.MetricConfig{
{ {
...@@ -126,14 +128,14 @@ func main() { ...@@ -126,14 +128,14 @@ func main() {
}, },
} }
// Initialize genkit with plugins using flag/env values // Initialize genkit with plugins using flag/env values
g, err := genkit.Init(ctx, genkit.WithPlugins( g, err := genkit.Init(ctx, genkit.WithPlugins(
&deepseek.DeepSeek{APIKey: *deepseekAPIKey}, &deepseek.DeepSeek{APIKey: *deepseekAPIKey},
&milvus.Milvus{Addr: *milvusAddr}, &milvus.Milvus{Addr: *milvusAddr},
&graphrag.GraphKnowledge{Addr: *graphragAddr}, &graphrag.GraphKnowledge{Addr: *graphragAddr},
&googlegenai.GoogleAI{APIKey: *googleAIApiKey}, &googlegenai.GoogleAI{APIKey: *googleAIApiKey},
&evaluators.GenkitEval{Metrics: metrics}, &evaluators.GenkitEval{Metrics: metrics},
)) ))
if err != nil { if err != nil {
log.Fatal().Msg(err.Error()) log.Fatal().Msg(err.Error())
...@@ -250,6 +252,16 @@ func main() { ...@@ -250,6 +252,16 @@ func main() {
log.Fatal().Msgf("InitQAStore failed: %v", err) log.Fatal().Msgf("InitQAStore failed: %v", err)
} }
// Initialize KnowledgeClient with test parameters
kc := knowledge.NewKnowledgeClient(knowledge.ClientConfig{
Endpoint: "lkeap.tencentcloudapi.com",
Region: "ap-guangzhou",
})
if err := kc.Init(ctx); err != nil {
log.Fatal().Msgf("Failed to initialize KnowledgeClient: %v", err)
}
log.Info().Msg("KnowledgeClient initialized successfully")
// Define a simple flow that generates jokes about a given topic // Define a simple flow that generates jokes about a given topic
genkit.DefineFlow(g, "chat", func(ctx context.Context, input *ChatInput) (Response, error) { genkit.DefineFlow(g, "chat", func(ctx context.Context, input *ChatInput) (Response, error) {
...@@ -319,7 +331,6 @@ func main() { ...@@ -319,7 +331,6 @@ func main() {
begin := time.Now() begin := time.Now()
graphResponse, err := ai.Retrieve(ctx, graphRetriever, ai.WithDocs(dRequest)) graphResponse, err := ai.Retrieve(ctx, graphRetriever, ai.WithDocs(dRequest))
if err != nil { if err != nil {
log.Error().Msgf("graph Retrieve err.Error() %s", err.Error()) log.Error().Msgf("graph Retrieve err.Error() %s", err.Error())
...@@ -333,10 +344,7 @@ func main() { ...@@ -333,10 +344,7 @@ func main() {
log.Info().Msgf("promptInput.Graph : %s", promptInput.Graph) log.Info().Msgf("promptInput.Graph : %s", promptInput.Graph)
} }
fmt.Println("graph time", time.Since(begin).Seconds())
fmt.Println("graph time",time.Since(begin).Seconds())
resp, err := simpleQaPrompt.Execute(ctx, ai.WithInput(promptInput)) resp, err := simpleQaPrompt.Execute(ctx, ai.WithInput(promptInput))
...@@ -347,18 +355,46 @@ func main() { ...@@ -347,18 +355,46 @@ func main() {
}, nil }, nil
} }
if lastok {
if promptInput.Summary == ""{
promptInput.Summary = resp.Text()
}
qa.UpdateQAFields(context.Background(), idx, "", resp.Text()) log.Info().
Str("from",input.From).
Str("from_id",input.FromID).
Str("to",input.To).
Str("to_id",input.ToID).
Str("promptInput.Query",promptInput.Query).
Str("resp.Text()",resp.Text()).
Str("promptInput.Summary",promptInput.Summary).Msg("QueryRewriteWithSummary")
res, err := kc.QueryRewriteWithSummary(context.Background(), promptInput.Query, resp.Text(), promptInput.Summary)
if err != nil {
log.Error().Msg(err.Error())
} else {
qa.UpdateQAFields(context.Background(), idx, res.RewrittenQuery, resp.Text())
/*
{"RewrittenQuery":"Conversation summary: The available knowledge base does not contain information about the capital of the UK.","RawResponse":{"Response":{"Content":"Conversation summary: The available knowledge base does not contain information about the capital of the UK.","Usage":{"InputTokens":74,"OutputTokens":19,"TotalTokens":93},"RequestId":"15f1ce0c-a83f-4d95-af22-33a3bd829e8d"}}}
*/
}
} else {
qa.UpdateQAFields(context.Background(), idx, "", resp.Text())
}
log.Info(). log.Info().
Str("question",promptInput.Query). Str("from",input.From).
Str("context",promptInput.Context). Str("from_id",input.FromID).
Str("graph",promptInput.Graph). Str("to",input.To).
Str("last summary",promptInput.Summary). Str("to_id",input.ToID).
Str("answer", resp.Text()). Str("question", promptInput.Query).
Msg("Question and answer pair recorded") Str("context", promptInput.Context).
Str("graph", promptInput.Graph).
Str("last summary", promptInput.Summary).
Str("answer", resp.Text()).
Msg("Question and answer pair recorded")
return Response{ return Response{
Data: resp.Text(), Data: resp.Text(),
...@@ -392,7 +428,6 @@ func main() { ...@@ -392,7 +428,6 @@ func main() {
} }
} }
type Response struct { type Response struct {
Data string `json:"data"` Data string `json:"data"`
Code int `json:"code"` Code int `json:"code"`
......
...@@ -16,231 +16,210 @@ import ( ...@@ -16,231 +16,210 @@ import (
const provider = "deepseek" const provider = "deepseek"
var ( var (
mediaSupportedModels = []string{deepseek.DeepSeekChat, deepseek.DeepSeekCoder, deepseek.DeepSeekReasoner} mediaSupportedModels = []string{deepseek.DeepSeekChat, deepseek.DeepSeekCoder, deepseek.DeepSeekReasoner}
roleMapping = map[ai.Role]string{ roleMapping = map[ai.Role]string{
ai.RoleUser: deepseek.ChatMessageRoleUser, ai.RoleUser: deepseek.ChatMessageRoleUser,
ai.RoleModel: deepseek.ChatMessageRoleAssistant, ai.RoleModel: deepseek.ChatMessageRoleAssistant,
ai.RoleSystem: deepseek.ChatMessageRoleSystem, ai.RoleSystem: deepseek.ChatMessageRoleSystem,
ai.RoleTool: deepseek.ChatMessageRoleTool, ai.RoleTool: deepseek.ChatMessageRoleTool,
} }
) )
// DeepSeek holds configuration for the plugin. // DeepSeek holds configuration for the plugin.
type DeepSeek struct { type DeepSeek struct {
APIKey string // DeepSeek API key APIKey string // DeepSeek API key
mu sync.Mutex // Mutex to control access. mu sync.Mutex // Mutex to control access.
initted bool // Whether the plugin has been initialized. initted bool // Whether the plugin has been initialized.
} }
// Name returns the provider name. // Name returns the provider name.
func (d DeepSeek) Name() string { func (d DeepSeek) Name() string {
return provider return provider
} }
// ModelDefinition represents a model with its name and type. // ModelDefinition represents a model with its name and type.
type ModelDefinition struct { type ModelDefinition struct {
Name string Name string
Type string Type string
} }
// DefineModel defines a DeepSeek model in Genkit. // DefineModel defines a DeepSeek model in Genkit.
func (d *DeepSeek) DefineModel(g *genkit.Genkit, model ModelDefinition, info *ai.ModelInfo) ai.Model { func (d *DeepSeek) DefineModel(g *genkit.Genkit, model ModelDefinition, info *ai.ModelInfo) ai.Model {
log.Info(). log.Info().
Str("method", "DeepSeek.DefineModel"). Str("method", "DeepSeek.DefineModel").
Str("model_name", model.Name). Str("model_name", model.Name).
Msg("Defining DeepSeek model") Msg("Defining DeepSeek model")
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock() defer d.mu.Unlock()
if !d.initted { if !d.initted {
log.Error().Str("method", "DeepSeek.DefineModel").Msg("DeepSeek not initialized") log.Error().Str("method", "DeepSeek.DefineModel").Msg("DeepSeek not initialized")
panic("deepseek.Init not called") panic("deepseek.Init not called")
} }
// Define model info, supporting multiturn and system role. // Define model info, supporting multiturn and system role.
mi := ai.ModelInfo{ mi := ai.ModelInfo{
Label: model.Name, Label: model.Name,
Supports: &ai.ModelSupports{ Supports: &ai.ModelSupports{
Multiturn: true, Multiturn: true,
SystemRole: true, SystemRole: true,
Media: false, // DeepSeek API primarily supports text. Media: false, // DeepSeek API primarily supports text.
Tools: false, // Tools not yet supported in this implementation. Tools: false, // Tools not yet supported in this implementation.
}, },
Versions: []string{}, Versions: []string{},
} }
if info != nil { if info != nil {
mi = *info mi = *info
} }
meta := &ai.ModelInfo{ meta := &ai.ModelInfo{
Label: model.Name, Label: model.Name,
Supports: mi.Supports, Supports: mi.Supports,
Versions: []string{}, Versions: []string{},
} }
gen := &generator{model: model, apiKey: d.APIKey} gen := &generator{model: model, apiKey: d.APIKey}
modelDef := genkit.DefineModel(g, provider, model.Name, meta, gen.generate) modelDef := genkit.DefineModel(g, provider, model.Name, meta, gen.generate)
log.Info(). log.Info().
Str("method", "DeepSeek.DefineModel"). Str("method", "DeepSeek.DefineModel").
Str("model_name", model.Name). Str("model_name", model.Name).
Msg("Model defined successfully") Msg("Model defined successfully")
return modelDef return modelDef
} }
// Init initializes the DeepSeek plugin. // Init initializes the DeepSeek plugin.
func (d *DeepSeek) Init(ctx context.Context, g *genkit.Genkit) error { func (d *DeepSeek) Init(ctx context.Context, g *genkit.Genkit) error {
log.Info().Str("method", "DeepSeek.Init").Msg("Initializing DeepSeek plugin") log.Info().Str("method", "DeepSeek.Init").Msg("Initializing DeepSeek plugin")
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock() defer d.mu.Unlock()
if d.initted { if d.initted {
log.Error().Str("method", "DeepSeek.Init").Msg("Plugin already initialized") log.Error().Str("method", "DeepSeek.Init").Msg("Plugin already initialized")
return fmt.Errorf("deepseek.Init already called") return fmt.Errorf("deepseek.Init already called")
} }
if d == nil || d.APIKey == "" { if d == nil || d.APIKey == "" {
log.Error().Str("method", "DeepSeek.Init").Msg("APIKey is required") log.Error().Str("method", "DeepSeek.Init").Msg("APIKey is required")
return fmt.Errorf("deepseek: need APIKey") return fmt.Errorf("deepseek: need APIKey")
} }
d.initted = true d.initted = true
log.Info().Str("method", "DeepSeek.Init").Msg("Initialization successful") log.Info().Str("method", "DeepSeek.Init").Msg("Initialization successful")
return nil return nil
} }
// generator handles model generation. // generator handles model generation.
type generator struct { type generator struct {
model ModelDefinition model ModelDefinition
apiKey string apiKey string
} }
// generate implements the Genkit model generation interface. // generate implements the Genkit model generation interface.
func (g *generator) generate(ctx context.Context, input *ai.ModelRequest, cb func(context.Context, *ai.ModelResponseChunk) error) (*ai.ModelResponse, error) { func (g *generator) generate(ctx context.Context, input *ai.ModelRequest, cb func(context.Context, *ai.ModelResponseChunk) error) (*ai.ModelResponse, error) {
log.Info(). log.Info().
Str("method", "generator.generate"). Str("method", "generator.generate").
Str("model_name", g.model.Name). Str("model_name", g.model.Name).
Int("messages", len(input.Messages)). Int("messages", len(input.Messages)).
Msg("Starting model generation") Msg("Starting model generation")
if len(input.Messages) == 0 { if len(input.Messages) == 0 {
log.Error().Str("method", "generator.generate").Msg("Prompt or messages required") log.Error().Str("method", "generator.generate").Msg("Prompt or messages required")
return nil, fmt.Errorf("prompt or messages required") return nil, fmt.Errorf("prompt or messages required")
} }
// Initialize DeepSeek client. // Initialize DeepSeek client.
client := deepseek.NewClient(g.apiKey) client := deepseek.NewClient(g.apiKey)
log.Debug().Str("method", "generator.generate").Msg("DeepSeek client initialized") log.Debug().Str("method", "generator.generate").Msg("DeepSeek client initialized")
// Create a chat completion request // Create a chat completion request
request := &deepseek.ChatCompletionRequest{ request := &deepseek.ChatCompletionRequest{
Model: g.model.Name, Model: g.model.Name,
} }
for _, msg := range input.Messages { for _, msg := range input.Messages {
role, ok := roleMapping[msg.Role] role, ok := roleMapping[msg.Role]
if !ok { if !ok {
log.Error(). log.Error().
Str("method", "generator.generate"). Str("method", "generator.generate").
Str("role", string(msg.Role)). Str("role", string(msg.Role)).
Msg("Unsupported role") Msg("Unsupported role")
return nil, fmt.Errorf("unsupported role: %s", msg.Role) return nil, fmt.Errorf("unsupported role: %s", msg.Role)
} }
content := concatMessageParts(msg.Content) content := concatMessageParts(msg.Content)
request.Messages = append(request.Messages, deepseek.ChatCompletionMessage{ request.Messages = append(request.Messages, deepseek.ChatCompletionMessage{
Role: role, Role: role,
Content: content, Content: content,
}) })
log.Debug(). log.Debug().
Str("method", "generator.generate"). Str("method", "generator.generate").
Str("role", role). Str("role", role).
Str("content", content). Str("content", content).
Msg("Added message to request") Msg("Added message to request")
} }
// Send the request and handle the response // Send the request and handle the response
response, err := client.CreateChatCompletion(ctx, request) response, err := client.CreateChatCompletion(ctx, request)
if err != nil { if err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "generator.generate"). Str("method", "generator.generate").
Msg("Failed to create chat completion") Msg("Failed to create chat completion")
return nil, fmt.Errorf("create chat completion: %w", err) return nil, fmt.Errorf("create chat completion: %w", err)
} }
log.Debug(). log.Debug().
Str("method", "generator.generate"). Str("method", "generator.generate").
Int("choices", len(response.Choices)). Int("choices", len(response.Choices)).
Msg("Received chat completion response") Msg("Received chat completion response")
// Create a final response with the merged chunks // Create a final response with the merged chunks
finalResponse := &ai.ModelResponse{ finalResponse := &ai.ModelResponse{
Request: input, Request: input,
FinishReason: ai.FinishReason("stop"), FinishReason: ai.FinishReason("stop"),
Message: &ai.Message{ Message: &ai.Message{
Role: ai.RoleModel, Role: ai.RoleModel,
}, },
} }
for _, chunk := range response.Choices { for _, chunk := range response.Choices {
log.Debug(). log.Debug().
Str("method", "generator.generate"). Str("method", "generator.generate").
Int("index", chunk.Index). Int("index", chunk.Index).
Str("content", chunk.Message.Content). Str("content", chunk.Message.Content).
Msg("Processing response chunk") Msg("Processing response chunk")
p := ai.Part{ p := ai.Part{
Text: chunk.Message.Content, Text: chunk.Message.Content,
Kind: ai.PartKind(chunk.Index), Kind: ai.PartKind(chunk.Index),
} }
finalResponse.Message.Content = append(finalResponse.Message.Content, &p) finalResponse.Message.Content = append(finalResponse.Message.Content, &p)
} }
log.Info(). log.Info().
Str("method", "generator.generate"). Str("method", "generator.generate").
Str("model_name", g.model.Name). Str("model_name", g.model.Name).
Int("content_parts", len(finalResponse.Message.Content)). Int("content_parts", len(finalResponse.Message.Content)).
Msg("Model generation completed successfully") Msg("Model generation completed successfully")
return finalResponse, nil return finalResponse, nil
} }
// concatMessageParts concatenates message parts into a single string. // concatMessageParts concatenates message parts into a single string.
func concatMessageParts(parts []*ai.Part) string { func concatMessageParts(parts []*ai.Part) string {
log.Debug(). log.Debug().
Str("method", "concatMessageParts"). Str("method", "concatMessageParts").
Int("parts", len(parts)). Int("parts", len(parts)).
Msg("Concatenating message parts") Msg("Concatenating message parts")
var sb strings.Builder var sb strings.Builder
for _, part := range parts { for _, part := range parts {
if part.IsText() { if part.IsText() {
sb.WriteString(part.Text) sb.WriteString(part.Text)
} }
// Ignore non-text parts (e.g., media, tools) as DeepSeek API doesn't support them. // Ignore non-text parts (e.g., media, tools) as DeepSeek API doesn't support them.
} }
result := sb.String() result := sb.String()
log.Debug(). log.Debug().
Str("method", "concatMessageParts"). Str("method", "concatMessageParts").
Str("result", result). Str("result", result).
Msg("Concatenation complete") Msg("Concatenation complete")
return result return result
} }
// package deepseek // package deepseek
// import ( // import (
......
...@@ -38,236 +38,236 @@ import ( ...@@ -38,236 +38,236 @@ import (
// Client 知识库客户端 // Client 知识库客户端
type Client struct { type Client struct {
BaseURL string // 基础URL,例如 "http://54.92.111.204:5670" BaseURL string // 基础URL,例如 "http://54.92.111.204:5670"
} }
// SpaceRequest 创建空间的请求结构体 // SpaceRequest 创建空间的请求结构体
type SpaceRequest struct { type SpaceRequest struct {
ID int `json:"id"` ID int `json:"id"`
Name string `json:"name"` Name string `json:"name"`
VectorType string `json:"vector_type"` VectorType string `json:"vector_type"`
DomainType string `json:"domain_type"` DomainType string `json:"domain_type"`
Desc string `json:"desc"` Desc string `json:"desc"`
Owner string `json:"owner"` Owner string `json:"owner"`
SpaceID int `json:"space_id"` SpaceID int `json:"space_id"`
} }
// DocumentRequest 添加文档的请求结构体 // DocumentRequest 添加文档的请求结构体
type DocumentRequest struct { type DocumentRequest struct {
DocName string `json:"doc_name"` DocName string `json:"doc_name"`
DocID int `json:"doc_id"` DocID int `json:"doc_id"`
DocType string `json:"doc_type"` DocType string `json:"doc_type"`
DocToken string `json:"doc_token"` DocToken string `json:"doc_token"`
Content string `json:"content"` Content string `json:"content"`
Source string `json:"source"` Source string `json:"source"`
Labels string `json:"labels"` Labels string `json:"labels"`
Questions []string `json:"questions"` Questions []string `json:"questions"`
Metadata map[string]interface{} `json:"metadata"` Metadata map[string]interface{} `json:"metadata"`
} }
// ChunkParameters 分片参数 // ChunkParameters 分片参数
type ChunkParameters struct { type ChunkParameters struct {
ChunkStrategy string `json:"chunk_strategy"` ChunkStrategy string `json:"chunk_strategy"`
TextSplitter string `json:"text_splitter"` TextSplitter string `json:"text_splitter"`
SplitterType string `json:"splitter_type"` SplitterType string `json:"splitter_type"`
ChunkSize int `json:"chunk_size"` ChunkSize int `json:"chunk_size"`
ChunkOverlap int `json:"chunk_overlap"` ChunkOverlap int `json:"chunk_overlap"`
Separator string `json:"separator"` Separator string `json:"separator"`
EnableMerge bool `json:"enable_merge"` EnableMerge bool `json:"enable_merge"`
} }
// SyncBatchRequest 同步批量处理的请求结构体 // SyncBatchRequest 同步批量处理的请求结构体
type SyncBatchRequest struct { type SyncBatchRequest struct {
DocID int `json:"doc_id"` DocID int `json:"doc_id"`
SpaceID string `json:"space_id"` SpaceID string `json:"space_id"`
ModelName string `json:"model_name"` ModelName string `json:"model_name"`
ChunkParameters ChunkParameters `json:"chunk_parameters"` ChunkParameters ChunkParameters `json:"chunk_parameters"`
} }
// NewClient 创建新的客户端实例 // NewClient 创建新的客户端实例
func NewClient(ip string, port int) *Client { func NewClient(ip string, port int) *Client {
log.Info(). log.Info().
Str("method", "NewClient"). Str("method", "NewClient").
Str("ip", ip). Str("ip", ip).
Int("port", port). Int("port", port).
Msg("Creating new GraphRAG client") Msg("Creating new GraphRAG client")
return &Client{ return &Client{
BaseURL: fmt.Sprintf("http://%s:%d", ip, port), BaseURL: fmt.Sprintf("http://%s:%d", ip, port),
} }
} }
// AddSpace 创建知识空间 // AddSpace 创建知识空间
func (c *Client) AddSpace(req SpaceRequest) (*http.Response, error) { func (c *Client) AddSpace(req SpaceRequest) (*http.Response, error) {
log.Info(). log.Info().
Str("method", "Client.AddSpace"). Str("method", "Client.AddSpace").
Str("name", req.Name). Str("name", req.Name).
Str("owner", req.Owner). Str("owner", req.Owner).
Msg("Adding knowledge space") Msg("Adding knowledge space")
url := fmt.Sprintf("%s/knowledge/space/add", c.BaseURL) url := fmt.Sprintf("%s/knowledge/space/add", c.BaseURL)
body, err := json.Marshal(req) body, err := json.Marshal(req)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.AddSpace").Msg("Failed to marshal request") log.Error().Err(err).Str("method", "Client.AddSpace").Msg("Failed to marshal request")
return nil, fmt.Errorf("failed to marshal request: %w", err) return nil, fmt.Errorf("failed to marshal request: %w", err)
} }
httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.AddSpace").Msg("Failed to create request") log.Error().Err(err).Str("method", "Client.AddSpace").Msg("Failed to create request")
return nil, fmt.Errorf("failed to create request: %w", err) return nil, fmt.Errorf("failed to create request: %w", err)
} }
httpReq.Header.Set("Accept", "application/json") httpReq.Header.Set("Accept", "application/json")
httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{} client := &http.Client{}
resp, err := client.Do(httpReq) resp, err := client.Do(httpReq)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.AddSpace").Msg("Failed to send request") log.Error().Err(err).Str("method", "Client.AddSpace").Msg("Failed to send request")
return nil, fmt.Errorf("failed to send request: %w", err) return nil, fmt.Errorf("failed to send request: %w", err)
} }
log.Info(). log.Info().
Str("method", "Client.AddSpace"). Str("method", "Client.AddSpace").
Int("status_code", resp.StatusCode). Int("status_code", resp.StatusCode).
Msg("Space addition request completed") Msg("Space addition request completed")
return resp, nil return resp, nil
} }
// AddDocument 添加文档 // AddDocument 添加文档
func (c *Client) AddDocument(spaceID string, req DocumentRequest) (*http.Response, error) { func (c *Client) AddDocument(spaceID string, req DocumentRequest) (*http.Response, error) {
log.Info(). log.Info().
Str("method", "Client.AddDocument"). Str("method", "Client.AddDocument").
Str("space_id", spaceID). Str("space_id", spaceID).
Str("doc_name", req.DocName). Str("doc_name", req.DocName).
Msg("Adding document") Msg("Adding document")
url := fmt.Sprintf("%s/knowledge/%s/document/add", c.BaseURL, spaceID) url := fmt.Sprintf("%s/knowledge/%s/document/add", c.BaseURL, spaceID)
body, err := json.Marshal(req) body, err := json.Marshal(req)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.AddDocument").Msg("Failed to marshal request") log.Error().Err(err).Str("method", "Client.AddDocument").Msg("Failed to marshal request")
return nil, fmt.Errorf("failed to marshal request: %w", err) return nil, fmt.Errorf("failed to marshal request: %w", err)
} }
httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.AddDocument").Msg("Failed to create request") log.Error().Err(err).Str("method", "Client.AddDocument").Msg("Failed to create request")
return nil, fmt.Errorf("failed to create request: %w", err) return nil, fmt.Errorf("failed to create request: %w", err)
} }
httpReq.Header.Set("Accept", "application/json") httpReq.Header.Set("Accept", "application/json")
httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{} client := &http.Client{}
resp, err := client.Do(httpReq) resp, err := client.Do(httpReq)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.AddDocument").Msg("Failed to send request") log.Error().Err(err).Str("method", "Client.AddDocument").Msg("Failed to send request")
return nil, fmt.Errorf("failed to send request: %w", err) return nil, fmt.Errorf("failed to send request: %w", err)
} }
log.Info(). log.Info().
Str("method", "Client.AddDocument"). Str("method", "Client.AddDocument").
Str("space_id", spaceID). Str("space_id", spaceID).
Int("status_code", resp.StatusCode). Int("status_code", resp.StatusCode).
Msg("Document addition request completed") Msg("Document addition request completed")
return resp, nil return resp, nil
} }
// SyncDocumentsRequest defines the request body for the sync documents endpoint. // SyncDocumentsRequest defines the request body for the sync documents endpoint.
type SyncDocumentsRequest struct { type SyncDocumentsRequest struct {
DocIDs []string `json:"doc_ids"` DocIDs []string `json:"doc_ids"`
} }
// SyncDocuments sends a POST request to sync documents for the given spaceID. // SyncDocuments sends a POST request to sync documents for the given spaceID.
func (c *Client) SyncDocuments(spaceID string, docIDs []string) (success bool, err error) { func (c *Client) SyncDocuments(spaceID string, docIDs []string) (success bool, err error) {
log.Info(). log.Info().
Str("method", "Client.SyncDocuments"). Str("method", "Client.SyncDocuments").
Str("space_id", spaceID). Str("space_id", spaceID).
Strs("doc_ids", docIDs). Strs("doc_ids", docIDs).
Msg("Syncing documents") Msg("Syncing documents")
url := fmt.Sprintf("%s/knowledge/%s/document/sync", c.BaseURL, spaceID) url := fmt.Sprintf("%s/knowledge/%s/document/sync", c.BaseURL, spaceID)
reqBody := SyncDocumentsRequest{ reqBody := SyncDocumentsRequest{
DocIDs: docIDs, DocIDs: docIDs,
} }
body, err := json.Marshal(reqBody) body, err := json.Marshal(reqBody)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.SyncDocuments").Msg("Failed to marshal request") log.Error().Err(err).Str("method", "Client.SyncDocuments").Msg("Failed to marshal request")
return false, fmt.Errorf("failed to marshal request: %w", err) return false, fmt.Errorf("failed to marshal request: %w", err)
} }
httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.SyncDocuments").Msg("Failed to create request") log.Error().Err(err).Str("method", "Client.SyncDocuments").Msg("Failed to create request")
return false, fmt.Errorf("failed to create request: %w", err) return false, fmt.Errorf("failed to create request: %w", err)
} }
httpReq.Header.Set("Accept", "application/json") httpReq.Header.Set("Accept", "application/json")
httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{} client := &http.Client{}
resp, err := client.Do(httpReq) resp, err := client.Do(httpReq)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.SyncDocuments").Msg("Failed to send request") log.Error().Err(err).Str("method", "Client.SyncDocuments").Msg("Failed to send request")
return false, fmt.Errorf("failed to send request: %w", err) return false, fmt.Errorf("failed to send request: %w", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body) respBody, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.SyncDocuments").Msg("Failed to read response body") log.Error().Err(err).Str("method", "Client.SyncDocuments").Msg("Failed to read response body")
return false, fmt.Errorf("failed to read response body: %w", err) return false, fmt.Errorf("failed to read response body: %w", err)
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
log.Error(). log.Error().
Str("method", "Client.SyncDocuments"). Str("method", "Client.SyncDocuments").
Int("status_code", resp.StatusCode). Int("status_code", resp.StatusCode).
Str("response_body", string(respBody)). Str("response_body", string(respBody)).
Msg("Sync request failed") Msg("Sync request failed")
return false, fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(respBody)) return false, fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(respBody))
} }
log.Info(). log.Info().
Str("method", "Client.SyncDocuments"). Str("method", "Client.SyncDocuments").
Str("space_id", spaceID). Str("space_id", spaceID).
Msg("Documents synced successfully") Msg("Documents synced successfully")
return true, nil return true, nil
} }
// SyncBatchDocument 同步批量处理文档 // SyncBatchDocument 同步批量处理文档
func (c *Client) SyncBatchDocument(spaceID string, req []SyncBatchRequest) (*http.Response, error) { func (c *Client) SyncBatchDocument(spaceID string, req []SyncBatchRequest) (*http.Response, error) {
log.Info(). log.Info().
Str("method", "Client.SyncBatchDocument"). Str("method", "Client.SyncBatchDocument").
Str("space_id", spaceID). Str("space_id", spaceID).
Int("requests", len(req)). Int("requests", len(req)).
Msg("Syncing batch documents") Msg("Syncing batch documents")
url := fmt.Sprintf("%s/knowledge/%s/document/sync_batch", c.BaseURL, spaceID) url := fmt.Sprintf("%s/knowledge/%s/document/sync_batch", c.BaseURL, spaceID)
body, err := json.Marshal(req) body, err := json.Marshal(req)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.SyncBatchDocument").Msg("Failed to marshal request") log.Error().Err(err).Str("method", "Client.SyncBatchDocument").Msg("Failed to marshal request")
return nil, fmt.Errorf("failed to marshal request: %w", err) return nil, fmt.Errorf("failed to marshal request: %w", err)
} }
httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.SyncBatchDocument").Msg("Failed to create request") log.Error().Err(err).Str("method", "Client.SyncBatchDocument").Msg("Failed to create request")
return nil, fmt.Errorf("failed to create request: %w", err) return nil, fmt.Errorf("failed to create request: %w", err)
} }
httpReq.Header.Set("Accept", "application/json") httpReq.Header.Set("Accept", "application/json")
httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{} client := &http.Client{}
resp, err := client.Do(httpReq) resp, err := client.Do(httpReq)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Client.SyncBatchDocument").Msg("Failed to send request") log.Error().Err(err).Str("method", "Client.SyncBatchDocument").Msg("Failed to send request")
return nil, fmt.Errorf("failed to send request: %w", err) return nil, fmt.Errorf("failed to send request: %w", err)
} }
log.Info(). log.Info().
Str("method", "Client.SyncBatchDocument"). Str("method", "Client.SyncBatchDocument").
Str("space_id", spaceID). Str("space_id", spaceID).
Int("status_code", resp.StatusCode). Int("status_code", resp.StatusCode).
Msg("Batch document sync request completed") Msg("Batch document sync request completed")
return resp, nil return resp, nil
} }
// The provider used in the registry. // The provider used in the registry.
...@@ -275,644 +275,626 @@ const provider = "graphrag" ...@@ -275,644 +275,626 @@ const provider = "graphrag"
// Field names for schema. // Field names for schema.
const ( const (
idField = "id" idField = "id"
textField = "text" textField = "text"
metadataField = "metadata" metadataField = "metadata"
) )
// GraphKnowledge holds configuration for the plugin. // GraphKnowledge holds configuration for the plugin.
type GraphKnowledge struct { type GraphKnowledge struct {
Addr string // Knowledge server address (host:port, e.g., "54.92.111.204:5670"). Addr string // Knowledge server address (host:port, e.g., "54.92.111.204:5670").
client *Client // Knowledge client. client *Client // Knowledge client.
mu sync.Mutex // Mutex to control access. mu sync.Mutex // Mutex to control access.
initted bool // Whether the plugin has been initialized. initted bool // Whether the plugin has been initialized.
} }
// Name returns the plugin name. // Name returns the plugin name.
func (k *GraphKnowledge) Name() string { func (k *GraphKnowledge) Name() string {
return provider return provider
} }
// Init initializes the GraphKnowledge plugin. // Init initializes the GraphKnowledge plugin.
func (k *GraphKnowledge) Init(ctx context.Context, g *genkit.Genkit) (err error) { func (k *GraphKnowledge) Init(ctx context.Context, g *genkit.Genkit) (err error) {
log.Info().Str("method", "GraphKnowledge.Init").Msg("Initializing GraphKnowledge plugin") log.Info().Str("method", "GraphKnowledge.Init").Msg("Initializing GraphKnowledge plugin")
if k == nil { if k == nil {
k = &GraphKnowledge{} k = &GraphKnowledge{}
} }
k.mu.Lock() k.mu.Lock()
defer k.mu.Unlock() defer k.mu.Unlock()
defer func() { defer func() {
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "GraphKnowledge.Init").Msg("Initialization failed") log.Error().Err(err).Str("method", "GraphKnowledge.Init").Msg("Initialization failed")
err = fmt.Errorf("graphrag.Init: %w", err) err = fmt.Errorf("graphrag.Init: %w", err)
} else { } else {
log.Info().Str("method", "GraphKnowledge.Init").Msg("Initialization successful") log.Info().Str("method", "GraphKnowledge.Init").Msg("Initialization successful")
} }
}() }()
if k.initted { if k.initted {
return errors.New("plugin already initialized") return errors.New("plugin already initialized")
} }
// Load configuration. // Load configuration.
addr := k.Addr addr := k.Addr
if addr == "" { if addr == "" {
addr = "54.92.111.204:5670" // Default address. addr = "54.92.111.204:5670" // Default address.
} }
// Initialize Knowledge client. // Initialize Knowledge client.
host, port := parseAddr(addr) host, port := parseAddr(addr)
client := NewClient(host, port) client := NewClient(host, port)
k.client = client k.client = client
k.initted = true k.initted = true
return nil return nil
} }
// parseAddr splits host:port into host and port. // parseAddr splits host:port into host and port.
func parseAddr(addr string) (string, int) { func parseAddr(addr string) (string, int) {
parts := strings.Split(addr, ":") parts := strings.Split(addr, ":")
if len(parts) != 2 { if len(parts) != 2 {
log.Warn(). log.Warn().
Str("method", "parseAddr"). Str("method", "parseAddr").
Str("addr", addr). Str("addr", addr).
Msg("Invalid address format, using default") Msg("Invalid address format, using default")
return "54.92.111.204", 5670 return "54.92.111.204", 5670
} }
port, err := strconv.Atoi(parts[1]) port, err := strconv.Atoi(parts[1])
if err != nil { if err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "parseAddr"). Str("method", "parseAddr").
Str("port", parts[1]). Str("port", parts[1]).
Msg("Failed to parse port, using default") Msg("Failed to parse port, using default")
return "54.92.111.204", 5670 return "54.92.111.204", 5670
} }
return parts[0], port return parts[0], port
} }
// DefineIndexerAndRetriever defines an Indexer and Retriever for a Knowledge space. // DefineIndexerAndRetriever defines an Indexer and Retriever for a Knowledge space.
func DefineIndexerAndRetriever(ctx context.Context, g *genkit.Genkit) (ai.Indexer, ai.Retriever, error) { func DefineIndexerAndRetriever(ctx context.Context, g *genkit.Genkit) (ai.Indexer, ai.Retriever, error) {
log.Info().Str("method", "DefineIndexerAndRetriever").Msg("Defining indexer and retriever") log.Info().Str("method", "DefineIndexerAndRetriever").Msg("Defining indexer and retriever")
spaceID := "" spaceID := ""
modelName := "Qwen/Qwen2.5-Coder-32B-Instruct" modelName := "Qwen/Qwen2.5-Coder-32B-Instruct"
k := genkit.LookupPlugin(g, provider) k := genkit.LookupPlugin(g, provider)
if k == nil { if k == nil {
log.Error().Str("method", "DefineIndexerAndRetriever").Msg("GraphRAG plugin not found") log.Error().Str("method", "DefineIndexerAndRetriever").Msg("GraphRAG plugin not found")
return nil, nil, errors.New("graphrag plugin not found; did you call genkit.Init with the graphrag plugin?") return nil, nil, errors.New("graphrag plugin not found; did you call genkit.Init with the graphrag plugin?")
} }
knowledge := k.(*GraphKnowledge) knowledge := k.(*GraphKnowledge)
ds, err := knowledge.newDocStore(ctx, spaceID, modelName) ds, err := knowledge.newDocStore(ctx, spaceID, modelName)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "DefineIndexerAndRetriever").Msg("Failed to create doc store") log.Error().Err(err).Str("method", "DefineIndexerAndRetriever").Msg("Failed to create doc store")
return nil, nil, err return nil, nil, err
} }
indexer := genkit.DefineIndexer(g, provider, spaceID, ds.Index) indexer := genkit.DefineIndexer(g, provider, spaceID, ds.Index)
retriever := genkit.DefineRetriever(g, provider, spaceID, ds.Retrieve) retriever := genkit.DefineRetriever(g, provider, spaceID, ds.Retrieve)
log.Info().Str("method", "DefineIndexerAndRetriever").Msg("Indexer and retriever defined successfully") log.Info().Str("method", "DefineIndexerAndRetriever").Msg("Indexer and retriever defined successfully")
return indexer, retriever, nil return indexer, retriever, nil
} }
// docStore defines an Indexer and a Retriever. // docStore defines an Indexer and a Retriever.
type docStore struct { type docStore struct {
client *Client client *Client
spaceID string spaceID string
modelName string modelName string
} }
// newDocStore creates a docStore. // newDocStore creates a docStore.
func (k *GraphKnowledge) newDocStore(ctx context.Context, spaceID, modelName string) (*docStore, error) { func (k *GraphKnowledge) newDocStore(ctx context.Context, spaceID, modelName string) (*docStore, error) {
log.Info(). log.Info().
Str("method", "GraphKnowledge.newDocStore"). Str("method", "GraphKnowledge.newDocStore").
Str("space_id", spaceID). Str("space_id", spaceID).
Str("model_name", modelName). Str("model_name", modelName).
Msg("Creating new doc store") Msg("Creating new doc store")
if k.client == nil { if k.client == nil {
log.Error().Str("method", "GraphKnowledge.newDocStore").Msg("GraphRAG client not initialized") log.Error().Str("method", "GraphKnowledge.newDocStore").Msg("GraphRAG client not initialized")
return nil, errors.New("graphrag.Init not called") return nil, errors.New("graphrag.Init not called")
} }
log.Info().Str("method", "GraphKnowledge.newDocStore").Msg("Doc store created successfully") log.Info().Str("method", "GraphKnowledge.newDocStore").Msg("Doc store created successfully")
return &docStore{ return &docStore{
client: k.client, client: k.client,
spaceID: spaceID, spaceID: spaceID,
modelName: modelName, modelName: modelName,
}, nil }, nil
} }
// Indexer returns the indexer for a space. // Indexer returns the indexer for a space.
func Indexer(g *genkit.Genkit, spaceID string) ai.Indexer { func Indexer(g *genkit.Genkit, spaceID string) ai.Indexer {
log.Info(). log.Info().
Str("method", "Indexer"). Str("method", "Indexer").
Str("space_id", spaceID). Str("space_id", spaceID).
Msg("Looking up indexer") Msg("Looking up indexer")
indexer := genkit.LookupIndexer(g, provider, spaceID) indexer := genkit.LookupIndexer(g, provider, spaceID)
if indexer == nil { if indexer == nil {
log.Warn(). log.Warn().
Str("method", "Indexer"). Str("method", "Indexer").
Str("space_id", spaceID). Str("space_id", spaceID).
Msg("Indexer not found") Msg("Indexer not found")
} }
return indexer return indexer
} }
// Retriever returns the retriever for a space. // Retriever returns the retriever for a space.
func Retriever(g *genkit.Genkit, spaceID string) ai.Retriever { func Retriever(g *genkit.Genkit, spaceID string) ai.Retriever {
log.Info(). log.Info().
Str("method", "Retriever"). Str("method", "Retriever").
Str("space_id", spaceID). Str("space_id", spaceID).
Msg("Looking up retriever") Msg("Looking up retriever")
retriever := genkit.LookupRetriever(g, provider, spaceID) retriever := genkit.LookupRetriever(g, provider, spaceID)
if retriever == nil { if retriever == nil {
log.Warn(). log.Warn().
Str("method", "Retriever"). Str("method", "Retriever").
Str("space_id", spaceID). Str("space_id", spaceID).
Msg("Retriever not found") Msg("Retriever not found")
} }
return retriever return retriever
} }
// generateRandomDocName generates a random alphanumeric string of the specified length. // generateRandomDocName generates a random alphanumeric string of the specified length.
func GenerateRandomDocName(length int) (string, error) { func GenerateRandomDocName(length int) (string, error) {
log.Debug(). log.Debug().
Str("method", "GenerateRandomDocName"). Str("method", "GenerateRandomDocName").
Int("length", length). Int("length", length).
Msg("Generating random document name") Msg("Generating random document name")
const charset = "abcdefghijklmnopqrstuvwxyz0123456789" const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
var result strings.Builder var result strings.Builder
result.Grow(length) result.Grow(length)
for i := 0; i < length; i++ { for i := 0; i < length; i++ {
idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset)))) idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
if err != nil { if err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "GenerateRandomDocName"). Str("method", "GenerateRandomDocName").
Msg("Failed to generate random index") Msg("Failed to generate random index")
return "", fmt.Errorf("failed to generate random index: %w", err) return "", fmt.Errorf("failed to generate random index: %w", err)
} }
result.WriteByte(charset[idx.Int64()]) result.WriteByte(charset[idx.Int64()])
} }
docName := result.String() docName := result.String()
log.Debug(). log.Debug().
Str("method", "GenerateRandomDocName"). Str("method", "GenerateRandomDocName").
Str("doc_name", docName). Str("doc_name", docName).
Msg("Generated document name") Msg("Generated document name")
return docName, nil return docName, nil
} }
// ParseJSONResponse parses a JSON byte slice and extracts the success boolean and data fields as a string. // ParseJSONResponse parses a JSON byte slice and extracts the success boolean and data fields as a string.
func ParseJSONResponse(jsonBytes []byte) (success bool, data string, err error) { func ParseJSONResponse(jsonBytes []byte) (success bool, data string, err error) {
log.Debug(). log.Debug().
Str("method", "ParseJSONResponse"). Str("method", "ParseJSONResponse").
Str("json", string(jsonBytes)). Str("json", string(jsonBytes)).
Msg("Parsing JSON response") Msg("Parsing JSON response")
// Define struct to capture only the needed fields // Define struct to capture only the needed fields
type jsonResponse struct { type jsonResponse struct {
Success bool `json:"success"` Success bool `json:"success"`
Data int `json:"data"` Data int `json:"data"`
} }
var resp jsonResponse var resp jsonResponse
if err := json.Unmarshal(jsonBytes, &resp); err != nil { if err := json.Unmarshal(jsonBytes, &resp); err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "ParseJSONResponse"). Str("method", "ParseJSONResponse").
Msg("Failed to unmarshal JSON") Msg("Failed to unmarshal JSON")
return false, "", fmt.Errorf("failed to unmarshal JSON: %w", err) return false, "", fmt.Errorf("failed to unmarshal JSON: %w", err)
} }
dataStr := fmt.Sprintf("%d", resp.Data) dataStr := fmt.Sprintf("%d", resp.Data)
log.Debug(). log.Debug().
Str("method", "ParseJSONResponse"). Str("method", "ParseJSONResponse").
Bool("success", resp.Success). Bool("success", resp.Success).
Str("data", dataStr). Str("data", dataStr).
Msg("Parsed JSON response") Msg("Parsed JSON response")
return resp.Success, dataStr, nil return resp.Success, dataStr, nil
} }
type IndexReqOption struct { type IndexReqOption struct {
UserId string UserId string
UserName string UserName string
} }
const DocNameKey = "doc_name" const DocNameKey = "doc_name"
// Index implements the Indexer.Index method. // Index implements the Indexer.Index method.
func (ds *docStore) Index(ctx context.Context, req *ai.IndexerRequest) error { func (ds *docStore) Index(ctx context.Context, req *ai.IndexerRequest) error {
log.Info(). log.Info().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Str("space_id", ds.spaceID). Str("space_id", ds.spaceID).
Int("documents", len(req.Documents)). Int("documents", len(req.Documents)).
Msg("Starting index operation") Msg("Starting index operation")
if len(req.Documents) == 0 { if len(req.Documents) == 0 {
log.Debug(). log.Debug().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Str("space_id", ds.spaceID). Str("space_id", ds.spaceID).
Msg("No documents to index") Msg("No documents to index")
return nil return nil
} }
// Type-assert req.Options to IndexReqOption // Type-assert req.Options to IndexReqOption
opt, ok := req.Options.(*IndexReqOption) opt, ok := req.Options.(*IndexReqOption)
if !ok { if !ok {
log.Error(). log.Error().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Str("options_type", fmt.Sprintf("%T", req.Options)). Str("options_type", fmt.Sprintf("%T", req.Options)).
Msg("Invalid options type") Msg("Invalid options type")
return fmt.Errorf("invalid options type: got %T, want *IndexReqOption", req.Options) return fmt.Errorf("invalid options type: got %T, want *IndexReqOption", req.Options)
} }
// Validate required fields // Validate required fields
if opt.UserId == "" { if opt.UserId == "" {
log.Error().Str("method", "docStore.Index").Msg("UserId is required") log.Error().Str("method", "docStore.Index").Msg("UserId is required")
return fmt.Errorf("UserId is required in IndexReqOption") return fmt.Errorf("UserId is required in IndexReqOption")
} }
if opt.UserName == "" { if opt.UserName == "" {
log.Error().Str("method", "docStore.Index").Msg("UserName is required") log.Error().Str("method", "docStore.Index").Msg("UserName is required")
return fmt.Errorf("UserName is required in IndexReqOption") return fmt.Errorf("UserName is required in IndexReqOption")
} }
// Create knowledge space // Create knowledge space
spaceReq := SpaceRequest{ spaceReq := SpaceRequest{
Name: opt.UserId, Name: opt.UserId,
VectorType: "KnowledgeGraph", VectorType: "KnowledgeGraph",
DomainType: "Normal", DomainType: "Normal",
Desc: opt.UserName, Desc: opt.UserName,
Owner: opt.UserId, Owner: opt.UserId,
} }
resp, err := ds.client.AddSpace(spaceReq) resp, err := ds.client.AddSpace(spaceReq)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "docStore.Index").Msg("Failed to add space") log.Error().Err(err).Str("method", "docStore.Index").Msg("Failed to add space")
return fmt.Errorf("add space: %w", err) return fmt.Errorf("add space: %w", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
log.Error(). log.Error().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("status_code", resp.StatusCode). Int("status_code", resp.StatusCode).
Str("response_body", string(body)). Str("response_body", string(body)).
Msg("Add space failed") Msg("Add space failed")
return fmt.Errorf("add space failed with status %d: %s", resp.StatusCode, string(body)) return fmt.Errorf("add space failed with status %d: %s", resp.StatusCode, string(body))
} }
log.Info().Str("method", "docStore.Index").Str("space_id", opt.UserId).Msg("Space created successfully") log.Info().Str("method", "docStore.Index").Str("space_id", opt.UserId).Msg("Space created successfully")
spaceId := opt.UserId spaceId := opt.UserId
// Index each document // Index each document
for i, doc := range req.Documents { for i, doc := range req.Documents {
// Use DocName from metadata // Use DocName from metadata
docName, ok := doc.Metadata[DocNameKey].(string) docName, ok := doc.Metadata[DocNameKey].(string)
if !ok { if !ok {
log.Error(). log.Error().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i). Int("index", i).
Msg("Missing doc_name in metadata") Msg("Missing doc_name in metadata")
return fmt.Errorf("must provide doc_name key in metadata") return fmt.Errorf("must provide doc_name key in metadata")
} }
if docName == "" { if docName == "" {
log.Error(). log.Error().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i). Int("index", i).
Msg("doc_name is empty") Msg("doc_name is empty")
return fmt.Errorf("must provide non-empty doc_name str value in metadata") return fmt.Errorf("must provide non-empty doc_name str value in metadata")
} }
log.Debug(). log.Debug().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i). Int("index", i).
Str("doc_name", docName). Str("doc_name", docName).
Msg("Processing document") Msg("Processing document")
// Add document // Add document
var sb strings.Builder var sb strings.Builder
for _, p := range doc.Content { for _, p := range doc.Content {
if p.IsText() { if p.IsText() {
sb.WriteString(p.Text) sb.WriteString(p.Text)
} }
} }
text := sb.String() text := sb.String()
log.Debug(). log.Debug().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i). Int("index", i).
Str("text", text). Str("text", text).
Msg("Extracted document text") Msg("Extracted document text")
docReq := DocumentRequest{ docReq := DocumentRequest{
DocName: docName, DocName: docName,
Source: "api", Source: "api",
DocType: "TEXT", DocType: "TEXT",
Content: text, Content: text,
Labels: "", Labels: "",
Metadata: doc.Metadata, Metadata: doc.Metadata,
} }
resp, err := ds.client.AddDocument(spaceId, docReq) resp, err := ds.client.AddDocument(spaceId, docReq)
if err != nil { if err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i+1). Int("index", i+1).
Msg("Failed to add document") Msg("Failed to add document")
return fmt.Errorf("add document %d: %w", i+1, err) return fmt.Errorf("add document %d: %w", i+1, err)
} }
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
resp.Body.Close() resp.Body.Close()
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i+1). Int("index", i+1).
Msg("Failed to read add document response") Msg("Failed to read add document response")
return fmt.Errorf("read add document response %d: %w", i+1, err) return fmt.Errorf("read add document response %d: %w", i+1, err)
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
log.Error(). log.Error().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i+1). Int("index", i+1).
Int("status_code", resp.StatusCode). Int("status_code", resp.StatusCode).
Str("response_body", string(body)). Str("response_body", string(body)).
Msg("Add document failed") Msg("Add document failed")
return fmt.Errorf("add document %d failed with status %d: %s", i+1, resp.StatusCode, string(body)) return fmt.Errorf("add document %d failed with status %d: %s", i+1, resp.StatusCode, string(body))
} }
// Parse AddDocument response // Parse AddDocument response
ok, idx, err := ParseJSONResponse(body) ok, idx, err := ParseJSONResponse(body)
if err != nil { if err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i+1). Int("index", i+1).
Msg("Failed to parse add document") Msg("Failed to parse add document")
return fmt.Errorf("parse add document response %d: %w", i+1, err) return fmt.Errorf("parse add document response %d: %w", i+1, err)
} }
if !ok { if !ok {
log.Error(). log.Error().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i+1). Int("index", i+1).
Str("data", idx). Str("data", idx).
Msg("Add document response indicated failure") Msg("Add document response indicated failure")
return fmt.Errorf("add document %d failed: response success=false, data=%s", i+1, idx) return fmt.Errorf("add document %d failed: response success=false, data=%s", i+1, idx)
} }
log.Info(). log.Info().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i+1). Int("index", i+1).
Str("doc_id", idx). Str("doc_id", idx).
Msg("Document added successfully") Msg("Document added successfully")
// Sync document // Sync document
_, err = ds.client.SyncDocuments(spaceId, []string{idx}) _, err = ds.client.SyncDocuments(spaceId, []string{idx})
if err != nil { if err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i+1). Int("index", i+1).
Msg("Failed to sync document") Msg("Failed to sync document")
return fmt.Errorf("sync document %d: %w", i+1, err) return fmt.Errorf("sync document %d: %w", i+1, err)
} }
} }
log.Info(). log.Info().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Str("space_id", ds.spaceID). Str("space_id", ds.spaceID).
Int("documents", len(req.Documents)). Int("documents", len(req.Documents)).
Msg("Index operation completed successfully") Msg("Index operation completed successfully")
return nil return nil
} }
// ChatRequest defines the request structure for chat completions. // ChatRequest defines the request structure for chat completions.
type ChatRequest struct { type ChatRequest struct {
Model string `json:"model"` Model string `json:"model"`
Messages string `json:"messages"` Messages string `json:"messages"`
Temperature float64 `json:"temperature"` Temperature float64 `json:"temperature"`
TopP float64 `json:"top_p"` TopP float64 `json:"top_p"`
TopK int `json:"top_k"` TopK int `json:"top_k"`
N int `json:"n"` N int `json:"n"`
MaxTokens int64 `json:"max_tokens"` MaxTokens int64 `json:"max_tokens"`
Stream bool `json:"stream"` Stream bool `json:"stream"`
RepetitionPenalty float64 `json:"repetition_penalty"` RepetitionPenalty float64 `json:"repetition_penalty"`
FrequencyPenalty float64 `json:"frequency_penalty"` FrequencyPenalty float64 `json:"frequency_penalty"`
PresencePenalty float64 `json:"presence_penalty"` PresencePenalty float64 `json:"presence_penalty"`
ChatMode string `json:"chat_mode"` ChatMode string `json:"chat_mode"`
ChatParam string `json:"chat_param"` ChatParam string `json:"chat_param"`
EnableVis bool `json:"enable_vis"` EnableVis bool `json:"enable_vis"`
} }
// ChatResponse defines the response structure from the API. // ChatResponse defines the response structure from the API.
type ChatResponse struct { type ChatResponse struct {
ID string `json:"id"` ID string `json:"id"`
Object string `json:"object"` Object string `json:"object"`
Created int64 `json:"created"` Created int64 `json:"created"`
Model string `json:"model"` Model string `json:"model"`
Choices []struct { Choices []struct {
Index int `json:"index"` Index int `json:"index"`
Message struct { Message struct {
Role string `json:"role"` Role string `json:"role"`
Content string `json:"content"` Content string `json:"content"`
ReasoningContent interface{} `json:"reasoning_content"` ReasoningContent interface{} `json:"reasoning_content"`
} `json:"message"` } `json:"message"`
FinishReason interface{} `json:"finish_reason"` FinishReason interface{} `json:"finish_reason"`
} `json:"choices"` } `json:"choices"`
Usage struct { Usage struct {
PromptTokens int `json:"prompt_tokens"` PromptTokens int `json:"prompt_tokens"`
TotalTokens int `json:"total_tokens"` TotalTokens int `json:"total_tokens"`
CompletionTokens int `json:"completion_tokens"` CompletionTokens int `json:"completion_tokens"`
} `json:"usage"` } `json:"usage"`
} }
// Assuming ai.Part has a Text() method or Text field to get string content. // Assuming ai.Part has a Text() method or Text field to get string content.
func partsToString(parts []*ai.Part) string { func partsToString(parts []*ai.Part) string {
log.Debug(). log.Debug().
Str("method", "partsToString"). Str("method", "partsToString").
Int("parts", len(parts)). Int("parts", len(parts)).
Msg("Converting parts to string") Msg("Converting parts to string")
var texts []string var texts []string
for _, part := range parts { for _, part := range parts {
texts = append(texts, part.Text) texts = append(texts, part.Text)
} }
result := strings.Join(texts, " ") result := strings.Join(texts, " ")
log.Debug(). log.Debug().
Str("method", "partsToString"). Str("method", "partsToString").
Str("result", result). Str("result", result).
Msg("Conversion complete") Msg("Conversion complete")
return result return result
} }
// Retrieve implements the Retriever.Retrieve method. // Retrieve implements the Retriever.Retrieve method.
func (ds *docStore) Retrieve(ctx context.Context, req *ai.RetrieverRequest) (*ai.RetrieverResponse, error) { func (ds *docStore) Retrieve(ctx context.Context, req *ai.RetrieverRequest) (*ai.RetrieverResponse, error) {
log.Info(). log.Info().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("space_id", ds.spaceID). Str("space_id", ds.spaceID).
Msg("Starting retrieve operation") Msg("Starting retrieve operation")
// Format query for retrieval. // Format query for retrieval.
queryContent := partsToString(req.Query.Content) queryContent := partsToString(req.Query.Content)
queryText := fmt.Sprintf("Search for: %s", queryContent) queryText := fmt.Sprintf("Search for: %s", queryContent)
log.Debug(). log.Debug().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("query", queryText). Str("query", queryText).
Msg("Formatted query") Msg("Formatted query")
if req.Query.Metadata == nil { if req.Query.Metadata == nil {
log.Error(). log.Error().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("metadata_type", fmt.Sprintf("%T", req.Query.Metadata)). Str("metadata_type", fmt.Sprintf("%T", req.Query.Metadata)).
Msg("Query metadata is nil") Msg("Query metadata is nil")
return nil, fmt.Errorf("req.Query.Metadata must be not nil, got type %T", req.Query.Metadata) return nil, fmt.Errorf("req.Query.Metadata must be not nil, got type %T", req.Query.Metadata)
} }
for k, v := range req.Query.Metadata { for k, v := range req.Query.Metadata {
log.Debug(). log.Debug().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("key", k). Str("key", k).
Interface("value", v). Interface("value", v).
Msg("Metadata entry") Msg("Metadata entry")
} }
// Extract username and user_id from req.Query.Metadata // Extract username and user_id from req.Query.Metadata
userName, ok := req.Query.Metadata[util.UserNameKey].(string) userName, ok := req.Query.Metadata[util.UserNameKey].(string)
if !ok { if !ok {
log.Error().Str("method", "docStore.Retrieve").Msg("Missing username in metadata") log.Error().Str("method", "docStore.Retrieve").Msg("Missing username in metadata")
return nil, fmt.Errorf("req.Query.Metadata must provide username key") return nil, fmt.Errorf("req.Query.Metadata must provide username key")
} }
userId, ok := req.Query.Metadata[util.UserIdKey].(string) userId, ok := req.Query.Metadata[util.UserIdKey].(string)
if !ok { if !ok {
log.Error().Str("method", "docStore.Retrieve").Msg("Missing user_id in metadata") log.Error().Str("method", "docStore.Retrieve").Msg("Missing user_id in metadata")
return nil, fmt.Errorf("req.Query.Metadata must provide user_id key") return nil, fmt.Errorf("req.Query.Metadata must provide user_id key")
} }
// Prepare request for chat completions endpoint. // Prepare request for chat completions endpoint.
url := fmt.Sprintf("%s/api/v2/chat/completions", ds.client.BaseURL) url := fmt.Sprintf("%s/api/v2/chat/completions", ds.client.BaseURL)
chatReq := ChatRequest{ chatReq := ChatRequest{
Model: ds.modelName, Model: ds.modelName,
Messages: queryText, Messages: queryText,
Temperature: 0.7, Temperature: 0.7,
TopP: 1, TopP: 1,
TopK: -1, TopK: -1,
N: 1, N: 1,
MaxTokens: 0, MaxTokens: 0,
Stream: false, Stream: false,
RepetitionPenalty: 1, RepetitionPenalty: 1,
FrequencyPenalty: 0, FrequencyPenalty: 0,
PresencePenalty: 0, PresencePenalty: 0,
ChatMode: "chat_knowledge", ChatMode: "chat_knowledge",
ChatParam: userId, ChatParam: userId,
EnableVis: true, EnableVis: true,
} }
log.Debug(). log.Debug().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("url", url). Str("url", url).
Interface("chat_request", chatReq). Interface("chat_request", chatReq).
Msg("Preparing chat completion request") Msg("Preparing chat completion request")
body, err := json.Marshal(chatReq) body, err := json.Marshal(chatReq)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "docStore.Retrieve").Msg("Failed to marshal chat request") log.Error().Err(err).Str("method", "docStore.Retrieve").Msg("Failed to marshal chat request")
return nil, fmt.Errorf("marshal chat request: %w", err) return nil, fmt.Errorf("marshal chat request: %w", err)
} }
httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(body)) httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(body))
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "docStore.Retrieve").Msg("Failed to create chat request") log.Error().Err(err).Str("method", "docStore.Retrieve").Msg("Failed to create chat request")
return nil, fmt.Errorf("create chat request: %w", err) return nil, fmt.Errorf("create chat request: %w", err)
} }
httpReq.Header.Set("Accept", "application/json") httpReq.Header.Set("Accept", "application/json")
httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{} client := &http.Client{}
resp, err := client.Do(httpReq) resp, err := client.Do(httpReq)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "docStore.Retrieve").Msg("Failed to send chat request") log.Error().Err(err).Str("method", "docStore.Retrieve").Msg("Failed to send chat request")
return nil, fmt.Errorf("send chat request: %w", err) return nil, fmt.Errorf("send chat request: %w", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
log.Error(). log.Error().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Int("status_code", resp.StatusCode). Int("status_code", resp.StatusCode).
Str("response_body", string(body)). Str("response_body", string(body)).
Msg("Chat completion failed") Msg("Chat completion failed")
return nil, fmt.Errorf("chat completion failed with status %d: %s", resp.StatusCode, string(body)) return nil, fmt.Errorf("chat completion failed with status %d: %s", resp.StatusCode, string(body))
} }
// Parse response // Parse response
var chatResp ChatResponse var chatResp ChatResponse
respBody, err := io.ReadAll(resp.Body) respBody, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "docStore.Retrieve").Msg("Failed to read response body") log.Error().Err(err).Str("method", "docStore.Retrieve").Msg("Failed to read response body")
return nil, fmt.Errorf("read chat response body: %w", err) return nil, fmt.Errorf("read chat response body: %w", err)
} }
if err := json.Unmarshal(respBody, &chatResp); err != nil { if err := json.Unmarshal(respBody, &chatResp); err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("raw_response", string(respBody)). Str("raw_response", string(respBody)).
Msg("Failed to decode chat response") Msg("Failed to decode chat response")
return nil, fmt.Errorf("decode chat response: %w, raw response: %s", err, string(respBody)) return nil, fmt.Errorf("decode chat response: %w, raw response: %s", err, string(respBody))
} }
log.Debug(). log.Debug().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Interface("chat_response", chatResp). Interface("chat_response", chatResp).
Msg("Parsed chat response") Msg("Parsed chat response")
// Convert response to ai.Document // Convert response to ai.Document
var docs []*ai.Document var docs []*ai.Document
if len(chatResp.Choices) > 0 { if len(chatResp.Choices) > 0 {
content := chatResp.Choices[0].Message.Content content := chatResp.Choices[0].Message.Content
metadata := map[string]interface{}{ metadata := map[string]interface{}{
util.UserIdKey: userId, util.UserIdKey: userId,
util.UserNameKey: userName, util.UserNameKey: userName,
} }
aiDoc := ai.DocumentFromText(content, metadata) aiDoc := ai.DocumentFromText(content, metadata)
docs = append(docs, aiDoc) docs = append(docs, aiDoc)
log.Debug(). log.Debug().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("content", content). Str("content", content).
Interface("metadata", metadata). Interface("metadata", metadata).
Msg("Created document from response") Msg("Created document from response")
} }
log.Info(). log.Info().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("space_id", ds.spaceID). Str("space_id", ds.spaceID).
Int("documents", len(docs)). Int("documents", len(docs)).
Msg("Retrieve operation completed successfully") Msg("Retrieve operation completed successfully")
return &ai.RetrieverResponse{ return &ai.RetrieverResponse{
Documents: docs, Documents: docs,
}, nil }, nil
} }
// // Copyright 2025 Google LLC // // Copyright 2025 Google LLC
// // // //
// // Licensed under the Apache License, Version 2.0 (the "License"); // // Licensed under the Apache License, Version 2.0 (the "License");
......
...@@ -10,41 +10,40 @@ import ( ...@@ -10,41 +10,40 @@ import (
) )
func main() { func main() {
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密 // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性 // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性
// 以下代码示例仅供参考,建议采用更安全的方式来使用密钥 // 以下代码示例仅供参考,建议采用更安全的方式来使用密钥
// 请参见:https://cloud.tencent.com/document/product/1278/85305 // 请参见:https://cloud.tencent.com/document/product/1278/85305
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取 // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
//os.Setenv("TENCENTCLOUD_SECRET_ID", "AKID64oLfmfLtESUJ6i8LPSM4gCVbiniQuBF")
//os.Setenv("TENCENTCLOUD_SECRET_KEY", "rX2JMBnBMJ2YqulOo37xa5OUMSN4Xnpd")
//os.Setenv("TENCENTCLOUD_SECRET_ID", "AKID64oLfmfLtESUJ6i8LPSM4gCVbiniQuBF") credential := common.NewCredential(
//os.Setenv("TENCENTCLOUD_SECRET_KEY", "rX2JMBnBMJ2YqulOo37xa5OUMSN4Xnpd") "AKID64oLfmfLtESUJ6i8LPSM4gCVbiniQuBF",
"rX2JMBnBMJ2YqulOo37xa5OUMSN4Xnpd",
)
// 使用临时密钥示例
// credential := common.NewTokenCredential("SecretId", "SecretKey", "Token")
// 实例化一个client选项,可选的,没有特殊需求可以跳过
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "lkeap.tencentcloudapi.com"
// 实例化要请求产品的client对象,clientProfile是可选的
client, _ := lkeap.NewClient(credential, "ap-guangzhou", cpf)
credential := common.NewCredential( // 实例化一个请求对象,每个接口都会对应一个request对象
"AKID64oLfmfLtESUJ6i8LPSM4gCVbiniQuBF", request := lkeap.NewQueryRewriteRequest()
"rX2JMBnBMJ2YqulOo37xa5OUMSN4Xnpd",
)
// 使用临时密钥示例
// credential := common.NewTokenCredential("SecretId", "SecretKey", "Token")
// 实例化一个client选项,可选的,没有特殊需求可以跳过
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "lkeap.tencentcloudapi.com"
// 实例化要请求产品的client对象,clientProfile是可选的
client, _ := lkeap.NewClient(credential, "ap-guangzhou", cpf)
// 实例化一个请求对象,每个接口都会对应一个request对象 request.Model = common.StringPtr("*")
request := lkeap.NewQueryRewriteRequest() // 返回的resp是一个QueryRewriteResponse的实例,与请求对象对应
response, err := client.QueryRewrite(request)
request.Model = common.StringPtr("*") if _, ok := err.(*errors.TencentCloudSDKError); ok {
// 返回的resp是一个QueryRewriteResponse的实例,与请求对象对应 fmt.Printf("An API error has returned: %s", err)
response, err := client.QueryRewrite(request) return
if _, ok := err.(*errors.TencentCloudSDKError); ok { }
fmt.Printf("An API error has returned: %s", err) if err != nil {
return panic(err)
} }
if err != nil { // 输出json格式的字符串回包
panic(err) fmt.Printf("%s", response.ToJsonString())
} }
// 输出json格式的字符串回包
fmt.Printf("%s", response.ToJsonString())
}
\ No newline at end of file
// // Copyright 2025
// //
// // Licensed under the Apache License, Version 2.0 (the "License");
// // you may not use this file except in compliance with the License.
// // You may obtain a copy of the License at
// //
// // http://www.apache.org/licenses/LICENSE-2.0
// //
// // Unless required by applicable law or agreed to in writing, software
// // distributed under the License is distributed on an "AS IS" BASIS,
// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// // See the License for the specific language governing permissions and
// // limitations under the License.
// //
// // SPDX-License-Identifier: Apache-2.0
// package knowledge
// import (
// "context"
// "encoding/json"
// "fmt"
// "os"
// "strings"
// "sync"
// "github.com/rs/zerolog/log"
// "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
// "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
// "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
// lkeap "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/lkeap/v20240522"
// )
// // ClientConfig holds configuration options for the Tencent Cloud LKEAP client.
// type ClientConfig struct {
// SecretID string // Tencent Cloud Secret ID
// SecretKey string // Tencent Cloud Secret Key
// Token string // Optional: Temporary token for authentication
// Endpoint string // API endpoint (default: lkeap.tencentcloudapi.com)
// Region string // Tencent Cloud region (optional)
// }
// // KnowledgeClient manages interactions with the Tencent Cloud LKEAP API.
// type KnowledgeClient struct {
// client *lkeap.Client
// config ClientConfig
// mu sync.Mutex
// initted bool
// }
// // NewKnowledgeClient creates a new KnowledgeClient with the given configuration.
// func NewKnowledgeClient(config ClientConfig) *KnowledgeClient {
// log.Info().
// Str("method", "NewKnowledgeClient").
// Str("endpoint", config.Endpoint).
// Str("region", config.Region).
// Str("secret_id", maskCredential(config.SecretID)).
// Str("token", maskCredential(config.Token)).
// Msg("Creating new KnowledgeClient")
// return &KnowledgeClient{
// config: config,
// }
// }
// // Init initializes the KnowledgeClient.
// func (kc *KnowledgeClient) Init(ctx context.Context) error {
// log.Info().Str("method", "KnowledgeClient.Init").Msg("Initializing KnowledgeClient")
// kc.mu.Lock()
// defer kc.mu.Unlock()
// if kc.initted {
// log.Error().Str("method", "KnowledgeClient.Init").Msg("Client already initialized")
// return fmt.Errorf("knowledge client already initialized")
// }
// // Load configuration from environment variables if not set
// if kc.config.SecretID == "" {
// kc.config.SecretID = os.Getenv("TENCENTCLOUD_SECRET_ID")
// }
// if kc.config.SecretKey == "" {
// kc.config.SecretKey = os.Getenv("TENCENTCLOUD_SECRET_KEY")
// }
// if kc.config.Token == "" {
// kc.config.Token = os.Getenv("TENCENTCLOUD_TOKEN")
// }
// if kc.config.Endpoint == "" {
// kc.config.Endpoint = "lkeap.tencentcloudapi.com"
// }
// if kc.config.Region == "" {
// kc.config.Region = "ap-guangzhou"
// }
// // Validate configuration
// if kc.config.SecretID == "" || kc.config.SecretKey == "" {
// log.Error().Str("method", "KnowledgeClient.Init").Msg("SecretID and SecretKey are required")
// return fmt.Errorf("knowledge: SecretID and SecretKey are required")
// }
// // Create credential
// var credential *common.Credential
// if kc.config.Token != "" {
// credential = common.NewTokenCredential(kc.config.SecretID, kc.config.SecretKey, kc.config.Token)
// log.Debug().Str("method", "KnowledgeClient.Init").Msg("Using temporary token credential")
// } else {
// credential = common.NewCredential(kc.config.SecretID, kc.config.SecretKey)
// log.Debug().Str("method", "KnowledgeClient.Init").Msg("Using standard credential")
// }
// // Create client profile
// cpf := profile.NewClientProfile()
// cpf.HttpProfile.Endpoint = kc.config.Endpoint
// // Initialize client
// client, err := lkeap.NewClient(credential, kc.config.Region, cpf)
// if err != nil {
// log.Error().
// Err(err).
// Str("method", "KnowledgeClient.Init").
// Msg("Failed to create LKEAP client")
// return err
// }
// kc.client = client
// kc.initted = true
// log.Info().Str("method", "KnowledgeClient.Init").Msg("Initialization successful")
// return nil
// }
// // QueryRewriteRequest defines the input for a query rewrite operation.
// type QueryRewriteRequest struct {
// Messages []*lkeap.Message // Multi-turn conversation history (up to 4 turns)
// Model string // Model name for query rewriting
// }
// // QueryRewriteResponse defines the output of a query rewrite operation.
// type QueryRewriteResponse struct {
// RewrittenQuery string // The rewritten query
// RawResponse *lkeap.QueryRewriteResponse
// }
// // QueryRewrite performs a query rewrite using the Tencent Cloud LKEAP API.
// func (kc *KnowledgeClient) QueryRewrite(ctx context.Context, req QueryRewriteRequest) (*QueryRewriteResponse, error) {
// log.Info().
// Str("method", "KnowledgeClient.QueryRewrite").
// Int("message_count", len(req.Messages)).
// Str("model", req.Model).
// Msg("Starting query rewrite operation")
// if !kc.initted {
// log.Error().Str("method", "KnowledgeClient.QueryRewrite").Msg("Client not initialized")
// return nil, fmt.Errorf("knowledge client not initialized; call Init first")
// }
// // Validate input
// if len(req.Messages) == 0 {
// log.Error().Str("method", "KnowledgeClient.QueryRewrite").Msg("At least one message is required")
// return nil, fmt.Errorf("at least one message is required")
// }
// if len(req.Messages) > 4 {
// log.Warn().
// Str("method", "KnowledgeClient.QueryRewrite").
// Int("message_count", len(req.Messages)).
// Msg("Message count exceeds 4, truncating to 4")
// req.Messages = req.Messages[:4]
// }
// for i, msg := range req.Messages {
// if msg.Role == nil || *msg.Role == "" {
// log.Error().
// Str("method", "KnowledgeClient.QueryRewrite").
// Int("index", i).
// Msg("Role is required in each message")
// return nil, fmt.Errorf("message at index %d missing role", i)
// }
// if *msg.Role != "user" && *msg.Role != "assistant" {
// log.Error().
// Str("method", "KnowledgeClient.QueryRewrite").
// Int("index", i).
// Str("role", *msg.Role).
// Msg("Invalid role; must be 'user' or 'assistant'")
// return nil, fmt.Errorf("invalid role '%s' at index %d", *msg.Role, i)
// }
// if msg.Content == nil || *msg.Content == "" {
// log.Error().
// Str("method", "KnowledgeClient.QueryRewrite").
// Int("index", i).
// Msg("Content is required in each message")
// return nil, fmt.Errorf("message at index %d missing content", i)
// }
// log.Debug().
// Str("method", "KnowledgeClient.QueryRewrite").
// Int("index", i).
// Str("role", *msg.Role).
// Str("content", *msg.Content).
// Msg("Validated message")
// }
// if req.Model == "" {
// log.Warn().Str("method", "KnowledgeClient.QueryRewrite").Msg("Model not specified, using default")
// req.Model = "lke-query-rewrite-base"
// }
// // Create Tencent Cloud request
// tencentReq := lkeap.NewQueryRewriteRequest()
// tencentReq.Messages = req.Messages
// if req.Model != "" {
// tencentReq.Model = common.StringPtr(req.Model)
// }
// // Debug request
// tencentReqAsJson, _ := json.Marshal(tencentReq)
// log.Debug().
// Str("method", "KnowledgeClient.QueryRewrite").
// Str("request_json", string(tencentReqAsJson)).
// Msg("Prepared Tencent Cloud request")
// // Perform request
// response, err := kc.client.QueryRewriteWithContext(ctx, tencentReq)
// if err != nil {
// if _, ok := err.(*errors.TencentCloudSDKError); ok {
// log.Error().
// Err(err).
// Str("method", "KnowledgeClient.QueryRewrite").
// Msg("Tencent Cloud API error")
// return nil, fmt.Errorf("tencent cloud api error: %w", err)
// }
// log.Error().
// Err(err).
// Str("method", "KnowledgeClient.QueryRewrite").
// Msg("Failed to perform query rewrite")
// return nil, fmt.Errorf("query rewrite failed: %w", err)
// }
// // Extract response fields
// var rewrittenQuery string
// var requestId string
// if response.Response.Content != nil {
// rewrittenQuery = *response.Response.Content
// }
// if response.Response.RequestId != nil {
// requestId = *response.Response.RequestId
// }
// result := &QueryRewriteResponse{
// RewrittenQuery: rewrittenQuery,
// RawResponse: response,
// }
// log.Info().
// Str("method", "KnowledgeClient.QueryRewrite").
// Str("rewritten_query", rewrittenQuery).
// Str("request_id", requestId).
// Interface("usage", response.Response.Usage).
// Str("raw_response", response.ToJsonString()).
// Msg("Query rewrite operation completed successfully")
// return result, nil
// }
// // QueryRewriteWithSummary wraps QueryRewrite to handle a user question, assistant answer, and history summary.
// func (kc *KnowledgeClient) QueryRewriteWithSummary(ctx context.Context, userQuestion, assistantAnswer, historySummary string) (*QueryRewriteResponse, error) {
// log.Info().
// Str("method", "KnowledgeClient.QueryRewriteWithSummary").
// Str("user_question", userQuestion).
// Str("assistant_answer", assistantAnswer).
// Str("history_summary", historySummary).
// Msg("Starting query rewrite with summary operation")
// if userQuestion == "" || assistantAnswer == "" {
// log.Error().Str("method", "KnowledgeClient.QueryRewriteWithSummary").Msg("User question and assistant answer are required")
// return nil, fmt.Errorf("user question and assistant answer are required")
// }
// // Construct messages
// messages := []*lkeap.Message{
// {
// Role: common.StringPtr("user"),
// Content: common.StringPtr(userQuestion),
// },
// {
// Role: common.StringPtr("assistant"),
// Content: common.StringPtr(assistantAnswer),
// },
// }
// // Append history summary as an assistant message if provided
// if historySummary != "" {
// messages = append(messages, &lkeap.Message{
// Role: common.StringPtr("assistant"),
// Content: common.StringPtr(fmt.Sprintf("Conversation summary: %s", historySummary)),
// })
// }
// // Create request
// req := QueryRewriteRequest{
// Messages: messages,
// Model: "lke-query-rewrite-base",
// }
// // Call QueryRewrite
// return kc.QueryRewrite(ctx, req)
// }
// // maskCredential masks sensitive credentials for logging
// func maskCredential(cred string) string {
// if len(cred) <= 8 {
// return strings.Repeat("*", len(cred))
// }
// return cred[:4] + strings.Repeat("*", len(cred)-8) + cred[len(cred)-4:]
// }
// Copyright 2025
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
package knowledge package knowledge
import ( import (
...@@ -341,278 +17,275 @@ import ( ...@@ -341,278 +17,275 @@ import (
// ClientConfig holds configuration options for the Tencent Cloud LKEAP client. // ClientConfig holds configuration options for the Tencent Cloud LKEAP client.
type ClientConfig struct { type ClientConfig struct {
SecretID string // Tencent Cloud Secret ID SecretID string // Tencent Cloud Secret ID
SecretKey string // Tencent Cloud Secret Key SecretKey string // Tencent Cloud Secret Key
Token string // Optional: Temporary token for authentication Token string // Optional: Temporary token for authentication
Endpoint string // API endpoint (default: lkeap.tencentcloudapi.com) Endpoint string // API endpoint (default: lkeap.tencentcloudapi.com)
Region string // Tencent Cloud region (optional) Region string // Tencent Cloud region (optional)
} }
// KnowledgeClient manages interactions with the Tencent Cloud LKEAP API. // KnowledgeClient manages interactions with the Tencent Cloud LKEAP API.
type KnowledgeClient struct { type KnowledgeClient struct {
client *lkeap.Client client *lkeap.Client
config ClientConfig config ClientConfig
mu sync.Mutex mu sync.Mutex
initted bool initted bool
} }
// NewKnowledgeClient creates a new KnowledgeClient with the given configuration. // NewKnowledgeClient creates a new KnowledgeClient with the given configuration.
func NewKnowledgeClient(config ClientConfig) *KnowledgeClient { func NewKnowledgeClient(config ClientConfig) *KnowledgeClient {
log.Info(). log.Info().
Str("method", "NewKnowledgeClient"). Str("method", "NewKnowledgeClient").
Str("endpoint", config.Endpoint). Str("endpoint", config.Endpoint).
Str("region", config.Region). Str("region", config.Region).
Str("secret_id", maskCredential(config.SecretID)). Str("secret_id", maskCredential(config.SecretID)).
Str("token", maskCredential(config.Token)). Str("token", maskCredential(config.Token)).
Msg("Creating new KnowledgeClient") Msg("Creating new KnowledgeClient")
return &KnowledgeClient{ return &KnowledgeClient{
config: config, config: config,
} }
} }
// Init initializes the KnowledgeClient. // Init initializes the KnowledgeClient.
func (kc *KnowledgeClient) Init(ctx context.Context) error { func (kc *KnowledgeClient) Init(ctx context.Context) error {
log.Info().Str("method", "KnowledgeClient.Init").Msg("Initializing KnowledgeClient") log.Info().Str("method", "KnowledgeClient.Init").Msg("Initializing KnowledgeClient")
kc.mu.Lock() kc.mu.Lock()
defer kc.mu.Unlock() defer kc.mu.Unlock()
if kc.initted { if kc.initted {
log.Error().Str("method", "KnowledgeClient.Init").Msg("Client already initialized") log.Error().Str("method", "KnowledgeClient.Init").Msg("Client already initialized")
return fmt.Errorf("knowledge client already initialized") return fmt.Errorf("knowledge client already initialized")
} }
// Load configuration from environment variables if not set // Load configuration from environment variables if not set
if kc.config.SecretID == "" { if kc.config.SecretID == "" {
kc.config.SecretID = os.Getenv("TENCENTCLOUD_SECRET_ID") kc.config.SecretID = os.Getenv("TENCENTCLOUD_SECRET_ID")
} }
if kc.config.SecretKey == "" { if kc.config.SecretKey == "" {
kc.config.SecretKey = os.Getenv("TENCENTCLOUD_SECRET_KEY") kc.config.SecretKey = os.Getenv("TENCENTCLOUD_SECRET_KEY")
} }
if kc.config.Token == "" { if kc.config.Token == "" {
kc.config.Token = os.Getenv("TENCENTCLOUD_TOKEN") kc.config.Token = os.Getenv("TENCENTCLOUD_TOKEN")
} }
if kc.config.Endpoint == "" { if kc.config.Endpoint == "" {
kc.config.Endpoint = "lkeap.tencentcloudapi.com" kc.config.Endpoint = "lkeap.tencentcloudapi.com"
} }
if kc.config.Region == "" { if kc.config.Region == "" {
kc.config.Region = "ap-guangzhou" // Default to ap-guangzhou as per curl kc.config.Region = "ap-guangzhou" // Default to ap-guangzhou as per curl
} }
// Validate configuration // Validate configuration
if kc.config.SecretID == "" || kc.config.SecretKey == "" { if kc.config.SecretID == "" || kc.config.SecretKey == "" {
log.Error().Str("method", "KnowledgeClient.Init").Msg("SecretID and SecretKey are required") log.Error().Str("method", "KnowledgeClient.Init").Msg("SecretID and SecretKey are required")
return fmt.Errorf("knowledge: SecretID and SecretKey are required") return fmt.Errorf("knowledge: SecretID and SecretKey are required")
} }
// Create credential // Create credential
var credential *common.Credential var credential *common.Credential
if kc.config.Token != "" { if kc.config.Token != "" {
credential = common.NewTokenCredential(kc.config.SecretID, kc.config.SecretKey, kc.config.Token) credential = common.NewTokenCredential(kc.config.SecretID, kc.config.SecretKey, kc.config.Token)
log.Debug().Str("method", "KnowledgeClient.Init").Msg("Using temporary token credential") log.Debug().Str("method", "KnowledgeClient.Init").Msg("Using temporary token credential")
} else { } else {
credential = common.NewCredential(kc.config.SecretID, kc.config.SecretKey) credential = common.NewCredential(kc.config.SecretID, kc.config.SecretKey)
log.Debug().Str("method", "KnowledgeClient.Init").Msg("Using standard credential") log.Debug().Str("method", "KnowledgeClient.Init").Msg("Using standard credential")
} }
// Create client profile // Create client profile
cpf := profile.NewClientProfile() cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = kc.config.Endpoint cpf.HttpProfile.Endpoint = kc.config.Endpoint
// Initialize client // Initialize client
client, err := lkeap.NewClient(credential, kc.config.Region, cpf) client, err := lkeap.NewClient(credential, kc.config.Region, cpf)
if err != nil { if err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "KnowledgeClient.Init"). Str("method", "KnowledgeClient.Init").
Msg("Failed to create LKEAP client") Msg("Failed to create LKEAP client")
return err return err
} }
kc.client = client kc.client = client
kc.initted = true kc.initted = true
log.Info().Str("method", "KnowledgeClient.Init").Msg("Initialization successful") log.Info().Str("method", "KnowledgeClient.Init").Msg("Initialization successful")
return nil return nil
} }
// QueryRewriteRequest defines the input for a query rewrite operation. // QueryRewriteRequest defines the input for a query rewrite operation.
type QueryRewriteRequest struct { type QueryRewriteRequest struct {
Messages []*lkeap.Message // Multi-turn conversation history (up to 4 turns) Messages []*lkeap.Message // Multi-turn conversation history (up to 4 turns)
Model string // Model name for query rewriting Model string // Model name for query rewriting
} }
// QueryRewriteResponse defines the output of a query rewrite operation. // QueryRewriteResponse defines the output of a query rewrite operation.
type QueryRewriteResponse struct { type QueryRewriteResponse struct {
RewrittenQuery string // The rewritten query RewrittenQuery string // The rewritten query
RawResponse *lkeap.QueryRewriteResponse RawResponse *lkeap.QueryRewriteResponse
} }
// QueryRewrite performs a query rewrite using the Tencent Cloud LKEAP API. // QueryRewrite performs a query rewrite using the Tencent Cloud LKEAP API.
func (kc *KnowledgeClient) QueryRewrite(ctx context.Context, req QueryRewriteRequest) (*QueryRewriteResponse, error) { func (kc *KnowledgeClient) QueryRewrite(ctx context.Context, req QueryRewriteRequest) (*QueryRewriteResponse, error) {
log.Info(). log.Info().
Str("method", "KnowledgeClient.QueryRewrite"). Str("method", "KnowledgeClient.QueryRewrite").
Int("message_count", len(req.Messages)). Int("message_count", len(req.Messages)).
Str("model", req.Model). Str("model", req.Model).
Msg("Starting query rewrite operation") Msg("Starting query rewrite operation")
if !kc.initted { if !kc.initted {
log.Error().Str("method", "KnowledgeClient.QueryRewrite").Msg("Client not initialized") log.Error().Str("method", "KnowledgeClient.QueryRewrite").Msg("Client not initialized")
return nil, fmt.Errorf("knowledge client not initialized; call Init first") return nil, fmt.Errorf("knowledge client not initialized; call Init first")
} }
// Validate input // Validate input
if len(req.Messages) == 0 { if len(req.Messages) == 0 {
log.Error().Str("method", "KnowledgeClient.QueryRewrite").Msg("At least one message is required") log.Error().Str("method", "KnowledgeClient.QueryRewrite").Msg("At least one message is required")
return nil, fmt.Errorf("at least one message is required") return nil, fmt.Errorf("at least one message is required")
} }
if len(req.Messages) > 4 { if len(req.Messages) > 4 {
log.Warn(). log.Warn().
Str("method", "KnowledgeClient.QueryRewrite"). Str("method", "KnowledgeClient.QueryRewrite").
Int("message_count", len(req.Messages)). Int("message_count", len(req.Messages)).
Msg("Message count exceeds 4, truncating to 4") Msg("Message count exceeds 4, truncating to 4")
req.Messages = req.Messages[:4] req.Messages = req.Messages[:4]
} }
for i, msg := range req.Messages { for i, msg := range req.Messages {
if msg.Role == nil || *msg.Role == "" { if msg.Role == nil || *msg.Role == "" {
log.Error(). log.Error().
Str("method", "KnowledgeClient.QueryRewrite"). Str("method", "KnowledgeClient.QueryRewrite").
Int("index", i). Int("index", i).
Msg("Role is required in each message") Msg("Role is required in each message")
return nil, fmt.Errorf("message at index %d missing role", i) return nil, fmt.Errorf("message at index %d missing role", i)
} }
if *msg.Role != "user" && *msg.Role != "assistant" { if *msg.Role != "user" && *msg.Role != "assistant" {
log.Error(). log.Error().
Str("method", "KnowledgeClient.QueryRewrite"). Str("method", "KnowledgeClient.QueryRewrite").
Int("index", i). Int("index", i).
Str("role", *msg.Role). Str("role", *msg.Role).
Msg("Invalid role; must be 'user' or 'assistant'") Msg("Invalid role; must be 'user' or 'assistant'")
return nil, fmt.Errorf("invalid role '%s' at index %d", *msg.Role, i) return nil, fmt.Errorf("invalid role '%s' at index %d", *msg.Role, i)
} }
if msg.Content == nil || *msg.Content == "" { if msg.Content == nil || *msg.Content == "" {
log.Error(). log.Error().
Str("method", "KnowledgeClient.QueryRewrite"). Str("method", "KnowledgeClient.QueryRewrite").
Int("index", i). Int("index", i).
Msg("Content is required in each message") Msg("Content is required in each message")
return nil, fmt.Errorf("message at index %d missing content", i) return nil, fmt.Errorf("message at index %d missing content", i)
} }
log.Debug(). log.Debug().
Str("method", "KnowledgeClient.QueryRewrite"). Str("method", "KnowledgeClient.QueryRewrite").
Int("index", i). Int("index", i).
Str("role", *msg.Role). Str("role", *msg.Role).
Str("content", *msg.Content). Str("content", *msg.Content).
Msg("Validated message") Msg("Validated message")
} }
if req.Model == "" { if req.Model == "" {
log.Warn().Str("method", "KnowledgeClient.QueryRewrite").Msg("Model not specified, using default") log.Warn().Str("method", "KnowledgeClient.QueryRewrite").Msg("Model not specified, using default")
req.Model = "lke-query-rewrite-base" // Default as per curl req.Model = "lke-query-rewrite-base" // Default as per curl
} }
// Create Tencent Cloud request // Create Tencent Cloud request
tencentReq := lkeap.NewQueryRewriteRequest() tencentReq := lkeap.NewQueryRewriteRequest()
tencentReq.Messages = req.Messages tencentReq.Messages = req.Messages
if req.Model != "" { if req.Model != "" {
tencentReq.Model = common.StringPtr(req.Model) tencentReq.Model = common.StringPtr(req.Model)
} }
// Debug request // Debug request
tencentReqAsJson, _ := json.Marshal(tencentReq) tencentReqAsJson, _ := json.Marshal(tencentReq)
log.Debug(). log.Debug().
Str("method", "KnowledgeClient.QueryRewrite"). Str("method", "KnowledgeClient.QueryRewrite").
Str("request_json", string(tencentReqAsJson)). Str("request_json", string(tencentReqAsJson)).
Msg("Prepared Tencent Cloud request") Msg("Prepared Tencent Cloud request")
// Perform request // Perform request
response, err := kc.client.QueryRewriteWithContext(ctx, tencentReq) response, err := kc.client.QueryRewriteWithContext(ctx, tencentReq)
if err != nil { if err != nil {
if _, ok := err.(*errors.TencentCloudSDKError); ok { if _, ok := err.(*errors.TencentCloudSDKError); ok {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "KnowledgeClient.QueryRewrite"). Str("method", "KnowledgeClient.QueryRewrite").
Msg("Tencent Cloud API error") Msg("Tencent Cloud API error")
return nil, fmt.Errorf("tencent cloud api error: %w", err) return nil, fmt.Errorf("tencent cloud api error: %w", err)
} }
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "KnowledgeClient.QueryRewrite"). Str("method", "KnowledgeClient.QueryRewrite").
Msg("Failed to perform query rewrite") Msg("Failed to perform query rewrite")
return nil, fmt.Errorf("query rewrite failed: %w", err) return nil, fmt.Errorf("query rewrite failed: %w", err)
} }
// Extract response fields // Extract response fields
var rewrittenQuery string var rewrittenQuery string
var requestId string var requestId string
if response.Response.Content != nil { if response.Response.Content != nil {
rewrittenQuery = *response.Response.Content rewrittenQuery = *response.Response.Content
} }
if response.Response.RequestId != nil { if response.Response.RequestId != nil {
requestId = *response.Response.RequestId requestId = *response.Response.RequestId
} }
result := &QueryRewriteResponse{ result := &QueryRewriteResponse{
RewrittenQuery: rewrittenQuery, RewrittenQuery: rewrittenQuery,
RawResponse: response, RawResponse: response,
} }
log.Info(). log.Info().
Str("method", "KnowledgeClient.QueryRewrite"). Str("method", "KnowledgeClient.QueryRewrite").
Str("rewritten_query", rewrittenQuery). Str("rewritten_query", rewrittenQuery).
Str("request_id", requestId). Str("request_id", requestId).
Interface("usage", response.Response.Usage). Interface("usage", response.Response.Usage).
Str("raw_response", response.ToJsonString()). Str("raw_response", response.ToJsonString()).
Msg("Query rewrite operation completed successfully") Msg("Query rewrite operation completed successfully")
return result, nil return result, nil
} }
// maskCredential masks sensitive credentials for logging // maskCredential masks sensitive credentials for logging
func maskCredential(cred string) string { func maskCredential(cred string) string {
if len(cred) <= 8 { if len(cred) <= 8 {
return strings.Repeat("*", len(cred)) return strings.Repeat("*", len(cred))
} }
return cred[:4] + strings.Repeat("*", len(cred)-8) + cred[len(cred)-4:] return cred[:4] + strings.Repeat("*", len(cred)-8) + cred[len(cred)-4:]
} }
// QueryRewriteWithSummary wraps QueryRewrite to handle a user question, assistant answer, and history summary. // QueryRewriteWithSummary wraps QueryRewrite to handle a user question, assistant answer, and history summary.
func (kc *KnowledgeClient) QueryRewriteWithSummary(ctx context.Context, userQuestion, assistantAnswer, historySummary string) (*QueryRewriteResponse, error) { func (kc *KnowledgeClient) QueryRewriteWithSummary(ctx context.Context, userQuestion, assistantAnswer, historySummary string) (*QueryRewriteResponse, error) {
log.Info(). log.Info().
Str("method", "KnowledgeClient.QueryRewriteWithSummary"). Str("method", "KnowledgeClient.QueryRewriteWithSummary").
Str("user_question", userQuestion). Str("user_question", userQuestion).
Str("assistant_answer", assistantAnswer). Str("assistant_answer", assistantAnswer).
Str("history_summary", historySummary). Str("history_summary", historySummary).
Msg("Starting query rewrite with summary operation") Msg("Starting query rewrite with summary operation")
if userQuestion == "" || assistantAnswer == "" { if userQuestion == "" || assistantAnswer == "" {
log.Error().Str("method", "KnowledgeClient.QueryRewriteWithSummary").Msg("User question and assistant answer are required") log.Error().Str("method", "KnowledgeClient.QueryRewriteWithSummary").Msg("User question and assistant answer are required")
return nil, fmt.Errorf("user question and assistant answer are required") return nil, fmt.Errorf("user question and assistant answer are required")
} }
// Construct messages // Construct messages
messages := []*lkeap.Message{ messages := []*lkeap.Message{
{ {
Role: common.StringPtr("user"), Role: common.StringPtr("user"),
Content: common.StringPtr(userQuestion), Content: common.StringPtr(userQuestion),
}, },
{ {
Role: common.StringPtr("assistant"), Role: common.StringPtr("assistant"),
Content: common.StringPtr(assistantAnswer), Content: common.StringPtr(assistantAnswer),
}, },
} }
// Append history summary as an assistant message if provided // Append history summary as an assistant message if provided
if historySummary != "" { if historySummary != "" {
messages = append(messages, &lkeap.Message{ messages = append(messages, &lkeap.Message{
Role: common.StringPtr("user"), Role: common.StringPtr("user"),
Content: common.StringPtr(fmt.Sprintf("Conversation summary: %s", historySummary)), Content: common.StringPtr(fmt.Sprintf("Conversation summary: %s", historySummary)),
}) })
} }
// Create request // Create request
req := QueryRewriteRequest{ req := QueryRewriteRequest{
Messages: messages, Messages: messages,
Model: "lke-query-rewrite-base", Model: "lke-query-rewrite-base",
} }
// Call QueryRewrite // Call QueryRewrite
return kc.QueryRewrite(ctx, req) return kc.QueryRewrite(ctx, req)
} }
// // Copyright 2025
// //
// // Licensed under the Apache License, Version 2.0 (the "License");
// // you may not use this file except in compliance with the License.
// // You may obtain a copy of the License at
// //
// // http://www.apache.org/licenses/LICENSE-2.0
// //
// // Unless required by applicable law or agreed to in writing, software
// // distributed under the License is distributed on an "AS IS" BASIS,
// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// // See the License for the specific language governing permissions and
// // limitations under the License.
// //
// // SPDX-License-Identifier: Apache-2.0
// package knowledge
// import (
// "context"
// "os"
// "testing"
// "github.com/rs/zerolog"
// "github.com/rs/zerolog/log"
// "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
// lkeap "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/lkeap/v20240522"
// )
// func TestMain(m *testing.M) {
// // Configure zerolog for human-readable console output during tests
// zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
// log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
// // Run tests
// os.Exit(m.Run())
// }
// func TestKnowledgeClient_QueryRewrite(t *testing.T) {
// // Warning: Do not hardcode credentials in production code. Use environment variables or a secure vault.
// os.Setenv("TENCENTCLOUD_SECRET_ID", "AKID64oLfmfLtESUJ6i8LPSM4gCVbiniQuBF")
// os.Setenv("TENCENTCLOUD_SECRET_KEY", "rX2JMBnBMJ2YqulOo37xa5OUMSN4Xnpd")
// defer func() {
// os.Unsetenv("TENCENTCLOUD_SECRET_ID")
// os.Unsetenv("TENCENTCLOUD_SECRET_KEY")
// }()
// // Create client configuration
// config := ClientConfig{
// Endpoint: "lkeap.tencentcloudapi.com",
// Region: "ap-guangzhou",
// }
// // Initialize client
// client := NewKnowledgeClient(config)
// ctx := context.Background()
// // Test cases
// tests := []struct {
// name string
// messages []*lkeap.Message
// model string
// expectError bool
// }{
// {
// name: "CurlPayload",
// messages: []*lkeap.Message{
// {
// Role: common.StringPtr("user"),
// Content: common.StringPtr("你的家在哪里"),
// },
// {
// Role: common.StringPtr("assistant"),
// Content: common.StringPtr("国内"),
// },
// {
// Role: common.StringPtr("user"),
// Content: common.StringPtr("国内哪里"),
// },
// },
// model: "lke-query-rewrite-base",
// expectError: true,
// },
// {
// name: "ValidMultiTurnConversation",
// messages: []*lkeap.Message{
// {
// Role: common.StringPtr("user"),
// Content: common.StringPtr("What is the capital of France?"),
// },
// {
// Role: common.StringPtr("assistant"),
// Content: common.StringPtr("The capital of France is Paris."),
// },
// {
// Role: common.StringPtr("user"),
// Content: common.StringPtr("Tell me more about Paris."),
// },
// {
// Role: common.StringPtr("assistant"),
// Content: common.StringPtr("Paris is known for its art, culture, and landmarks like the Eiffel Tower."),
// },
// },
// model: "",
// expectError: true,
// },
// {
// name: "EmptyMessages",
// messages: []*lkeap.Message{},
// model: "",
// expectError: true,
// },
// {
// name: "InvalidRole",
// messages: []*lkeap.Message{
// {
// Role: common.StringPtr("invalid-role"),
// Content: common.StringPtr("Test query"),
// },
// },
// model: "",
// expectError: true,
// },
// }
// for _, tt := range tests {
// t.Run(tt.name, func(t *testing.T) {
// // Initialize client for each test
// if err := client.Init(ctx); err != nil {
// t.Fatalf("Failed to initialize KnowledgeClient: %v", err)
// }
// // Perform query rewrite
// req := QueryRewriteRequest{
// Messages: tt.messages,
// Model: tt.model,
// }
// resp, err := client.QueryRewrite(ctx, req)
// // Check error expectation
// if tt.expectError {
// if err == nil {
// t.Error("Expected error, got none")
// } else {
// log.Debug().
// Str("method", "TestKnowledgeClient_QueryRewrite").
// Str("test_name", tt.name).
// Err(err).
// Msg("Received expected error")
// }
// return
// }
// // Check response
// if err != nil {
// t.Errorf("QueryRewrite failed: %v", err)
// }
// if resp.RewrittenQuery == "" {
// t.Error("Expected non-empty rewritten query")
// }
// log.Info().
// Str("method", "TestKnowledgeClient_QueryRewrite").
// Str("test_name", tt.name).
// Str("rewritten_query", resp.RewrittenQuery).
// Msg("Query rewrite successful")
// })
// }
// }
// func TestKnowledgeClient_QueryRewriteWithSummary(t *testing.T) {
// // Warning: Do not hardcode credentials in production code. Use environment variables or a secure vault.
// os.Setenv("TENCENTCLOUD_SECRET_ID", "AKID64oLfmfLtESUJ6i8LPSM4gCVbiniQuBF")
// os.Setenv("TENCENTCLOUD_SECRET_KEY", "rX2JMBnBMJ2YqulOo37xa5OUMSN4Xnpd")
// defer func() {
// os.Unsetenv("TENCENTCLOUD_SECRET_ID")
// os.Unsetenv("TENCENTCLOUD_SECRET_KEY")
// }()
// // Create client configuration
// config := ClientConfig{
// Endpoint: "lkeap.tencentcloudapi.com",
// Region: "ap-guangzhou",
// }
// // Initialize client
// client := NewKnowledgeClient(config)
// ctx := context.Background()
// // Test cases
// tests := []struct {
// name string
// userQuestion string
// assistantAnswer string
// historySummary string
// expectError bool
// }{
// {
// name: "ValidWithSummary",
// userQuestion: "你的家在哪里",
// assistantAnswer: "国内",
// historySummary: "User asked about location preferences earlier.",
// expectError: true, // Expect error due to potentially invalid credentials
// },
// {
// name: "ValidWithoutSummary",
// userQuestion: "你的家在哪里",
// assistantAnswer: "国内",
// historySummary: "",
// expectError: true,
// },
// {
// name: "EmptyQuestion",
// userQuestion: "",
// assistantAnswer: "国内",
// historySummary: "Summary",
// expectError: true,
// },
// {
// name: "EmptyAnswer",
// userQuestion: "你的家在哪里",
// assistantAnswer: "",
// historySummary: "Summary",
// expectError: true,
// },
// }
// for _, tt := range tests {
// t.Run(tt.name, func(t *testing.T) {
// // Initialize client for each test
// if err := client.Init(ctx); err != nil {
// t.Fatalf("Failed to initialize KnowledgeClient: %v", err)
// }
// // Perform query rewrite with summary
// resp, err := client.QueryRewriteWithSummary(ctx, tt.userQuestion, tt.assistantAnswer, tt.historySummary)
// // Check error expectation
// if tt.expectError {
// if err == nil {
// t.Error("Expected error, got none")
// } else {
// log.Debug().
// Str("method", "TestKnowledgeClient_QueryWithSummary").
// Str("test_name", tt.name).
// Str("error", err.Error()).
// Msg("Received expected error")
// }
// return
// }
// // Check response
// if err != nil {
// t.Errorf("QueryRewriteWithSummary failed: %v", err)
// }
// if resp.RewrittenQuery == "" {
// t.Error("Expected non-empty rewritten query")
// }
// log.Info().
// Str("method", "TestKnowledgeClient_QueryWithSummary").
// Str("test_name", tt.name).
// Str("rewritten_query", resp.RewrittenQuery).
// Msg("Query rewrite with summary successful")
// })
// }
// }
// Copyright 2025
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
package knowledge package knowledge
import ( import (
...@@ -296,221 +12,180 @@ import ( ...@@ -296,221 +12,180 @@ import (
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
// Configure zerolog for human-readable console output during tests // Configure zerolog for human-readable console output during tests
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
// Run tests // Run tests
os.Exit(m.Run()) os.Exit(m.Run())
} }
func TestKnowledgeClient_QueryRewrite(t *testing.T) { func TestKnowledgeClient_QueryRewrite(t *testing.T) {
// Warning: Do not hardcode credentials in production code. Use environment variables or a secure vault. // Warning: Do not hardcode credentials in production code. Use environment variables or a secure vault.
// The credentials below are placeholders for testing purposes. // The credentials below are placeholders for testing purposes.
os.Setenv("TENCENTCLOUD_SECRET_ID", "AKID64oLfmfLtESUJ6i8LPSM4gCVbiniQuBF") os.Setenv("TENCENTCLOUD_SECRET_ID", "AKID64oLfmfLtESUJ6i8LPSM4gCVbiniQuBF")
os.Setenv("TENCENTCLOUD_SECRET_KEY", "rX2JMBnBMJ2YqulOo37xa5OUMSN4Xnpd") os.Setenv("TENCENTCLOUD_SECRET_KEY", "rX2JMBnBMJ2YqulOo37xa5OUMSN4Xnpd")
defer func() { defer func() {
os.Unsetenv("TENCENTCLOUD_SECRET_ID") os.Unsetenv("TENCENTCLOUD_SECRET_ID")
os.Unsetenv("TENCENTCLOUD_SECRET_KEY") os.Unsetenv("TENCENTCLOUD_SECRET_KEY")
}() }()
// Create client configuration // Create client configuration
config := ClientConfig{ config := ClientConfig{
Endpoint: "lkeap.tencentcloudapi.com", Endpoint: "lkeap.tencentcloudapi.com",
Region: "ap-guangzhou", Region: "ap-guangzhou",
} }
// Initialize client // Initialize client
client := NewKnowledgeClient(config) client := NewKnowledgeClient(config)
ctx := context.Background() ctx := context.Background()
// Test cases // Test cases
tests := []struct { tests := []struct {
name string name string
messages []*lkeap.Message messages []*lkeap.Message
model string model string
expectError bool expectError bool
}{ }{
{ {
name: "CurlPayload", name: "CurlPayload",
messages: []*lkeap.Message{ messages: []*lkeap.Message{
{ {
Role: common.StringPtr("user"), Role: common.StringPtr("user"),
Content: common.StringPtr("你的家在哪里"), Content: common.StringPtr("你的家在哪里"),
}, },
{ {
Role: common.StringPtr("assistant"), Role: common.StringPtr("assistant"),
Content: common.StringPtr("国内"), Content: common.StringPtr("国内"),
}, },
{ {
Role: common.StringPtr("user"), Role: common.StringPtr("user"),
Content: common.StringPtr("国内哪里"), Content: common.StringPtr("国内哪里"),
}, },
}, },
model: "lke-query-rewrite-base", model: "lke-query-rewrite-base",
expectError: true, // Expect error due to potentially invalid credentials expectError: true, // Expect error due to potentially invalid credentials
}, },
// { }
// name: "ValidMultiTurnConversation",
// messages: []*lkeap.Message{ for _, tt := range tests {
// { t.Run(tt.name, func(t *testing.T) {
// Role: common.StringPtr("user"), // Initialize client for each test
// Content: common.StringPtr("What is the capital of France?"), if err := client.Init(ctx); err != nil {
// }, t.Fatalf("Failed to initialize KnowledgeClient: %v", err)
// { }
// Role: common.StringPtr("assistant"),
// Content: common.StringPtr("The capital of France is Paris."), // Perform query rewrite
// }, req := QueryRewriteRequest{
// { Messages: tt.messages,
// Role: common.StringPtr("user"), Model: tt.model,
// Content: common.StringPtr("Tell me more about Paris."), }
// }, resp, err := client.QueryRewrite(ctx, req)
// {
// Role: common.StringPtr("assistant"), // Check error expectation
// Content: common.StringPtr("Paris is known for its art, culture, and landmarks like the Eiffel Tower."), if tt.expectError {
// }, if err == nil {
// }, t.Error("Expected error, got none")
// model: "", } else {
// expectError: true, log.Debug().
// }, Str("method", "TestKnowledgeClient_QueryRewrite").
// { Str("test_name", tt.name).
// name: "EmptyMessages", Err(err).
// messages: []*lkeap.Message{}, Msg("Received expected error")
// model: "", }
// expectError: true, return
// }, }
// {
// name: "InvalidRole", // Check response (only for non-error cases)
// messages: []*lkeap.Message{ if err != nil {
// { t.Fatalf("QueryRewrite failed: %v", err)
// Role: common.StringPtr("invalid-role"), }
// Content: common.StringPtr("Test query"), if resp.RewrittenQuery == "" {
// }, t.Error("Expected non-empty rewritten query")
// }, }
// model: "",
// expectError: true, log.Info().
// }, Str("method", "TestKnowledgeClient_QueryRewrite").
} Str("test_name", tt.name).
Str("rewritten_query", resp.RewrittenQuery).
for _, tt := range tests { Msg("Query rewrite successful")
t.Run(tt.name, func(t *testing.T) { })
// Initialize client for each test }
if err := client.Init(ctx); err != nil {
t.Fatalf("Failed to initialize KnowledgeClient: %v", err)
}
// Perform query rewrite
req := QueryRewriteRequest{
Messages: tt.messages,
Model: tt.model,
}
resp, err := client.QueryRewrite(ctx, req)
// Check error expectation
if tt.expectError {
if err == nil {
t.Error("Expected error, got none")
} else {
log.Debug().
Str("method", "TestKnowledgeClient_QueryRewrite").
Str("test_name", tt.name).
Err(err).
Msg("Received expected error")
}
return
}
// Check response (only for non-error cases)
if err != nil {
t.Fatalf("QueryRewrite failed: %v", err)
}
if resp.RewrittenQuery == "" {
t.Error("Expected non-empty rewritten query")
}
log.Info().
Str("method", "TestKnowledgeClient_QueryRewrite").
Str("test_name", tt.name).
Str("rewritten_query", resp.RewrittenQuery).
Msg("Query rewrite successful")
})
}
} }
func TestKnowledgeClient_QueryRewriteWithSummary(t *testing.T) { func TestKnowledgeClient_QueryRewriteWithSummary(t *testing.T) {
// Warning: Do not hardcode credentials in production code. Use environment variables or a secure vault. // Warning: Do not hardcode credentials in production code. Use environment variables or a secure vault.
os.Setenv("TENCENTCLOUD_SECRET_ID", "AKID64oLfmfLtESUJ6i8LPSM4gCVbiniQuBF") os.Setenv("TENCENTCLOUD_SECRET_ID", "AKID64oLfmfLtESUJ6i8LPSM4gCVbiniQuBF")
os.Setenv("TENCENTCLOUD_SECRET_KEY", "rX2JMBnBMJ2YqulOo37xa5OUMSN4Xnpd") os.Setenv("TENCENTCLOUD_SECRET_KEY", "rX2JMBnBMJ2YqulOo37xa5OUMSN4Xnpd")
defer func() { defer func() {
os.Unsetenv("TENCENTCLOUD_SECRET_ID") os.Unsetenv("TENCENTCLOUD_SECRET_ID")
os.Unsetenv("TENCENTCLOUD_SECRET_KEY") os.Unsetenv("TENCENTCLOUD_SECRET_KEY")
}() }()
// Create client configuration // Create client configuration
config := ClientConfig{ config := ClientConfig{
Endpoint: "lkeap.tencentcloudapi.com", Endpoint: "lkeap.tencentcloudapi.com",
Region: "ap-guangzhou", Region: "ap-guangzhou",
} }
// Initialize client // Initialize client
client := NewKnowledgeClient(config) client := NewKnowledgeClient(config)
ctx := context.Background() ctx := context.Background()
// Test cases // Test cases
tests := []struct { tests := []struct {
name string name string
userQuestion string userQuestion string
assistantAnswer string assistantAnswer string
historySummary string historySummary string
expectError bool expectError bool
}{ }{
{ {
name: "ValidWithSummary", name: "ValidWithSummary",
userQuestion: "你的家在哪里", userQuestion: "你的家在哪里",
assistantAnswer: "国内", assistantAnswer: "国内",
historySummary: "User asked about location preferences earlier.", historySummary: "null", //"User asked about location preferences earlier.",
expectError: true, // Expect error due to potentially invalid credentials expectError: true, // Expect error due to potentially invalid credentials
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// Initialize client for each test // Initialize client for each test
if err := client.Init(ctx); err != nil { if err := client.Init(ctx); err != nil {
t.Fatalf("Failed to initialize KnowledgeClient: %v", err) t.Fatalf("Failed to initialize KnowledgeClient: %v", err)
} }
// Perform query rewrite with summary // Perform query rewrite with summary
resp, err := client.QueryRewriteWithSummary(ctx, tt.userQuestion, tt.assistantAnswer, tt.historySummary) resp, err := client.QueryRewriteWithSummary(ctx, tt.userQuestion, tt.assistantAnswer, tt.historySummary)
// Check error expectation // Check error expectation
if tt.expectError { if tt.expectError {
if err == nil { if err == nil {
t.Error("Expected error, got none") t.Error("Expected error, got none")
} else { } else {
log.Debug(). log.Debug().
Str("method", "TestKnowledgeClient_QueryWithSummary"). Str("method", "TestKnowledgeClient_QueryWithSummary").
Str("test_name", tt.name). Str("test_name", tt.name).
Str("error", err.Error()). Str("error", err.Error()).
Msg("Received expected error") Msg("Received expected error")
} }
return return
} }
// Check response // Check response
if err != nil { if err != nil {
t.Errorf("QueryRewriteWithSummary failed: %v", err) t.Errorf("QueryRewriteWithSummary failed: %v", err)
} }
if resp.RewrittenQuery == "" { if resp.RewrittenQuery == "" {
t.Error("Expected non-empty rewritten query") t.Error("Expected non-empty rewritten query")
} }
log.Info(). log.Info().
Str("method", "TestKnowledgeClient_QueryWithSummary"). Str("method", "TestKnowledgeClient_QueryWithSummary").
Str("test_name", tt.name). Str("test_name", tt.name).
Str("rewritten_query", resp.RewrittenQuery). Str("rewritten_query", resp.RewrittenQuery).
Msg("Query rewrite with summary successful") Msg("Query rewrite with summary successful")
}) })
} }
} }
\ No newline at end of file
...@@ -39,640 +39,611 @@ const provider = "milvus" ...@@ -39,640 +39,611 @@ const provider = "milvus"
// Field names for Milvus schema. // Field names for Milvus schema.
const ( const (
idField = "id" idField = "id"
vectorField = "vector" vectorField = "vector"
textField = "text" textField = "text"
metadataField = "metadata" metadataField = "metadata"
) )
// Milvus holds configuration for the plugin. // Milvus holds configuration for the plugin.
type Milvus struct { type Milvus struct {
// Milvus server address (host:port, e.g., "localhost:19530"). // Milvus server address (host:port, e.g., "localhost:19530").
// Defaults to MILVUS_ADDRESS environment variable. // Defaults to MILVUS_ADDRESS environment variable.
Addr string Addr string
// Username for authentication. // Username for authentication.
// Defaults to MILVUS_USERNAME. // Defaults to MILVUS_USERNAME.
Username string Username string
// Password for authentication. // Password for authentication.
// Defaults to MILVUS_PASSWORD. // Defaults to MILVUS_PASSWORD.
Password string Password string
// Token for authentication (alternative to username/password). // Token for authentication (alternative to username/password).
// Defaults to MILVUS_TOKEN. // Defaults to MILVUS_TOKEN.
Token string Token string
client client.Client // Milvus client. client client.Client // Milvus client.
mu sync.Mutex // Mutex to control access. mu sync.Mutex // Mutex to control access.
initted bool // Whether the plugin has been initialized. initted bool // Whether the plugin has been initialized.
} }
// Name returns the plugin name. // Name returns the plugin name.
func (m *Milvus) Name() string { func (m *Milvus) Name() string {
return provider return provider
} }
// Init initializes the Milvus plugin. // Init initializes the Milvus plugin.
func (m *Milvus) Init(ctx context.Context, g *genkit.Genkit) (err error) { func (m *Milvus) Init(ctx context.Context, g *genkit.Genkit) (err error) {
log.Info().Str("method", "Milvus.Init").Msg("Initializing Milvus plugin") log.Info().Str("method", "Milvus.Init").Msg("Initializing Milvus plugin")
if m == nil { if m == nil {
m = &Milvus{} m = &Milvus{}
} }
m.mu.Lock() m.mu.Lock()
defer m.mu.Unlock() defer m.mu.Unlock()
defer func() { defer func() {
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Milvus.Init").Msg("Initialization failed") log.Error().Err(err).Str("method", "Milvus.Init").Msg("Initialization failed")
err = fmt.Errorf("milvus.Init: %w", err) err = fmt.Errorf("milvus.Init: %w", err)
} else { } else {
log.Info().Str("method", "Milvus.Init").Msg("Initialization successful") log.Info().Str("method", "Milvus.Init").Msg("Initialization successful")
} }
}() }()
if m.initted { if m.initted {
return errors.New("plugin already initialized") return errors.New("plugin already initialized")
} }
// Load configuration. // Load configuration.
addr := m.Addr addr := m.Addr
if addr == "" { if addr == "" {
addr = os.Getenv("MILVUS_ADDRESS") addr = os.Getenv("MILVUS_ADDRESS")
} }
if addr == "" { if addr == "" {
return errors.New("milvus address required") return errors.New("milvus address required")
} }
username := m.Username username := m.Username
if username == "" { if username == "" {
username = os.Getenv("MILVUS_USERNAME") username = os.Getenv("MILVUS_USERNAME")
} }
password := m.Password password := m.Password
if password == "" { if password == "" {
password = os.Getenv("MILVUS_PASSWORD") password = os.Getenv("MILVUS_PASSWORD")
} }
token := m.Token token := m.Token
if token == "" { if token == "" {
token = os.Getenv("MILVUS_TOKEN") token = os.Getenv("MILVUS_TOKEN")
} }
// Initialize Milvus client. // Initialize Milvus client.
config := client.Config{ config := client.Config{
Address: addr, Address: addr,
Username: username, Username: username,
Password: password, Password: password,
APIKey: token, APIKey: token,
} }
client, err := client.NewClient(ctx, config) client, err := client.NewClient(ctx, config)
if err != nil { if err != nil {
return fmt.Errorf("failed to initialize Milvus client: %v", err) return fmt.Errorf("failed to initialize Milvus client: %v", err)
} }
m.client = client m.client = client
m.initted = true m.initted = true
return nil return nil
} }
// CollectionConfig holds configuration for an indexer/retriever pair. // CollectionConfig holds configuration for an indexer/retriever pair.
type CollectionConfig struct { type CollectionConfig struct {
// Milvus collection name. Must not be empty. // Milvus collection name. Must not be empty.
Collection string Collection string
// Embedding vector dimension (e.g., 1536 for text-embedding-ada-002). // Embedding vector dimension (e.g., 1536 for text-embedding-ada-002).
Dimension int Dimension int
// Embedder for generating vectors. // Embedder for generating vectors.
Embedder ai.Embedder Embedder ai.Embedder
// Embedder options. // Embedder options.
EmbedderOptions any EmbedderOptions any
} }
// DefineIndexerAndRetriever defines an Indexer and Retriever for a Milvus collection. // DefineIndexerAndRetriever defines an Indexer and Retriever for a Milvus collection.
func DefineIndexerAndRetriever(ctx context.Context, g *genkit.Genkit, cfg CollectionConfig) (ai.Indexer, ai.Retriever, error) { func DefineIndexerAndRetriever(ctx context.Context, g *genkit.Genkit, cfg CollectionConfig) (ai.Indexer, ai.Retriever, error) {
log.Info(). log.Info().
Str("method", "DefineIndexerAndRetriever"). Str("method", "DefineIndexerAndRetriever").
Str("collection", cfg.Collection). Str("collection", cfg.Collection).
Int("dimension", cfg.Dimension). Int("dimension", cfg.Dimension).
Msg("Defining indexer and retriever") Msg("Defining indexer and retriever")
if cfg.Embedder == nil { if cfg.Embedder == nil {
log.Error().Str("method", "DefineIndexerAndRetriever").Msg("Embedder required") log.Error().Str("method", "DefineIndexerAndRetriever").Msg("Embedder required")
return nil, nil, errors.New("milvus: Embedder required") return nil, nil, errors.New("milvus: Embedder required")
} }
if cfg.Collection == "" { if cfg.Collection == "" {
log.Error().Str("method", "DefineIndexerAndRetriever").Msg("Collection name required") log.Error().Str("method", "DefineIndexerAndRetriever").Msg("Collection name required")
return nil, nil, errors.New("milvus: collection name required") return nil, nil, errors.New("milvus: collection name required")
} }
if cfg.Dimension <= 0 { if cfg.Dimension <= 0 {
log.Error().Str("method", "DefineIndexerAndRetriever").Int("dimension", cfg.Dimension).Msg("Dimension must be positive") log.Error().Str("method", "DefineIndexerAndRetriever").Int("dimension", cfg.Dimension).Msg("Dimension must be positive")
return nil, nil, errors.New("milvus: dimension must be positive") return nil, nil, errors.New("milvus: dimension must be positive")
} }
m := genkit.LookupPlugin(g, provider) m := genkit.LookupPlugin(g, provider)
if m == nil { if m == nil {
log.Error().Str("method", "DefineIndexerAndRetriever").Msg("Milvus plugin not found") log.Error().Str("method", "DefineIndexerAndRetriever").Msg("Milvus plugin not found")
return nil, nil, errors.New("milvus plugin not found; did you call genkit.Init with the milvus plugin?") return nil, nil, errors.New("milvus plugin not found; did you call genkit.Init with the milvus plugin?")
} }
milvus := m.(*Milvus) milvus := m.(*Milvus)
ds, err := milvus.newDocStore(ctx, &cfg) ds, err := milvus.newDocStore(ctx, &cfg)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "DefineIndexerAndRetriever").Str("collection", cfg.Collection).Msg("Failed to create doc store") log.Error().Err(err).Str("method", "DefineIndexerAndRetriever").Str("collection", cfg.Collection).Msg("Failed to create doc store")
return nil, nil, err return nil, nil, err
} }
indexer := genkit.DefineIndexer(g, provider, cfg.Collection, ds.Index) indexer := genkit.DefineIndexer(g, provider, cfg.Collection, ds.Index)
retriever := genkit.DefineRetriever(g, provider, cfg.Collection, ds.Retrieve) retriever := genkit.DefineRetriever(g, provider, cfg.Collection, ds.Retrieve)
log.Info().Str("method", "DefineIndexerAndRetriever").Str("collection", cfg.Collection).Msg("Indexer and retriever defined successfully") log.Info().Str("method", "DefineIndexerAndRetriever").Str("collection", cfg.Collection).Msg("Indexer and retriever defined successfully")
return indexer, retriever, nil return indexer, retriever, nil
} }
// docStore defines an Indexer and a Retriever. // docStore defines an Indexer and a Retriever.
type docStore struct { type docStore struct {
client client.Client client client.Client
collection string collection string
dimension int dimension int
embedder ai.Embedder embedder ai.Embedder
embedderOptions map[string]interface{} embedderOptions map[string]interface{}
} }
// newDocStore creates a docStore. // newDocStore creates a docStore.
func (m *Milvus) newDocStore(ctx context.Context, cfg *CollectionConfig) (*docStore, error) { func (m *Milvus) newDocStore(ctx context.Context, cfg *CollectionConfig) (*docStore, error) {
log.Info(). log.Info().
Str("method", "Milvus.newDocStore"). Str("method", "Milvus.newDocStore").
Str("collection", cfg.Collection). Str("collection", cfg.Collection).
Int("dimension", cfg.Dimension). Int("dimension", cfg.Dimension).
Msg("Creating new doc store") Msg("Creating new doc store")
if m.client == nil { if m.client == nil {
log.Error().Str("method", "Milvus.newDocStore").Msg("Milvus client not initialized") log.Error().Str("method", "Milvus.newDocStore").Msg("Milvus client not initialized")
return nil, errors.New("milvus.Init not called") return nil, errors.New("milvus.Init not called")
} }
// Check/create collection. // Check/create collection.
exists, err := m.client.HasCollection(ctx, cfg.Collection) exists, err := m.client.HasCollection(ctx, cfg.Collection)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msg("Failed to check collection") log.Error().Err(err).Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msg("Failed to check collection")
return nil, fmt.Errorf("failed to check collection %q: %v", cfg.Collection, err) return nil, fmt.Errorf("failed to check collection %q: %v", cfg.Collection, err)
} }
if !exists { if !exists {
// Define schema with textField as primary key, plus user_id and username fields. // Define schema with textField as primary key, plus user_id and username fields.
schema := &entity.Schema{ schema := &entity.Schema{
CollectionName: cfg.Collection, CollectionName: cfg.Collection,
Fields: []*entity.Field{ Fields: []*entity.Field{
{ {
Name: vectorField, Name: vectorField,
DataType: entity.FieldTypeFloatVector, DataType: entity.FieldTypeFloatVector,
TypeParams: map[string]string{ TypeParams: map[string]string{
"dim": fmt.Sprintf("%d", cfg.Dimension), "dim": fmt.Sprintf("%d", cfg.Dimension),
}, },
}, },
{ {
Name: textField, Name: textField,
DataType: entity.FieldTypeVarChar, DataType: entity.FieldTypeVarChar,
PrimaryKey: true, PrimaryKey: true,
TypeParams: map[string]string{ TypeParams: map[string]string{
"max_length": "65535", "max_length": "65535",
}, },
}, },
{ {
Name: metadataField, Name: metadataField,
DataType: entity.FieldTypeJSON, DataType: entity.FieldTypeJSON,
}, },
{ {
Name: "user_id", Name: "user_id",
DataType: entity.FieldTypeVarChar, DataType: entity.FieldTypeVarChar,
TypeParams: map[string]string{ TypeParams: map[string]string{
"max_length": "128", "max_length": "128",
}, },
}, },
{ {
Name: "username", Name: "username",
DataType: entity.FieldTypeVarChar, DataType: entity.FieldTypeVarChar,
TypeParams: map[string]string{ TypeParams: map[string]string{
"max_length": "128", "max_length": "128",
}, },
}, },
}, },
} }
err = m.client.CreateCollection(ctx, schema, entity.DefaultShardNumber) err = m.client.CreateCollection(ctx, schema, entity.DefaultShardNumber)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msg("Failed to create collection") log.Error().Err(err).Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msg("Failed to create collection")
return nil, fmt.Errorf("failed to create collection %q: %v", cfg.Collection, err) return nil, fmt.Errorf("failed to create collection %q: %v", cfg.Collection, err)
} }
// Create HNSW index for vectorField. // Create HNSW index for vectorField.
index, err := entity.NewIndexHNSW( index, err := entity.NewIndexHNSW(
entity.L2, entity.L2,
8, // M 8, // M
96, // efConstruction 96, // efConstruction
) )
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msg("Failed to create HNSW index") log.Error().Err(err).Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msg("Failed to create HNSW index")
return nil, fmt.Errorf("entity.NewIndexHNSW: %v", err) return nil, fmt.Errorf("entity.NewIndexHNSW: %v", err)
} }
err = m.client.CreateIndex(ctx, cfg.Collection, vectorField, index, false) err = m.client.CreateIndex(ctx, cfg.Collection, vectorField, index, false)
if err != nil { if err != nil {
log.Error().Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msgf("Failed to create index: %s",err.Error()) log.Error().Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msgf("Failed to create index: %s", err.Error())
return nil, fmt.Errorf("failed to create index: %v", err) return nil, fmt.Errorf("failed to create index: %v", err)
} }
} }
// Load collection. // Load collection.
err = m.client.LoadCollection(ctx, cfg.Collection, false) err = m.client.LoadCollection(ctx, cfg.Collection, false)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msg("Failed to load collection") log.Error().Err(err).Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msg("Failed to load collection")
return nil, fmt.Errorf("failed to load collection %q: %v", cfg.Collection, err) return nil, fmt.Errorf("failed to load collection %q: %v", cfg.Collection, err)
} }
// Convert EmbedderOptions to map[string]interface{}. // Convert EmbedderOptions to map[string]interface{}.
var embedderOptions map[string]interface{} var embedderOptions map[string]interface{}
if cfg.EmbedderOptions != nil { if cfg.EmbedderOptions != nil {
opts, ok := cfg.EmbedderOptions.(map[string]interface{}) opts, ok := cfg.EmbedderOptions.(map[string]interface{})
if !ok { if !ok {
log.Error(). log.Error().
Str("method", "Milvus.newDocStore"). Str("method", "Milvus.newDocStore").
Str("type", fmt.Sprintf("%T", cfg.EmbedderOptions)). Str("type", fmt.Sprintf("%T", cfg.EmbedderOptions)).
Msg("EmbedderOptions must be a map[string]interface{}") Msg("EmbedderOptions must be a map[string]interface{}")
return nil, fmt.Errorf("EmbedderOptions must be a map[string]interface{}, got %T", cfg.EmbedderOptions) return nil, fmt.Errorf("EmbedderOptions must be a map[string]interface{}, got %T", cfg.EmbedderOptions)
} }
embedderOptions = opts embedderOptions = opts
} else { } else {
embedderOptions = make(map[string]interface{}) embedderOptions = make(map[string]interface{})
} }
log.Info().Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msg("Doc store created successfully") log.Info().Str("method", "Milvus.newDocStore").Str("collection", cfg.Collection).Msg("Doc store created successfully")
return &docStore{ return &docStore{
client: m.client, client: m.client,
collection: cfg.Collection, collection: cfg.Collection,
dimension: cfg.Dimension, dimension: cfg.Dimension,
embedder: cfg.Embedder, embedder: cfg.Embedder,
embedderOptions: embedderOptions, embedderOptions: embedderOptions,
}, nil }, nil
} }
// Indexer returns the indexer for a collection. // Indexer returns the indexer for a collection.
func Indexer(g *genkit.Genkit, collection string) ai.Indexer { func Indexer(g *genkit.Genkit, collection string) ai.Indexer {
log.Info().Str("method", "Indexer").Str("collection", collection).Msg("Looking up indexer") log.Info().Str("method", "Indexer").Str("collection", collection).Msg("Looking up indexer")
indexer := genkit.LookupIndexer(g, provider, collection) indexer := genkit.LookupIndexer(g, provider, collection)
if indexer == nil { if indexer == nil {
log.Warn().Str("method", "Indexer").Str("collection", collection).Msg("Indexer not found") log.Warn().Str("method", "Indexer").Str("collection", collection).Msg("Indexer not found")
} }
return indexer return indexer
} }
// Retriever returns the retriever for a collection. // Retriever returns the retriever for a collection.
func Retriever(g *genkit.Genkit, collection string) ai.Retriever { func Retriever(g *genkit.Genkit, collection string) ai.Retriever {
log.Info().Str("method", "Retriever").Str("collection", collection).Msg("Looking up retriever") log.Info().Str("method", "Retriever").Str("collection", collection).Msg("Looking up retriever")
retriever := genkit.LookupRetriever(g, provider, collection) retriever := genkit.LookupRetriever(g, provider, collection)
if retriever == nil { if retriever == nil {
log.Warn().Str("method", "Retriever").Str("collection", collection).Msg("Retriever not found") log.Warn().Str("method", "Retriever").Str("collection", collection).Msg("Retriever not found")
} }
return retriever return retriever
} }
// Index implements the Indexer.Index method. // Index implements the Indexer.Index method.
func (ds *docStore) Index(ctx context.Context, req *ai.IndexerRequest) error { func (ds *docStore) Index(ctx context.Context, req *ai.IndexerRequest) error {
log.Info(). log.Info().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Str("collection", ds.collection). Str("collection", ds.collection).
Int("documents", len(req.Documents)). Int("documents", len(req.Documents)).
Msg("Starting index operation") Msg("Starting index operation")
if len(req.Documents) == 0 { if len(req.Documents) == 0 {
log.Debug().Str("method", "docStore.Index").Str("collection", ds.collection).Msg("No documents to index") log.Debug().Str("method", "docStore.Index").Str("collection", ds.collection).Msg("No documents to index")
return nil return nil
} }
// Embed documents. // Embed documents.
ereq := &ai.EmbedRequest{ ereq := &ai.EmbedRequest{
Input: req.Documents, Input: req.Documents,
Options: ds.embedderOptions, Options: ds.embedderOptions,
} }
eres, err := ds.embedder.Embed(ctx, ereq) eres, err := ds.embedder.Embed(ctx, ereq)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "docStore.Index").Str("collection", ds.collection).Msg("Embedding failed") log.Error().Err(err).Str("method", "docStore.Index").Str("collection", ds.collection).Msg("Embedding failed")
return fmt.Errorf("milvus index embedding failed: %w", err) return fmt.Errorf("milvus index embedding failed: %w", err)
} }
// Validate embedding count matches document count. // Validate embedding count matches document count.
if len(eres.Embeddings) != len(req.Documents) { if len(eres.Embeddings) != len(req.Documents) {
log.Error(). log.Error().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Str("collection", ds.collection). Str("collection", ds.collection).
Int("embeddings", len(eres.Embeddings)). Int("embeddings", len(eres.Embeddings)).
Int("documents", len(req.Documents)). Int("documents", len(req.Documents)).
Msg("Mismatch in embedding and document count") Msg("Mismatch in embedding and document count")
return fmt.Errorf("mismatch: got %d embeddings for %d documents", len(eres.Embeddings), len(req.Documents)) return fmt.Errorf("mismatch: got %d embeddings for %d documents", len(eres.Embeddings), len(req.Documents))
} }
// Prepare row-based data. // Prepare row-based data.
var rows []interface{} var rows []interface{}
for i, emb := range eres.Embeddings { for i, emb := range eres.Embeddings {
doc := req.Documents[i] doc := req.Documents[i]
if doc.Metadata == nil { if doc.Metadata == nil {
log.Error().Str("method", "docStore.Index").Int("index", i).Msg("Document metadata is nil") log.Error().Str("method", "docStore.Index").Int("index", i).Msg("Document metadata is nil")
return fmt.Errorf("req.Query.Metadata must be not nil, got type %T", doc.Metadata) return fmt.Errorf("req.Query.Metadata must be not nil, got type %T", doc.Metadata)
} }
// Extract username and user_id from req.Query.Metadata // Extract username and user_id from req.Query.Metadata
userName, ok := doc.Metadata[util.UserNameKey].(string) userName, ok := doc.Metadata[util.UserNameKey].(string)
if !ok { if !ok {
log.Error().Str("method", "docStore.Index").Int("index", i).Msg("Missing username in metadata") log.Error().Str("method", "docStore.Index").Int("index", i).Msg("Missing username in metadata")
return fmt.Errorf("req.Query.Metadata must provide username key") return fmt.Errorf("req.Query.Metadata must provide username key")
} }
userId, ok := doc.Metadata[util.UserIdKey].(string) userId, ok := doc.Metadata[util.UserIdKey].(string)
if !ok { if !ok {
log.Error().Str("method", "docStore.Index").Int("index", i).Msg("Missing user_id in metadata") log.Error().Str("method", "docStore.Index").Int("index", i).Msg("Missing user_id in metadata")
return fmt.Errorf("req.Query.Metadata must provide user_id key") return fmt.Errorf("req.Query.Metadata must provide user_id key")
} }
var sb strings.Builder var sb strings.Builder
for _, p := range doc.Content { for _, p := range doc.Content {
if p.IsText() { if p.IsText() {
sb.WriteString(p.Text) sb.WriteString(p.Text)
} }
} }
text := sb.String() text := sb.String()
metadata := doc.Metadata metadata := doc.Metadata
if metadata == nil { if metadata == nil {
metadata = make(map[string]interface{}) metadata = make(map[string]interface{})
} }
// Create row with explicit metadata field. // Create row with explicit metadata field.
row := make(map[string]interface{}) row := make(map[string]interface{})
row["vector"] = emb.Embedding row["vector"] = emb.Embedding
row["text"] = text row["text"] = text
row["user_id"] = userId row["user_id"] = userId
row["username"] = userName row["username"] = userName
row["metadata"] = metadata row["metadata"] = metadata
rows = append(rows, row) rows = append(rows, row)
log.Debug(). log.Debug().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Int("index", i). Int("index", i).
Str("collection", ds.collection). Str("collection", ds.collection).
Int("vector_length", len(emb.Embedding)). Int("vector_length", len(emb.Embedding)).
Str("text", text). Str("text", text).
Str("user_id", userId). Str("user_id", userId).
Str("username", userName). Str("username", userName).
Interface("metadata", metadata). Interface("metadata", metadata).
Msg("Prepared row for insertion") Msg("Prepared row for insertion")
} }
log.Info(). log.Info().
Str("method", "docStore.Index"). Str("method", "docStore.Index").
Str("collection", ds.collection). Str("collection", ds.collection).
Int("rows", len(rows)). Int("rows", len(rows)).
Msg("Inserting rows into Milvus") Msg("Inserting rows into Milvus")
// Insert rows into Milvus. // Insert rows into Milvus.
_, err = ds.client.InsertRows(ctx, ds.collection, "", rows) _, err = ds.client.InsertRows(ctx, ds.collection, "", rows)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "docStore.Index").Str("collection", ds.collection).Msg("Failed to insert rows") log.Error().Err(err).Str("method", "docStore.Index").Str("collection", ds.collection).Msg("Failed to insert rows")
return fmt.Errorf("milvus insert rows failed: %w", err) return fmt.Errorf("milvus insert rows failed: %w", err)
} }
log.Info().Str("method", "docStore.Index").Str("collection", ds.collection).Int("rows", len(rows)).Msg("Index operation completed successfully") log.Info().Str("method", "docStore.Index").Str("collection", ds.collection).Int("rows", len(rows)).Msg("Index operation completed successfully")
return nil return nil
} }
// RetrieverOptions for Milvus retrieval. // RetrieverOptions for Milvus retrieval.
type RetrieverOptions struct { type RetrieverOptions struct {
Count int `json:"count,omitempty"` // Max documents to retrieve. Count int `json:"count,omitempty"` // Max documents to retrieve.
MetricType string `json:"metric_type,omitempty"` // Similarity metric (e.g., "L2", "IP"). MetricType string `json:"metric_type,omitempty"` // Similarity metric (e.g., "L2", "IP").
} }
// Retrieve implements the Retriever.Retrieve method. // Retrieve implements the Retriever.Retrieve method.
func (ds *docStore) Retrieve(ctx context.Context, req *ai.RetrieverRequest) (*ai.RetrieverResponse, error) { func (ds *docStore) Retrieve(ctx context.Context, req *ai.RetrieverRequest) (*ai.RetrieverResponse, error) {
log.Info(). log.Info().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("collection", ds.collection). Str("collection", ds.collection).
Msg("Starting retrieve operation") Msg("Starting retrieve operation")
if req.Query.Metadata == nil { if req.Query.Metadata == nil {
log.Error().Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Query metadata is nil") log.Error().Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Query metadata is nil")
return nil, fmt.Errorf("req.Query.Metadata must be not nil, got type %T", req.Query.Metadata) return nil, fmt.Errorf("req.Query.Metadata must be not nil, got type %T", req.Query.Metadata)
} }
// Extract username and user_id from req.Query.Metadata // Extract username and user_id from req.Query.Metadata
userName, ok := req.Query.Metadata[util.UserNameKey].(string) userName, ok := req.Query.Metadata[util.UserNameKey].(string)
if !ok { if !ok {
log.Error().Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Missing username in metadata") log.Error().Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Missing username in metadata")
return nil, fmt.Errorf("req.Query.Metadata must provide username key") return nil, fmt.Errorf("req.Query.Metadata must provide username key")
} }
userId, ok := req.Query.Metadata[util.UserIdKey].(string) userId, ok := req.Query.Metadata[util.UserIdKey].(string)
if !ok { if !ok {
log.Error().Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Missing user_id in metadata") log.Error().Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Missing user_id in metadata")
return nil, fmt.Errorf("req.Query.Metadata must provide user_id key") return nil, fmt.Errorf("req.Query.Metadata must provide user_id key")
} }
count := 3 // Default. count := 3 // Default.
metricTypeStr := "L2" metricTypeStr := "L2"
if req.Options != nil { if req.Options != nil {
ropt, ok := req.Options.(*RetrieverOptions) ropt, ok := req.Options.(*RetrieverOptions)
if !ok { if !ok {
log.Error(). log.Error().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("collection", ds.collection). Str("collection", ds.collection).
Str("options_type", fmt.Sprintf("%T", req.Options)). Str("options_type", fmt.Sprintf("%T", req.Options)).
Msg("Invalid options type") Msg("Invalid options type")
return nil, fmt.Errorf("milvus.Retrieve options have type %T, want %T", req.Options, &RetrieverOptions{}) return nil, fmt.Errorf("milvus.Retrieve options have type %T, want %T", req.Options, &RetrieverOptions{})
} }
if ropt.Count > 0 { if ropt.Count > 0 {
count = ropt.Count count = ropt.Count
} }
if ropt.MetricType != "" { if ropt.MetricType != "" {
metricTypeStr = ropt.MetricType metricTypeStr = ropt.MetricType
} }
} }
// Map string metric type to entity.MetricType. // Map string metric type to entity.MetricType.
var metricType entity.MetricType var metricType entity.MetricType
switch metricTypeStr { switch metricTypeStr {
case "L2": case "L2":
metricType = entity.L2 metricType = entity.L2
case "IP": case "IP":
metricType = entity.IP metricType = entity.IP
default: default:
log.Error().Str("method", "docStore.Retrieve").Str("metric_type", metricTypeStr).Msg("Unsupported metric type") log.Error().Str("method", "docStore.Retrieve").Str("metric_type", metricTypeStr).Msg("Unsupported metric type")
return nil, fmt.Errorf("unsupported metric type: %s", metricTypeStr) return nil, fmt.Errorf("unsupported metric type: %s", metricTypeStr)
} }
// Embed query. // Embed query.
ereq := &ai.EmbedRequest{ ereq := &ai.EmbedRequest{
Input: []*ai.Document{req.Query}, Input: []*ai.Document{req.Query},
Options: ds.embedderOptions, Options: ds.embedderOptions,
} }
eres, err := ds.embedder.Embed(ctx, ereq) eres, err := ds.embedder.Embed(ctx, ereq)
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Embedding failed") log.Error().Err(err).Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Embedding failed")
return nil, fmt.Errorf("milvus retrieve embedding failed: %v", err) return nil, fmt.Errorf("milvus retrieve embedding failed: %v", err)
} }
if len(eres.Embeddings) == 0 { if len(eres.Embeddings) == 0 {
log.Error().Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("No embeddings generated") log.Error().Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("No embeddings generated")
return nil, errors.New("no embeddings generated for query") return nil, errors.New("no embeddings generated for query")
} }
queryVector := entity.FloatVector(eres.Embeddings[0].Embedding) queryVector := entity.FloatVector(eres.Embeddings[0].Embedding)
// Create search parameters. // Create search parameters.
searchParams, err := entity.NewIndexHNSWSearchParam(64) // ef searchParams, err := entity.NewIndexHNSWSearchParam(64) // ef
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Failed to create HNSW search parameters") log.Error().Err(err).Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Failed to create HNSW search parameters")
return nil, fmt.Errorf("NewIndexHNSWSearchParam failed: %v", err) return nil, fmt.Errorf("NewIndexHNSWSearchParam failed: %v", err)
} }
// Define filter expression for user_id // Define filter expression for user_id
expr := fmt.Sprintf("user_id == %q", userId) expr := fmt.Sprintf("user_id == %q", userId)
log.Debug(). log.Debug().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("collection", ds.collection). Str("collection", ds.collection).
Str("user_id", userId). Str("user_id", userId).
Str("metric_type", metricTypeStr). Str("metric_type", metricTypeStr).
Int("count", count). Int("count", count).
Msg("Performing vector search") Msg("Performing vector search")
// Perform vector search to get IDs, text, and metadata. // Perform vector search to get IDs, text, and metadata.
results, err := ds.client.Search( results, err := ds.client.Search(
ctx, ctx,
ds.collection, ds.collection,
[]string{}, []string{},
expr, expr,
[]string{textField, metadataField}, []string{textField, metadataField},
[]entity.Vector{queryVector}, []entity.Vector{queryVector},
vectorField, vectorField,
metricType, metricType,
count, count,
searchParams, searchParams,
) )
if err != nil { if err != nil {
log.Error().Err(err).Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Search failed") log.Error().Err(err).Str("method", "docStore.Retrieve").Str("collection", ds.collection).Msg("Search failed")
return nil, fmt.Errorf("milvus search failed: %v", err) return nil, fmt.Errorf("milvus search failed: %v", err)
} }
// Process search results. // Process search results.
var docs []*ai.Document var docs []*ai.Document
for _, result := range results { for _, result := range results {
// Find text and metadata columns in search results. // Find text and metadata columns in search results.
var textCol, metaCol entity.Column var textCol, metaCol entity.Column
for _, col := range result.Fields { for _, col := range result.Fields {
if col.Name() == textField { if col.Name() == textField {
textCol = col textCol = col
} }
if col.Name() == metadataField { if col.Name() == metadataField {
metaCol = col metaCol = col
} }
} }
// Ensure text column exists. // Ensure text column exists.
if textCol == nil { if textCol == nil {
log.Error(). log.Error().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("collection", ds.collection). Str("collection", ds.collection).
Str("field", textField). Str("field", textField).
Msg("Text column not found in search results") Msg("Text column not found in search results")
return nil, fmt.Errorf("text column %s not found in search results", textField) return nil, fmt.Errorf("text column %s not found in search results", textField)
} }
// Iterate over rows. // Iterate over rows.
for i := 0; i < result.ResultCount; i++ { for i := 0; i < result.ResultCount; i++ {
// Get text value. // Get text value.
text, err := textCol.GetAsString(i) text, err := textCol.GetAsString(i)
if err != nil { if err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("collection", ds.collection). Str("collection", ds.collection).
Int("index", i). Int("index", i).
Msg("Failed to parse text") Msg("Failed to parse text")
continue continue
} }
// Get metadata value (optional). // Get metadata value (optional).
var metadata map[string]interface{} var metadata map[string]interface{}
if metaCol != nil { if metaCol != nil {
metaStr, err := metaCol.GetAsString(i) metaStr, err := metaCol.GetAsString(i)
if err == nil && metaStr != "" { if err == nil && metaStr != "" {
if err := json.Unmarshal([]byte(metaStr), &metadata); err != nil { if err := json.Unmarshal([]byte(metaStr), &metadata); err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("collection", ds.collection). Str("collection", ds.collection).
Int("index", i). Int("index", i).
Msg("Failed to parse metadata") Msg("Failed to parse metadata")
continue continue
} }
} else if err != nil { } else if err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("collection", ds.collection). Str("collection", ds.collection).
Int("index", i). Int("index", i).
Msg("Failed to get metadata string") Msg("Failed to get metadata string")
} }
} }
// Ensure metadata includes user_id and username from query. // Ensure metadata includes user_id and username from query.
if metadata == nil { if metadata == nil {
metadata = make(map[string]interface{}) metadata = make(map[string]interface{})
} }
metadata[util.UserIdKey] = userId metadata[util.UserIdKey] = userId
metadata[util.UserNameKey] = userName metadata[util.UserNameKey] = userName
// Create document. // Create document.
doc := ai.DocumentFromText(text, metadata) doc := ai.DocumentFromText(text, metadata)
docs = append(docs, doc) docs = append(docs, doc)
log.Debug(). log.Debug().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("collection", ds.collection). Str("collection", ds.collection).
Int("index", i). Int("index", i).
Str("text", text). Str("text", text).
Interface("metadata", metadata). Interface("metadata", metadata).
Msg("Processed search result") Msg("Processed search result")
} }
} }
log.Info(). log.Info().
Str("method", "docStore.Retrieve"). Str("method", "docStore.Retrieve").
Str("collection", ds.collection). Str("collection", ds.collection).
Int("documents", len(docs)). Int("documents", len(docs)).
Msg("Retrieve operation completed successfully") Msg("Retrieve operation completed successfully")
return &ai.RetrieverResponse{ return &ai.RetrieverResponse{
Documents: docs, Documents: docs,
}, nil }, nil
} }
// // Copyright 2025 Google LLC // // Copyright 2025 Google LLC
// // // //
// // Licensed under the Apache License, Version 2.0 (the "License"); // // Licensed under the Apache License, Version 2.0 (the "License");
......
...@@ -9,7 +9,6 @@ import ( ...@@ -9,7 +9,6 @@ import (
_ "github.com/lib/pq" _ "github.com/lib/pq"
) )
type QA struct { type QA struct {
ID int64 // 主键 ID int64 // 主键
CreatedAt time.Time // 创建时间 CreatedAt time.Time // 创建时间
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment