fix: copilot not working (#3425)

This commit is contained in:
Alex Yang
2023-07-27 17:28:21 -07:00
committed by GitHub
parent aa69a7cad2
commit f9929ebd61
11 changed files with 165 additions and 181 deletions

View File

@@ -11,7 +11,7 @@
"@affine/component": "workspace:*",
"@toeverything/plugin-infra": "workspace:*",
"idb": "^7.1.1",
"langchain": "^0.0.107",
"langchain": "^0.0.118",
"marked": "^5.1.1",
"marked-gfm-heading-id": "^3.0.4",
"marked-mangle": "^1.1.0",

View File

@@ -1,6 +1,4 @@
import { Button, FlexWrapper, Input } from '@affine/component';
import { SettingRow } from '@affine/component/setting-components';
import { SettingWrapper } from '@affine/component/setting-components';
import { useAtom } from 'jotai';
import { type ReactElement, useCallback } from 'react';
@@ -9,43 +7,28 @@ import { conversationHistoryDBName } from '../core/langchain/message-history';
export const DebugContent = (): ReactElement => {
const [key, setKey] = useAtom(openAIApiKeyAtom);
const desc = (
<>
<span>You can get your API key from </span>
<a
target="_blank"
rel="noreferrer"
href="https://beta.openai.com/account/api-keys"
>
here.
</a>
</>
);
return (
<div>
<SettingWrapper title={'Ai Copilot'}>
<SettingRow name={'openAI API key'} desc={desc}></SettingRow>
<FlexWrapper justifyContent="space-between">
<Input
defaultValue={key ?? undefined}
onChange={useCallback(
(newValue: string) => {
setKey(newValue);
},
[setKey]
)}
/>
<Button
size="large"
onClick={() => {
indexedDB.deleteDatabase(conversationHistoryDBName);
location.reload();
}}
>
{'Clean conversations'}
</Button>
</FlexWrapper>
</SettingWrapper>
<FlexWrapper justifyContent="space-between">
<Input
defaultValue={key ?? undefined}
onChange={useCallback(
(newValue: string) => {
setKey(newValue);
},
[setKey]
)}
/>
<Button
size="large"
onClick={() => {
indexedDB.deleteDatabase(conversationHistoryDBName);
location.reload();
}}
>
{'Clean conversations'}
</Button>
</FlexWrapper>
</div>
);
};

View File

@@ -1,118 +0,0 @@
// fixme: vector store has not finished
import type { DBSchema } from 'idb';
import { Document } from 'langchain/document';
import { Embeddings } from 'langchain/embeddings';
import { VectorStore } from 'langchain/vectorstores';
import { similarity as ml_distance_similarity } from 'ml-distance';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface VectorDBV1 extends DBSchema {
vector: {
key: string;
value: Vector;
};
}
interface Vector {
id: string;
content: string;
embedding: number[];
metadata: Record<string, unknown>;
}
export interface MemoryVectorStoreArgs {
similarity?: typeof ml_distance_similarity.cosine;
}
export class IndexedDBVectorStore extends VectorStore {
memoryVectors: any[] = [];
similarity: typeof ml_distance_similarity.cosine;
constructor(
embeddings: Embeddings,
{ similarity, ...rest }: MemoryVectorStoreArgs = {}
) {
super(embeddings, rest);
this.similarity = similarity ?? ml_distance_similarity.cosine;
}
async addDocuments(documents: Document[]): Promise<void> {
const texts = documents.map(({ pageContent }) => pageContent);
return this.addVectors(
await this.embeddings.embedDocuments(texts),
documents
);
}
async addVectors(vectors: number[][], documents: Document[]): Promise<void> {
const memoryVectors = vectors.map((embedding, idx) => ({
content: documents[idx].pageContent,
embedding,
metadata: documents[idx].metadata,
}));
this.memoryVectors = this.memoryVectors.concat(memoryVectors);
}
async similaritySearchVectorWithScore(
query: number[],
k: number
): Promise<[Document, number][]> {
const searches = this.memoryVectors
.map((vector, index) => ({
similarity: this.similarity(query, vector.embedding),
index,
}))
.sort((a, b) => (a.similarity > b.similarity ? -1 : 0))
.slice(0, k);
const result: [Document, number][] = searches.map(search => [
new Document({
metadata: this.memoryVectors[search.index].metadata,
pageContent: this.memoryVectors[search.index].content,
}),
search.similarity,
]);
return result;
}
static override async fromTexts(
texts: string[],
metadatas: object[] | object,
embeddings: Embeddings,
dbConfig?: MemoryVectorStoreArgs
): Promise<IndexedDBVectorStore> {
const docs: Document[] = [];
for (let i = 0; i < texts.length; i += 1) {
const metadata = Array.isArray(metadatas) ? metadatas[i] : metadatas;
const newDoc = new Document({
pageContent: texts[i],
metadata,
});
docs.push(newDoc);
}
return IndexedDBVectorStore.fromDocuments(docs, embeddings, dbConfig);
}
static override async fromDocuments(
docs: Document[],
embeddings: Embeddings,
dbConfig?: MemoryVectorStoreArgs
): Promise<IndexedDBVectorStore> {
const instance = new this(embeddings, dbConfig);
await instance.addDocuments(docs);
return instance;
}
static async fromExistingIndex(
embeddings: Embeddings,
dbConfig?: MemoryVectorStoreArgs
): Promise<IndexedDBVectorStore> {
const instance = new this(embeddings, dbConfig);
return instance;
}
}

View File

@@ -2,6 +2,7 @@ import type { PluginContext } from '@toeverything/plugin-infra/entry';
import { createElement } from 'react';
import { createRoot } from 'react-dom/client';
import { DebugContent } from './UI/debug-content';
import { DetailContent } from './UI/detail-content';
import { HeaderItem } from './UI/header-item';
@@ -29,5 +30,19 @@ export const entry = (context: PluginContext) => {
root.unmount();
};
});
context.register('setting', div => {
const root = createRoot(div);
root.render(
createElement(
context.utils.PluginProvider,
{},
createElement(DebugContent)
)
);
return () => {
root.unmount();
};
});
return () => {};
};