合并上游
This commit is contained in:
denislov 2025-02-12 01:46:50 +08:00
commit 8c54530d58
28 changed files with 568 additions and 343 deletions

View File

@ -1,25 +0,0 @@
diff --git a/src/libsql-db.js b/src/libsql-db.js
index 58c42e4910bd0e53bc497ff9b9702b1f7a961266..250bc97c50a9b790e8798441d904d040f2d2af43 100644
--- a/src/libsql-db.js
+++ b/src/libsql-db.js
@@ -41,9 +41,9 @@ export class LibSqlDb {
}
async similaritySearch(query, k) {
const statement = `SELECT id, pageContent, uniqueLoaderId, source, metadata,
- vector_distance_cos(vector, vector32('[${query.join(',')}]'))
+ vector_distance_cos(vector, vector32('[${query.join(',')}]')) as distance
FROM ${this.tableName}
- ORDER BY vector_distance_cos(vector, vector32('[${query.join(',')}]')) ASC
+ ORDER BY distance ASC
LIMIT ${k};`;
this.debug(`Executing statement - ${truncateCenterString(statement, 700)}`);
const results = await this.client.execute(statement);
@@ -52,7 +52,7 @@ export class LibSqlDb {
return {
metadata,
pageContent: result.pageContent.toString(),
- score: 1,
+ score: 1 - result.distance,
};
});
}

View File

@ -1,16 +1,10 @@
diff --git a/src/markdown-loader.js b/src/markdown-loader.js diff --git a/src/markdown-loader.js b/src/markdown-loader.js
index 8a17cb7f5a68d90d2be21682db6e95ce22a3e71c..9ee868ef9d4ff3dc914b3abc3c8006deb1e9c6c6 100644 index eaf30b114a273e68abbb92c8b07018495e63f4cb..4b06519bdb51845e4693fe877da9de01c7a81039 100644
--- a/src/markdown-loader.js --- a/src/markdown-loader.js
+++ b/src/markdown-loader.js +++ b/src/markdown-loader.js
@@ -1,5 +1,4 @@ @@ -21,7 +21,7 @@ export class MarkdownLoader extends BaseLoader {
import { micromark } from 'micromark';
-import { mdxJsx } from 'micromark-extension-mdx-jsx';
import { gfmHtml, gfm } from 'micromark-extension-gfm';
import createDebugMessages from 'debug';
import fs from 'node:fs';
@@ -21,7 +20,7 @@ export class MarkdownLoader extends BaseLoader {
? (await getSafe(this.filePathOrUrl, { format: 'buffer' })).body ? (await getSafe(this.filePathOrUrl, { format: 'buffer' })).body
: await stream2buffer(fs.createReadStream(this.filePathOrUrl)); : await streamToBuffer(fs.createReadStream(this.filePathOrUrl));
this.debug('MarkdownLoader stream created'); this.debug('MarkdownLoader stream created');
- const result = micromark(buffer, { extensions: [gfm(), mdxJsx()], htmlExtensions: [gfmHtml()] }); - const result = micromark(buffer, { extensions: [gfm(), mdxJsx()], htmlExtensions: [gfmHtml()] });
+ const result = micromark(buffer, { extensions: [gfm()], htmlExtensions: [gfmHtml()] }); + const result = micromark(buffer, { extensions: [gfm()], htmlExtensions: [gfmHtml()] });

View File

@ -1,72 +1,21 @@
diff --git a/src/core/rag-embedding.js b/src/core/rag-embedding.js
index 50c3c4064af17bc4c7c46554d8f2419b3afceb0e..632c9b2e04d2e0e3bb09ef1cd8f29d2560e6afc1 100644
--- a/src/core/rag-embedding.js
+++ b/src/core/rag-embedding.js
@@ -1,10 +1,8 @@
export class RAGEmbedding {
static singleton;
static async init(embeddingModel) {
- if (!this.singleton) {
- await embeddingModel.init();
- this.singleton = new RAGEmbedding(embeddingModel);
- }
+ await embeddingModel.init();
+ this.singleton = new RAGEmbedding(embeddingModel);
}
static getInstance() {
return RAGEmbedding.singleton;
diff --git a/src/loaders/local-path-loader.d.ts b/src/loaders/local-path-loader.d.ts diff --git a/src/loaders/local-path-loader.d.ts b/src/loaders/local-path-loader.d.ts
index 48c20e68c469cd309be2dc8f28e44c1bd04a26e9..87002be39e7305a02e2a607b0c0d95cbbc359f9d 100644 index 48c20e68c469cd309be2dc8f28e44c1bd04a26e9..1c16d83bcbf9b7140292793d6cbb8c04281949d9 100644
--- a/src/loaders/local-path-loader.d.ts --- a/src/loaders/local-path-loader.d.ts
+++ b/src/loaders/local-path-loader.d.ts +++ b/src/loaders/local-path-loader.d.ts
@@ -1,19 +1,29 @@ @@ -4,8 +4,10 @@ export declare class LocalPathLoader extends BaseLoader<{
-import { BaseLoader } from '@llm-tools/embedjs-interfaces';
+import { BaseLoader } from "@llm-tools/embedjs-interfaces";
export declare class LocalPathLoader extends BaseLoader<{
- type: 'LocalPathLoader';
+ type: "LocalPathLoader";
}> { }> {
- private readonly debug; private readonly debug;
- private readonly path; private readonly path;
- constructor({ path }: { - constructor({ path }: {
- path: string; + constructor({ path, chunkSize, chunkOverlap }: {
- }); path: string;
- getUnfilteredChunks(): AsyncGenerator<{ + chunkSize?: number;
- metadata: { + chunkOverlap?: number;
- type: "LocalPathLoader"; });
- originalPath: string; getUnfilteredChunks(): AsyncGenerator<{
- source: string; metadata: {
- };
- pageContent: string;
- }, void, unknown>;
- private recursivelyAddPath;
+ private readonly debug;
+ private readonly path;
+ constructor({
+ path,
+ chunkSize,
+ chunkOverlap,
+ }: {
+ path: string;
+ chunkSize?: number;
+ chunkOverlap?: number;
+ });
+ getUnfilteredChunks(): AsyncGenerator<
+ {
+ metadata: {
+ type: "LocalPathLoader";
+ originalPath: string;
+ source: string;
+ };
+ pageContent: string;
+ },
+ void,
+ unknown
+ >;
+ private recursivelyAddPath;
}
diff --git a/src/loaders/local-path-loader.js b/src/loaders/local-path-loader.js diff --git a/src/loaders/local-path-loader.js b/src/loaders/local-path-loader.js
index 4cf8a6bd1d890244c8ec49d4a05ee3bd58861c79..fd0fe1951c73da315b0c9bf4a8f33effbadb9f8f 100644 index 4cf8a6bd1d890244c8ec49d4a05ee3bd58861c79..ec8215b01195a21ef20f3c5d56ecc99f186bb596 100644
--- a/src/loaders/local-path-loader.js --- a/src/loaders/local-path-loader.js
+++ b/src/loaders/local-path-loader.js +++ b/src/loaders/local-path-loader.js
@@ -8,8 +8,8 @@ import { BaseLoader } from '@llm-tools/embedjs-interfaces'; @@ -8,8 +8,8 @@ import { BaseLoader } from '@llm-tools/embedjs-interfaces';
@ -75,7 +24,7 @@ index 4cf8a6bd1d890244c8ec49d4a05ee3bd58861c79..fd0fe1951c73da315b0c9bf4a8f33eff
path; path;
- constructor({ path }) { - constructor({ path }) {
- super(`LocalPathLoader_${md5(path)}`, { path }); - super(`LocalPathLoader_${md5(path)}`, { path });
+ constructor({ path, chunkSize, chunkOverlap}) { + constructor({ path, chunkSize, chunkOverlap }) {
+ super(`LocalPathLoader_${md5(path)}`, { path }, chunkSize ?? 1000, chunkOverlap ?? 0); + super(`LocalPathLoader_${md5(path)}`, { path }, chunkSize ?? 1000, chunkOverlap ?? 0);
this.path = path; this.path = path;
} }
@ -95,21 +44,15 @@ index 4cf8a6bd1d890244c8ec49d4a05ee3bd58861c79..fd0fe1951c73da315b0c9bf4a8f33eff
yield { yield {
pageContent: result.pageContent, pageContent: result.pageContent,
diff --git a/src/util/mime.d.ts b/src/util/mime.d.ts diff --git a/src/util/mime.d.ts b/src/util/mime.d.ts
index 57f56a1b8edc98366af9f84d671676c41c2f01ca..f53856fa9c78afbeee9e085c7ed0b3a131f8ee5a 100644 index 57f56a1b8edc98366af9f84d671676c41c2f01ca..14be3b5727cff6eb1978838045e9a788f8f53bfb 100644
--- a/src/util/mime.d.ts --- a/src/util/mime.d.ts
+++ b/src/util/mime.d.ts +++ b/src/util/mime.d.ts
@@ -1,2 +1,7 @@ @@ -1,2 +1,2 @@
-import { BaseLoader } from '@llm-tools/embedjs-interfaces'; import { BaseLoader } from '@llm-tools/embedjs-interfaces';
-export declare function createLoaderFromMimeType(loaderData: string, mimeType: string): Promise<BaseLoader>; -export declare function createLoaderFromMimeType(loaderData: string, mimeType: string): Promise<BaseLoader>;
+import { BaseLoader } from "@llm-tools/embedjs-interfaces"; +export declare function createLoaderFromMimeType(loaderData: string, mimeType: string, chunkSize?: number, chunkOverlap?: number): Promise<BaseLoader>;
+export declare function createLoaderFromMimeType(
+ loaderData: string,
+ mimeType: string,
+ chunkSize?: number,
+ chunkOverlap?: number
+): Promise<BaseLoader>;
diff --git a/src/util/mime.js b/src/util/mime.js diff --git a/src/util/mime.js b/src/util/mime.js
index 9af30bd5b8cf42985f547073a4c19756292c33a3..54ae20343131a533ab70236d3060b6accc8f6126 100644 index b6426a859968e2bf6206795f70333e90ae27aeb7..16ae2adb863f8d7abfa757f1c5cc39f6bb1c44fa 100644
--- a/src/util/mime.js --- a/src/util/mime.js
+++ b/src/util/mime.js +++ b/src/util/mime.js
@@ -1,7 +1,9 @@ @@ -1,7 +1,9 @@
@ -117,7 +60,7 @@ index 9af30bd5b8cf42985f547073a4c19756292c33a3..54ae20343131a533ab70236d3060b6ac
import createDebugMessages from 'debug'; import createDebugMessages from 'debug';
import { TextLoader } from '../loaders/text-loader.js'; import { TextLoader } from '../loaders/text-loader.js';
-export async function createLoaderFromMimeType(loaderData, mimeType) { -export async function createLoaderFromMimeType(loaderData, mimeType) {
+import fs from 'node:fs'; +import fs from 'node:fs'
+ +
+export async function createLoaderFromMimeType(loaderData, mimeType, chunkSize, chunkOverlap) { +export async function createLoaderFromMimeType(loaderData, mimeType, chunkSize, chunkOverlap) {
createDebugMessages('embedjs:util:createLoaderFromMimeType')(`Incoming mime type '${mimeType}'`); createDebugMessages('embedjs:util:createLoaderFromMimeType')(`Incoming mime type '${mimeType}'`);
@ -137,7 +80,7 @@ index 9af30bd5b8cf42985f547073a4c19756292c33a3..54ae20343131a533ab70236d3060b6ac
}); });
createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported ExcelLoader'); createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported ExcelLoader');
- return new ExcelLoader({ filePathOrUrl: loaderData }); - return new ExcelLoader({ filePathOrUrl: loaderData });
+ return new ExcelLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap }); + return new ExcelLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap });
} }
case 'application/pdf': { case 'application/pdf': {
const { PdfLoader } = await import('@llm-tools/embedjs-loader-pdf').catch(() => { const { PdfLoader } = await import('@llm-tools/embedjs-loader-pdf').catch(() => {
@ -157,19 +100,17 @@ index 9af30bd5b8cf42985f547073a4c19756292c33a3..54ae20343131a533ab70236d3060b6ac
} }
case 'text/plain': { case 'text/plain': {
const fineType = mime.getType(loaderData); const fineType = mime.getType(loaderData);
@@ -42,24 +44,26 @@ export async function createLoaderFromMimeType(loaderData, mimeType) { @@ -42,24 +44,24 @@ export async function createLoaderFromMimeType(loaderData, mimeType) {
throw new Error('Package `@llm-tools/embedjs-loader-csv` needs to be installed to load CSV files'); throw new Error('Package `@llm-tools/embedjs-loader-csv` needs to be installed to load CSV files');
}); });
createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported CsvLoader'); createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported CsvLoader');
- return new CsvLoader({ filePathOrUrl: loaderData }); - return new CsvLoader({ filePathOrUrl: loaderData });
+ return new CsvLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap }); + return new CsvLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap });
+ }
+ else{
+ const content = fs.readFileSync(loaderData, 'utf-8');
+ return new TextLoader({ text: content, chunkSize, chunkOverlap });
} }
- else - else
- return new TextLoader({ text: loaderData }); - return new TextLoader({ text: loaderData });
+ const content = fs.readFileSync(loaderData, 'utf-8');
+ return new TextLoader({ text: content, chunkSize, chunkOverlap });
} }
case 'application/csv': { case 'application/csv': {
const { CsvLoader } = await import('@llm-tools/embedjs-loader-csv').catch(() => { const { CsvLoader } = await import('@llm-tools/embedjs-loader-csv').catch(() => {
@ -189,7 +130,7 @@ index 9af30bd5b8cf42985f547073a4c19756292c33a3..54ae20343131a533ab70236d3060b6ac
} }
case 'text/xml': { case 'text/xml': {
const { SitemapLoader } = await import('@llm-tools/embedjs-loader-sitemap').catch(() => { const { SitemapLoader } = await import('@llm-tools/embedjs-loader-sitemap').catch(() => {
@@ -67,14 +71,14 @@ export async function createLoaderFromMimeType(loaderData, mimeType) { @@ -67,14 +69,14 @@ export async function createLoaderFromMimeType(loaderData, mimeType) {
}); });
createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported SitemapLoader'); createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported SitemapLoader');
if (await SitemapLoader.test(loaderData)) { if (await SitemapLoader.test(loaderData)) {
@ -206,12 +147,12 @@ index 9af30bd5b8cf42985f547073a4c19756292c33a3..54ae20343131a533ab70236d3060b6ac
} }
case 'text/x-markdown': case 'text/x-markdown':
case 'text/markdown': { case 'text/markdown': {
@@ -82,7 +86,7 @@ export async function createLoaderFromMimeType(loaderData, mimeType) { @@ -82,7 +84,7 @@ export async function createLoaderFromMimeType(loaderData, mimeType) {
throw new Error('Package `@llm-tools/embedjs-loader-markdown` needs to be installed to load markdown files'); throw new Error('Package `@llm-tools/embedjs-loader-markdown` needs to be installed to load markdown files');
}); });
createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported MarkdownLoader'); createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported MarkdownLoader');
- return new MarkdownLoader({ filePathOrUrl: loaderData }); - return new MarkdownLoader({ filePathOrUrl: loaderData });
+ return new MarkdownLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap }); + return new MarkdownLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap });
} }
case undefined: case 'image/png':
throw new Error(`MIME type could not be detected. Please file an issue if you think this is a bug.`); case 'image/jpeg': {

View File

@ -1,54 +0,0 @@
diff --git a/src/util/strings.cjs b/src/util/strings.cjs
index 9933cc6e3866c476b47342a29ddb206eb90fa4a5..2965c4f2808bf94af9ef3e2ec889e5552e30e6ae 100644
--- a/src/util/strings.cjs
+++ b/src/util/strings.cjs
@@ -38,13 +38,16 @@ function toTitleCase(str) {
});
}
function isValidURL(url) {
- try {
- new URL(url);
- return true;
- }
- catch {
- return false;
+ if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('ftp://')) {
+ try {
+ new URL(url);
+ return true;
+ }
+ catch {
+ return false;
+ }
}
+ return false;
}
function isValidJson(str) {
try {
diff --git a/src/util/strings.js b/src/util/strings.js
index f5c1655512099b880fc5022e95d5e0c4d1d073f2..1a64bd662a22efd2effd9d2846ffcf0b93391963 100644
--- a/src/util/strings.js
+++ b/src/util/strings.js
@@ -29,13 +29,16 @@ export function toTitleCase(str) {
});
}
export function isValidURL(url) {
- try {
- new URL(url);
- return true;
- }
- catch {
- return false;
+ if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('ftp://')) {
+ try {
+ new URL(url);
+ return true;
+ }
+ catch {
+ return false;
+ }
}
+ return false;
}
export function isValidJson(str) {
try {

View File

@ -8,9 +8,5 @@
<true/> <true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key> <key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/> <true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@ -20,7 +20,8 @@ export default defineConfig({
'@llm-tools/embedjs-loader-xml', '@llm-tools/embedjs-loader-xml',
'@llm-tools/embedjs-loader-pdf', '@llm-tools/embedjs-loader-pdf',
'@llm-tools/embedjs-loader-sitemap', '@llm-tools/embedjs-loader-sitemap',
'@llm-tools/embedjs-libsql' '@llm-tools/embedjs-libsql',
'@llm-tools/embedjs-loader-image'
] ]
}), }),
...visualizerPlugin('main') ...visualizerPlugin('main')

View File

@ -51,16 +51,18 @@
"@electron-toolkit/utils": "^3.0.0", "@electron-toolkit/utils": "^3.0.0",
"@electron/notarize": "^2.5.0", "@electron/notarize": "^2.5.0",
"@google/generative-ai": "^0.21.0", "@google/generative-ai": "^0.21.0",
"@llm-tools/embedjs": "patch:@llm-tools/embedjs@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.25-ec5645cf36.patch", "@llm-tools/embedjs": "patch:@llm-tools/embedjs@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch",
"@llm-tools/embedjs-libsql": "patch:@llm-tools/embedjs-libsql@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-libsql-npm-0.1.25-fad000d74c.patch", "@llm-tools/embedjs-libsql": "^0.1.28",
"@llm-tools/embedjs-loader-csv": "^0.1.25", "@llm-tools/embedjs-loader-csv": "^0.1.28",
"@llm-tools/embedjs-loader-markdown": "patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.25-d1d536d640.patch", "@llm-tools/embedjs-loader-image": "^0.1.28",
"@llm-tools/embedjs-loader-msoffice": "^0.1.25", "@llm-tools/embedjs-loader-markdown": "patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch",
"@llm-tools/embedjs-loader-pdf": "^0.1.25", "@llm-tools/embedjs-loader-msoffice": "^0.1.28",
"@llm-tools/embedjs-loader-sitemap": "^0.1.25", "@llm-tools/embedjs-loader-pdf": "^0.1.28",
"@llm-tools/embedjs-loader-web": "^0.1.25", "@llm-tools/embedjs-loader-sitemap": "^0.1.28",
"@llm-tools/embedjs-loader-xml": "^0.1.25", "@llm-tools/embedjs-loader-web": "^0.1.28",
"@llm-tools/embedjs-openai": "^0.1.25", "@llm-tools/embedjs-loader-xml": "^0.1.28",
"@llm-tools/embedjs-openai": "^0.1.28",
"@notionhq/client": "^2.2.15",
"@types/react-infinite-scroll-component": "^5.0.0", "@types/react-infinite-scroll-component": "^5.0.0",
"adm-zip": "^0.5.16", "adm-zip": "^0.5.16",
"apache-arrow": "^18.1.0", "apache-arrow": "^18.1.0",
@ -151,7 +153,6 @@
}, },
"resolutions": { "resolutions": {
"pdf-parse@npm:1.1.1": "patch:pdf-parse@npm%3A1.1.1#~/.yarn/patches/pdf-parse-npm-1.1.1-04a6109b2a.patch", "pdf-parse@npm:1.1.1": "patch:pdf-parse@npm%3A1.1.1#~/.yarn/patches/pdf-parse-npm-1.1.1-04a6109b2a.patch",
"@llm-tools/embedjs-utils@npm:0.1.25": "patch:@llm-tools/embedjs-utils@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-utils-npm-0.1.25-fd8fe8a193.patch",
"@langchain/openai@npm:^0.3.16": "patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch", "@langchain/openai@npm:^0.3.16": "patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch",
"@langchain/openai@npm:>=0.1.0 <0.4.0": "patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch" "@langchain/openai@npm:>=0.1.0 <0.4.0": "patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch"
}, },

View File

@ -88,6 +88,7 @@
"input.topics": " Topics ", "input.topics": " Topics ",
"input.translate": "Translate to {{target_language}}", "input.translate": "Translate to {{target_language}}",
"input.upload": "Upload image or document file", "input.upload": "Upload image or document file",
"input.upload.document": "Upload document file (model does not support images)",
"input.web_search": "Enable web search", "input.web_search": "Enable web search",
"input.knowledge_base": "Knowledge Base", "input.knowledge_base": "Knowledge Base",
"message.new.branch": "New Branch", "message.new.branch": "New Branch",
@ -115,8 +116,11 @@
"topics.edit.placeholder": "Enter new name", "topics.edit.placeholder": "Enter new name",
"topics.edit.title": "Edit Name", "topics.edit.title": "Edit Name",
"topics.export.image": "Export as image", "topics.export.image": "Export as image",
"topics.export.notion": "Export to Notion",
"topics.export.md": "Export as markdown", "topics.export.md": "Export as markdown",
"topics.export.title": "Export", "topics.export.title": "Export",
"topics.pinned": "Pinned Topics",
"topics.unpinned": "Unpinned Topics",
"topics.export.word": "Export as Word", "topics.export.word": "Export as Word",
"topics.list": "Topic List", "topics.list": "Topic List",
"topics.move_to": "Move to", "topics.move_to": "Move to",
@ -296,7 +300,12 @@
"error.get_embedding_dimensions": "Failed to get embedding dimensions", "error.get_embedding_dimensions": "Failed to get embedding dimensions",
"group.delete.title": "Delete Group Message", "group.delete.title": "Delete Group Message",
"group.delete.content": "Deleting a group message will delete the user's question and all assistant's answers", "group.delete.content": "Deleting a group message will delete the user's question and all assistant's answers",
"mention.title": "Switch model answer" "mention.title": "Switch model answer",
"error.notion.export": "Notion import failed",
"error.notion.no_api_key": "Notion ApiKey or Notion DatabaseID is not configured",
"success.notion.export": "Notion import successful",
"warn.notion.exporting": "Notion is importing, please do not import repeatedly",
"citations": "References"
}, },
"minapp": { "minapp": {
"title": "MinApp", "title": "MinApp",
@ -421,7 +430,11 @@
"webdav.autoSync.off": "Off", "webdav.autoSync.off": "Off",
"webdav.noSync": "Waiting for next backup", "webdav.noSync": "Waiting for next backup",
"webdav.syncError": "Backup Error", "webdav.syncError": "Backup Error",
"webdav.lastSync": "Last Backup" "webdav.lastSync": "Last Backup",
"notion.api_key":"Notion API Key",
"notion.database_id":"Notion Database ID",
"notion.title":"Notion Configuration"
}, },
"quickAssistant": { "quickAssistant": {
"title": "Quick Assistant", "title": "Quick Assistant",

View File

@ -83,6 +83,7 @@
"input.topics": " トピック ", "input.topics": " トピック ",
"input.translate": "{{target_language}}に翻訳", "input.translate": "{{target_language}}に翻訳",
"input.upload": "画像またはドキュメントをアップロード", "input.upload": "画像またはドキュメントをアップロード",
"input.upload.document": "ドキュメントをアップロード(モデルは画像をサポートしません)",
"input.web_search": "ウェブ検索を有効にする", "input.web_search": "ウェブ検索を有効にする",
"input.knowledge_base": "ナレッジベース", "input.knowledge_base": "ナレッジベース",
"message.new.branch": "新しいブランチ", "message.new.branch": "新しいブランチ",
@ -111,10 +112,13 @@
"topics.edit.title": "名前を編集", "topics.edit.title": "名前を編集",
"topics.export.image": "画像としてエクスポート", "topics.export.image": "画像としてエクスポート",
"topics.export.md": "Markdownとしてエクスポート", "topics.export.md": "Markdownとしてエクスポート",
"topics.export.notion": "Notion にエクスポート",
"topics.export.title": "エクスポート", "topics.export.title": "エクスポート",
"topics.export.word": "Wordとしてエクスポート", "topics.export.word": "Wordとしてエクスポート",
"topics.list": "トピックリスト", "topics.list": "トピックリスト",
"topics.move_to": "移動先", "topics.move_to": "移動先",
"topics.pinned": "トピックを固定",
"topics.unpinned": "固定解除",
"topics.title": "トピック", "topics.title": "トピック",
"translate": "翻訳", "translate": "翻訳",
"resend": "再送信", "resend": "再送信",
@ -290,7 +294,12 @@
"error.get_embedding_dimensions": "埋込み次元を取得できませんでした", "error.get_embedding_dimensions": "埋込み次元を取得できませんでした",
"group.delete.title": "分組メッセージを削除", "group.delete.title": "分組メッセージを削除",
"group.delete.content": "分組メッセージを削除するとユーザーの質問と助け手の回答がすべて削除されます", "group.delete.content": "分組メッセージを削除するとユーザーの質問と助け手の回答がすべて削除されます",
"mention.title": "モデルを切り替える" "mention.title": "モデルを切り替える",
"error.notion.export": "Notion インポートに失敗",
"error.notion.no_api_key": "Notion ApiKey または Notion DatabaseID が設定されていません",
"success.notion.export": "Notion へのインポートに成功",
"warn.notion.exporting": "Notion 正在インポート中です。重複インポートしないでください。",
"citations": "参考文献"
}, },
"minapp": { "minapp": {
"title": "ミニアプリ", "title": "ミニアプリ",
@ -413,7 +422,10 @@
"webdav.autoSync.off": "オフ", "webdav.autoSync.off": "オフ",
"webdav.noSync": "次回のバックアップを待っています", "webdav.noSync": "次回のバックアップを待っています",
"webdav.syncError": "バックアップエラー", "webdav.syncError": "バックアップエラー",
"webdav.lastSync": "最終同期" "webdav.lastSync": "最終同期",
"notion.api_key":"Notion APIキー",
"notion.database_id":"Notion データベースID",
"notion.title":"Notion 設定"
}, },
"quickAssistant": { "quickAssistant": {
"title": "クイックアシスタント", "title": "クイックアシスタント",

View File

@ -83,6 +83,7 @@
"input.topics": " Топики ", "input.topics": " Топики ",
"input.translate": "Перевести на {{target_language}}", "input.translate": "Перевести на {{target_language}}",
"input.upload": "Загрузить изображение или документ", "input.upload": "Загрузить изображение или документ",
"input.upload.document": "Загрузить документ (модель не поддерживает изображения)",
"input.web_search": "Включить веб-поиск", "input.web_search": "Включить веб-поиск",
"input.knowledge_base": "База знаний", "input.knowledge_base": "База знаний",
"message.new.branch": "Новая ветка", "message.new.branch": "Новая ветка",
@ -111,10 +112,13 @@
"topics.edit.title": "Редактировать заголовок", "topics.edit.title": "Редактировать заголовок",
"topics.export.image": "Экспорт как изображение", "topics.export.image": "Экспорт как изображение",
"topics.export.md": "Экспорт как markdown", "topics.export.md": "Экспорт как markdown",
"topics.export.notion": "Экспорт в Notion",
"topics.export.title": "Экспорт", "topics.export.title": "Экспорт",
"topics.export.word": "Экспорт как Word", "topics.export.word": "Экспорт как Word",
"topics.list": "Список топиков", "topics.list": "Список топиков",
"topics.move_to": "Переместить в", "topics.move_to": "Переместить в",
"topics.pinned": "Закрепленные темы",
"topics.unpinned": "Открепленные темы",
"topics.title": "Топики", "topics.title": "Топики",
"translate": "Перевести", "translate": "Перевести",
"resend": "Переотправить", "resend": "Переотправить",
@ -291,7 +295,12 @@
"error.get_embedding_dimensions": "Не удалось получить размерность встраивания", "error.get_embedding_dimensions": "Не удалось получить размерность встраивания",
"group.delete.title": "Удалить группу сообщений", "group.delete.title": "Удалить группу сообщений",
"group.delete.content": "Удаление группы сообщений удалит пользовательский вопрос и все ответы помощника", "group.delete.content": "Удаление группы сообщений удалит пользовательский вопрос и все ответы помощника",
"mention.title": "Переключить модель ответа" "mention.title": "Переключить модель ответа",
"error.notion.export": "Импорт в Notion не удался",
"error.notion.no_api_key": "Notion ApiKey или Notion DatabaseID не настроен",
"success.notion.export": "Импорт в Notion выполнен успешно",
"warn.notion.exporting": "Идет импорт в Notion, пожалуйста, не повторяйте импорт",
"citations": "Источники"
}, },
"minapp": { "minapp": {
"title": "Встроенные приложения", "title": "Встроенные приложения",
@ -414,7 +423,10 @@
"webdav.autoSync.off": "Выключено", "webdav.autoSync.off": "Выключено",
"webdav.noSync": "Ожидание следующего резервного копирования", "webdav.noSync": "Ожидание следующего резервного копирования",
"webdav.syncError": "Ошибка резервного копирования", "webdav.syncError": "Ошибка резервного копирования",
"webdav.lastSync": "Последняя синхронизация" "webdav.lastSync": "Последняя синхронизация",
"notion.api_key":"Ключ API Notion",
"notion.database_id":"ID базы данных Notion",
"notion.title":"Настройки Notion"
}, },
"quickAssistant": { "quickAssistant": {
"title": "Быстрый помощник", "title": "Быстрый помощник",

View File

@ -88,6 +88,7 @@
"input.topics": " 话题 ", "input.topics": " 话题 ",
"input.translate": "翻译成{{target_language}}", "input.translate": "翻译成{{target_language}}",
"input.upload": "上传图片或文档", "input.upload": "上传图片或文档",
"input.upload.document": "上传文档(模型不支持图片)",
"input.web_search": "开启网络搜索", "input.web_search": "开启网络搜索",
"input.knowledge_base": "知识库", "input.knowledge_base": "知识库",
"message.new.branch": "分支", "message.new.branch": "分支",
@ -116,11 +117,14 @@
"topics.edit.title": "编辑话题名", "topics.edit.title": "编辑话题名",
"topics.export.image": "导出为图片", "topics.export.image": "导出为图片",
"topics.export.md": "导出为 Markdown", "topics.export.md": "导出为 Markdown",
"topics.export.notion": "导出到 Notion",
"topics.export.title": "导出", "topics.export.title": "导出",
"topics.export.word": "导出为 Word", "topics.export.word": "导出为 Word",
"topics.list": "话题列表", "topics.list": "话题列表",
"topics.move_to": "移动到", "topics.move_to": "移动到",
"topics.title": "话题", "topics.title": "话题",
"topics.pinned":"固定话题",
"topics.unpinned":"取消固定",
"translate": "翻译", "translate": "翻译",
"resend": "重新发送", "resend": "重新发送",
"thinking": "思考中", "thinking": "思考中",
@ -297,7 +301,12 @@
"error.get_embedding_dimensions": "获取嵌入维度失败", "error.get_embedding_dimensions": "获取嵌入维度失败",
"group.delete.title": "删除分组消息", "group.delete.title": "删除分组消息",
"group.delete.content": "删除分组消息会删除用户提问和所有助手的回答", "group.delete.content": "删除分组消息会删除用户提问和所有助手的回答",
"mention.title": "切换模型回答" "mention.title": "切换模型回答",
"error.notion.export":"Notion 导入失败",
"error.notion.no_api_key":"未配置Notion ApiKey或Notion DatabaseID",
"success.notion.export":"导入Notion成功",
"warn.notion.exporting":"Notion正在导入请勿重复导入",
"citations": "引用内容"
}, },
"minapp": { "minapp": {
"title": "小程序", "title": "小程序",
@ -420,7 +429,10 @@
"webdav.autoSync.off": "关闭", "webdav.autoSync.off": "关闭",
"webdav.noSync": "等待下次备份", "webdav.noSync": "等待下次备份",
"webdav.syncError": "备份错误", "webdav.syncError": "备份错误",
"webdav.lastSync": "上次备份时间" "webdav.lastSync": "上次备份时间",
"notion.api_key": "Notion 密钥",
"notion.database_id": "Notion 数据库ID",
"notion.title": "Notion 配置"
}, },
"quickAssistant": { "quickAssistant": {
"title": "快捷助手", "title": "快捷助手",

View File

@ -88,6 +88,7 @@
"input.topics": " 話題 ", "input.topics": " 話題 ",
"input.translate": "翻譯成{{target_language}}", "input.translate": "翻譯成{{target_language}}",
"input.upload": "上傳圖片或文檔", "input.upload": "上傳圖片或文檔",
"input.upload.document": "上傳文檔(模型不支持圖片)",
"input.web_search": "開啟網路搜索", "input.web_search": "開啟網路搜索",
"input.knowledge_base": "知識庫", "input.knowledge_base": "知識庫",
"message.new.branch": "分支", "message.new.branch": "分支",
@ -116,11 +117,14 @@
"topics.edit.title": "編輯名稱", "topics.edit.title": "編輯名稱",
"topics.export.image": "匯出為圖片", "topics.export.image": "匯出為圖片",
"topics.export.md": "匯出為 Markdown", "topics.export.md": "匯出為 Markdown",
"topics.export.notion": "匯出到 Notion",
"topics.export.title": "匯出", "topics.export.title": "匯出",
"topics.export.word": "導出為 Word", "topics.export.word": "導出為 Word",
"topics.list": "話題列表", "topics.list": "話題列表",
"topics.move_to": "移動到", "topics.move_to": "移動到",
"topics.title": "話題", "topics.title": "話題",
"topics.pinned": "固定話題",
"topics.unpinned": "取消固定",
"translate": "翻譯", "translate": "翻譯",
"resend": "重新發送", "resend": "重新發送",
"thinking": "思考中", "thinking": "思考中",
@ -296,7 +300,12 @@
"error.get_embedding_dimensions": "獲取嵌入維度失敗", "error.get_embedding_dimensions": "獲取嵌入維度失敗",
"group.delete.title": "刪除分組消息", "group.delete.title": "刪除分組消息",
"group.delete.content": "刪除分組消息會刪除用戶提問和所有助手的回答", "group.delete.content": "刪除分組消息會刪除用戶提問和所有助手的回答",
"mention.title": "切換模型回答" "mention.title": "切換模型回答",
"error.notion.export": "Notion 匯入失敗",
"error.notion.no_api_key": "未配置 Notion ApiKey 或 Notion DatabaseID",
"success.notion.export": "匯入 Notion 成功",
"warn.notion.exporting": "Notion 正在匯入,請勿重複匯入",
"citations": "參考文獻"
}, },
"minapp": { "minapp": {
"title": "小程序", "title": "小程序",
@ -419,7 +428,10 @@
"webdav.autoSync.off": "關閉", "webdav.autoSync.off": "關閉",
"webdav.noSync": "等待下次備份", "webdav.noSync": "等待下次備份",
"webdav.syncError": "備份錯誤", "webdav.syncError": "備份錯誤",
"webdav.lastSync": "上次同步時間" "webdav.lastSync": "上次同步時間",
"notion.api_key": "Notion 金鑰",
"notion.database_id": "Notion 資料庫 ID",
"notion.title": "Notion 配置"
}, },
"quickAssistant": { "quickAssistant": {
"title": "快捷助手", "title": "快捷助手",

View File

@ -37,7 +37,10 @@ const AttachmentButton: FC<Props> = ({ model, files, setFiles, ToolbarButton, di
} }
return ( return (
<Tooltip placement="top" title={t('chat.input.upload')} arrow> <Tooltip
placement="top"
title={isVisionModel(model) ? t('chat.input.upload') : t('chat.input.upload.document')}
arrow>
<ToolbarButton type="text" className={files.length ? 'active' : ''} onClick={onSelectFile} disabled={disabled}> <ToolbarButton type="text" className={files.length ? 'active' : ''} onClick={onSelectFile} disabled={disabled}>
<PaperClipOutlined style={{ rotate: '135deg' }} /> <PaperClipOutlined style={{ rotate: '135deg' }} />
</ToolbarButton> </ToolbarButton>

View File

@ -1,9 +1,9 @@
import { SyncOutlined, TranslationOutlined } from '@ant-design/icons' import { InfoCircleOutlined, SyncOutlined, TranslationOutlined } from '@ant-design/icons'
import { Message, Model } from '@renderer/types' import { Message, Model } from '@renderer/types'
import { getBriefInfo } from '@renderer/utils' import { getBriefInfo } from '@renderer/utils'
import { withMessageThought } from '@renderer/utils/formats' import { withMessageThought } from '@renderer/utils/formats'
import { Divider, Flex } from 'antd' import { Divider, Flex } from 'antd'
import React, { Fragment } from 'react' import React, { Fragment, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import BeatLoader from 'react-spinners/BeatLoader' import BeatLoader from 'react-spinners/BeatLoader'
import styled from 'styled-components' import styled from 'styled-components'
@ -23,6 +23,40 @@ const MessageContent: React.FC<Props> = ({ message: _message, model }) => {
const { t } = useTranslation() const { t } = useTranslation()
const message = withMessageThought(_message) const message = withMessageThought(_message)
// Process content to make citation numbers clickable
const processedContent = useMemo(() => {
if (!message.content || !message.metadata?.citations) return message.content
let content = message.content
const citations = message.metadata.citations
// Convert [n] format to superscript numbers and make them clickable
content = content.replace(/\[(\d+)\]/g, (match, num) => {
const index = parseInt(num) - 1
if (index >= 0 && index < citations.length) {
// Use <sup> tag for superscript and make it a link
return `[<sup>${num}</sup>](${citations[index]})`
}
return match
})
return content
}, [message.content, message.metadata?.citations])
// Format citations for display
const formattedCitations = useMemo(() => {
if (!message.metadata?.citations?.length) return null
return message.metadata.citations.map((url, index) => {
try {
const hostname = new URL(url).hostname
return { number: index + 1, url, hostname }
} catch {
return { number: index + 1, url, hostname: url }
}
})
}, [message.metadata?.citations])
if (message.status === 'sending') { if (message.status === 'sending') {
return ( return (
<MessageContentLoading> <MessageContentLoading>
@ -46,7 +80,20 @@ const MessageContent: React.FC<Props> = ({ message: _message, model }) => {
{message.mentions?.map((model) => <MentionTag key={model.id}>{'@' + model.name}</MentionTag>)} {message.mentions?.map((model) => <MentionTag key={model.id}>{'@' + model.name}</MentionTag>)}
</Flex> </Flex>
<MessageThought message={message} /> <MessageThought message={message} />
<Markdown message={message} /> <Markdown message={{ ...message, content: processedContent }} />
{formattedCitations && (
<CitationsContainer>
<CitationsTitle>
{t('message.citations')}
<InfoCircleOutlined style={{ fontSize: '14px', marginLeft: '4px', opacity: 0.6 }} />
</CitationsTitle>
{formattedCitations.map(({ number, url, hostname }) => (
<CitationLink key={number} href={url} target="_blank" rel="noopener noreferrer">
{number}. <span className="hostname">{hostname}</span>
</CitationLink>
))}
</CitationsContainer>
)}
{message.translatedContent && ( {message.translatedContent && (
<Fragment> <Fragment>
<Divider style={{ margin: 0, marginBottom: 10 }}> <Divider style={{ margin: 0, marginBottom: 10 }}>
@ -78,4 +125,39 @@ const MentionTag = styled.span`
color: var(--color-link); color: var(--color-link);
` `
const CitationsContainer = styled.div`
background-color: rgb(242, 247, 253);
border-radius: 4px;
padding: 8px 12px;
margin: 12px 0;
display: flex;
flex-direction: column;
gap: 4px;
body[theme-mode='dark'] & {
background-color: rgba(255, 255, 255, 0.05);
}
`
const CitationsTitle = styled.div`
font-weight: 500;
margin-bottom: 4px;
color: var(--color-text-1);
`
const CitationLink = styled.a`
font-size: 14px;
line-height: 1.6;
text-decoration: none;
color: var(--color-text-1);
.hostname {
color: var(--color-link);
}
&:hover {
text-decoration: underline;
}
`
export default React.memo(MessageContent) export default React.memo(MessageContent)

View File

@ -197,7 +197,7 @@ const MessageMenubar: FC<Props> = (props) => {
const onRegenerate = async () => { const onRegenerate = async () => {
await modelGenerating() await modelGenerating()
const _message: Message = resetAssistantMessage(message, assistantModel) const _message: Message = resetAssistantMessage(message, model || assistantModel)
onEditMessage?.(_message) onEditMessage?.(_message)
} }

View File

@ -24,7 +24,7 @@ const MessageThought: FC<Props> = ({ message }) => {
} }
const thinkingTime = message.metrics?.time_thinking_millsec || 0 const thinkingTime = message.metrics?.time_thinking_millsec || 0
const thinkingTimeSecounds = (thinkingTime / 1000).toFixed(1) const thinkingTimeSeconds = (thinkingTime / 1000).toFixed(1)
return ( return (
<CollapseContainer <CollapseContainer
@ -37,7 +37,7 @@ const MessageThought: FC<Props> = ({ message }) => {
label: ( label: (
<MessageTitleLabel> <MessageTitleLabel>
<TinkingText> <TinkingText>
{isThinking ? t('chat.thinking') : t('chat.deeply_thought', { secounds: thinkingTimeSecounds })} {isThinking ? t('chat.thinking') : t('chat.deeply_thought', { secounds: thinkingTimeSeconds })}
</TinkingText> </TinkingText>
{isThinking && <BarLoader color="#9254de" />} {isThinking && <BarLoader color="#9254de" />}
</MessageTitleLabel> </MessageTitleLabel>

View File

@ -4,8 +4,8 @@ import {
DeleteOutlined, DeleteOutlined,
EditOutlined, EditOutlined,
FolderOutlined, FolderOutlined,
UploadOutlined PushpinOutlined,
} from '@ant-design/icons' UploadOutlined} from '@ant-design/icons'
import DragableList from '@renderer/components/DragableList' import DragableList from '@renderer/components/DragableList'
import PromptPopup from '@renderer/components/Popups/PromptPopup' import PromptPopup from '@renderer/components/Popups/PromptPopup'
import Scrollbar from '@renderer/components/Scrollbar' import Scrollbar from '@renderer/components/Scrollbar'
@ -18,7 +18,7 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import store from '@renderer/store' import store from '@renderer/store'
import { setGenerating } from '@renderer/store/runtime' import { setGenerating } from '@renderer/store/runtime'
import { Assistant, Topic } from '@renderer/types' import { Assistant, Topic } from '@renderer/types'
import { exportTopicAsMarkdown, topicToMarkdown } from '@renderer/utils/export' import { exportTopicAsMarkdown, exportTopicToNotion, topicToMarkdown } from '@renderer/utils/export'
import { Dropdown, MenuProps } from 'antd' import { Dropdown, MenuProps } from 'antd'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { findIndex } from 'lodash' import { findIndex } from 'lodash'
@ -40,6 +40,14 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
const borderRadius = showTopicTime ? 12 : 'var(--list-item-border-radius)' const borderRadius = showTopicTime ? 12 : 'var(--list-item-border-radius)'
const onPinTopic = useCallback(
(topic: Topic) => {
const updatedTopic = { ...topic, pinned: !topic.pinned }
updateTopic(updatedTopic)
},
[updateTopic]
)
const onDeleteTopic = useCallback( const onDeleteTopic = useCallback(
async (topic: Topic) => { async (topic: Topic) => {
await modelGenerating() await modelGenerating()
@ -106,6 +114,14 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
} }
} }
}, },
{
label: topic.pinned ? t('chat.topics.unpinned') : t('chat.topics.pinned'),
key: 'pin',
icon: <PushpinOutlined />,
onClick() {
onPinTopic(topic)
}
},
{ {
label: t('chat.topics.clear.title'), label: t('chat.topics.clear.title'),
key: 'clear-messages', key: 'clear-messages',
@ -133,6 +149,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
key: 'markdown', key: 'markdown',
onClick: () => exportTopicAsMarkdown(topic) onClick: () => exportTopicAsMarkdown(topic)
}, },
{ {
label: t('chat.topics.export.word'), label: t('chat.topics.export.word'),
key: 'word', key: 'word',
@ -140,7 +157,12 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
const markdown = await topicToMarkdown(topic) const markdown = await topicToMarkdown(topic)
window.api.export.toWord(markdown, topic.name) window.api.export.toWord(markdown, topic.name)
} }
} },
{
label: t('chat.topics.export.notion'),
key: 'notion',
onClick: () => exportTopicToNotion(topic)
},
] ]
} }
] ]
@ -160,7 +182,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
}) })
} }
if (assistant.topics.length > 1) { if (assistant.topics.length > 1 && !topic.pinned) {
menus.push({ type: 'divider' }) menus.push({ type: 'divider' })
menus.push({ menus.push({
label: t('common.delete'), label: t('common.delete'),
@ -173,7 +195,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
return menus return menus
}, },
[assistant, assistants, onClearMessages, onDeleteTopic, onMoveTopic, t, updateTopic] [assistant, assistants, onClearMessages, onPinTopic, onDeleteTopic, onMoveTopic, t, updateTopic]
) )
return ( return (
@ -191,14 +213,15 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
{showTopicTime && ( {showTopicTime && (
<TopicTime className="time">{dayjs(topic.createdAt).format('MM/DD HH:mm')}</TopicTime> <TopicTime className="time">{dayjs(topic.createdAt).format('MM/DD HH:mm')}</TopicTime>
)} )}
{isActive && ( <MenuButton className="pin">{topic.pinned && <PushpinOutlined />}</MenuButton>
{isActive && !topic.pinned && (
<MenuButton <MenuButton
className="menu" className="menu"
onClick={(e) => { onClick={(e) => {
e.stopPropagation() e.stopPropagation()
if (assistant.topics.length === 1) { if (assistant.topics.length === 1) {
return onClearMessages() return onClearMessages()
} }
onDeleteTopic(topic) onDeleteTopic(topic)
}}> }}>
<CloseOutlined /> <CloseOutlined />

View File

@ -9,7 +9,7 @@ import { getModelUniqId } from '@renderer/services/ModelService'
import { KnowledgeBase } from '@renderer/types' import { KnowledgeBase } from '@renderer/types'
import { Alert, Form, Input, InputNumber, Modal, Select, Slider } from 'antd' import { Alert, Form, Input, InputNumber, Modal, Select, Slider } from 'antd'
import { sortBy } from 'lodash' import { sortBy } from 'lodash'
import { useState } from 'react' import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
interface ShowParams { interface ShowParams {
@ -35,6 +35,10 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
const { providers } = useProviders() const { providers } = useProviders()
const { base, updateKnowledgeBase } = useKnowledge(_base.id) const { base, updateKnowledgeBase } = useKnowledge(_base.id)
useEffect(() => {
form.setFieldsValue({ documentCount: base?.documentCount || 6 })
}, [base, form])
if (!base) { if (!base) {
resolve(null) resolve(null)
return null return null
@ -118,7 +122,6 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
style={{ width: '100%' }} style={{ width: '100%' }}
min={1} min={1}
max={15} max={15}
defaultValue={base.documentCount || 6}
step={1} step={1}
marks={{ 1: '1', 6: t('knowledge.document_count_default'), 15: '15' }} marks={{ 1: '1', 6: t('knowledge.document_count_default'), 15: '15' }}
/> />

View File

@ -2,15 +2,72 @@ import { FileSearchOutlined, FolderOpenOutlined, SaveOutlined } from '@ant-desig
import { HStack } from '@renderer/components/Layout' import { HStack } from '@renderer/components/Layout'
import { useTheme } from '@renderer/context/ThemeProvider' import { useTheme } from '@renderer/context/ThemeProvider'
import { backup, reset, restore } from '@renderer/services/BackupService' import { backup, reset, restore } from '@renderer/services/BackupService'
import { RootState, useAppDispatch } from '@renderer/store'
import { setNotionApiKey, setNotionDatabaseID } from '@renderer/store/settings'
import { AppInfo } from '@renderer/types' import { AppInfo } from '@renderer/types'
import { Button, Modal, Typography } from 'antd' import { Button,Modal, Typography } from 'antd'
import Input from 'antd/es/input/Input'
import { FC, useEffect, useState } from 'react' import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import styled from 'styled-components' import styled from 'styled-components'
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..' import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
import WebDavSettings from './WebDavSettings' import WebDavSettings from './WebDavSettings'
// 新增的 NotionSettings 组件
const NotionSettings: FC = () => {
const { t } = useTranslation()
const { theme } = useTheme()
const dispatch = useAppDispatch()
// 这里可以添加 Notion 相关的状态和逻辑
// 例如:
const notionApiKey = useSelector((state: RootState) => state.settings.notionApiKey);
const notionDatabaseID = useSelector((state: RootState) => state.settings.notionDatabaseID);
const handleNotionTokenChange = (e: React.ChangeEvent<HTMLInputElement>) => {
dispatch(setNotionApiKey(e.target.value))
};
const handleNotionDatabaseIdChange = (e: React.ChangeEvent<HTMLInputElement>) => {
dispatch(setNotionDatabaseID(e.target.value))
};
return (
<SettingGroup theme={theme}>
<SettingTitle>{t('settings.data.notion.title')}</SettingTitle>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.data.notion.api_key')}</SettingRowTitle>
<HStack alignItems="center" gap="5px">
<Input.Password
type="text"
value={notionApiKey || ''}
onChange={handleNotionTokenChange}
onBlur={handleNotionTokenChange}
style={{ width: 250 }}
/>
</HStack>
</SettingRow>
<SettingDivider /> {/* 添加分割线 */}
<SettingRow>
<SettingRowTitle>{t('settings.data.notion.database_id')}</SettingRowTitle>
<HStack alignItems="center" gap="5px">
<Input
type="text"
value={notionDatabaseID || ''}
onChange={handleNotionDatabaseIdChange}
onBlur={handleNotionDatabaseIdChange}
style={{ width: 250 }}
/>
</HStack>
</SettingRow>
</SettingGroup>
)
}
const DataSettings: FC = () => { const DataSettings: FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const [appInfo, setAppInfo] = useState<AppInfo>() const [appInfo, setAppInfo] = useState<AppInfo>()
@ -79,6 +136,7 @@ const DataSettings: FC = () => {
<SettingGroup theme={theme}> <SettingGroup theme={theme}>
<WebDavSettings /> <WebDavSettings />
</SettingGroup> </SettingGroup>
<NotionSettings />
<SettingGroup theme={theme}> <SettingGroup theme={theme}>
<SettingTitle>{t('settings.data.data.title')}</SettingTitle> <SettingTitle>{t('settings.data.data.title')}</SettingTitle>
<SettingDivider /> <SettingDivider />
@ -107,6 +165,7 @@ const DataSettings: FC = () => {
</HStack> </HStack>
</SettingRow> </SettingRow>
</SettingGroup> </SettingGroup>
</SettingContainer> </SettingContainer>
) )
} }

View File

@ -113,7 +113,11 @@ const ShortcutSettings: FC = () => {
const key = e.key const key = e.key
if (key.length === 1 && !['Control', 'Alt', 'Shift', 'Meta'].includes(key)) { if (key.length === 1 && !['Control', 'Alt', 'Shift', 'Meta'].includes(key)) {
keys.push(key.toUpperCase()) if (key === ' ') {
keys.push('Space')
} else {
keys.push(key.toUpperCase())
}
} }
if (!isValidShortcut(keys)) { if (!isValidShortcut(keys)) {

View File

@ -243,6 +243,9 @@ export default class OpenAIProvider extends BaseProvider {
const delta = chunk.choices[0]?.delta const delta = chunk.choices[0]?.delta
// Extract citations from the raw response if available
const citations = (chunk as OpenAI.Chat.Completions.ChatCompletionChunk & { citations?: string[] })?.citations
onChunk({ onChunk({
text: delta?.content || '', text: delta?.content || '',
// @ts-ignore key is not typed // @ts-ignore key is not typed
@ -253,7 +256,8 @@ export default class OpenAIProvider extends BaseProvider {
time_completion_millsec, time_completion_millsec,
time_first_token_millsec, time_first_token_millsec,
time_thinking_millsec time_thinking_millsec
} },
citations
}) })
} }
} }
@ -337,7 +341,11 @@ export default class OpenAIProvider extends BaseProvider {
max_tokens: 1000 max_tokens: 1000
}) })
return removeSpecialCharacters(response.choices[0].message?.content?.substring(0, 50) || '') // 针对思考类模型的返回,总结仅截取</think>之后的内容
let content = response.choices[0].message?.content || ''
content = content.replace(/^<think>(.*?)<\/think>/s, '')
return removeSpecialCharacters(content.substring(0, 50))
} }
public async generateText({ prompt, content }: { prompt: string; content: string }): Promise<string> { public async generateText({ prompt, content }: { prompt: string; content: string }): Promise<string> {

View File

@ -7,11 +7,12 @@ interface ChunkCallbackData {
usage?: OpenAI.Completions.CompletionUsage usage?: OpenAI.Completions.CompletionUsage
metrics?: Metrics metrics?: Metrics
search?: GroundingMetadata search?: GroundingMetadata
citations?: string[]
} }
interface CompletionsParams { interface CompletionsParams {
messages: Message[] messages: Message[]
assistant: Assistant assistant: Assistant
onChunk: ({ text, reasoning_content, usage, metrics, search }: ChunkCallbackData) => void onChunk: ({ text, reasoning_content, usage, metrics, search, citations }: ChunkCallbackData) => void
onFilterMessages: (messages: Message[]) => void onFilterMessages: (messages: Message[]) => void
} }

View File

@ -52,12 +52,13 @@ export async function fetchChatCompletion({
try { try {
let _messages: Message[] = [] let _messages: Message[] = []
let isFirstChunk = true
await AI.completions({ await AI.completions({
messages: filterUsefulMessages(messages), messages: filterUsefulMessages(messages),
assistant, assistant,
onFilterMessages: (messages) => (_messages = messages), onFilterMessages: (messages) => (_messages = messages),
onChunk: ({ text, reasoning_content, usage, metrics, search }) => { onChunk: ({ text, reasoning_content, usage, metrics, search, citations }) => {
message.content = message.content + text || '' message.content = message.content + text || ''
message.usage = usage message.usage = usage
message.metrics = metrics message.metrics = metrics
@ -70,6 +71,15 @@ export async function fetchChatCompletion({
message.metadata = { groundingMetadata: search } message.metadata = { groundingMetadata: search }
} }
// Handle citations from Perplexity API
if (isFirstChunk && citations) {
message.metadata = {
...message.metadata,
citations
}
isFirstChunk = false
}
onResponse({ ...message, status: 'pending' }) onResponse({ ...message, status: 'pending' })
} }
}) })

View File

@ -25,6 +25,11 @@ export interface RuntimeState {
resourcesPath: string resourcesPath: string
update: UpdateState update: UpdateState
webdavSync: WebDAVSyncState webdavSync: WebDAVSyncState
export: ExportState
}
export interface ExportState {
isExporting: boolean
} }
const initialState: RuntimeState = { const initialState: RuntimeState = {
@ -45,6 +50,9 @@ const initialState: RuntimeState = {
lastSyncTime: null, lastSyncTime: null,
syncing: false, syncing: false,
lastSyncError: null lastSyncError: null
},
export: {
isExporting: false
} }
} }
@ -75,7 +83,11 @@ const runtimeSlice = createSlice({
}, },
setWebDAVSyncState: (state, action: PayloadAction<Partial<WebDAVSyncState>>) => { setWebDAVSyncState: (state, action: PayloadAction<Partial<WebDAVSyncState>>) => {
state.webdavSync = { ...state.webdavSync, ...action.payload } state.webdavSync = { ...state.webdavSync, ...action.payload }
} },
setExportState: (state, action: PayloadAction<Partial<ExportState>>) => {
state.export = { ...state.export, ...action.payload }
},
} }
}) })
@ -87,7 +99,8 @@ export const {
setFilesPath, setFilesPath,
setResourcesPath, setResourcesPath,
setUpdateState, setUpdateState,
setWebDAVSyncState setWebDAVSyncState,
setExportState
} = runtimeSlice.actions } = runtimeSlice.actions
export default runtimeSlice.reducer export default runtimeSlice.reducer

View File

@ -65,6 +65,8 @@ export interface SettingsState {
enableQuickAssistant: boolean enableQuickAssistant: boolean
clickTrayToShowQuickAssistant: boolean clickTrayToShowQuickAssistant: boolean
multiModelMessageStyle: MultiModelMessageStyle multiModelMessageStyle: MultiModelMessageStyle
notionDatabaseID: string | null
notionApiKey: string | null
} }
export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold' export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold'
@ -115,7 +117,9 @@ const initialState: SettingsState = {
narrowMode: false, narrowMode: false,
enableQuickAssistant: false, enableQuickAssistant: false,
clickTrayToShowQuickAssistant: false, clickTrayToShowQuickAssistant: false,
multiModelMessageStyle: 'fold' multiModelMessageStyle: 'fold',
notionDatabaseID: '',
notionApiKey: ''
} }
const settingsSlice = createSlice({ const settingsSlice = createSlice({
@ -263,6 +267,12 @@ const settingsSlice = createSlice({
}, },
setMultiModelMessageStyle: (state, action: PayloadAction<'horizontal' | 'vertical' | 'fold'>) => { setMultiModelMessageStyle: (state, action: PayloadAction<'horizontal' | 'vertical' | 'fold'>) => {
state.multiModelMessageStyle = action.payload state.multiModelMessageStyle = action.payload
},
setNotionDatabaseID: (state, action: PayloadAction<string>) => {
state.notionDatabaseID = action.payload
},
setNotionApiKey: (state, action: PayloadAction<string>) => {
state.notionApiKey = action.payload
} }
} }
}) })
@ -312,7 +322,9 @@ export const {
setNarrowMode, setNarrowMode,
setClickTrayToShowQuickAssistant, setClickTrayToShowQuickAssistant,
setEnableQuickAssistant, setEnableQuickAssistant,
setMultiModelMessageStyle setMultiModelMessageStyle,
setNotionDatabaseID,
setNotionApiKey
} = settingsSlice.actions } = settingsSlice.actions
export default settingsSlice.reducer export default settingsSlice.reducer

View File

@ -67,6 +67,8 @@ export type Message = {
metadata?: { metadata?: {
// Gemini // Gemini
groundingMetadata?: any groundingMetadata?: any
// Perplexity
citations?: string[]
} }
askId?: string askId?: string
useful?: boolean useful?: boolean
@ -87,6 +89,7 @@ export type Topic = {
createdAt: string createdAt: string
updatedAt: string updatedAt: string
messages: Message[] messages: Message[]
pinned?: boolean
} }
export type User = { export type User = {

View File

@ -1,4 +1,8 @@
import { Client } from '@notionhq/client'
import db from '@renderer/databases' import db from '@renderer/databases'
import i18n from '@renderer/i18n'
import store from '@renderer/store'
import { setExportState } from '@renderer/store/runtime'
import { Message, Topic } from '@renderer/types' import { Message, Topic } from '@renderer/types'
export const messageToMarkdown = (message: Message) => { export const messageToMarkdown = (message: Message) => {
@ -29,3 +33,56 @@ export const exportTopicAsMarkdown = async (topic: Topic) => {
const markdown = await topicToMarkdown(topic) const markdown = await topicToMarkdown(topic)
window.api.file.save(fileName, markdown) window.api.file.save(fileName, markdown)
} }
export const exportTopicToNotion = async (topic: Topic) => {
const { isExporting } = store.getState().runtime.export
if (isExporting) {
window.message.warning({ content: i18n.t('message.warn.notion.exporting'), key: 'notion-exporting' })
return
}
setExportState({
isExporting: true
})
const { notionDatabaseID, notionApiKey } = store.getState().settings
if (!notionApiKey || !notionDatabaseID) {
window.message.error({ content: i18n.t('message.error.notion.no_api_key'), key: 'notion-no-apikey-error' })
return
}
try {
const notion = new Client({ auth: notionApiKey });
const markdown = await topicToMarkdown(topic);
const requestBody = JSON.stringify({ md: markdown })
const res = await fetch('https://md2notion.hilars.dev', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: requestBody
});
const data = await res.json();
const notionBlocks = data;
const response = await notion.pages.create({
parent: { database_id: notionDatabaseID },
properties: {
Name: {
title: [{ text: { content: topic.name } }]
}
},
children: notionBlocks // 使用转换后的块
});
window.message.success({ content: i18n.t('message.success.notion.export'), key: 'notion-success' })
return response
} catch (error:any) {
window.message.error({ content: i18n.t('message.error.notion.export'), key: 'notion-error' })
return null
} finally {
setExportState({
isExporting: false
})
}
};

260
yarn.lock
View File

@ -1321,7 +1321,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@langchain/core@npm:^0.3.25, @langchain/core@npm:^0.3.26": "@langchain/core@npm:^0.3.26":
version: 0.3.27 version: 0.3.27
resolution: "@langchain/core@npm:0.3.27" resolution: "@langchain/core@npm:0.3.27"
dependencies: dependencies:
@ -1355,6 +1355,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@langchain/openai@npm:>=0.1.0 <0.5.0":
version: 0.4.3
resolution: "@langchain/openai@npm:0.4.3"
dependencies:
js-tiktoken: "npm:^1.0.12"
openai: "npm:^4.77.0"
zod: "npm:^3.22.4"
zod-to-json-schema: "npm:^3.22.3"
peerDependencies:
"@langchain/core": ">=0.3.39 <0.4.0"
checksum: 10c0/796427ae6fb5dcdd7a4f38a5f295ef265da3bdd7ab79e2cfe94ecf9e33c5596f4a30c649f29f19079b20742f4036af354b91ff7c243a4c183919f8a78e999c64
languageName: node
linkType: hard
"@langchain/openai@patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch": "@langchain/openai@patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch":
version: 0.3.16 version: 0.3.16
resolution: "@langchain/openai@patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch::version=0.3.16&hash=642f39" resolution: "@langchain/openai@patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch::version=0.3.16&hash=642f39"
@ -1480,18 +1494,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-interfaces@npm:0.1.25":
version: 0.1.25
resolution: "@llm-tools/embedjs-interfaces@npm:0.1.25"
dependencies:
"@langchain/core": "npm:^0.3.25"
debug: "npm:^4.4.0"
md5: "npm:^2.3.0"
uuid: "npm:^11.0.3"
checksum: 10c0/0aea17be12d659a73425c3c7b879c658fda3f9d4bcc207f3df8a5be06386013a4ebc29fbefb00e6bb0b021f06f7daad4bccb3cfa708a027974fe48739c5d8bf3
languageName: node
linkType: hard
"@llm-tools/embedjs-interfaces@npm:0.1.28": "@llm-tools/embedjs-interfaces@npm:0.1.28":
version: 0.1.28 version: 0.1.28
resolution: "@llm-tools/embedjs-interfaces@npm:0.1.28" resolution: "@llm-tools/embedjs-interfaces@npm:0.1.28"
@ -1504,31 +1506,19 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-libsql@npm:0.1.25": "@llm-tools/embedjs-libsql@npm:^0.1.28":
version: 0.1.25 version: 0.1.28
resolution: "@llm-tools/embedjs-libsql@npm:0.1.25" resolution: "@llm-tools/embedjs-libsql@npm:0.1.28"
dependencies: dependencies:
"@libsql/client": "npm:^0.14.0" "@libsql/client": "npm:^0.14.0"
"@llm-tools/embedjs-interfaces": "npm:0.1.25" "@llm-tools/embedjs-interfaces": "npm:0.1.28"
"@llm-tools/embedjs-utils": "npm:0.1.25" "@llm-tools/embedjs-utils": "npm:0.1.28"
debug: "npm:^4.4.0" debug: "npm:^4.4.0"
checksum: 10c0/7a21019ed4d7cb1f8eb21ae9015c31b2e9d5ee517e741b88d5660fb322fc0d6bc163070034036c58e0fc67437dd39aeb3aa30b9874ef1dd553a3c5a2c5bbbe55 checksum: 10c0/ec2c50d89a9d601618d153839fc1545bef38a8a7475f2b6ed21a5d876d7964b958950872df64719a3a851a6d48a3a613233c854a091726ebdf2d992c1fdadf7f
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-libsql@patch:@llm-tools/embedjs-libsql@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-libsql-npm-0.1.25-fad000d74c.patch": "@llm-tools/embedjs-loader-csv@npm:^0.1.28":
version: 0.1.25
resolution: "@llm-tools/embedjs-libsql@patch:@llm-tools/embedjs-libsql@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-libsql-npm-0.1.25-fad000d74c.patch::version=0.1.25&hash=769e98"
dependencies:
"@libsql/client": "npm:^0.14.0"
"@llm-tools/embedjs-interfaces": "npm:0.1.25"
"@llm-tools/embedjs-utils": "npm:0.1.25"
debug: "npm:^4.4.0"
checksum: 10c0/83113c33249fdb391b01d56a1e9078ec65315e60dbe6097187acc3685377edb16d9c07c6a26ef043c145f2b851868da8dfe2808555aa57a5e1a9aaf9d9b734f2
languageName: node
linkType: hard
"@llm-tools/embedjs-loader-csv@npm:^0.1.25":
version: 0.1.28 version: 0.1.28
resolution: "@llm-tools/embedjs-loader-csv@npm:0.1.28" resolution: "@llm-tools/embedjs-loader-csv@npm:0.1.28"
dependencies: dependencies:
@ -1541,37 +1531,53 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-loader-markdown@npm:0.1.25": "@llm-tools/embedjs-loader-image@npm:^0.1.28":
version: 0.1.25 version: 0.1.28
resolution: "@llm-tools/embedjs-loader-markdown@npm:0.1.25" resolution: "@llm-tools/embedjs-loader-image@npm:0.1.28"
dependencies: dependencies:
"@llm-tools/embedjs-interfaces": "npm:0.1.25" "@langchain/core": "npm:^0.3.26"
"@llm-tools/embedjs-loader-web": "npm:0.1.25" "@llm-tools/embedjs-interfaces": "npm:0.1.28"
"@llm-tools/embedjs-utils": "npm:0.1.28"
debug: "npm:^4.4.0"
exifremove: "npm:^1.0.1"
md5: "npm:^2.3.0"
mime: "npm:^4.0.6"
stream-mime-type: "npm:^2.0.0"
checksum: 10c0/e9414dde0d10afe1541bfe464b80f5cb61ec523f28dce62ab931a3f4ffde93c5589fde8e7a7f5751e7dc20d68fe2d28883925ba65e2542ee9d43002591568af1
languageName: node
linkType: hard
"@llm-tools/embedjs-loader-markdown@npm:0.1.28":
version: 0.1.28
resolution: "@llm-tools/embedjs-loader-markdown@npm:0.1.28"
dependencies:
"@llm-tools/embedjs-interfaces": "npm:0.1.28"
"@llm-tools/embedjs-loader-web": "npm:0.1.28"
debug: "npm:^4.4.0" debug: "npm:^4.4.0"
md5: "npm:^2.3.0" md5: "npm:^2.3.0"
micromark: "npm:^4.0.1" micromark: "npm:^4.0.1"
micromark-extension-gfm: "npm:^3.0.0" micromark-extension-gfm: "npm:^3.0.0"
micromark-extension-mdx-jsx: "npm:^3.0.1" micromark-extension-mdx-jsx: "npm:^3.0.1"
checksum: 10c0/9cc540a654ece8d108766962017225167d324c40fd1911b1ccd5b749820a3b4e475ca008ef02fc0094bf698de6a87cda672168c4741d2aabb549380b0b64937c checksum: 10c0/deb86848c57cdaf1aa89cd3382505aa4cc53c170d68105a97da1f6ebaff508ed6db1f164004ae1e0426266c29e15435a5bc092eb37ca4fb81ee574940daf1c0e
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-loader-markdown@patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.25-d1d536d640.patch": "@llm-tools/embedjs-loader-markdown@patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch":
version: 0.1.25 version: 0.1.28
resolution: "@llm-tools/embedjs-loader-markdown@patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.25-d1d536d640.patch::version=0.1.25&hash=3a7d12" resolution: "@llm-tools/embedjs-loader-markdown@patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch::version=0.1.28&hash=9c7dea"
dependencies: dependencies:
"@llm-tools/embedjs-interfaces": "npm:0.1.25" "@llm-tools/embedjs-interfaces": "npm:0.1.28"
"@llm-tools/embedjs-loader-web": "npm:0.1.25" "@llm-tools/embedjs-loader-web": "npm:0.1.28"
debug: "npm:^4.4.0" debug: "npm:^4.4.0"
md5: "npm:^2.3.0" md5: "npm:^2.3.0"
micromark: "npm:^4.0.1" micromark: "npm:^4.0.1"
micromark-extension-gfm: "npm:^3.0.0" micromark-extension-gfm: "npm:^3.0.0"
micromark-extension-mdx-jsx: "npm:^3.0.1" micromark-extension-mdx-jsx: "npm:^3.0.1"
checksum: 10c0/8e91a1260f8c94ec516be13a5105055bf140b5c63a85fa3c7641cc8f6799a0410ddb6bce61db858e91712f7dbc2b333269eb7c3ce813c1d95416f49f4f4f31a5 checksum: 10c0/4be7354294c9cc1ee5b93e0bf49a218894e5a0ad63f344300a9277751fb7517d7ff9ab51594eb63f548d303cec0c747507a5df29bd92deb506bf9829f1675f67
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-loader-msoffice@npm:^0.1.25": "@llm-tools/embedjs-loader-msoffice@npm:^0.1.28":
version: 0.1.28 version: 0.1.28
resolution: "@llm-tools/embedjs-loader-msoffice@npm:0.1.28" resolution: "@llm-tools/embedjs-loader-msoffice@npm:0.1.28"
dependencies: dependencies:
@ -1584,7 +1590,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-loader-pdf@npm:^0.1.25": "@llm-tools/embedjs-loader-pdf@npm:^0.1.28":
version: 0.1.28 version: 0.1.28
resolution: "@llm-tools/embedjs-loader-pdf@npm:0.1.28" resolution: "@llm-tools/embedjs-loader-pdf@npm:0.1.28"
dependencies: dependencies:
@ -1597,7 +1603,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-loader-sitemap@npm:^0.1.25": "@llm-tools/embedjs-loader-sitemap@npm:^0.1.28":
version: 0.1.28 version: 0.1.28
resolution: "@llm-tools/embedjs-loader-sitemap@npm:0.1.28" resolution: "@llm-tools/embedjs-loader-sitemap@npm:0.1.28"
dependencies: dependencies:
@ -1610,21 +1616,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-loader-web@npm:0.1.25": "@llm-tools/embedjs-loader-web@npm:0.1.28, @llm-tools/embedjs-loader-web@npm:^0.1.28":
version: 0.1.25
resolution: "@llm-tools/embedjs-loader-web@npm:0.1.25"
dependencies:
"@langchain/textsplitters": "npm:^0.1.0"
"@llm-tools/embedjs-interfaces": "npm:0.1.25"
"@llm-tools/embedjs-utils": "npm:0.1.25"
debug: "npm:^4.4.0"
html-to-text: "npm:^9.0.5"
md5: "npm:^2.3.0"
checksum: 10c0/abbf9cc252fb928745a04152fedafc03c07eef03190e10cca830dc41e8a24b35055cb2a9011633311dcf069ffa111582b65c592e1a0ce0ae84f1b4d5e468d8b2
languageName: node
linkType: hard
"@llm-tools/embedjs-loader-web@npm:0.1.28, @llm-tools/embedjs-loader-web@npm:^0.1.25":
version: 0.1.28 version: 0.1.28
resolution: "@llm-tools/embedjs-loader-web@npm:0.1.28" resolution: "@llm-tools/embedjs-loader-web@npm:0.1.28"
dependencies: dependencies:
@ -1638,7 +1630,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-loader-xml@npm:^0.1.25": "@llm-tools/embedjs-loader-xml@npm:^0.1.28":
version: 0.1.28 version: 0.1.28
resolution: "@llm-tools/embedjs-loader-xml@npm:0.1.28" resolution: "@llm-tools/embedjs-loader-xml@npm:0.1.28"
dependencies: dependencies:
@ -1650,7 +1642,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-openai@npm:^0.1.25": "@llm-tools/embedjs-openai@npm:^0.1.28":
version: 0.1.28 version: 0.1.28
resolution: "@llm-tools/embedjs-openai@npm:0.1.28" resolution: "@llm-tools/embedjs-openai@npm:0.1.28"
dependencies: dependencies:
@ -1662,15 +1654,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-utils@npm:0.1.25":
version: 0.1.25
resolution: "@llm-tools/embedjs-utils@npm:0.1.25"
dependencies:
"@llm-tools/embedjs-interfaces": "npm:0.1.25"
checksum: 10c0/8220181ac574e1088562f1c7913709806e6558ab02a535b888abd6267a4a03f9a45984998fddf07b4930c91a7f829458754f1da7488f453aee824de08958705a
languageName: node
linkType: hard
"@llm-tools/embedjs-utils@npm:0.1.28": "@llm-tools/embedjs-utils@npm:0.1.28":
version: 0.1.28 version: 0.1.28
resolution: "@llm-tools/embedjs-utils@npm:0.1.28" resolution: "@llm-tools/embedjs-utils@npm:0.1.28"
@ -1680,44 +1663,35 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs-utils@patch:@llm-tools/embedjs-utils@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-utils-npm-0.1.25-fd8fe8a193.patch": "@llm-tools/embedjs@npm:0.1.28":
version: 0.1.25 version: 0.1.28
resolution: "@llm-tools/embedjs-utils@patch:@llm-tools/embedjs-utils@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-utils-npm-0.1.25-fd8fe8a193.patch::version=0.1.25&hash=faf2ca" resolution: "@llm-tools/embedjs@npm:0.1.28"
dependencies:
"@llm-tools/embedjs-interfaces": "npm:0.1.25"
checksum: 10c0/c639f64a26023b21b2cd8b2bf5007541f4b1382ac3358d9404a531fcee66482ea94fe151b7d39b44a6cb903b975eb379101c4cca896d8a08a070330355e67c19
languageName: node
linkType: hard
"@llm-tools/embedjs@npm:0.1.25":
version: 0.1.25
resolution: "@llm-tools/embedjs@npm:0.1.25"
dependencies: dependencies:
"@langchain/textsplitters": "npm:^0.1.0" "@langchain/textsplitters": "npm:^0.1.0"
"@llm-tools/embedjs-interfaces": "npm:0.1.25" "@llm-tools/embedjs-interfaces": "npm:0.1.28"
"@llm-tools/embedjs-utils": "npm:0.1.25" "@llm-tools/embedjs-utils": "npm:0.1.28"
debug: "npm:^4.4.0" debug: "npm:^4.4.0"
langchain: "npm:^0.3.7" langchain: "npm:^0.3.8"
md5: "npm:^2.3.0" md5: "npm:^2.3.0"
mime: "npm:^4.0.6" mime: "npm:^4.0.6"
stream-mime-type: "npm:^2.0.0" stream-mime-type: "npm:^2.0.0"
checksum: 10c0/8a6afefbd8e304c037104eddf0fe35086165eb3a381b866e874bf6901c2da8a6dfc76d3d7eb027f7c694a163133154e3be7be777c362119a48925a6b56cce5db checksum: 10c0/0ade20a97b987c5b24175e1e46c7f0917e91b0acde712e9a759fb9fe7f48b04a79fca28c2e4cc8702a25e6cd7fba8a9351bf1f7e5bbf1373f0f60de047703d7a
languageName: node languageName: node
linkType: hard linkType: hard
"@llm-tools/embedjs@patch:@llm-tools/embedjs@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.25-ec5645cf36.patch": "@llm-tools/embedjs@patch:@llm-tools/embedjs@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch":
version: 0.1.25 version: 0.1.28
resolution: "@llm-tools/embedjs@patch:@llm-tools/embedjs@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.25-ec5645cf36.patch::version=0.1.25&hash=3b8a9c" resolution: "@llm-tools/embedjs@patch:@llm-tools/embedjs@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch::version=0.1.28&hash=51ffc6"
dependencies: dependencies:
"@langchain/textsplitters": "npm:^0.1.0" "@langchain/textsplitters": "npm:^0.1.0"
"@llm-tools/embedjs-interfaces": "npm:0.1.25" "@llm-tools/embedjs-interfaces": "npm:0.1.28"
"@llm-tools/embedjs-utils": "npm:0.1.25" "@llm-tools/embedjs-utils": "npm:0.1.28"
debug: "npm:^4.4.0" debug: "npm:^4.4.0"
langchain: "npm:^0.3.7" langchain: "npm:^0.3.8"
md5: "npm:^2.3.0" md5: "npm:^2.3.0"
mime: "npm:^4.0.6" mime: "npm:^4.0.6"
stream-mime-type: "npm:^2.0.0" stream-mime-type: "npm:^2.0.0"
checksum: 10c0/3ef5fb0068e662d9fc3ff794c0c200fca91fba548d1989a628ad2c3576e3f97838f3abca683adc77b1774d57e09c6d155c1c4b9d69eb20aac26bd274148f72a1 checksum: 10c0/2af2bcd2f6476eaf02ed9ff5e189ecb03d663146cd5229c3c32b45fef8a37fd84a48193f8d067eaf9339e89a5035502c4ff48d2370dfd655294daa8b4e365841
languageName: node languageName: node
linkType: hard linkType: hard
@ -1776,6 +1750,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@notionhq/client@npm:^2.2.15":
version: 2.2.15
resolution: "@notionhq/client@npm:2.2.15"
dependencies:
"@types/node-fetch": "npm:^2.5.10"
node-fetch: "npm:^2.6.1"
checksum: 10c0/4153c2e5b47d2ba141d025f2753d0e79ca9b9f25bd8bbdfa9dbf74fe4c2e157ea7964c59387d05163972c4575830bdc48d02db29270e244d81398df0f89fd7dd
languageName: node
linkType: hard
"@npmcli/agent@npm:^3.0.0": "@npmcli/agent@npm:^3.0.0":
version: 3.0.0 version: 3.0.0
resolution: "@npmcli/agent@npm:3.0.0" resolution: "@npmcli/agent@npm:3.0.0"
@ -2640,7 +2624,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/node-fetch@npm:^2.6.4": "@types/node-fetch@npm:^2.5.10, @types/node-fetch@npm:^2.6.4":
version: 2.6.12 version: 2.6.12
resolution: "@types/node-fetch@npm:2.6.12" resolution: "@types/node-fetch@npm:2.6.12"
dependencies: dependencies:
@ -3001,16 +2985,18 @@ __metadata:
"@google/generative-ai": "npm:^0.21.0" "@google/generative-ai": "npm:^0.21.0"
"@hello-pangea/dnd": "npm:^16.6.0" "@hello-pangea/dnd": "npm:^16.6.0"
"@kangfenmao/keyv-storage": "npm:^0.1.0" "@kangfenmao/keyv-storage": "npm:^0.1.0"
"@llm-tools/embedjs": "patch:@llm-tools/embedjs@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.25-ec5645cf36.patch" "@llm-tools/embedjs": "patch:@llm-tools/embedjs@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch"
"@llm-tools/embedjs-libsql": "patch:@llm-tools/embedjs-libsql@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-libsql-npm-0.1.25-fad000d74c.patch" "@llm-tools/embedjs-libsql": "npm:^0.1.28"
"@llm-tools/embedjs-loader-csv": "npm:^0.1.25" "@llm-tools/embedjs-loader-csv": "npm:^0.1.28"
"@llm-tools/embedjs-loader-markdown": "patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.25#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.25-d1d536d640.patch" "@llm-tools/embedjs-loader-image": "npm:^0.1.28"
"@llm-tools/embedjs-loader-msoffice": "npm:^0.1.25" "@llm-tools/embedjs-loader-markdown": "patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch"
"@llm-tools/embedjs-loader-pdf": "npm:^0.1.25" "@llm-tools/embedjs-loader-msoffice": "npm:^0.1.28"
"@llm-tools/embedjs-loader-sitemap": "npm:^0.1.25" "@llm-tools/embedjs-loader-pdf": "npm:^0.1.28"
"@llm-tools/embedjs-loader-web": "npm:^0.1.25" "@llm-tools/embedjs-loader-sitemap": "npm:^0.1.28"
"@llm-tools/embedjs-loader-xml": "npm:^0.1.25" "@llm-tools/embedjs-loader-web": "npm:^0.1.28"
"@llm-tools/embedjs-openai": "npm:^0.1.25" "@llm-tools/embedjs-loader-xml": "npm:^0.1.28"
"@llm-tools/embedjs-openai": "npm:^0.1.28"
"@notionhq/client": "npm:^2.2.15"
"@reduxjs/toolkit": "npm:^2.2.5" "@reduxjs/toolkit": "npm:^2.2.5"
"@types/adm-zip": "npm:^0" "@types/adm-zip": "npm:^0"
"@types/fs-extra": "npm:^11" "@types/fs-extra": "npm:^11"
@ -4510,6 +4496,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"console-table-printer@npm:^2.12.1":
version: 2.12.1
resolution: "console-table-printer@npm:2.12.1"
dependencies:
simple-wcswidth: "npm:^1.0.1"
checksum: 10c0/8f28e9c0ae5df77f5d60da3da002ecd95ebe1812b0b9e0a6d2795c81b5121b39774f32506bccf68830a838ca4d8fbb2ab8824e729dba2c5e30cdeb9df4dd5f2b
languageName: node
linkType: hard
"convert-source-map@npm:^2.0.0": "convert-source-map@npm:^2.0.0":
version: 2.0.0 version: 2.0.0
resolution: "convert-source-map@npm:2.0.0" resolution: "convert-source-map@npm:2.0.0"
@ -5967,6 +5962,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"exifremove@npm:^1.0.1":
version: 1.0.1
resolution: "exifremove@npm:1.0.1"
checksum: 10c0/df5a30087b262a35f2932e4d8b567206ec5f39518740c1552361e9a6eee0a38819359bd8ab2bd5df391647c4dea8d572995852f355cf8fc504df6b1685d82921
languageName: node
linkType: hard
"expand-template@npm:^2.0.3": "expand-template@npm:^2.0.3":
version: 2.0.3 version: 2.0.3
resolution: "expand-template@npm:2.0.3" resolution: "expand-template@npm:2.0.3"
@ -8278,16 +8280,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"langchain@npm:^0.3.7": "langchain@npm:^0.3.8":
version: 0.3.10 version: 0.3.15
resolution: "langchain@npm:0.3.10" resolution: "langchain@npm:0.3.15"
dependencies: dependencies:
"@langchain/openai": "npm:>=0.1.0 <0.4.0" "@langchain/openai": "npm:>=0.1.0 <0.5.0"
"@langchain/textsplitters": "npm:>=0.0.0 <0.2.0" "@langchain/textsplitters": "npm:>=0.0.0 <0.2.0"
js-tiktoken: "npm:^1.0.12" js-tiktoken: "npm:^1.0.12"
js-yaml: "npm:^4.1.0" js-yaml: "npm:^4.1.0"
jsonpointer: "npm:^5.0.1" jsonpointer: "npm:^5.0.1"
langsmith: "npm:^0.2.8" langsmith: "npm:>=0.2.8 <0.4.0"
openapi-types: "npm:^12.1.3" openapi-types: "npm:^12.1.3"
p-retry: "npm:4" p-retry: "npm:4"
uuid: "npm:^10.0.0" uuid: "npm:^10.0.0"
@ -8300,6 +8302,7 @@ __metadata:
"@langchain/cerebras": "*" "@langchain/cerebras": "*"
"@langchain/cohere": "*" "@langchain/cohere": "*"
"@langchain/core": ">=0.2.21 <0.4.0" "@langchain/core": ">=0.2.21 <0.4.0"
"@langchain/deepseek": "*"
"@langchain/google-genai": "*" "@langchain/google-genai": "*"
"@langchain/google-vertexai": "*" "@langchain/google-vertexai": "*"
"@langchain/google-vertexai-web": "*" "@langchain/google-vertexai-web": "*"
@ -8320,6 +8323,8 @@ __metadata:
optional: true optional: true
"@langchain/cohere": "@langchain/cohere":
optional: true optional: true
"@langchain/deepseek":
optional: true
"@langchain/google-genai": "@langchain/google-genai":
optional: true optional: true
"@langchain/google-vertexai": "@langchain/google-vertexai":
@ -8342,7 +8347,27 @@ __metadata:
optional: true optional: true
typeorm: typeorm:
optional: true optional: true
checksum: 10c0/4120bc57dfb4d9ca1235e7c6f35227ada5be6e92e818b7a7d7e01f65fe937b1ffc172c8bb58f4861067e1475f72921dbae07ecb0b006077fa52f3cdfe9702d17 checksum: 10c0/3c21e31d6a470f50125ffa77dc85e5dec116f3e9d8a319c32bd1de558ca026149fa0656468e676978031147cf3c8875a9239e620c1ee749be0bca4224d5f1848
languageName: node
linkType: hard
"langsmith@npm:>=0.2.8 <0.4.0":
version: 0.3.7
resolution: "langsmith@npm:0.3.7"
dependencies:
"@types/uuid": "npm:^10.0.0"
chalk: "npm:^4.1.2"
console-table-printer: "npm:^2.12.1"
p-queue: "npm:^6.6.2"
p-retry: "npm:4"
semver: "npm:^7.6.3"
uuid: "npm:^10.0.0"
peerDependencies:
openai: "*"
peerDependenciesMeta:
openai:
optional: true
checksum: 10c0/68ada1d5120376467bbf7edca17b0629f3d5a2588c91d2396a372b69217e3de960487f1c4109c36e38e0ee6a467d5f81e4b59d8f3312e480af5bb01007d179f3
languageName: node languageName: node
linkType: hard linkType: hard
@ -9845,7 +9870,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"node-fetch@npm:^2.6.7": "node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7":
version: 2.7.0 version: 2.7.0
resolution: "node-fetch@npm:2.7.0" resolution: "node-fetch@npm:2.7.0"
dependencies: dependencies:
@ -12681,6 +12706,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"simple-wcswidth@npm:^1.0.1":
version: 1.0.1
resolution: "simple-wcswidth@npm:1.0.1"
checksum: 10c0/2befead4c97134424aa3fba593a81daa9934fd61b9e4c65374b57ac5eecc2f2be1984b017bbdbc919923e19b77f2fcbdb94434789b9643fa8c3fde3a2a6a4b6f
languageName: node
linkType: hard
"sitemapper@npm:^3.2.20": "sitemapper@npm:^3.2.20":
version: 3.2.20 version: 3.2.20
resolution: "sitemapper@npm:3.2.20" resolution: "sitemapper@npm:3.2.20"