Skip to content

Commit 535ccfa

Browse files
Merge pull request #206 from olasunkanmi-SE/file-upload-feature
feat(file-upload): Enable local file uploads and processing
2 parents d46402e + d7bdf29 commit 535ccfa

File tree

15 files changed

+95
-159
lines changed

15 files changed

+95
-159
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ node_modules
66
# Sentry Config File
77
.sentryclirc
88
config.ts
9-
patterns
9+
database
10+
files
1011
webviewUi/dist
1112
samples

src/agents/architecture.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
```
1+
```mermaid
22
graph TD
33
A[GeminiLLM] -->|Initializes| B[GoogleGenerativeAI]
44
A -->|Creates| C[Orchestrator]
@@ -36,4 +36,4 @@ graph TD
3636
Y[Function Call Retry] -->|Improves| L
3737
3838
Z[Memory Management] -->|Optimizes| I
39-
```
39+
```

src/agents/file-upload.ts

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,50 @@ import {
66
GoogleGenAI,
77
} from "@google/genai";
88
import * as path from "path";
9+
import * as vscode from "vscode";
910
import { BaseAiAgent } from "./base";
1011
import { Orchestrator } from "./orchestrator";
12+
import { IEventPayload } from "../emitter/interface";
1113

12-
export class FileUploadAgent extends BaseAiAgent {
14+
export class FileUploadAgent extends BaseAiAgent implements vscode.Disposable {
1315
private readonly ai: GoogleGenAI;
1416
protected readonly orchestrator: Orchestrator;
1517
private static readonly PROCESSING_WAIT_TIME_MS = 6000;
1618
private static readonly MAX_CACHE_PAGE_SIZE = 10;
1719
private static readonly CACHE_MODEL = "gemini-1.5-flash-002";
20+
private readonly disposables: vscode.Disposable[] = [];
21+
private static instance: FileUploadAgent;
1822
constructor(private readonly apiKey: string) {
1923
super();
2024
this.ai = new GoogleGenAI({ apiKey: this.apiKey });
2125
this.orchestrator = Orchestrator.getInstance();
26+
this.disposables.push(
27+
this.orchestrator.onFileUpload(this.handleLocalFileUpload.bind(this)),
28+
);
29+
}
30+
31+
static initialize(apiKey: string) {
32+
if (!FileUploadAgent.instance) {
33+
FileUploadAgent.instance = new FileUploadAgent(apiKey);
34+
}
35+
return FileUploadAgent.instance;
36+
}
37+
38+
private async handleLocalFileUpload(event: IEventPayload) {
39+
try {
40+
if (!event.message?.length) {
41+
this.logger.info("Error while uploading file, try again");
42+
return;
43+
}
44+
const data = JSON.parse(event.message);
45+
const { fileName, filePath } = data;
46+
if (fileName && filePath) {
47+
await this.uploadAndProcessFile(filePath, fileName);
48+
}
49+
} catch (error: any) {
50+
console.log(error);
51+
throw new Error(error);
52+
}
2253
}
2354

2455
private async uploadFile(filePath: string, displayName: string) {
@@ -105,7 +136,7 @@ export class FileUploadAgent extends BaseAiAgent {
105136

106137
return {
107138
response: response.text,
108-
fileName,
139+
fileName: fileName ?? file.displayName,
109140
cache: cached.name,
110141
};
111142
} catch (error) {
@@ -134,7 +165,8 @@ export class FileUploadAgent extends BaseAiAgent {
134165
model: FileUploadAgent.CACHE_MODEL,
135166
config: {
136167
contents: createUserContent(fileContent),
137-
systemInstruction: "You are an expert analyzing documents",
168+
systemInstruction:
169+
"You are an expert analyzing documents. Give a detail analysis of this doc in markdown",
138170
},
139171
});
140172
this.logger.info("Cache created:", cached);
@@ -162,29 +194,7 @@ export class FileUploadAgent extends BaseAiAgent {
162194
}
163195
}
164196

165-
async getCaches(): Promise<CachedContent[]> {
166-
this.logger.info("Retrieving caches");
167-
const caches: CachedContent[] = [];
168-
169-
try {
170-
const pager = await this.ai.caches.list({
171-
config: { pageSize: FileUploadAgent.MAX_CACHE_PAGE_SIZE },
172-
});
173-
174-
let page = pager.page;
175-
176-
do {
177-
caches.push(...page);
178-
if (!pager.hasNextPage()) break;
179-
page = await pager.nextPage();
180-
} while (true);
181-
182-
return caches;
183-
} catch (error) {
184-
console.log("Failed to retrieve caches", error);
185-
throw new Error(
186-
`Failed to retrieve caches: ${error instanceof Error ? error.message : String(error)}`,
187-
);
188-
}
197+
public dispose(): void {
198+
this.disposables.forEach((d) => d.dispose());
189199
}
190200
}

src/application/constant.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export enum COMMON {
2525
}
2626
export const GROQ_CONFIG = {
2727
temperature: 0.1,
28-
max_tokens: 5024,
28+
max_tokens: 500024,
2929
top_p: 1,
3030
stream: false,
3131
stop: null,

src/commands/knowledge-base.ts

Lines changed: 0 additions & 92 deletions
This file was deleted.

src/emitter/agent-emitter.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ export class EventEmitter extends BaseEmitter<Record<string, IEventPayload>> {
1313
this.createEvent("onSecretChange");
1414
onBootstrap: vscode.Event<IEventPayload> = this.createEvent("onBootstrap");
1515
onFileUpload: vscode.Event<IEventPayload> = this.createEvent("onFileUpload");
16-
onFileProcessedSuccess: vscode.Event<IEventPayload> = this.createEvent(
16+
onFileProcessSuccess: vscode.Event<IEventPayload> = this.createEvent(
1717
"onFileProcessSuccess",
1818
);
1919
onActiveworkspaceUpdate: vscode.Event<IEventPayload> = this.createEvent(
2020
"onActiveworkspaceUpdate",
2121
);
22+
onFilesRetrieved: vscode.Event<IEventPayload> =
23+
this.createEvent("onFilesRetrieved");
2224

2325
/**
2426
* Emits a generic event with specified status, message, and optional data.

src/emitter/interface.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ type AgentEventKeys =
88
| "onSecretChange"
99
| "onBootstrap"
1010
| "onActiveworkspaceUpdate"
11-
| "onFileUpload";
11+
| "onFileUpload"
12+
| "onFileProcessSuccess"
13+
| "onFilesRetrieved";
1214

1315
export type IAgentEventMap = Record<AgentEventKeys, IEventPayload>;
1416

src/extension.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { GenerateCommitMessage } from "./commands/generate-commit-message";
1414
import { GenerateUnitTest } from "./commands/generate-unit-test";
1515
import { InLineChat } from "./commands/inline-chat";
1616
import { InterviewMe } from "./commands/interview-me";
17-
import { ReadFromKnowledgeBase } from "./commands/knowledge-base";
1817
import { OptimizeCode } from "./commands/optimize";
1918
import { RefactorCode } from "./commands/refactor";
2019
import { ReviewCode } from "./commands/review";
@@ -30,7 +29,9 @@ import { GroqWebViewProvider } from "./providers/groq";
3029
import { FileManager } from "./services/file-manager";
3130
import { initializeGenerativeAiEnvironment } from "./services/generative-ai-model-manager";
3231
import { Credentials } from "./services/github-authentication";
33-
import { getConfigValue } from "./utils/utils";
32+
import { getAPIKey, getConfigValue } from "./utils/utils";
33+
import { FileUploadAgent } from "./agents/file-upload";
34+
import * as fs from "fs";
3435

3536
const {
3637
geminiKey,
@@ -50,9 +51,13 @@ const logger = new Logger("extension");
5051
let quickFixCodeAction: vscode.Disposable;
5152
let agentEventEmmitter: EventEmitter;
5253

53-
async function connectToDatabase() {
54+
async function connectToDatabase(context: vscode.ExtensionContext) {
5455
try {
55-
const dbPath = path.join(__dirname, "..", "patterns", "aii.db");
56+
const dbDir = path.join(context.extensionPath, "database");
57+
if (!fs.existsSync(dbDir)) {
58+
fs.mkdirSync(dbDir);
59+
}
60+
const dbPath = path.join(__dirname, "..", "database", "aii.db");
5661
const urlPath = dbPath.replace(/\\/g, "/");
5762
const isWindows = dbPath.includes("\\");
5863
const filePath = isWindows ? `file:/${urlPath}` : `file:${urlPath}`;
@@ -65,7 +70,7 @@ async function connectToDatabase() {
6570

6671
async function createFileDB(context: vscode.ExtensionContext) {
6772
try {
68-
const fileUploader = new FileManager(context);
73+
const fileUploader = new FileManager(context, "database");
6974
const files = await fileUploader.getFiles();
7075
if (!files?.find((file) => file.includes("dev.db"))) {
7176
await fileUploader.createFile("dev.db");
@@ -79,14 +84,16 @@ async function createFileDB(context: vscode.ExtensionContext) {
7984
export async function activate(context: vscode.ExtensionContext) {
8085
try {
8186
await createFileDB(context);
82-
await connectToDatabase();
87+
await connectToDatabase(context);
8388
const credentials = new Credentials();
89+
const geminiApiKey = getAPIKey("gemini");
90+
FileUploadAgent.initialize(geminiApiKey);
8491
await credentials.initialize(context);
8592
const session: vscode.AuthenticationSession | undefined =
8693
await credentials.getSession();
8794
logger.info(`Logged into GitHub as ${session?.account.label}`);
8895
Memory.getInstance();
89-
const fileUpload = new FileManager(context);
96+
9097
// TODO This is broken. Need to Fix
9198
// const index = CodeIndexingService.createInstance();
9299
// Get each of the folders and call the next line for each
@@ -136,11 +143,6 @@ export async function activate(context: vscode.ExtensionContext) {
136143
`${USER_MESSAGE} creates the code chart...`,
137144
context,
138145
);
139-
const codePattern = fileUpload;
140-
const knowledgeBase = new ReadFromKnowledgeBase(
141-
`${USER_MESSAGE} generate your code pattern...`,
142-
context,
143-
);
144146
const generateCommitMessage = new GenerateCommitMessage(
145147
`${USER_MESSAGE} generates a commit message...`,
146148
context,
@@ -169,8 +171,6 @@ export async function activate(context: vscode.ExtensionContext) {
169171
errorMessage,
170172
).execute(errorMessage),
171173
[explain]: async () => await explainCode.execute(),
172-
[pattern]: async () => await codePattern.uploadFileHandler(),
173-
[knowledge]: async () => await knowledgeBase.execute(),
174174
[commitMessage]: async () =>
175175
await generateCommitMessage.execute("commitMessage"),
176176
[generateCodeChart]: async () => await codeChartGenerator.execute(),

src/llms/gemini/gemini.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class GeminiLLM
4646
this.logger = new Logger("GeminiLLM");
4747
this.groqLLM = GroqLLM.getInstance({
4848
apiKey: getAPIKey("groq"),
49-
model: "deepseek-r1-distill-llama-70b",
49+
model: "deepseek-r1-distill-qwen-32b",
5050
});
5151
}
5252

src/providers/base.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export abstract class BaseWebViewProvider implements vscode.Disposable {
3131
protected readonly generativeAiModel: string,
3232
context: vscode.ExtensionContext,
3333
) {
34-
this.fileManager = FileManager.initialize(context);
34+
this.fileManager = FileManager.initialize(context, "files");
3535
this.fileService = FileService.getInstance();
3636
this._context = context;
3737
this.orchestrator = Orchestrator.getInstance();
@@ -48,6 +48,7 @@ export abstract class BaseWebViewProvider implements vscode.Disposable {
4848
this.orchestrator.onActiveworkspaceUpdate(
4949
this.handleGenericEvents.bind(this),
5050
),
51+
this.orchestrator.onFileUpload(this.handleModelResponseEvent.bind(this)),
5152
);
5253
}
5354

@@ -78,6 +79,7 @@ export abstract class BaseWebViewProvider implements vscode.Disposable {
7879
setTimeout(async () => {
7980
await this.publishWorkSpace();
8081
}, 2000);
82+
await this.getFiles();
8183
}
8284

8385
private async setWebviewHtml(view: vscode.WebviewView): Promise<void> {
@@ -87,6 +89,16 @@ export abstract class BaseWebViewProvider implements vscode.Disposable {
8789
);
8890
}
8991

92+
private async getFiles() {
93+
const files: string[] = await this.fileManager.getFileNames();
94+
if (files?.length) {
95+
await this.currentWebView?.webview.postMessage({
96+
type: "onFilesRetrieved",
97+
message: JSON.stringify(files),
98+
});
99+
}
100+
}
101+
90102
private async publishWorkSpace(): Promise<void> {
91103
try {
92104
const filesAndDirs: IContextInfo =

0 commit comments

Comments
 (0)