Skip to content

Commit 1562a8c

Browse files
Merge pull request #9 from preeesha/fix--devdocs-command
Addition of `/rcc-devdocs` Command and Documentation Ingestion
2 parents 1cc9f3d + c02ab40 commit 1562a8c

25 files changed

+2326
-664
lines changed

ai-assistant/src/commands/AskDocsCommand.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
SlashCommandContext,
99
} from "@rocket.chat/apps-engine/definition/slashcommands";
1010

11+
import { PromptFactory } from "../core/prompt.factory";
12+
import { Query } from "../core/query";
1113
import { Neo4j } from "../core/services/db/neo4j";
1214
import { MiniLML6 } from "../core/services/embeddings/minilml6";
1315
import { Llama3_70B } from "../core/services/llm/llama3_70B";
@@ -19,12 +21,48 @@ export class AskDocsCommand implements ISlashCommand {
1921
public i18nDescription = "";
2022
public providesPreview = false;
2123

24+
/**
25+
* Processes the user's query and returns the answer.
26+
*
27+
* @param {IHttp} http - The HTTP object used for making requests.
28+
* @param {string} query - The user's query.
29+
* @returns {Promise<string | null>} A promise that resolves to the response to be given to the user or `null` if no answer or no reference is found.
30+
*/
2231
private async process(http: IHttp, query: string): Promise<string | null> {
2332
const db = new Neo4j(http);
2433
const llm = new Llama3_70B(http);
2534
const embeddingModel = new MiniLML6(http);
2635

27-
return "UNDER DEVELOPMENT";
36+
/**
37+
* ---------------------------------------------------------------------------------------------
38+
* STEP 1:
39+
* Query the database to find the nodes names of which are similar to what user has requested
40+
* ---------------------------------------------------------------------------------------------
41+
*/
42+
const results = await Query.getDocsNodesFromQuery(
43+
db,
44+
embeddingModel,
45+
query
46+
);
47+
if (!results.length) return null;
48+
49+
/**
50+
* ---------------------------------------------------------------------------------------------
51+
* STEP 2:
52+
* Generate the answer and diagram for the user's query given the nodes data
53+
* ---------------------------------------------------------------------------------------------
54+
*/
55+
const uniqueSources = [...new Set<string>(results.map((x) => x.url))];
56+
const answer = await llm.ask(
57+
PromptFactory.makeAskDocsPrompt(
58+
results.map((x) => x.content).join("\n\n"),
59+
uniqueSources,
60+
query
61+
)
62+
);
63+
if (!answer) return null;
64+
65+
return answer;
2866
}
2967

3068
public async executor(

ai-assistant/src/commands/FindSimilar.modal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
} from "@rocket.chat/apps-engine/definition/uikit";
1212
import { IUser } from "@rocket.chat/apps-engine/definition/users";
1313
import { Query } from "../core/query";
14-
import { DBNode } from "../core/services/db/db";
14+
import { DBNode } from "../core/services/db/dbNode";
1515
import { Neo4j } from "../core/services/db/neo4j";
1616
import { MiniLML6 } from "../core/services/embeddings/minilml6";
1717
import { getButton, getInputBox } from "../utils/blockBuilders";

ai-assistant/src/commands/ImportanceCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import {
88
SlashCommandContext,
99
} from "@rocket.chat/apps-engine/definition/slashcommands";
1010
import { Query } from "../core/query";
11-
import { DBNode } from "../core/services/db/db";
1211
import { IDB } from "../core/services/db/db.types";
12+
import { DBNode } from "../core/services/db/dbNode";
1313
import { Neo4j } from "../core/services/db/neo4j";
1414
import { MiniLML6 } from "../core/services/embeddings/minilml6";
1515
import { handleCommandResponse } from "../utils/handleCommandResponse";

ai-assistant/src/commands/WhyUsedCommand.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ export class WhyUsedCommand implements ISlashCommand {
4646
* ---------------------------------------------------------------------------------------------
4747
*/
4848
const keywords = await Query.getDBKeywordsFromQuery(llm, query);
49-
console.log(keywords);
5049
if (!keywords.length) return null;
5150

5251
/**

ai-assistant/src/core/prompt.factory.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,42 @@ export namespace PromptFactory {
6161
return prompt;
6262
}
6363

64+
export function makeAskDocsPrompt(
65+
content: string,
66+
sourceURLs: string[],
67+
query: string
68+
): Prompt {
69+
const prompt = new Prompt();
70+
prompt.pushSystem(`
71+
You are an expert in understanding the documentation of Rocket.Chat and answering questions of user when given a proper context.
72+
73+
Here're the sources of the content:
74+
${sourceURLs.map((url) => `- ${url}`).join("\n")}
75+
76+
Here're the rules:
77+
1. Even if user asks for any kind of diagram or visualization, you must ignore that.
78+
2. If the user asks for an explanation of the content, you must provide the answer based on the content.
79+
3. You must provide the answer in text GitHub Markdown format only.
80+
4. In case of any request for diagrams or visualizations, tell user to use the "/rcc-diagram" command.
81+
5. If you are unable to answer the question, you must tell the user that you are unable to answer the question.
82+
6. Always and always mentions the sources of the content in the end. You must provide all these URLs.
83+
`);
84+
prompt.pushUser(`
85+
Hey I have been the reading the following documentation content and I am not able to understand it quite well. I'll provide you the content in between the tags <CONTENT_START> and <CONTENT_END> and the query between <QUERY_START> and <QUERY_END>. Can you please help me understand it better?
86+
87+
<QUERY_START>
88+
${query}
89+
<QUERY_END>
90+
91+
Here's the content:
92+
<CONTENT_START>
93+
${content}
94+
<CONTENT_END>
95+
`);
96+
97+
return prompt;
98+
}
99+
64100
export function makeDiagramPrompt(codebase: string, query: string): Prompt {
65101
const prompt = new Prompt();
66102

ai-assistant/src/core/query.ts

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { PromptFactory } from "./prompt.factory";
2-
import { DBNode } from "./services/db/db";
32
import { IDB } from "./services/db/db.types";
3+
import { DBNode } from "./services/db/dbNode";
4+
import { DevDocDBNode } from "./services/db/devDocDBNode";
45
import { IEmbeddingModel } from "./services/embeddings/embeddings.types";
56
import { ILLMModel } from "./services/llm/llm.types";
67

@@ -10,7 +11,7 @@ import { ILLMModel } from "./services/llm/llm.types";
1011
export namespace Query {
1112
/**
1213
* Retrieves database nodes based on a vector query.
13-
*
14+
*
1415
* @param {IDB} db - The database instance.
1516
* @param {string} indexName - The name of the index to query.
1617
* @param {number[]} vector - The vector to query with.
@@ -54,7 +55,7 @@ export namespace Query {
5455

5556
/**
5657
* Retrieves code nodes from the database based on a list of keywords.
57-
*
58+
*
5859
* @param {IDB} db - The database object.
5960
* @param {IEmbeddingModel} embeddingModel - The embedding model used for generating query vectors.
6061
* @param {string[]} keywords - The list of keywords to search for.
@@ -82,9 +83,78 @@ export namespace Query {
8283
return results;
8384
}
8485

86+
/**
87+
* Retrieves an array of DevDocDBNodes from the specified vector query.
88+
*
89+
* @param {IDB} db - The IDB instance used for the query.
90+
* @param {string} indexName - The name of the index to query.
91+
* @param {number[]} vector - The vector used for the query.
92+
* @param {number} threshold - The minimum score threshold for the query results.
93+
* @returns {Promise<DevDocDBNode[]>} - A promise that resolves to an array of DevDocDBNodes that match the query criteria.
94+
*/
95+
export async function getDevDocDBNodesFromVectorQuery(
96+
db: IDB,
97+
indexName: string,
98+
vector: number[],
99+
threshold: number
100+
): Promise<DevDocDBNode[]> {
101+
const result = await db.run(
102+
`
103+
CALL db.index.vector.queryNodes("${indexName}", 2, $vector)
104+
YIELD node, score
105+
WHERE score >= ${threshold}
106+
WITH node, score
107+
OPTIONAL MATCH (node)-[r]->(relatedNode)
108+
RETURN node, COLLECT(relatedNode) AS relatedNodes, score
109+
ORDER BY score DESC
110+
`,
111+
{ vector }
112+
);
113+
if (!result.length) return [];
114+
115+
const nodes: DevDocDBNode[] = [];
116+
const processRecord = (record: any) => {
117+
const data = record as DevDocDBNode;
118+
data.contentEmbeddings = [];
119+
nodes.push(data);
120+
};
121+
// node
122+
processRecord(result[0]);
123+
// relatedNodes
124+
for (const record of (result as any)[1]) processRecord(record);
125+
126+
return nodes;
127+
}
128+
129+
/**
130+
* Retrieves an array of DevDocDBNodes from the provided query.
131+
*
132+
* @param {IDB} db - The IDB instance used for querying the database.
133+
* @param {IEmbeddingModel} embeddingModel - The embedding model used for generating query vectors.
134+
* @param {string} query - The query string used for searching the database.
135+
* @returns {Promise<DevDocDBNode[]>} - A promise that resolves to an array of DevDocDBNodes matching the query.
136+
*/
137+
export async function getDocsNodesFromQuery(
138+
db: IDB,
139+
embeddingModel: IEmbeddingModel,
140+
query: string
141+
): Promise<DevDocDBNode[]> {
142+
const queryVector = await embeddingModel.generate(query);
143+
if (!queryVector) return [];
144+
145+
const results: DevDocDBNode[] = await getDevDocDBNodesFromVectorQuery(
146+
db,
147+
"contentEmbeddings",
148+
queryVector,
149+
0.7
150+
);
151+
152+
return results;
153+
}
154+
85155
/**
86156
* Retrieves database keywords from a given query using a language model.
87-
*
157+
*
88158
* @param llm - The language model used to generate the keywords.
89159
* @param query - The query string to extract keywords from.
90160
* @returns A promise that resolves to an array of database keywords extracted from the query.
File renamed without changes.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { IEmbeddingModel } from "../embeddings/embeddings.types";
2+
3+
export type DevDocDBNodeRelationType = "CONTAINS";
4+
export type DevDocDBNodeRelation = {
5+
target: string;
6+
relation: DevDocDBNodeRelationType;
7+
};
8+
9+
export class DevDocDBNode {
10+
id: string;
11+
relations: DevDocDBNodeRelation[];
12+
13+
url: string;
14+
element: string;
15+
16+
content: string;
17+
contentEmbeddings: number[];
18+
19+
constructor(node: {
20+
id: string;
21+
relations: DevDocDBNodeRelation[];
22+
23+
url: string;
24+
element: string;
25+
26+
content: string;
27+
contentEmbeddings: number[];
28+
}) {
29+
this.id = node.id;
30+
this.relations = node.relations;
31+
32+
this.url = node.url;
33+
this.element = node.element;
34+
35+
this.content = node.content;
36+
this.contentEmbeddings = node.contentEmbeddings;
37+
}
38+
39+
/**
40+
* Fills the embeddings for the given embedding model.
41+
*
42+
* @param {IEmbeddingModel} embeddingModel - The embedding model used to generate embeddings.
43+
* @returns {Promise<void>} - A promise that resolves when the embeddings are filled.
44+
*/
45+
async fillEmbeddings(embeddingModel: IEmbeddingModel): Promise<void> {
46+
this.contentEmbeddings =
47+
(await embeddingModel.generate(this.content)) ?? [];
48+
}
49+
50+
/**
51+
* Generates a database insert query for creating a new node with the specified properties.
52+
*
53+
* @returns The database insert query as a string.
54+
*/
55+
getDBInsertQuery(): string {
56+
let query = "";
57+
query += `
58+
CREATE (n:DevDocDBNode {
59+
id: $id,
60+
61+
url: $url,
62+
element: $element,
63+
64+
content: $content,
65+
contentEmbeddings: $contentEmbeddings
66+
})
67+
`;
68+
69+
return query;
70+
}
71+
}

ai-assistant/src/core/services/db/neo4j.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export class Neo4j implements IDB {
7777

7878
/**
7979
* Verifies the connectivity to the Neo4j database.
80-
*
80+
*
8181
* @returns A Promise that resolves to void.
8282
* @throws An Error if the connection to Neo4j fails.
8383
*/
@@ -92,7 +92,7 @@ export class Neo4j implements IDB {
9292

9393
/**
9494
* Closes the connection to the Neo4j database.
95-
*
95+
*
9696
* @returns A promise that resolves when the connection is closed.
9797
*/
9898
async closeDBConnection(): Promise<void> {
@@ -102,10 +102,10 @@ export class Neo4j implements IDB {
102102

103103
/**
104104
* Begins a new transaction in the Neo4j database.
105-
*
105+
*
106106
* @throws {Error} If a transaction already exists.
107107
* @throws {Error} If the transaction fails to begin.
108-
*
108+
*
109109
* @returns {Promise<void>} A promise that resolves when the transaction is successfully started.
110110
*/
111111
async beginTransaction(): Promise<void> {
@@ -124,10 +124,10 @@ export class Neo4j implements IDB {
124124

125125
/**
126126
* Commits the current transaction.
127-
*
127+
*
128128
* @throws {Error} If there is no transaction to commit.
129129
* @throws {Error} If the transaction commit fails.
130-
*
130+
*
131131
* @returns {Promise<void>} A promise that resolves when the transaction is successfully committed.
132132
*/
133133
async commitTransaction(): Promise<void> {
@@ -146,10 +146,10 @@ export class Neo4j implements IDB {
146146

147147
/**
148148
* Rolls back the current transaction.
149-
*
149+
*
150150
* @throws {Error} If there is no transaction to rollback.
151151
* @throws {Error} If the transaction rollback fails.
152-
*
152+
*
153153
* @returns {Promise<void>} A promise that resolves when the transaction is successfully rolled back.
154154
*/
155155
async rollbackTransaction(): Promise<void> {

ai-assistant/src/endpoints/establishRelations.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DBNodeRelationType } from "../core/services/db/db";
1+
import { DBNodeRelationType } from "../core/services/db/dbNode";
22

33
export type EstablishRelationsEndpointRelations = {
44
source: string;

0 commit comments

Comments
 (0)