Skip to content

Commit 722a3c1

Browse files
Merge pull request #286 from olasunkanmi-SE/vectorDB-phase4
Vector db phase4
2 parents 1068fca + 4523f4b commit 722a3c1

File tree

9 files changed

+2468
-155
lines changed

9 files changed

+2468
-155
lines changed

src/config/vector-db.config.ts

Lines changed: 585 additions & 0 deletions
Large diffs are not rendered by default.

src/extension.ts

Lines changed: 174 additions & 126 deletions
Large diffs are not rendered by default.
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import * as vscode from "vscode";
2+
import { Logger, LogLevel } from "../infrastructure/logger/logger";
3+
4+
/**
5+
* Generic observer interface
6+
*/
7+
export interface Observer<T> {
8+
update(data: T): void;
9+
}
10+
11+
/**
12+
* Observable configuration manager using the Observer pattern
13+
*/
14+
export class ConfigurationObservable<T> implements vscode.Disposable {
15+
private observers: Observer<T>[] = [];
16+
private logger: Logger;
17+
private disposables: vscode.Disposable[] = [];
18+
19+
constructor(private configurationSection: string) {
20+
this.logger = Logger.initialize("ConfigurationObservable", {
21+
minLevel: LogLevel.INFO,
22+
});
23+
24+
// Watch for configuration changes
25+
const configWatcher = vscode.workspace.onDidChangeConfiguration((event) => {
26+
if (event.affectsConfiguration(this.configurationSection)) {
27+
this.notifyObservers();
28+
}
29+
});
30+
31+
this.disposables.push(configWatcher);
32+
}
33+
34+
/**
35+
* Subscribe an observer to configuration changes
36+
*/
37+
subscribe(observer: Observer<T>): vscode.Disposable {
38+
this.observers.push(observer);
39+
this.logger.info(`Observer subscribed to ${this.configurationSection} changes`);
40+
41+
return {
42+
dispose: () => {
43+
this.unsubscribe(observer);
44+
},
45+
};
46+
}
47+
48+
/**
49+
* Unsubscribe an observer from configuration changes
50+
*/
51+
unsubscribe(observer: Observer<T>): void {
52+
const index = this.observers.indexOf(observer);
53+
if (index >= 0) {
54+
this.observers.splice(index, 1);
55+
this.logger.info(`Observer unsubscribed from ${this.configurationSection} changes`);
56+
}
57+
}
58+
59+
/**
60+
* Notify all observers of configuration changes
61+
*/
62+
private notifyObservers(): void {
63+
const config = this.getCurrentConfiguration();
64+
this.logger.info(`Notifying ${this.observers.length} observers of configuration change`);
65+
66+
for (const observer of this.observers) {
67+
try {
68+
observer.update(config);
69+
} catch (error) {
70+
this.logger.error("Error in configuration observer:", error);
71+
}
72+
}
73+
}
74+
75+
/**
76+
* Get current configuration - to be overridden by subclasses
77+
*/
78+
protected getCurrentConfiguration(): T {
79+
const config = vscode.workspace.getConfiguration(this.configurationSection);
80+
return config as unknown as T;
81+
}
82+
83+
/**
84+
* Manually trigger notification (useful for testing or forced updates)
85+
*/
86+
notify(): void {
87+
this.notifyObservers();
88+
}
89+
90+
/**
91+
* Get the number of current observers
92+
*/
93+
getObserverCount(): number {
94+
return this.observers.length;
95+
}
96+
97+
/**
98+
* Dispose of all resources
99+
*/
100+
dispose(): void {
101+
this.logger.info("Disposing configuration observable");
102+
this.observers.length = 0;
103+
this.disposables.forEach((d) => d.dispose());
104+
this.disposables.length = 0;
105+
}
106+
}
107+
108+
/**
109+
* Specific observable for Vector Database Configuration
110+
*/
111+
export class VectorDbConfigObservable extends ConfigurationObservable<any> {
112+
constructor() {
113+
super("codebuddy.vectorDb");
114+
}
115+
116+
protected getCurrentConfiguration(): any {
117+
const config = vscode.workspace.getConfiguration("codebuddy.vectorDb");
118+
119+
return {
120+
enabled: config.get("enabled", true),
121+
embeddingModel: config.get("embeddingModel", "gemini"),
122+
maxTokens: config.get("maxTokens", 6000),
123+
batchSize: config.get("batchSize", 10),
124+
searchResultLimit: config.get("searchResultLimit", 8),
125+
enableBackgroundProcessing: config.get("enableBackgroundProcessing", true),
126+
enableProgressNotifications: config.get("enableProgressNotifications", true),
127+
progressLocation: config.get("progressLocation", "notification"),
128+
debounceDelay: config.get("debounceDelay", 1000),
129+
performanceMode: config.get("performanceMode", "balanced"),
130+
fallbackToKeywordSearch: config.get("fallbackToKeywordSearch", true),
131+
cacheEnabled: config.get("cacheEnabled", true),
132+
logLevel: config.get("logLevel", "info"),
133+
slowSearchThreshold: config.get("slowSearchThreshold", 2000),
134+
};
135+
}
136+
}
137+
138+
/**
139+
* Configuration change listener that implements Observer pattern
140+
*/
141+
export class ConfigurationChangeListener implements Observer<any> {
142+
private logger: Logger;
143+
144+
constructor(
145+
private name: string,
146+
private callback: (config: any) => void
147+
) {
148+
this.logger = Logger.initialize(`ConfigListener_${name}`, {
149+
minLevel: LogLevel.INFO,
150+
});
151+
}
152+
153+
update(config: any): void {
154+
this.logger.info(`Configuration changed for ${this.name}`);
155+
try {
156+
this.callback(config);
157+
} catch (error) {
158+
this.logger.error(`Error in configuration callback for ${this.name}:`, error);
159+
}
160+
}
161+
}
162+
163+
/**
164+
* Factory for creating configuration observers
165+
*/
166+
export class ConfigurationObserverFactory {
167+
private static observables = new Map<string, ConfigurationObservable<any>>();
168+
169+
/**
170+
* Get or create an observable for a configuration section
171+
*/
172+
static getObservable<T>(section: string): ConfigurationObservable<T> {
173+
if (!this.observables.has(section)) {
174+
if (section === "codebuddy.vectorDb") {
175+
this.observables.set(section, new VectorDbConfigObservable());
176+
} else {
177+
this.observables.set(section, new ConfigurationObservable<T>(section));
178+
}
179+
}
180+
return this.observables.get(section) as ConfigurationObservable<T>;
181+
}
182+
183+
/**
184+
* Create a configuration listener
185+
*/
186+
static createListener<T>(name: string, callback: (config: T) => void): Observer<T> {
187+
return new ConfigurationChangeListener(name, callback);
188+
}
189+
190+
/**
191+
* Dispose of all observables
192+
*/
193+
static disposeAll(): void {
194+
for (const observable of this.observables.values()) {
195+
observable.dispose();
196+
}
197+
this.observables.clear();
198+
}
199+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { IDependencyContainer } from "../interfaces/services.interface";
2+
import { Logger, LogLevel } from "../infrastructure/logger/logger";
3+
4+
/**
5+
* Simple dependency injection container for vector database services
6+
*/
7+
export class DependencyContainer implements IDependencyContainer {
8+
private services = new Map<string, { factory: () => any; singleton: boolean; instance?: any }>();
9+
private logger: Logger;
10+
11+
constructor() {
12+
this.logger = Logger.initialize("DependencyContainer", {
13+
minLevel: LogLevel.INFO,
14+
});
15+
}
16+
17+
/**
18+
* Register a transient service (new instance each time)
19+
*/
20+
register<T>(token: string, factory: () => T): void {
21+
this.services.set(token, { factory, singleton: false });
22+
this.logger.info(`Registered transient service: ${token}`);
23+
}
24+
25+
/**
26+
* Register a singleton service (same instance always)
27+
*/
28+
registerSingleton<T>(token: string, factory: () => T): void {
29+
this.services.set(token, { factory, singleton: true });
30+
this.logger.info(`Registered singleton service: ${token}`);
31+
}
32+
33+
/**
34+
* Resolve a service from the container
35+
*/
36+
resolve<T>(token: string): T {
37+
const serviceInfo = this.services.get(token);
38+
if (!serviceInfo) {
39+
throw new Error(`Service '${token}' is not registered in the container`);
40+
}
41+
42+
if (serviceInfo.singleton) {
43+
if (!serviceInfo.instance) {
44+
serviceInfo.instance = serviceInfo.factory();
45+
this.logger.info(`Created singleton instance for: ${token}`);
46+
}
47+
return serviceInfo.instance;
48+
} else {
49+
this.logger.debug(`Creating new instance for: ${token}`);
50+
return serviceInfo.factory();
51+
}
52+
}
53+
54+
/**
55+
* Check if a service is registered
56+
*/
57+
isRegistered(token: string): boolean {
58+
return this.services.has(token);
59+
}
60+
61+
/**
62+
* Get all registered service tokens
63+
*/
64+
getRegisteredServices(): string[] {
65+
return Array.from(this.services.keys());
66+
}
67+
68+
/**
69+
* Dispose of all singleton instances and clear the container
70+
*/
71+
dispose(): void {
72+
this.logger.info("Disposing dependency container...");
73+
74+
// Dispose of singleton instances that have a dispose method
75+
for (const [token, serviceInfo] of this.services) {
76+
if (serviceInfo.singleton && serviceInfo.instance) {
77+
const instance = serviceInfo.instance;
78+
if (typeof instance.dispose === "function") {
79+
try {
80+
instance.dispose();
81+
this.logger.info(`Disposed singleton service: ${token}`);
82+
} catch (error) {
83+
this.logger.error(`Error disposing service ${token}:`, error);
84+
}
85+
}
86+
}
87+
}
88+
89+
this.services.clear();
90+
this.logger.info("Dependency container disposed");
91+
}
92+
}
93+
94+
/**
95+
* Service tokens for dependency injection
96+
*/
97+
export const ServiceTokens = {
98+
// Core services
99+
VECTOR_DATABASE_SERVICE: "IVectorDatabaseService",
100+
VECTOR_DB_WORKER_MANAGER: "IVectorDbWorkerManager",
101+
VECTOR_DB_SYNC_SERVICE: "IVectorDbSync",
102+
103+
// Context and search
104+
SMART_CONTEXT_EXTRACTOR: "ISmartContextExtractor",
105+
CODE_INDEXER: "ICodeIndexer",
106+
107+
// User interaction
108+
USER_FEEDBACK_SERVICE: "IUserFeedbackService",
109+
CONFIGURATION_MANAGER: "IConfigurationManager",
110+
111+
// Orchestration
112+
EMBEDDING_ORCHESTRATOR: "IEmbeddingOrchestrator",
113+
114+
// External dependencies
115+
EXTENSION_CONTEXT: "vscode.ExtensionContext",
116+
API_KEY: "ApiKey",
117+
LOGGER: "Logger",
118+
} as const;
119+
120+
/**
121+
* Global dependency container instance
122+
*/
123+
let globalContainer: DependencyContainer | null = null;
124+
125+
/**
126+
* Get or create the global dependency container
127+
*/
128+
export function getContainer(): DependencyContainer {
129+
if (!globalContainer) {
130+
globalContainer = new DependencyContainer();
131+
}
132+
return globalContainer;
133+
}
134+
135+
/**
136+
* Dispose of the global container
137+
*/
138+
export function disposeContainer(): void {
139+
if (globalContainer) {
140+
globalContainer.dispose();
141+
globalContainer = null;
142+
}
143+
}

0 commit comments

Comments
 (0)