Skip to content

Commit 4a1f7cb

Browse files
committed
to: basically done embedding context provider logic, but with a little bug
1 parent eebffc6 commit 4a1f7cb

File tree

7 files changed

+16461
-11720
lines changed

7 files changed

+16461
-11720
lines changed

backend/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,26 @@
4040
"@types/bcrypt": "^5.0.2",
4141
"@types/fs-extra": "^11.0.4",
4242
"@types/normalize-path": "^3.0.2",
43+
"@types/pacote": "^11.1.8",
4344
"@types/toposort": "^2.0.7",
44-
"toposort": "^2.0.2",
4545
"axios": "^1.7.7",
4646
"bcrypt": "^5.1.1",
4747
"class-validator": "^0.14.1",
48+
"fastembed": "^1.14.1",
4849
"fs-extra": "^11.2.0",
4950
"graphql": "^16.9.0",
5051
"graphql-subscriptions": "^2.0.0",
5152
"graphql-ws": "^5.16.0",
5253
"lodash": "^4.17.21",
5354
"markdown-to-txt": "^2.0.1",
5455
"normalize-path": "^3.0.0",
56+
"pacote": "^21.0.0",
5557
"reflect-metadata": "^0.2.2",
5658
"rxjs": "^7.8.1",
5759
"sqlite3": "^5.1.7",
5860
"subscriptions-transport-ws": "^0.11.0",
61+
"tar": "^7.4.3",
62+
"toposort": "^2.0.2",
5963
"typeorm": "^0.3.20",
6064
"uuid": "^10.0.0"
6165
},
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { Logger } from '@nestjs/common';
2+
import DependenciesEmbeddingHandler from '../dependencies-embedding-handler';
3+
4+
// Initialize a global logger instance
5+
const logger = new Logger('dependencies embed tester');
6+
7+
// Only run integration tests if INTEGRATION_TEST environment variable is set to '1'
8+
const isIntegrationTest = process.env.INTEGRATION_TEST === '1';
9+
10+
if (!isIntegrationTest) {
11+
logger.log(
12+
'Integration tests are skipped. Set INTEGRATION_TEST=1 to run them.',
13+
);
14+
} else {
15+
describe('DependenciesEmbeddingHandler Integration Tests', () => {
16+
let handler: DependenciesEmbeddingHandler;
17+
18+
// Increase the default timeout for integration tests
19+
jest.setTimeout(300000); // 5 minutes
20+
21+
beforeAll(async () => {
22+
logger.log(
23+
'Initializing DependenciesEmbeddingHandler for integration tests...',
24+
);
25+
handler = new DependenciesEmbeddingHandler();
26+
// Wait for the handler to initialize
27+
await new Promise((resolve) => setTimeout(resolve, 5000)); // Wait 5 seconds
28+
logger.log('Initialization complete.');
29+
});
30+
31+
afterAll(() => {
32+
logger.log('Integration tests completed.');
33+
});
34+
35+
/**
36+
* Integration Test Case: Add Real Packages and Perform a Search
37+
*
38+
* Purpose:
39+
* - To verify that DependenciesEmbeddingHandler can handle real packages by fetching their type definitions,
40+
* generating embeddings, and storing them correctly.
41+
* - To ensure that the search functionality can retrieve relevant packages based on a real query.
42+
*
43+
* Steps:
44+
* 1. Add multiple real npm packages using the addPackage method.
45+
* 2. Perform a search with a query related to one of the added packages.
46+
* 3. Validate that the search results include the relevant package(s) and are correctly ranked.
47+
*/
48+
test('should add real packages and perform a relevant search', async () => {
49+
// Define real packages to add
50+
const packagesToAdd = [
51+
{ name: 'lodash', version: '4.17.21' },
52+
// { name: 'express', version: '4.18.2' },
53+
// { name: 'react', version: '18.2.0' },
54+
// { name: 'typescript', version: '4.9.5' },
55+
];
56+
57+
logger.log('Adding real packages...');
58+
59+
// Add all packages concurrently
60+
await handler.addPackages(packagesToAdd);
61+
62+
logger.log('Packages added successfully.');
63+
64+
// Define a search query related to one of the packages, e.g., React
65+
const searchQuery = 'React component lifecycle methods';
66+
67+
logger.log('Executing search with query:', searchQuery);
68+
69+
// Perform the search
70+
const results = await handler.searchContext(searchQuery);
71+
72+
logger.log('Search results received.');
73+
74+
// Validate that results are returned
75+
expect(results.length).toBeGreaterThan(0);
76+
77+
// Check that at least one of the top results is related to 'react'
78+
const topResult = results[0];
79+
expect(topResult.name).toBe('react');
80+
expect(topResult.version).toBe('18.2.0');
81+
82+
logger.log('Top search result:', topResult);
83+
84+
// Optionally, you can print more details or perform additional assertions
85+
});
86+
});
87+
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import axios from 'axios';
2+
import DependenciesEmbeddingHandler from '../dependencies-embedding-handler';
3+
import { Logger } from '@nestjs/common';
4+
5+
// Initialize a global logger instance
6+
const logger = new Logger('dependencies embed tester');
7+
8+
// Mock axios to control HTTP requests during tests
9+
jest.mock('axios');
10+
const mockedAxios = axios as jest.Mocked<typeof axios>;
11+
12+
// Mock fastembed to control embedding behavior during tests
13+
jest.mock('fastembed', () => ({
14+
EmbeddingModel: {
15+
BGEBaseEN: 'BGEBaseEN',
16+
},
17+
FlagEmbedding: {
18+
init: jest.fn().mockResolvedValue({
19+
passageEmbed: jest.fn(async function* (
20+
types: string[],
21+
batchSize: number,
22+
) {
23+
for (const type of types) {
24+
// Yield simulated embedding data as Float32Array
25+
yield [new Float32Array([1, 2, 3])];
26+
}
27+
}),
28+
queryEmbed: jest.fn(async (query: string) => [1, 2, 3]),
29+
}),
30+
},
31+
}));
32+
33+
describe('DependenciesEmbeddingHandler', () => {
34+
let handler: DependenciesEmbeddingHandler;
35+
36+
beforeEach(() => {
37+
// Initialize a new instance of DependenciesEmbeddingHandler before each test
38+
handler = new DependenciesEmbeddingHandler();
39+
// Clear all mock calls and instances before each test
40+
jest.clearAllMocks();
41+
});
42+
43+
/**
44+
* Test Case: Successfully add a package with built-in type definitions
45+
*
46+
* Purpose:
47+
* - To verify that DependenciesEmbeddingHandler can correctly add a package that includes built-in type definitions.
48+
* - To ensure that the handler retrieves the package's package.json, extracts the type definitions, and generates embeddings.
49+
*
50+
* Steps:
51+
* 1. Mock axios.get to return a package.json containing the 'types' field.
52+
* 2. Mock axios.get to return the content of the type definitions file.
53+
* 3. Call the addPackage method to add the package.
54+
* 4. Verify that the package information is correctly stored, including the generated embedding.
55+
*/
56+
test('should successfully add a package with built-in types', async () => {
57+
// Mock the response for fetching package.json, including the 'types' field
58+
mockedAxios.get.mockImplementationOnce(() =>
59+
Promise.resolve({
60+
data: {
61+
name: 'test-package',
62+
version: '1.0.0',
63+
types: 'dist/index.d.ts',
64+
},
65+
}),
66+
);
67+
68+
// Mock the response for fetching the type definitions file
69+
mockedAxios.get.mockImplementationOnce(() =>
70+
Promise.resolve({
71+
data: `
72+
interface TestInterface {
73+
prop1: string;
74+
prop2: number;
75+
}
76+
77+
type TestType = {
78+
field1: string;
79+
field2: boolean;
80+
};
81+
`,
82+
}),
83+
);
84+
85+
// Add the package using the handler
86+
await handler.addPackage('test-package', '1.0.0');
87+
88+
// Retrieve the added package information
89+
const packageInfo = handler.getPackageInfo('test-package');
90+
91+
// Assertions to ensure the package was added correctly
92+
expect(packageInfo).toBeDefined();
93+
expect(packageInfo?.name).toBe('test-package');
94+
expect(packageInfo?.version).toBe('1.0.0');
95+
expect(packageInfo?.embedding).toBeDefined();
96+
});
97+
98+
/**
99+
* Test Case: Successfully search for relevant type definitions
100+
*
101+
* Purpose:
102+
* - To verify that DependenciesEmbeddingHandler can generate query embeddings from a search string and return the most relevant packages.
103+
* - To ensure that similarity calculations are accurate and results are correctly sorted based on similarity.
104+
*
105+
* Why the Search Returns Relevant Results:
106+
* - The `FlagEmbedding` mock is set up to return identical embeddings for both package types and the query.
107+
* - This setup ensures that the cosine similarity between the query embedding and each package's embedding is maximized for relevant packages.
108+
* - As a result, the search function can accurately identify and return the most relevant packages based on the query.
109+
*
110+
* Steps:
111+
* 1. Mock axios.get to return package.json and type definitions for two different packages.
112+
* 2. Call addPackage method to add both packages.
113+
* 3. Use a search query to call searchContext method.
114+
* 4. Verify that the search results contain the relevant package and are sorted by similarity.
115+
*/
116+
test('should successfully search for relevant type definitions', async () => {
117+
// Mock responses for the first package's package.json and type definitions
118+
mockedAxios.get
119+
.mockImplementationOnce(() =>
120+
Promise.resolve({
121+
data: {
122+
types: 'index.d.ts',
123+
},
124+
}),
125+
)
126+
.mockImplementationOnce(() =>
127+
Promise.resolve({
128+
data: `
129+
interface UserInterface {
130+
id: string;
131+
name: string;
132+
email: string;
133+
}
134+
`,
135+
}),
136+
)
137+
// Mock responses for the second package's package.json and type definitions
138+
.mockImplementationOnce(() =>
139+
Promise.resolve({
140+
data: {
141+
types: 'index.d.ts',
142+
},
143+
}),
144+
)
145+
.mockImplementationOnce(() =>
146+
Promise.resolve({
147+
data: `
148+
interface ProductInterface {
149+
id: string;
150+
price: number;
151+
description: string;
152+
}
153+
`,
154+
}),
155+
);
156+
157+
// Add the first package 'user-package'
158+
await handler.addPackage('user-package', '1.0.0');
159+
// Add the second package 'product-package'
160+
await handler.addPackage('product-package', '1.0.0');
161+
162+
const searchQuery = 'user interface with email';
163+
164+
// Log the search query
165+
logger.log('Search Query:', searchQuery);
166+
167+
// Perform the search using the handler
168+
const results = await handler.searchContext(searchQuery);
169+
170+
// Log the search results
171+
logger.log('Search Results:', results);
172+
173+
// Assertions to ensure that search results are as expected
174+
expect(results.length).toBeGreaterThan(0);
175+
expect(results[0].types?.content[0]).toContain('UserInterface');
176+
}, 100000);
177+
});

0 commit comments

Comments
 (0)