-
-
Notifications
You must be signed in to change notification settings - Fork 41
/
retrieval.ts
99 lines (87 loc) · 2.89 KB
/
retrieval.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import { createAIFunction } from '@dexaai/dexter/prompt'
import pMap from 'p-map'
import { z } from 'zod'
import type { File } from './db'
import { getObject } from './storage'
import { getNormalizedFileName } from './utils'
export async function processFilesForAssistant(files: File[]): Promise<void> {
console.log('processFilesForAssistant', files)
await pMap(files, processFileForAssistant, { concurrency: 4 })
}
/**
* Preprocess file for knowledge retrieval.
*
* This may include things like chunking and upserting into a vector database.
*
* At retrieval time, you are given an array of `file_ids` to filter by, so
* make sure to store the `file_id` in the database.
*/
export async function processFileForAssistant(file: File): Promise<void> {
console.log('processFileForAssistant', file)
// TODO: encapsulate the getObject and key in a `files` module
const object = await getObject(file.filename)
// @ts-expect-error TODO
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _ = await object.Body!.transformToString()
// TODO: we're not actually pre-processing the file yet
// @see https://github.com/transitive-bullshit/OpenOpenAI/issues/2
file.status = 'processed'
}
export const retrievalFunction = createAIFunction(
{
name: 'retrieval',
description: 'Retrieves relevant context from a set of attached files',
argsSchema: z.object({
query: z.string()
})
},
async () => {
// not used
throw new Error('This should never be called')
}
)
/**
* Performs knowledge retrieval for a given `query` on a set of `file_ids` for RAG.
*
* This function contains the runtime implementation of the built-in `retrieval`
* tool, which can be enabled on assistants to search arbitrary user files.
*
* TODO: implement actual semantic retrieval
* TODO: perform context compression / truncation of the retrieved context
*/
export async function retrievalTool({
query: _,
files
}: {
query: string
files: File[]
}): Promise<string[]> {
const filesWithContent = (
await pMap(files, getFileContent, {
concurrency: 8
})
).filter(Boolean)
return filesWithContent.map((file) => {
return `Filename: ${getNormalizedFileName(file.file)}\n\n${file.content}`
})
}
async function getFileContent(
file: File
): Promise<{ file: File; content: string } | null> {
// TODO: encapsulate the getObject and key in a `files` module
try {
const object = await getObject(file.filename)
const body = await object.Body!.transformToString()
// TODO: handle larger files; this is just a naive placeholder
const content = body.slice(0, 10000)
// TODO: handle non-text/markdown files like pdfs, images, and html
// TODO: actual chunking, compute embeddings, and store in vector db
return {
file,
content
}
} catch (err: any) {
console.error('error fetching file', file.id, err.message)
return null
}
}