diff --git a/.changeset/curly-owls-visit.md b/.changeset/curly-owls-visit.md new file mode 100644 index 0000000..414b3bc --- /dev/null +++ b/.changeset/curly-owls-visit.md @@ -0,0 +1,5 @@ +--- +"overide": minor +--- + +Now we are able to use response_format in case of OpenAi api diff --git a/.changeset/dry-peaches-bow.md b/.changeset/dry-peaches-bow.md new file mode 100644 index 0000000..97e5346 --- /dev/null +++ b/.changeset/dry-peaches-bow.md @@ -0,0 +1,5 @@ +--- +"overide": patch +--- + +Added support for `--path` option in `init` and `start` commands. This allows user to initialise and start overide in any target path on a system. diff --git a/.changeset/hungry-sheep-bathe.md b/.changeset/hungry-sheep-bathe.md new file mode 100644 index 0000000..bb8dd90 --- /dev/null +++ b/.changeset/hungry-sheep-bathe.md @@ -0,0 +1,5 @@ +--- +"overide": minor +--- + +Rollback: Embeddings and Dependency graph support. diff --git a/.changeset/popular-ravens-laugh.md b/.changeset/popular-ravens-laugh.md new file mode 100644 index 0000000..b0c8d62 --- /dev/null +++ b/.changeset/popular-ravens-laugh.md @@ -0,0 +1,5 @@ +--- +"overide": major +--- + +This change removes all other platform support besides OpenAI, as well as some code refactoring. diff --git a/README.md b/README.md index f03ad6c..0ce9c68 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,10 @@ - [Community](#community) - [License](#license) - ## Key Features - **IDE Agnostic**: Works with any IDE or text editor -- **AI-Powered Code Generation**: Uses OpenAI, DeepSeek, or Groq APIs +- **AI-Powered Code Generation**: Uses OpenAI API - **Live File Monitoring**: Continuously monitors files for code generation prompts - **Simple Prompting Syntax**: Uses intuitive `//> Accept the changes (y/n): -// ``` @@ -96,8 +89,8 @@ Configure Overide using `oi-config.json`: ```json { - "name": "project name", - "ignore": ["node_modules", "*.test.js"] + "name": "project name", + "ignore": ["node_modules", "*.test.js"] } ``` diff --git a/assets/prompt.structure.json b/assets/prompt.structure.json index 2291739..6b068dc 100644 --- a/assets/prompt.structure.json +++ b/assets/prompt.structure.json @@ -1,40 +1,14 @@ { - "openai": { - "systemMessage": "Your task is to provide accurate and efficient code completions and respond in VALID JSON.", - "context": "Below is the current code context:", - "format": "```[\n{\n \"find\": [lines to find],\n \"replace\": [lines to replace]\n},\n{\n \"find\": [lines to find],\n \"replace\": [lines to replace]\n}\n]\n```", - "instructions": [ - "Do not include comments explaining the changes.", - "Make sure that the structure of the file is maintained. Imports, Global Variables and should be placed at the top of the file below other imports or global variables.", - "Make sure that the {lines in find} accounts for empty spaces and empty lines in the existing code.", - "Include tabs and spaces in the replace lines to PROPERLY INDENT according to existing code.", - "Use the following JSON format to respond:" - ] - }, - "deepseek": { - "systemMessage": "You are a coding assistant API specialized in generating accurate and efficient code completions and responding in valid JSON.", - "context": "Below is the current code context:", - "format": "[\n{\n \"find\": [lines to find],\n \"replace\": [lines to replace]\n},\n{\n \"find\": [lines to find],\n \"replace\": [lines to replace]\n}\n]\n", - "instructions": [ - "Do not include comments explaining the changes.", - "Please provide the entire code block that should be replaced, and the entire new code block as the replacement, as list of lines.", - "Return the JSON inside a markdown block quote using triple backticks (```).", - "If code to be replaced is separated by even ONE OR MORE empty line then return multiple {find, replace} structures in a list", - "Include proper indentation in code.", - "Use the following JSON format to respond:" - ] - }, - "groq": { - "systemMessage": "You are a coding assistant API specialized in generating accurate and efficient code completions and responding in valid JSON while following the instructions STRICTLY.", - "context": "Below is the current code context:", - "format": "[\n{\n \"find\": [lines to find],\n \"replace\": [lines to replace]\n},\n{\n \"find\": [lines to find],\n \"replace\": [lines to replace]\n}\n]\n", - "instructions": [ - "Do not include comments explaining the changes.", - "Please provide the entire code block that should be replaced, and the entire new code block as the replacement, as list of lines.", - "Return the VALID JSON inside a markdown block quote using triple backticks (```).", - "If code to be replaced is separated by even ONE OR MORE empty line then return multiple {find, replace} structures in a list", - "Include proper indentation in code.", - "Use the following JSON format to respond:" - ] - } + "openai": { + "systemMessage": "Your task is to provide accurate and efficient code completions and respond in VALID JSON.", + "context": "Below is the current code context:", + "format": "```[\n{\n \"find\": [lines to find],\n \"replace\": [lines to replace]\n},\n{\n \"find\": [lines to find],\n \"replace\": [lines to replace]\n}\n]\n```", + "instructions": [ + "Do not include comments explaining the changes.", + "Make sure that the structure of the file is maintained. Imports, Global Variables and should be placed at the top of the file below other imports or global variables.", + "Make sure that the {lines in find} accounts for empty spaces and empty lines in the existing code.", + "Include tabs and spaces in the replace lines to PROPERLY INDENT according to existing code.", + "Use the following JSON format to respond:" + ] + } } diff --git a/package.json b/package.json index 00319ff..1541e07 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "groq-sdk": "^0.7.0", "inquirer": "^11.1.0", "open": "^10.1.0", - "openai": "^4.67.2", + "openai": "^4.77.0", "three": "^0.170.0", "tree-sitter": "^0.22.0", "tree-sitter-c": "^0.23.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e546a17..c64e3ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,8 +42,8 @@ importers: specifier: ^10.1.0 version: 10.1.0 openai: - specifier: ^4.67.2 - version: 4.69.0 + specifier: ^4.77.0 + version: 4.91.1 three: specifier: ^0.170.0 version: 0.170.0 @@ -1485,12 +1485,15 @@ packages: resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} engines: {node: '>=18'} - openai@4.69.0: - resolution: {integrity: sha512-S3hOHSkk609KqwgH+7dwFrSvO3Gm3Nk0YWGyPHNscoMH/Y2tH1qunMi7gtZnLbUv4/N1elqCp6bDior2401kCQ==} + openai@4.91.1: + resolution: {integrity: sha512-DbjrR0hIMQFbxz8+3qBsfPJnh3+I/skPgoSlT7f9eiZuhGBUissPQULNgx6gHNkLoZ3uS0uYS6eXPUdtg4nHzw==} hasBin: true peerDependencies: + ws: ^8.18.0 zod: ^3.23.8 peerDependenciesMeta: + ws: + optional: true zod: optional: true @@ -3509,7 +3512,7 @@ snapshots: is-inside-container: 1.0.0 is-wsl: 3.1.0 - openai@4.69.0: + openai@4.91.1: dependencies: '@types/node': 18.19.61 '@types/node-fetch': 2.6.11 diff --git a/src/commands/command.config.ts b/src/commands/command.config.ts index 576a9f8..b150f58 100644 --- a/src/commands/command.config.ts +++ b/src/commands/command.config.ts @@ -1,6 +1,3 @@ -import fs from 'fs'; -import path from 'path'; - import { ConfigOption } from '../models/model.options'; import { GlobalConfig, @@ -12,17 +9,15 @@ import CommandHelper from '../utilis/util.command.config'; import OiCommand from './abstract.command'; import { Command } from 'commander'; import inquirer, { Question } from 'inquirer'; -import serviceParser from '../services/service.parser'; import utilParser from '../utilis/util.parser'; /** * The `Config` class is responsible for handling both global and local configurations - * for the `overide` CLI application. It manages configuration settings for different platforms - * (like OpenAI and DeepSeek) and allows users to select an active platform, update config - * details, and manage ignored files and project-specific settings. + * for the `overide` CLI application. It manages configuration settings for OpenAI + * and allows users to update config details, and manage ignored files and project-specific settings. * * Responsibilities: - * - Prompt the user for platform-specific configuration details. + * - Prompt the user for OpenAI configuration details. * - Manage global configuration, including setting the active platform and updating platform settings. * - Handle local configuration updates, including project name and ignored files. * - Ensure that required directories and configuration files exist. @@ -37,7 +32,7 @@ class Config extends OiCommand { // Define supported platforms and their respective configuration prompts this.platforms = supportedPlatforms; - // Configuration questions for each platform (OpenAI, DeepSeek, Groq) + // Configuration questions for OpenAI this.platformQuestions = platformQuestions; } @@ -51,22 +46,17 @@ class Config extends OiCommand { .command('local') .description('Local configuration options') .option('-i, --ignore ', 'Ignore specific files or directories') - .option('-p, --parse', 'Creates dependency graph for the project') .option('-n, --name ', 'Set project name'); const globalConfig = configCommand .command('global') // Sub-command for global configuration .description('Global configuration options') - .option('-p, --platform', 'Set global variable like API keys and org IDs') - .option('-a, --set-active', 'Select active platform'); + .option('-p, --platform', 'Set global variable like API keys and org IDs'); configCommand.action(async options => { if (Object.keys(options).length === 0) { configCommand.outputHelp(); } - if (options.embedding) { - this.handleEmbeddingEnable(); - } }); localConfig.action(async options => { @@ -76,13 +66,7 @@ class Config extends OiCommand { if (Object.keys(options).length === 0) { localConfig.outputHelp(); } else { - if (options.parse) { - await this.generateDependencyGraph(options.verbose); - } else if (options.graph) { - this.handleDepGraphEnable(); - } else { - await this.handleLocalConfig(options); - } + await this.handleLocalConfig(options); } }); @@ -96,77 +80,9 @@ class Config extends OiCommand { if (options.platform) { await this.handleAddActivePlatform(); } - if (options.setActive) { - await this.handleChangeActivePlatform(); - } }); } - async handleDepGraphEnable(): Promise { - // Set the embeddings flag to true. - this.handleLocalConfig({}, true); - return; - } - - async handleEmbeddingEnable(): Promise { - // Check if OpenAi platform details are available. - const activePlatform = CommandHelper.getActiveServiceDetails(true); - if (!activePlatform) { - console.warn( - 'Overide supports embeddings over OpenAI\nEnabling this will incure additional cost' - ); - // Ask for open ai platform details. - const answers = await this.promptPlatformConfig('openai'); - - // Check if a global config file already exists, if not initialize an empty config - const existingConfig = CommandHelper.configExists(true) - ? await CommandHelper.readConfigFileData(true) - : {}; - - // Merge the new platform configuration with the existing config - const updatedConfig = { - ...existingConfig, - ['openai']: { - ...answers, - isActive: Object.keys(existingConfig as GlobalConfig).length === 0 ? true : false // Set isActive for first platform - } - }; - - // Save the updated global configuration - await CommandHelper.writeConfigFileData(true, updatedConfig); - } - - // Set the embeddings flag to true. - this.handleLocalConfig({}, true); - return; - } - - async generateDependencyGraph(verbose: boolean = false): Promise { - try { - // Get the current directory - const currentDir = process.cwd(); - - // Get the ignore list from the oi-config.json file - const config: LocalConfig = CommandHelper.readConfigFileData() as LocalConfig; - const ignoreList = config.ignore || []; - - // Generate dependency graphs for all files in the current directory - const dependencyGraphs = await serviceParser.makeProjectDepGraph( - currentDir, - ignoreList, - verbose - ); - - // Write the dependency graphs to a file - fs.writeFileSync( - path.join(currentDir, 'oi-dependency.json'), - JSON.stringify(dependencyGraphs, null, 2) - ); - } catch (error) { - console.error('Failed to generate dependency graph:', error); - } - } - // Method to handle switching the active platform async handleChangeActivePlatform(): Promise { // Get the global config file path @@ -179,7 +95,7 @@ class Config extends OiCommand { } // Read the existing global configuration file - const existingConfig = (await CommandHelper.readConfigFileData(true)) as GlobalConfig; + const existingConfig = CommandHelper.readConfigFileData(true) as GlobalConfig; // Get a list of available platforms from the existing configuration const activePlatforms = Object.keys(existingConfig); @@ -209,7 +125,7 @@ class Config extends OiCommand { }); // Save the updated configuration back to the global config file - await CommandHelper.writeConfigFileData(true, updatedConfig); + CommandHelper.writeConfigFileData(true, updatedConfig); console.log(`Successfully updated the active platform to: ${selectedPlatform}`); } @@ -246,7 +162,7 @@ class Config extends OiCommand { // Check if a global config file already exists, if not initialize an empty config const existingConfig = CommandHelper.configExists(true) - ? await CommandHelper.readConfigFileData(true) + ? CommandHelper.readConfigFileData(true) : {}; // Merge the new platform configuration with the existing config @@ -259,13 +175,13 @@ class Config extends OiCommand { }; // Save the updated global configuration - await CommandHelper.writeConfigFileData(true, updatedConfig); + CommandHelper.writeConfigFileData(true, updatedConfig); console.log('Run `overide config global -a | --set-active` to select active platform'); } // Handle the local configuration for the project - async handleLocalConfig(options: ConfigOption, embedding: boolean = false): Promise { + async handleLocalConfig(options: ConfigOption): Promise { // Get the path to the local configuration file // const configFilePath = CommandHelper.getConfigFilePath(); @@ -277,15 +193,8 @@ class Config extends OiCommand { process.exit(1); // Exit if the local config is not found } - if (!CommandHelper.dependencyFileExists()) { - console.error( - 'Dependency file (oi-dependency.json) not found. Run `overide init` to initialize the project.' - ); - process.exit(1); // Exit if the dependency file is not found - } - // Read the local configuration - const config: LocalConfig = (await CommandHelper.readConfigFileData()) as LocalConfig; + const config: LocalConfig = CommandHelper.readConfigFileData() as LocalConfig; // Update the list of ignored files if provided in options if (options.ignore) { @@ -309,13 +218,8 @@ class Config extends OiCommand { config.projectName = options.name; } - if (embedding) { - console.log('Embedding support for project enabled.'); - config.embedding = true; - } - // Save the updated local configuration - await CommandHelper.writeConfigFileData(false, config); + CommandHelper.writeConfigFileData(false, config); console.log('Local config updated successfully.'); } } diff --git a/src/commands/command.init.ts b/src/commands/command.init.ts index 5abbcca..49c34f6 100644 --- a/src/commands/command.init.ts +++ b/src/commands/command.init.ts @@ -24,6 +24,7 @@ class Initialize extends OiCommand { initCommand .option('-i, --ignore ', 'Specify files or directories to ignore') + .option('-p, --path ', 'Specify the path to the project directory') .option('-n, --project-name ', 'Specify a project name') .action((options: InitOption) => { this.initializeProject(options); @@ -47,58 +48,9 @@ class Initialize extends OiCommand { console.log('Overide Project initialized!'); console.log('\nNext steps:'); console.log( - "1. Use 'overide config global -p | --platform' to define the API KEYs, BASE URLs and Platforms" + "1. Use 'overide config global -p | --platform' to define the API KEYs, & BASE URLs" ); console.log("2. Run 'overide start' to start getting code suggestions."); - console.log("3. Run 'overide config -e' to enable embeddings based context."); - } - - /** - * Adds specified files to the ignore list in the configuration file (`oi-config.json`). - * - * @param {string|string[]} files - A file or an array of files to add to the ignore list. - */ - addIgnoreFiles(files: string[]): void { - const configPath = CommandHelper.getConfigFilePath(false); - - // Check if oi-config.json exists - if (!CommandHelper.configExists(false)) { - console.error(`Error: oi-config.json not found at ${configPath}`); - process.exit(1); - } - - try { - // Read the current configuration file - const config: LocalConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8')); - - // Ensure 'ignore' field exists in the configuration - if (!Array.isArray(config.ignore)) { - config.ignore = []; - } - - // Convert a single file string into an array - if (typeof files === 'string') { - files = [files]; - } - - // Add each file to the ignore list if it is not already present - files.forEach(file => { - if (!config.ignore.includes(file)) { - config.ignore.push(file); - console.log(`Added ${file} to ignore list.`); - } else { - console.log(`${file} is already in the ignore list.`); - } - }); - - // Write the updated configuration back to oi-config.json - fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); - console.log('Updated oi-config.json with new ignore files.'); - } catch (error) { - if (error instanceof Error) { - console.error(`Error updating oi-config.json: ${error.message}`); - } - } } /** @@ -111,14 +63,18 @@ class Initialize extends OiCommand { async initializeProject(options: InitOption): Promise { try { // Determine the output path for the configuration file - const outputPath = path.join(process.cwd(), 'oi-config.json'); - const dependencyFilePath = path.join(process.cwd(), 'oi-dependency.json'); + // Check if the path is passed in the options. + let outputPath = ''; + if (options.path) { + outputPath = path.join(options.path, 'oi-config.json'); + } else { + outputPath = path.join(process.cwd(), 'oi-config.json'); + } + const ignoreFiles = options.ignore || []; const verbose = options.verbose || false; const projectName = options.projectName || 'default-project'; - console.log(options); - // Verbose mode: Display the initialization options if (verbose) { console.log(`Initializing project with the following options:`); @@ -131,7 +87,6 @@ class Initialize extends OiCommand { // Default ignore files, including config and dependency files, and common directories const defaultIgnoreFiles = [ 'oi-config.json', - 'oi-dependency.json', '/(^|[/\\])../', 'node_modules', '*.swp', @@ -166,11 +121,6 @@ class Initialize extends OiCommand { console.log(`Project initialized with config at ${outputPath}`); } - if (!fs.existsSync(dependencyFilePath)) { - fs.writeFileSync(dependencyFilePath, JSON.stringify([], null, 2)); - console.log(`Project initialized with dependency file at ${dependencyFilePath}`); - } - // Display ASCII art and instructions after initialization this.displayAsciiArt(); } catch (error) { diff --git a/src/commands/command.start.ts b/src/commands/command.start.ts index 806b6a0..d415eb2 100644 --- a/src/commands/command.start.ts +++ b/src/commands/command.start.ts @@ -5,9 +5,6 @@ import startCommandHandlerImpl from '../handlers/handler.start'; import { StartOption } from '../models/model.options'; import { LocalConfig } from '../models/model.config'; -import { DependencyGraph } from '../models/model.depgraph'; -import serviceParser from '../services/service.parser'; -import utilParser from '../utilis/util.parser'; /** * The `Start` class extends `OiCommand` and is responsible for initiating @@ -22,7 +19,6 @@ import utilParser from '../utilis/util.parser'; */ class Start extends OiCommand { // Stores the contents of all files in the project - private dependencyGraph: DependencyGraph[] | null = []; private watcher: FSWatcher | null = null; /** @@ -32,12 +28,10 @@ class Start extends OiCommand { configureCommand(): void { const startCommand = this.program .command('start') + .option('-p, --path ', 'Specify the path to the project directory') .description('Start watching files for prompt changes'); this.addCommonOptions(startCommand); // Add common options such as --verbose - // Load the dependency graph from the oi-dependency.json file - this.dependencyGraph = configCommandUtil.loadDependencyGraph() as DependencyGraph[] | null; - startCommand.action((options: StartOption) => this.startWatch(options)); } @@ -51,7 +45,7 @@ class Start extends OiCommand { console.log('Watching files for prompts...'); try { - const { verbose } = options; + const { verbose, path } = options; if (!configCommandUtil.configExists()) { console.error('Error: oi-config.json file not found in the current directory.'); @@ -59,9 +53,13 @@ class Start extends OiCommand { } // get current config. - const config = (await configCommandUtil.readConfigFileData()) as LocalConfig; + const config = (await configCommandUtil.readConfigFileData(false, path)) as LocalConfig; const ignoredFiles = config.ignore || []; - const currentDir = process.cwd(); + const currentDir = options.path ?? process.cwd(); + + if (verbose) { + console.log(`Watching files in directory: ${currentDir}`); + } this.watcher = chokidar.watch(currentDir, { persistent: true, @@ -98,16 +96,6 @@ class Start extends OiCommand { console.log(`File ${filePath} has been changed`); } - // Set the dep graph to false if project language is not supported. - const language = utilParser.identifyLanguageByExtension(filePath); - if (language) { - utilParser.loadParserForLanguage(language); // Check if the dependency graph is empty - await serviceParser.makeProjectDepGraphInc(filePath, ignoreFiles, verbose); - if (verbose) { - console.log('Dependency graph updated...'); - } - } - await startCommandHandlerImpl.findPromptInFile(filePath, verbose); } diff --git a/src/models/model.config.ts b/src/models/model.config.ts index 5328573..5ab9c5d 100644 --- a/src/models/model.config.ts +++ b/src/models/model.config.ts @@ -10,9 +10,8 @@ export interface GlobalConfig { } export interface GlobalPlatformInfo { - apiKey?: string; // Optional, as Ollama does not require an API key - baseUrl?: string; // Optional, as not all platforms may have a baseUrl - orgId?: string; // Optional, specific to platforms like OpenAI + apiKey?: string; + orgId?: string; isActive: boolean; } @@ -21,16 +20,11 @@ export interface ActivePlatformDetails { platformConfig: GlobalPlatformInfo; } -export const supportedPlatforms = ['OpenAI', 'DeepSeek', 'Groq']; +export const supportedPlatforms = ['OpenAI']; export const platformQuestions = { openai: [ { type: 'input', name: 'apiKey', message: 'Enter your API key:' }, { type: 'input', name: 'orgId', message: 'Enter your Organization ID:' } - ], - deepseek: [ - { type: 'input', name: 'apiKey', message: 'Enter your API key:' }, - { type: 'input', name: 'baseUrl', message: 'Enter the BaseUrl to use:' } - ], - groq: [{ type: 'input', name: 'apiKey', message: 'Enter your Groq API key:' }] + ] }; diff --git a/src/models/model.depgraph.ts b/src/models/model.depgraph.ts deleted file mode 100644 index 8b9f8c9..0000000 --- a/src/models/model.depgraph.ts +++ /dev/null @@ -1,36 +0,0 @@ -export type FileContents = { - [filePath: string]: string; -}; - -// Define types for capturing class structure -export interface FunctionData { - class: string; - code: string; - embeddings?: number[]; // The embedding can be optional. -} - -export interface ClassData { - className: string; - embeddings?: number[]; // The embedding can be optional. -} - -export interface GlobalData { - code: string; - embeddings?: number[]; -} - -export interface DependencyGraph { - fileName: string; - path: string; - imports: string[]; - globals: GlobalData[]; - classes: ClassData[]; - functions: FunctionData[]; // Adding a separate array for top-level functions -} - -export interface LanguagePatterns { - language: string; - functionRegex: RegExp; - classRegex: RegExp; - importRegex?: RegExp; -} diff --git a/src/models/model.language.map.ts b/src/models/model.language.map.ts deleted file mode 100644 index 7c3e97d..0000000 --- a/src/models/model.language.map.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Mapping of file extensions to programming languages -export const extensionToLanguageMap: { [extension: string]: string } = { - '.js': 'javascript', - '.ts': 'typescript', - '.py': 'python', - '.java': 'java', - '.cpp': 'c_cpp', - '.cs': 'c_sharp', - '.rb': 'ruby', - '.go': 'go', - '.c': 'c' -}; diff --git a/src/models/model.options.ts b/src/models/model.options.ts index 4a8b177..5f285b1 100644 --- a/src/models/model.options.ts +++ b/src/models/model.options.ts @@ -1,4 +1,5 @@ export interface InitOption { + path?: string; ignore?: string[]; verbose?: boolean; projectName?: string; @@ -6,6 +7,7 @@ export interface InitOption { export interface StartOption { verbose?: boolean; + path?: string; } export interface ConfigOption { diff --git a/src/models/model.request.ts b/src/models/model.request.ts index b59f882..7a58234 100644 --- a/src/models/model.request.ts +++ b/src/models/model.request.ts @@ -1,10 +1,10 @@ +import { ResponseFormatJSONSchema } from 'openai/resources'; import { ChatCompletionMessageParam as OpenAIChatCompletionMessageParam } from 'openai/resources/chat/completions'; -import { ChatCompletionMessageParam as GroqChatCompletionMessageParam } from 'groq-sdk/resources/chat/completions'; import { ActivePlatformDetails } from './model.config'; export interface GeneralRequestObject { platform: ActivePlatformDetails; - metadata: OpenAiRequestObject | DeepSeekRequestObject | GroqRequestObject; + metadata: OpenAiRequestObject; } export interface OpenAiRequestObject { @@ -16,18 +16,5 @@ export interface OpenAiRequestObject { stream?: boolean; presence_penalty?: number; frequency_penalty?: number; -} - -export interface DeepSeekRequestObject { - model: string; - messages: OpenAIChatCompletionMessageParam[]; -} - -export interface GroqRequestObject { - model: string; - messages: GroqChatCompletionMessageParam[]; - temperature?: number; - max_tokens?: number; - top_p?: number; - stream?: boolean; + response_format: ResponseFormatJSONSchema; } diff --git a/src/services/service.dev.ts b/src/services/service.dev.ts index 263f66c..c33a29b 100644 --- a/src/services/service.dev.ts +++ b/src/services/service.dev.ts @@ -15,31 +15,6 @@ abstract class DevService { } class DevServiceImpl extends DevService { - /** - * Extracts a code block from the content based on a specific format. - * - * @param content - The content to extract the code block from. - * @param verbose - If true, logs the extracted block. - * @returns The extracted code block. - * @throws Error if no code block is found. - */ - extractCodeBlock(content: string, verbose: boolean = false): ReplacementBlock[] { - console.log(content); - const codeMatch = content.match(/```[\s\S]*?\n([\s\S]*?)\n```/); - if (codeMatch && codeMatch[1]) { - if (verbose) { - console.log(`Extracted Code Block: ${codeMatch[1]}`); - } - try { - return JSON.parse(codeMatch[1]) as ReplacementBlock[]; - } catch (err) { - throw new Error(`No valid JSON found in response ${(err as Error).message}`); - } - } else { - throw new Error('No code block found in the response'); - } - } - /** * Finds the index of the old block in the file content using a flexible matching approach. * diff --git a/src/services/service.embedding.ts b/src/services/service.embedding.ts deleted file mode 100644 index a30a980..0000000 --- a/src/services/service.embedding.ts +++ /dev/null @@ -1,110 +0,0 @@ -import CommandHelper from '../utilis/util.command.config'; -import { ActivePlatformDetails } from '../models/model.config'; -import { DependencyGraph } from '../models/model.depgraph'; -import serviceNetwork from './service.network'; -import serviceDev from './service.dev'; - -abstract class EmbeddingService {} - -class EmbeddingServiceImpl extends EmbeddingService { - /** - * Generates Embeddings for the given dependency graph. - * - * @param graph - The dependency graph node. - * @param embeddingServiceDetails - Platform details for OpenAI. - * @returns - Updated dependency graph node with embeddings. - */ - async getEmbeddingsForDepGraph(graph: DependencyGraph): Promise { - try { - // Is embedding is disabled return the graph without any change. - const isEmbeddingEnabled = CommandHelper.isEmbeddingEnabled(); - if (!isEmbeddingEnabled) { - return graph; - } - - // Get the embedding service detail - const activeServiceDetail: ActivePlatformDetails | null = - CommandHelper.getActiveServiceDetails(true); - - if (!activeServiceDetail) { - console.error('Open AI service details not found!'); - return graph; - } - - // Get the dependency graph. - for (const functions of graph.functions) { - functions.embeddings = await serviceNetwork.getCodeEmbedding( - functions.code, - activeServiceDetail - ); - } - - return graph; - } catch (e) { - if (e instanceof Error) { - console.error(e); - } - return graph; - } - } - - /** - * Generates a contextual prompt and gets embeddings for that. - * - * @param {string} prompt - Prompt entered by the user - * @param fileContent - File contents with the prompt. - * @returns {number[]} - Embeddings for the prompt - */ - async getEmbeddingForPrompt(prompt: string, fileContent: string): Promise { - const platformDetails = CommandHelper.getActiveServiceDetails(true); - - if (!platformDetails) { - return []; - } - - const fileLines = fileContent.split('\n'); - const promptIndex = serviceDev.findMatchingIndex(fileLines, prompt.split('\n')); - const remainingLines = fileLines.length - promptIndex - 1; - let lastIndex = fileLines.length - 1; - - if (remainingLines > 5) { - lastIndex = promptIndex + 5; - } - - const finalPromtArry = []; - - for (let i = promptIndex - 5; i < lastIndex; i++) { - finalPromtArry.push(fileLines[i]); - } - - const promptString = finalPromtArry.join('\n'); - - return await serviceNetwork.getCodeEmbedding(promptString, platformDetails); - } - - /** - * Checks the similarity of two vector embeddings. - * @param vec1 - Embedding prompt - * @param vec2 - Embedding for the functions. - * @returns {number} - Similarity factor. - */ - cosineSimilarity(vec1: number[], vec2: number[]): number { - if (vec1.length !== vec2.length) { - throw new Error('Vectors must be of the same length'); - } - - // Calculate the dot product safely - const dotProduct = vec1.reduce((sum, value, index) => sum + value * vec2[index]!, 0); - - const normVec1 = Math.sqrt(vec1.reduce((sum, value) => sum + value * value, 0)); - const normVec2 = Math.sqrt(vec2.reduce((sum, value) => sum + value * value, 0)); - - if (normVec1 === 0 || normVec2 === 0) { - throw new Error('Vectors must not be zero-vectors'); - } - - return dotProduct / (normVec1 * normVec2); - } -} - -export default new EmbeddingServiceImpl(); diff --git a/src/services/service.network.ts b/src/services/service.network.ts index fef4809..6664b1a 100644 --- a/src/services/service.network.ts +++ b/src/services/service.network.ts @@ -1,84 +1,28 @@ import OpenAI from 'openai'; -import Groq from 'groq-sdk'; - -import { - DeepSeekRequestObject, - GeneralRequestObject, - GroqRequestObject, - OpenAiRequestObject -} from '../models/model.request'; -import { - ChatCompletion, - ChatCompletionMessageParam as OpenAIChatCompletionMessageParam -} from 'openai/resources/chat/completions'; +import { GeneralRequestObject, OpenAiRequestObject } from '../models/model.request'; +import { ChatCompletion } from 'openai/resources/chat/completions'; import { ActivePlatformDetails } from '../models/model.config'; -import { ChatCompletionMessageParam as GroqChatCompletionMessageParam } from 'groq-sdk/resources/chat/completions'; import * as dotenv from 'dotenv'; dotenv.config(); abstract class NetworkService { - abstract getCodeEmbedding( - codeSnippet: string, - activeServiceDetails: ActivePlatformDetails - ): Promise; - abstract doRequest(requestData: GeneralRequestObject): Promise; abstract handleOpenAIRequest( activeServiceDetails: ActivePlatformDetails, metadata: OpenAiRequestObject ): Promise; - - abstract handleDeepSeekRequest( - activeServiceDetails: ActivePlatformDetails, - metadata: DeepSeekRequestObject - ): Promise; - - abstract handleGroqRequest( - activeServiceDetails: ActivePlatformDetails, - metadata: GroqRequestObject - ): Promise; } /** - * The `Network` class is responsible for making API requests to different - * services (OpenAI, DeepSeek, and Groq) to generate code based on the - * provided request data. + * The `Network` class is responsible for making API requests to OpenAI + * to generate code based on the provided request data. */ class NetworkServiceImpl extends NetworkService { /** - * - * @param codeSnippet - The string code from the dependency graph. - * @param activeServiceDetails - The API key and other metadata. - * @returns {Promise} The generated embedding. - */ - async getCodeEmbedding( - codeSnippet: string, - activeServiceDetails: ActivePlatformDetails - ): Promise { - try { - const { apiKey, orgId } = activeServiceDetails.platformConfig; - const openai = new OpenAI.OpenAI({ apiKey, organization: orgId }); - - // Call the OpenAI API to get embeddings - const response = await openai.embeddings.create({ - model: 'text-embedding-3-small', // Choose the embedding model - input: codeSnippet // The input text (code snippet) - }); - - // Extract the embedding from the response - const embedding = (response.data[0] as OpenAI.Embedding).embedding; // This is an array of numbers - return embedding; - } catch (error) { - console.error('Error fetching embedding:', error); - throw error; - } - } - - /** - * Generates code based on the active service (OpenAI, DeepSeek, or Groq). + * Generates code based on OpenAI service. * * @param {object} requestData - The request data containing service details and metadata. * @returns {Promise} - The generated code response. @@ -96,25 +40,10 @@ class NetworkServiceImpl extends NetworkService { const platform = activeServiceDetails.platform; // Handle requests based on the selected platform - switch (platform) { - case 'openai': - return this.handleOpenAIRequest(activeServiceDetails, { - ...metadata, - messages: metadata.messages as OpenAIChatCompletionMessageParam[] - }); - case 'deepseek': - return this.handleDeepSeekRequest(activeServiceDetails, { - ...metadata, - messages: metadata.messages as OpenAIChatCompletionMessageParam[] - }); - case 'groq': - return this.handleGroqRequest(activeServiceDetails, { - ...metadata, - messages: metadata.messages as GroqChatCompletionMessageParam[] - }); - default: - throw new Error('No valid model or platform selected.'); + if (platform === 'openai') { + return this.handleOpenAIRequest(activeServiceDetails, metadata as OpenAiRequestObject); } + throw new Error('No valid model or platform selected.'); } /** @@ -141,6 +70,7 @@ class NetworkServiceImpl extends NetworkService { ...metadata, stream: false }); + console.log(completions.choices[0]); return (completions.choices[0] as ChatCompletion.Choice).message.content || ''; // Return the content string from OpenAI completion } catch (error) { if (error instanceof Error) { @@ -150,74 +80,6 @@ class NetworkServiceImpl extends NetworkService { throw error; } } - - /** - * Handles requests to the DeepSeek service. - * - * @param {object} activeServiceDetails - The details of the active DeepSeek service. - * @param {object} metadata - The metadata for the API request. - * @returns {Promise} - The generated code response from DeepSeek. - * @throws Will throw an error if the API key or base URL is missing. - */ - async handleDeepSeekRequest( - activeServiceDetails: ActivePlatformDetails, - metadata: DeepSeekRequestObject - ): Promise { - const { apiKey, baseUrl } = activeServiceDetails.platformConfig; - - if (!apiKey || !baseUrl) { - throw new Error('API key or BaseUrl missing for DeepSeek.'); - } - - try { - const openai = new OpenAI.OpenAI({ apiKey, baseURL: baseUrl }); - const completions = await openai.chat.completions.create({ - ...metadata, - stream: false - }); - return (completions.choices[0] as ChatCompletion.Choice).message.content || ''; // Return the content string from DeepSeek completion - } catch (error) { - if (error instanceof Error) { - console.error(`Error generating code with DeepSeek: ${error.message}`); - throw error; // Rethrow error for handling at a higher level - } - throw error; - } - } - - /** - * Handles requests to the Groq service. - * - * @param {object} activeServiceDetails - The details of the active Groq service. - * @param {object} metadata - The metadata for the API request. - * @returns {Promise} - The generated code response from Groq. - * @throws Will throw an error if the API key is missing. - */ - async handleGroqRequest( - activeServiceDetails: ActivePlatformDetails, - metadata: GroqRequestObject - ): Promise { - const { apiKey } = activeServiceDetails.platformConfig; - - if (!apiKey) { - throw new Error('API key missing for Groq.'); - } - - try { - const groq = new Groq({ apiKey }); - const completions = await groq.chat.completions.create({ - ...metadata, - stream: false - }); - return (completions.choices[0] as ChatCompletion.Choice).message.content || ''; // Return the content string from Groq completion - } catch (error) { - if (error instanceof Error) { - console.error(`Error generating code with Groq: ${error.message}`); - throw error; - } - throw error; - } - } } export default new NetworkServiceImpl(); diff --git a/src/services/service.parser.ts b/src/services/service.parser.ts deleted file mode 100644 index de5de9a..0000000 --- a/src/services/service.parser.ts +++ /dev/null @@ -1,323 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -import Parser, { SyntaxNode } from 'tree-sitter'; // Import the Tree-sitter parser -import { ClassData, DependencyGraph, FunctionData, GlobalData } from '../models/model.depgraph'; -import utilParser from '../utilis/util.parser'; -import serviceEmbedding from './service.embedding'; -import utilCommandConfig from '../utilis/util.command.config'; - -abstract class ParserService {} - -// Implementation for ParserService -class ParserServiceImpl extends ParserService { - /** - * Generates a dependency graph for a single file. - * - * @param {string} filePath - The path to the file. - * @param {boolean} verbose - Enable verbose logging. - * @returns {DependencyGraph | null} - The dependency graph for the file or null if processing fails. - */ - async makeFileDepGraph( - filePath: string, - verbose: boolean = false - ): Promise { - const language = utilParser.identifyLanguageByExtension(filePath); - - if (!language) { - if (verbose) console.log(`Language not identified for file: ${filePath}`); - throw new Error('UNSUP_LANG'); - } - - // Load the corresponding Tree-sitter parser for the identified language - let parser: Parser | null = null; - try { - parser = utilParser.loadParserForLanguage(language); - } catch (error) { - if (verbose) { - console.log(`Tree-sitter parser not found or failed to load for ${language}: ${error}`); - } - return null; - } - - if (parser) { - const fileContent = fs.readFileSync(filePath, 'utf8'); - const tree = parser.parse(fileContent); - let depGraph: DependencyGraph = this.extractDepGraphNodeData(filePath, tree); - depGraph = await serviceEmbedding.getEmbeddingsForDepGraph(depGraph); - return depGraph; - } - - return null; - } - - /** - * Generates a dependency graph for all files in a given directory. - * - * @param {string} directory - The root directory for generating dependency graphs. - * @param {string[]} ignoreList - List of file patterns to ignore. - * @param {boolean} verbose - Enable verbose logging. - * @returns {DependencyGraph[]} - Array of dependency graphs for each file. - */ - async makeProjectDepGraph( - directory: string, - ignoreList: string[] = [], - verbose: boolean = false - ): Promise { - const filePaths = utilParser.getAllFilePaths(directory, ignoreList, verbose); - const dependencyGraphs: DependencyGraph[] = []; - - for (const filePath of filePaths) { - const language = utilParser.identifyLanguageByExtension(filePath); - - if (!language) { - if (verbose) console.log(`Language not identified for file: ${filePath}`); - continue; - } - - // Load the corresponding Tree-sitter parser for the identified language - let parser: Parser | null = null; - try { - parser = utilParser.loadParserForLanguage(language); - - if (parser) { - const fileContent = fs.readFileSync(filePath, 'utf8'); - const tree = parser.parse(fileContent); - let dependencyGraph = this.extractDepGraphNodeData(filePath, tree); - dependencyGraph = await serviceEmbedding.getEmbeddingsForDepGraph(dependencyGraph); - dependencyGraphs.push(dependencyGraph); - } - } catch (error) { - if (verbose) { - console.log(`Tree-sitter parser not found or failed to load for ${language} ${error}`); - } - continue; - } - } - - return dependencyGraphs; - } - - /** - * Incrementally updates the `oi-dependency.json` with the dependency graph of a single file. - * - * @param {string} filePath - The path to the file to be incrementally updated. - * @param {boolean} verbose - Enable verbose logging. - */ - async makeProjectDepGraphInc( - filePath: string, - ignoreList: string[], - verbose: boolean = false - ): Promise { - const dependencyFile = 'oi-dependency.json'; - let existingDependencies: DependencyGraph[] = []; - - // Load existing dependency data if the file exists - if (fs.existsSync(dependencyFile)) { - const rawData = fs.readFileSync(dependencyFile, 'utf8'); - existingDependencies = JSON.parse(rawData); - } - - // Check if the file or directory matches any ignore pattern - const shouldIgnore = ignoreList.some(ignorePattern => filePath.includes(ignorePattern)); - - if (shouldIgnore) { - if (verbose) { - console.log(`Skipping ignored path: ${filePath}`); // Log ignored paths if verbose - } - return true; - } - - try { - let newDependencyGraph = await this.makeFileDepGraph(filePath, verbose); - - if (newDependencyGraph) { - const index = existingDependencies.findIndex(dep => dep.path === filePath); - - if (index !== -1) { - // Update existing entry - existingDependencies[index] = newDependencyGraph; - if (verbose) console.log(`Updated dependency graph for file: ${filePath}`); - } else { - // Add new entry - existingDependencies.push(newDependencyGraph); - if (verbose) console.log(`Added new dependency graph for file: ${filePath}`); - } - - // Write updated dependencies back to files - fs.writeFileSync(dependencyFile, JSON.stringify(existingDependencies, null, 2)); - if (verbose) console.log(`Dependency file updated at ${dependencyFile}`); - } else { - if (verbose) console.log(`Failed to generate dependency graph for file: ${filePath}`); - return false; - } - } catch (e) { - if (e instanceof Error && e.message === 'UNSUP_LANG') { - console.error('Overide does not support dependency graph in this language'); - return false; - } - } - - return true; - } - - /** - * Finds the nearest relative node to the given one. - * - * @param filePath The center node for which to find dependency. - * @returns returns the list of nodes (DependencyGraph[]) which are nearest relative. - */ - makeContextFromDepGraph(filePath: string): DependencyGraph[] { - // Load the dependency graph - const depgraph: DependencyGraph[] | null = utilCommandConfig.loadDependencyGraph(); - - if (!depgraph) { - return []; - } - - const adjNodes: DependencyGraph[] = []; - - // Find the current file in dep graph. - let currentNode: DependencyGraph | undefined = depgraph.filter( - node => node.path === filePath - )[0]; - - if (!currentNode) { - return []; - } - - // Get all edges. - for (const importstm of currentNode.imports) { - // Get the fileName from the import. - const importParts = importstm.split('/'); - if (importParts.length === 1) { - continue; - } - const fileName = (importParts[importParts.length - 1] as string) - .replace("'", '') - .replace(';', ''); - - const graphOfImport: DependencyGraph[] | undefined = depgraph.filter(node => - node.path.includes(fileName) - ); - - if (!graphOfImport) { - continue; - } else { - adjNodes.push(...graphOfImport); - } - } - - return adjNodes; - } - - /** - * Extracts dependency information from parsed Tree-sitter tree. - * - * @param {string} filePath - The file path. - * @param {string} fileContent - The file content. - * @param {Parser.Tree} tree - The parsed syntax tree. - * @param {string} language - The language of the file. - * @returns {DependencyGraph} - The dependency graph for the file. - */ - private extractDepGraphNodeData(filePath: string, tree: Parser.Tree): DependencyGraph { - const fileName = path.basename(filePath); - const imports: string[] = []; - const classes: ClassData[] = []; - const globals: GlobalData[] = []; - const functions: FunctionData[] = []; - - const traverseNode = (node: SyntaxNode, currentClass: string | null = null): void => { - // Direct checks for import, class, and function nodes - if (node.isNamed) { - switch (node.type) { - // Combine import statements for different languages - case 'import_statement': - case 'preproc_include': // C/C++ #include - case 'import_from_statement': // Python's `from ... import ...` - case 'require_statement': // Ruby `require ...` - case 'using_directive': // C# `using ...` - imports.push(node.text); - break; - - // Combine class declarations for different languages - case 'class_definition': // Python - case 'class_declaration': // Java, JavaScript, TypeScript, C# - case 'class': // Ruby `class ... end` - case 'struct_declaration': // Go's struct as class - const classData = this.extractClassData(node); - currentClass = classData.className; - classes.push(classData); - break; - - // Combine function declarations for different languages - case 'function_declaration': // JavaScript, TypeScript, Go - case 'method_declaration': // Java, C#, C++ (methods) - case 'function_definition': // C, C++, Go, Python - case 'method_definition': // Ruby methods `def ... end` - functions.push(this.extractFunctionData(node, currentClass)); - break; - - // Global Code (any code outside of classes/functions) - default: - // If node is not part of class or function, it's global code - if ( - node.type === 'expression_statement' && - node.parent && - (node.parent.type === 'program' || node.parent.type === 'module') - ) { - globals.push({ code: node.text }); - } - break; - } - } - - // Recursively process named children - node.namedChildren.forEach(child => traverseNode(child, currentClass)); - }; - - // Begin traversal from the root node - traverseNode(tree.rootNode); - - return { - fileName, - path: filePath, - imports, - globals, - classes, - functions - }; - } - - /** - * Extracts data for a class. - * - * @param {Parser.SyntaxNode} classNode - The class node from Tree-sitter. - * @returns {ClassData} - The extracted class data. - */ - private extractClassData(classNode: Parser.SyntaxNode): ClassData { - const className = classNode.childForFieldName('name')?.text || 'UnnamedClass'; - return { - className, - embeddings: [] - }; - } - - /** - * Extracts data for a function, associating it with its class if provided. - * - * @param {Parser.SyntaxNode} functionNode - The function node. - * @param {string} fileContent - The content of the file. - * @param {string | null} className - The name of the class the function belongs to. - * @returns {FunctionData} - The extracted function data. - */ - private extractFunctionData( - functionNode: Parser.SyntaxNode, - className: string | null - ): FunctionData { - const code = functionNode.text; - return { class: className || '', code, embeddings: [] }; - } -} - -export default new ParserServiceImpl(); diff --git a/src/services/service.process/process.request.ts b/src/services/service.process/process.request.ts index d8aad9e..5743057 100644 --- a/src/services/service.process/process.request.ts +++ b/src/services/service.process/process.request.ts @@ -1,24 +1,19 @@ import { ActivePlatformDetails } from '../../models/model.config'; import { InsertionRequestInfo } from '../../models/model.prompts'; -import { - DeepSeekRequestObject, - GeneralRequestObject, - GroqRequestObject, - OpenAiRequestObject -} from '../../models/model.request'; +import { GeneralRequestObject, OpenAiRequestObject } from '../../models/model.request'; import CommandHelper from '../../utilis/util.command.config'; import { systemPromptServiceImpl } from '../service.prompts/service.system.prompt'; /** * The `FormatRequest` class is responsible for creating a dynamic request - * based on the active AI service platform (OpenAI or DeepSeek). It formats + * based on the active AI service platform (OpenAI). It formats * the prompt using `FormatPrompt` and constructs the request body accordingly. */ class ProcessRequest { /** * Creates a dynamic request object based on the active service platform. - * It calls either the OpenAI or DeepSeek-specific request formatting function. + * It calls the OpenAI-specific request formatting function. * * @param prompt - The raw prompt extracted from the file. * @param promptArray - The array of context around the prompt. @@ -39,19 +34,10 @@ class ProcessRequest { } // Determine which platform is active and create the appropriate request - switch (activeServiceDetails.platform) { - case 'openai': - return this.createOpenAIRequest(insertionRequest, activeServiceDetails, verbose); - - case 'deepseek': - return this.createDeepSeekRequest(insertionRequest, activeServiceDetails, verbose); - - case 'groq': - return this.createGroqRequest(insertionRequest, activeServiceDetails, verbose); - - default: - throw new Error(`Unsupported platform: ${activeServiceDetails.platform}`); + if (activeServiceDetails.platform === 'openai') { + return this.createOpenAIRequest(insertionRequest, activeServiceDetails, verbose); } + throw new Error(`Unsupported platform: ${activeServiceDetails.platform}`); } catch (error) { if (error instanceof Error) { console.error(`Error in creating request: ${error.message}`); @@ -81,14 +67,45 @@ class ProcessRequest { } const metadata: OpenAiRequestObject = { - model: 'gpt-4o', // Specify the model to use + model: 'gpt-4o', messages: messages, - temperature: 0.5, // Adjust temperature for creativity (lower = more deterministic) - max_tokens: 2500, // Max tokens for the response - n: 1, // Number of completions to generate - stream: false, // Whether to stream results - presence_penalty: 0, // Adjusts frequency of introducing new ideas - frequency_penalty: 0 // Adjusts repetition + temperature: 0.5, + max_tokens: 2500, + n: 1, + stream: false, + presence_penalty: 0, + frequency_penalty: 0, + response_format: { + type: 'json_schema', + json_schema: { + name: 'changes', + schema: { + type: 'object', + properties: { + changes: { + type: 'array', + items: { + type: 'object', + properties: { + find: { + type: 'array', + items: { type: 'string' } + }, + replace: { + type: 'array', + items: { type: 'string' } + } + }, + required: ['find', 'replace'], + additionalProperties: false + } + } + }, + required: ['changes'], + additionalProperties: false + } + } + } }; // Construct the request body for OpenAI API @@ -97,76 +114,6 @@ class ProcessRequest { metadata: metadata }; } - - /** - * Creates and formats the request for DeepSeek models. - * - * @param prompt - The raw prompt extracted from the file. - * @param promptArray - The array of context around the prompt. - * @param activeServiceDetails - Details about the active service (platform, apiKey, etc.). - * @param completionType - The type of completion being requested. - * @returns The request object for the DeepSeek API. - */ - async createDeepSeekRequest( - insertionRequest: InsertionRequestInfo, - activeServiceDetails: ActivePlatformDetails, - verbose: boolean - ): Promise { - // Getting the final prompt. - const messages = await systemPromptServiceImpl.getDeepSeekSystemMessage(insertionRequest); - - if (verbose) { - console.log(`Prompt Text : ${messages}`); - } - - // Making metadata - const metadata: DeepSeekRequestObject = { - model: 'deepseek-chat', - messages: messages - }; - - // Construct the request body for DeepSeek API - return { - platform: activeServiceDetails, - metadata: metadata - }; - } - - /** - * Creates and formats the request for Groq models. - * - * @param prompt - The raw prompt extracted from the file. - * @param promptArray - The array of context around the prompt. - * @param activeServiceDetails - Details about the active service (platform, apiKey, etc.). - * @param completionType - The type of completion being requested. - * @param verbose - Whether to log the request details. - * @returns The request object for the Groq API. - */ - async createGroqRequest( - insertionRequest: InsertionRequestInfo, - activeServiceDetails: ActivePlatformDetails, - verbose: boolean - ): Promise { - const messages = await systemPromptServiceImpl.getGroqSystemMessage(insertionRequest); - - if (verbose) { - console.log(`Prompt Text : ${messages}`); - } - - const metadata: GroqRequestObject = { - model: 'llama-3.1-70b-versatile', - messages: messages, - temperature: 0.94, - max_tokens: 2048, - top_p: 1, - stream: false - }; - - return { - platform: activeServiceDetails, - metadata: metadata - }; - } } export default new ProcessRequest(); diff --git a/src/services/service.process/process.response.ts b/src/services/service.process/process.response.ts index 4a52147..124c88d 100644 --- a/src/services/service.process/process.response.ts +++ b/src/services/service.process/process.response.ts @@ -1,35 +1,22 @@ import { ReplacementBlock } from '../../models/model.response'; -import serviceDev from '../service.dev'; /** * The `FormatResponse` class is responsible for formatting the response received from - * AI service platforms like OpenAI, DeepSeek, and Groq. It extracts code blocks from - * the response content and returns them for further processing. + * OpenAI. It extracts code blocks from the response content and returns them for + * further processing. */ class ProcessResponse { /** - * Formats the response based on the active service platform. - * Calls the appropriate formatting function for OpenAI, DeepSeek, or Groq. + * Formats the response from OpenAI. * * @param response - The API response object. - * @param completionType - Type of completion to aid in code extraction. * @param verbose - Whether to log the formatting process. * @returns The formatted code block extracted from the response, or null if not found. */ - async formatResponse( - response: string, - verbose: boolean = false - ): Promise { + async formatResponse(response: string): Promise { try { - const replacementObject: ReplacementBlock[] = serviceDev.extractCodeBlock(response, verbose); - for (const bloc of replacementObject) { - bloc.replace = bloc.replace.filter( - line => !line.includes('//>') || !line.includes('; - - abstract getDeepSeekSystemMessage( - insertionRequest: InsertionRequestInfo - ): Promise; - - abstract getGroqSystemMessage( - insertionRequest: InsertionRequestInfo - ): Promise; } class SystemPromptServiceImpl extends SystemPromptService { private static instance: SystemPromptServiceImpl; private basePrompt: SystemPromptInfo; - private hasDependencyGraph: boolean; - constructor() { + private constructor() { super(); - this.hasDependencyGraph = false; this.basePrompt = promptStructure; } @@ -47,10 +31,6 @@ class SystemPromptServiceImpl extends SystemPromptService { return SystemPromptServiceImpl.instance; } - setDependencyExists(value: boolean): void { - this.hasDependencyGraph = value; - } - /** * Creates and formats a prompt for OpenAI models. * @@ -67,10 +47,7 @@ class SystemPromptServiceImpl extends SystemPromptService { // In all the cases load the system prompt const systemPrompt = (this.basePrompt[platform] as SystemPromptPlatformInfo).systemMessage; - const codeContext = this.getCodeContext( - insertionRequest.filePath, - insertionRequest.promptEmbedding ?? [] - ); + const codeContext = this.getCodeContext(insertionRequest.filePath); const instructions = this.getInstructions(platform); let format = ''; @@ -101,102 +78,6 @@ class SystemPromptServiceImpl extends SystemPromptService { } } - /** - * Creates and formats a prompt for DeepSeek models. - * - * @param {Array} contextArray - The array of context around the prompt. - * @param {string} prompt - The raw prompt text. - * @param {string} completionType - The type of completion (e.g., 'complete' or 'update'). - * @returns {Promise} The formatted DeepSeek prompt. - */ - async getDeepSeekSystemMessage( - insertionRequest: InsertionRequestInfo - ): Promise { - try { - const platform = 'deepseek'; - - // In all the cases load the system prompt - const systemPrompt = (this.basePrompt[platform] as SystemPromptPlatformInfo).systemMessage; - const codeContext = this.getCodeContext( - insertionRequest.filePath, - insertionRequest.promptEmbedding ?? [] - ); - const instructions = this.getInstructions(platform); - - let format = ''; - let contextPrompt = ''; - - contextPrompt = (this.basePrompt[platform] as SystemPromptPlatformInfo).context; - format = (this.basePrompt[platform] as SystemPromptPlatformInfo).format; - - const systemContent = `${systemPrompt}\n Instructions:${instructions}\n${format}\n${contextPrompt}:\n${codeContext}`; - const systemMessage: ChatCompletionMessageParam = { - role: 'system', - content: systemContent - }; - - const userMessage: ChatCompletionMessageParam = { - role: 'user', - content: insertionRequest.prompt - }; - - return [systemMessage, userMessage] as ChatCompletionMessageParam[]; - } catch (error) { - if (error instanceof Error) { - console.error(`Error generating DeepSeek prompt: ${error.message}`); - } - throw error; // Re-throw the error for further handling - } - } - - /** - * Creates and formats a prompt for Groq models. - * - * @param {Array} contextArray - The array of context around the prompt. - * @param {string} prompt - The raw prompt text. - * @param {string} completionType - The type of completion (e.g., 'complete' or 'update'). - * @returns {Promise} The formatted Groq prompt. - */ - async getGroqSystemMessage( - insertionRequest: InsertionRequestInfo - ): Promise { - try { - const platform = 'groq'; - - // In all the cases load the system prompt - const systemPrompt = (this.basePrompt[platform] as SystemPromptPlatformInfo).systemMessage; - const codeContext = this.getCodeContext( - insertionRequest.filePath, - insertionRequest.promptEmbedding ?? [] - ); - const instructions = this.getInstructions(platform); - - let format = ''; - let contextPrompt = ''; - - contextPrompt = (this.basePrompt[platform] as SystemPromptPlatformInfo).context; - format = (this.basePrompt[platform] as SystemPromptPlatformInfo).format; - - const systemContent = `${systemPrompt}\n Instructions:${instructions}\n${format}\n${contextPrompt}:\n${codeContext}`; - const systemMessage: GroqChatCompletionMessageParam = { - role: 'system', - content: systemContent - }; - - const userMessage: GroqChatCompletionMessageParam = { - role: 'user', - content: insertionRequest.prompt - }; - - return [systemMessage, userMessage] as GroqChatCompletionMessageParam[]; - } catch (error) { - if (error instanceof Error) { - console.error(`Error generating Groq prompt: ${error.message}`); - } - throw error; - } - } - /** * Generates a formatted string containing the file content and user prompt. * @@ -204,44 +85,10 @@ class SystemPromptServiceImpl extends SystemPromptService { * relevant to the user prompt. * @returns A formatted string that includes the first element of contextArray and the user prompt. */ - private getCodeContext(filePath: string, promptEmbd: number[]): string { - const contextGraph: DependencyGraph[] = serviceParser.makeContextFromDepGraph(filePath); + private getCodeContext(filePath: string): string { const contextInformation: string[] = []; - const currentFile = fs.readFileSync(filePath, 'utf-8'); contextInformation.push(currentFile); - - const isEmbeddingEnabled = CommandHelper.isEmbeddingEnabled(); - - for (const node of contextGraph) { - // Add file line - contextInformation.push('File : '); - contextInformation.push(node.path); - - // If there is no functions in the file or Embedding is not enabled. - // It doesn't make sense to just send random function.. rather should - // send the file itself. - if (node.functions.length === 0 || !isEmbeddingEnabled) { - const entireCode = fs.readFileSync(node.path, 'utf-8'); - contextInformation.push(entireCode); - continue; - } - - // Add class and functions - node.functions.forEach(func => { - if (!contextInformation.includes(func.class)) { - contextInformation.push(func.class); - } - - if (isEmbeddingEnabled) { - const similarity = serviceDependency.cosineSimilarity(promptEmbd, func.embeddings ?? []); - if (similarity >= 0.5) { - contextInformation.push(func.code); - } - } - }); - } - return contextInformation.join('\n'); } diff --git a/src/services/service.prompts/service.user.prompt.ts b/src/services/service.prompts/service.user.prompt.ts index 678dbc9..443e943 100644 --- a/src/services/service.prompts/service.user.prompt.ts +++ b/src/services/service.prompts/service.user.prompt.ts @@ -1,7 +1,5 @@ import cacheService from '../../services/service.cache'; import { InsertionRequestInfo, InsertionResponseInfo } from '../../models/model.prompts'; -import serviceEmbedding from '../service.embedding'; -import CommandHelper from '../../utilis/util.command.config'; abstract class UserPromptService { // Regular expressions to match specific prompt types @@ -101,9 +99,6 @@ class UserPromptServiceImpl extends UserPromptService { ): Promise { try { const insertionRequests: InsertionRequestInfo[] = []; - - const isEmbedding = CommandHelper.isEmbeddingEnabled(); - if (verbose) { console.log(`Searching for prompts in ${filePath}`); } @@ -117,12 +112,6 @@ class UserPromptServiceImpl extends UserPromptService { const prompt = match[1].trim(); let promptEmbedding: number[] = []; - if (isEmbedding) { - // Get embedding for current prompt. - promptEmbedding = await serviceEmbedding.getEmbeddingForPrompt(prompt, fileContent); - console.log(promptEmbedding.length); - } - // Add the insertion request to the list insertionRequests.push({ prompt: prompt, diff --git a/src/utilis/util.command.config.ts b/src/utilis/util.command.config.ts index b562adc..02e0d16 100644 --- a/src/utilis/util.command.config.ts +++ b/src/utilis/util.command.config.ts @@ -7,7 +7,6 @@ import { GlobalPlatformInfo, LocalConfig } from '../models/model.config'; -import { DependencyGraph } from '../models/model.depgraph'; /** * The `DirectoryHelper` class is responsible for managing configuration files and directories @@ -18,33 +17,6 @@ class ConfigCommandUtil { // File names for configuration private static configFileName = 'oi-config.json'; private static globalConfigFileName = 'oi-global-config.json'; - private static dependencyFileName = 'oi-dependency.json'; - - loadDependencyGraph(): DependencyGraph[] | null { - const dependencyFilePath = this.getDependencyFilePath(); - if (fs.existsSync(dependencyFilePath)) { - const dependencyData = fs.readFileSync(dependencyFilePath, 'utf-8'); - const dependencyGraph = JSON.parse(dependencyData); - return dependencyGraph; - } - return null; - } - - /** - * Get the file path for the dependency file. - */ - public getDependencyFilePath(): string { - return path.join(process.cwd(), ConfigCommandUtil.dependencyFileName); - } - - /** - * Checks if the dependency file exists. - * - * @returns {boolean} - True if the dependency file exists, false otherwise. - */ - public dependencyFileExists(): boolean { - return fs.existsSync(this.getDependencyFilePath()); - } /** * Checks if the specified configuration file (local or global) exists. @@ -52,8 +24,8 @@ class ConfigCommandUtil { * @param {boolean} global - True if checking the global config, false for local. * @returns {boolean} - True if the configuration file exists, false otherwise. */ - public configExists(global: boolean = false): boolean { - const configPath = this.getConfigFilePath(global); + public configExists(global: boolean = false, localPath?: string): boolean { + const configPath = this.getConfigFilePath(global, localPath); return fs.existsSync(configPath); } @@ -63,10 +35,16 @@ class ConfigCommandUtil { * @param {boolean} global - True if retrieving the global config file path. * @returns {string} - The full path to the configuration file. */ - public getConfigFilePath(global: boolean = false): string { + public getConfigFilePath(global: boolean = false, localPath?: string): string { if (global) { return path.join(this.getGlobalConfigDirectory(), ConfigCommandUtil.globalConfigFileName); } + + if (localPath) { + // Return the local config file path from the specified directory + return path.join(localPath, ConfigCommandUtil.configFileName); + } + // Return the local config file path return path.join(process.cwd(), ConfigCommandUtil.configFileName); } @@ -110,8 +88,11 @@ class ConfigCommandUtil { * @param {boolean} global - True if reading global config, false for local. * @returns {LocalConfig | GlobalConfig | null} - The configuration object or null if not found. */ - public readConfigFileData(global: boolean = false): LocalConfig | GlobalConfig | null { - const configPath = this.getConfigFilePath(global); + public readConfigFileData( + global: boolean = false, + localPath?: string + ): LocalConfig | GlobalConfig | null { + const configPath = this.getConfigFilePath(global, localPath); if (!this.configExists(global)) { console.error(`Configuration file not found at ${configPath}`); @@ -136,8 +117,12 @@ class ConfigCommandUtil { * @param {boolean} global - True if writing to global config, false for local. * @param {LocalConfig | GlobalConfig} data - The configuration data to write. */ - public writeConfigFileData(global: boolean = false, data: LocalConfig | GlobalConfig): void { - const configPath = this.getConfigFilePath(global); + public writeConfigFileData( + global: boolean = false, + data: LocalConfig | GlobalConfig, + localPath?: string + ): void { + const configPath = this.getConfigFilePath(global, localPath); // Ensure the directory exists this.makeRequiredDirectories(); @@ -152,11 +137,6 @@ class ConfigCommandUtil { } } - isEmbeddingEnabled(): boolean { - const localConfig = this.readConfigFileData() as LocalConfig; - return localConfig.embedding; - } - /** * Retrieves the details of the currently active AI service platform. * It reads the global configuration file to determine which platform is marked as active. diff --git a/src/utilis/util.parser.ts b/src/utilis/util.parser.ts index c24ab32..f5597d6 100644 --- a/src/utilis/util.parser.ts +++ b/src/utilis/util.parser.ts @@ -1,83 +1,11 @@ import fs from 'fs'; import path from 'path'; -// Loading the required Tree-sitter language modules -import Java from 'tree-sitter-java'; -import Python from 'tree-sitter-python'; -import Ruby from 'tree-sitter-ruby'; -import Go from 'tree-sitter-go'; -import JavaScript from 'tree-sitter-javascript'; -import TypeScript from 'tree-sitter-typescript'; -import Cpp from 'tree-sitter-cpp'; -import CSharp from 'tree-sitter-c-sharp'; -import C from 'tree-sitter-c'; - -import { extensionToLanguageMap } from '../models/model.language.map'; -import Parser from 'tree-sitter'; -import express from 'express'; - abstract class ParserUtil { abstract getAllFilePaths(directory: string, ignoreList: string[], verbose: boolean): string[]; } class ParserUtilImpl extends ParserUtil { - /** - * Loads a Tree-sitter parser for a given language. - * - * @param {string} language - The language to load. - * @returns {Parser} - The Tree-sitter parser instance for the language. - */ - loadParserForLanguage(language: string): Parser | null { - try { - const parser = new Parser(); - switch (language) { - case 'cpp': - parser.setLanguage(Cpp); - break; - case 'c++': - parser.setLanguage(Cpp); - break; - case 'c': - parser.setLanguage(C); - break; - case 'java': - parser.setLanguage(Java); - break; - case 'python': - parser.setLanguage(Python); - break; - case 'ruby': - parser.setLanguage(Ruby); - break; - case 'go': - parser.setLanguage(Go); - break; - case 'javascript': - parser.setLanguage(JavaScript); - break; - case 'typescript': - parser.setLanguage(TypeScript.typescript); - break; - case 'csharp': - parser.setLanguage(CSharp); - break; - default: - console.log('No Tree-sitter parser found for language:', language); - break; - } - return parser; - } catch (error) { - console.error(`Error loading Tree-sitter parser for ${language}:`, error); - } - return null; - } - - // Identify programming language based on file extension - identifyLanguageByExtension(filePath: string): string | undefined { - const extension = path.extname(filePath); - return extensionToLanguageMap[extension] || undefined; - } - /** * Recursively gathers all file paths in a directory, respecting ignore patterns. * @@ -127,52 +55,6 @@ class ParserUtilImpl extends ParserUtil { return filePaths; } - - // EXPERIMENTAL - /** - * Shows a 3d-visualization of the dependency graph. - * Nothing useful - just a cool thing to have. - */ - async showGraphInSpace(): Promise { - const open = (await import('open')).default; // Dynamic import for ES module - const app = express(); - - // Path to the assets directory (assuming assets are in the root of the project) - const assetsPath = path.join(__dirname, '..', 'assets'); // Go up one level from dist to get to src/assets - - // Serve the graph.html from the assets folder - app.get('/', (req, res) => { - const graphFilePath = path.join(assetsPath, 'graph.html'); - console.log(graphFilePath); - res.sendFile(graphFilePath); - }); - - // Serve static files (like JS, CSS) from the assets folder - app.use(express.static(assetsPath)); - - // Serve the oi-dependency.json from the root directory - app.get('/dependency-graph', (req, res) => { - const dependencyGraphPath = path.join(__dirname, '..', 'oi-dependency.json'); - fs.readFile(dependencyGraphPath, 'utf-8', (err, data) => { - if (err) { - res.status(500).send('Error loading dependency graph'); - return; - } - res.json(JSON.parse(data)); - }); - }); - - // Start the server - const port = 3000; - app.listen(port, () => { - console.log(`Server running at http://localhost:${port}`); - - // Automatically open the browser - open(`http://localhost:${port}`) - .then(() => console.log('Browser opened automatically.')) - .catch(err => console.error('Error opening browser:', err)); - }); - } } export default new ParserUtilImpl();