forked from continuedev/continue
-
Notifications
You must be signed in to change notification settings - Fork 1
/
DocsContextProvider.ts
136 lines (124 loc) · 4.45 KB
/
DocsContextProvider.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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import {
ContextItem,
ContextProviderDescription,
ContextProviderExtras,
ContextSubmenuItem,
LoadSubmenuItemsArgs,
} from "../../index.js";
import configs from "../../indexing/docs/preIndexedDocs.js";
import TransformersJsEmbeddingsProvider from "../../indexing/embeddings/TransformersJsEmbeddingsProvider.js";
import { BaseContextProvider } from "../index.js";
class DocsContextProvider extends BaseContextProvider {
static DEFAULT_N_RETRIEVE = 30;
static DEFAULT_N_FINAL = 15;
static description: ContextProviderDescription = {
title: "docs",
displayTitle: "Docs",
description: "Type to search docs",
type: "submenu",
};
async getContextItems(
query: string,
extras: ContextProviderExtras,
): Promise<ContextItem[]> {
// Not supported in JetBrains IDEs right now
if ((await extras.ide.getIdeInfo()).ideType === "jetbrains") {
throw new Error(
"The @docs context provider is not currently supported in JetBrains IDEs. We'll have an update soon!",
);
}
const { retrieveDocs } = await import("../../indexing/docs/db");
const embeddingsProvider = new TransformersJsEmbeddingsProvider();
const [vector] = await embeddingsProvider.embed([extras.fullInput]);
let chunks = await retrieveDocs(
query,
vector,
this.options?.nRetrieve ?? DocsContextProvider.DEFAULT_N_RETRIEVE,
embeddingsProvider.id,
);
if (extras.reranker) {
try {
const scores = await extras.reranker.rerank(extras.fullInput, chunks);
chunks.sort(
(a, b) => scores[chunks.indexOf(b)] - scores[chunks.indexOf(a)],
);
chunks = chunks.splice(
0,
this.options?.nFinal ?? DocsContextProvider.DEFAULT_N_FINAL,
);
} catch (e) {
console.warn(`Failed to rerank docs results: ${e}`);
chunks = chunks.splice(
0,
this.options?.nFinal ?? DocsContextProvider.DEFAULT_N_FINAL,
);
}
}
return [
...chunks
.map((chunk) => ({
name: chunk.filepath.includes("/tree/main") // For display of GitHub files
? chunk.filepath
.split("/")
.slice(1)
.join("/")
.split("/tree/main/")
.slice(1)
.join("/")
: chunk.otherMetadata?.title || chunk.filepath,
description: chunk.filepath, // new URL(chunk.filepath, query).toString(),
content: chunk.content,
}))
.reverse(),
{
name: "Instructions",
description: "Instructions",
content:
"Use the above documentation to answer the following question. You should not reference anything outside of what is shown, unless it is a commonly known concept. Reference URLs whenever possible using markdown formatting. If there isn't enough information to answer the question, suggest where the user might look to learn more.",
},
];
}
async loadSubmenuItems(
args: LoadSubmenuItemsArgs,
): Promise<ContextSubmenuItem[]> {
const { listDocs } = await import("../../indexing/docs/db");
const docs = await listDocs();
const submenuItems = docs.map((doc) => ({
title: doc.title,
description: new URL(doc.baseUrl).hostname,
id: doc.baseUrl,
}));
submenuItems.push(
...configs
// After it's actually downloaded, we don't want to show twice
.filter(
(config) => !submenuItems.some((item) => item.id === config.startUrl),
)
.map((config) => ({
title: config.title,
description: new URL(config.startUrl).hostname,
id: config.startUrl,
})),
);
// Sort submenuItems such that the objects with titles which don't occur in configs occur first, and alphabetized
submenuItems.sort((a, b) => {
const aTitleInConfigs = !!configs.find(
(config) => config.title === a.title,
);
const bTitleInConfigs = !!configs.find(
(config) => config.title === b.title,
);
// Primary criterion: Items not in configs come first
if (!aTitleInConfigs && bTitleInConfigs) {
return -1;
} else if (aTitleInConfigs && !bTitleInConfigs) {
return 1;
} else {
// Secondary criterion: Alphabetical order when both items are in the same category
return a.title.toString().localeCompare(b.title.toString());
}
});
return submenuItems;
}
}
export default DocsContextProvider;