Skip to content

Commit

Permalink
Added working
Browse files Browse the repository at this point in the history
  • Loading branch information
nang-dev committed Oct 14, 2024
1 parent d0ae803 commit f6b3445
Showing 1 changed file with 108 additions and 15 deletions.
123 changes: 108 additions & 15 deletions core/llm/llms/Aider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import { BaseLLM } from "../index.js";
import { streamSse, streamJSON } from "../stream.js";
import { checkTokens } from "../../db/token.js";
import { stripImages } from "../images.js";
import {
countTokens,
} from "../countTokens.js";
import { countTokens } from "../countTokens.js";
import * as cp from 'child_process';
import * as process from 'process';
import { PearAICredentials } from "../../pearaiServer/PearAICredentials.js";
import { getHeaders } from "../../pearaiServer/stubs/headers.js";
import { execSync } from 'child_process';
import * as os from 'os';


export const AIDER_QUESTION_MARKER = "[Yes]\\:"
export const AIDER_END_MARKER = '─────────────────────────────────────'
Expand All @@ -32,22 +35,71 @@ class Aider extends BaseLLM {

private aiderProcess: cp.ChildProcess | null = null;
private aiderOutput: string = '';
private credentials: PearAICredentials;

constructor(options: LLMOptions) {
super(options);
if (options.getCurrentDirectory) {
this.getCurrentDirectory = options.getCurrentDirectory;
}
this.credentials = new PearAICredentials(
options.getCredentials,
options.setCredentials || (async () => {})
);
console.log("Aider constructor called");
this.startAiderChat(this.model, this.apiKey);
}

public setPearAIAccessToken(value: string | undefined): void {
this.credentials.setAccessToken(value);
}

public setPearAIRefreshToken(value: string | undefined): void {
this.credentials.setRefreshToken(value);
}

private getUserShell(): string {
if (os.platform() === 'win32') {
return process.env.COMSPEC || 'cmd.exe';
}
return process.env.SHELL || '/bin/sh';
}

private getUserPath(): string {
try {
let command: string;
const shell = this.getUserShell();

if (os.platform() === 'win32') {
// For Windows, we'll use a PowerShell command
command = 'powershell -Command "[Environment]::GetEnvironmentVariable(\'Path\', \'User\') + \';\' + [Environment]::GetEnvironmentVariable(\'Path\', \'Machine\')"';
} else {
// For Unix-like systems (macOS, Linux)
command = `${shell} -ilc 'echo $PATH'`;
}

return execSync(command, { encoding: 'utf8' }).trim();
} catch (error) {
console.error('Error getting user PATH:', error);
return process.env.PATH || '';
}
}



private async _getHeaders() {
await this.credentials.checkAndUpdateCredentials();
return {
"Content-Type": "application/json",
...(await getHeaders()),
};
}

private captureAiderOutput(data: Buffer): void {
const lines = data.toString().replace(/\r\n|\r/g, '').split('\n');
console.log("Before lines:", lines)
let startIndex = 0;
for (let i = 0; i < lines.length; i++) {
// Look for [Yes] with any spaces in between due to terminal buffer edge bases
if (lines[i].match(/\[\s*Y\s*e\s*s\s*\]\s*:\s*/i) && !lines[i].trim().endsWith(':')) {
startIndex = i + 1;
break;
Expand All @@ -63,19 +115,14 @@ class Aider extends BaseLLM {
this.aiderOutput += filteredOutput;
}

startAiderChat(model: string, apiKey: string | undefined): Promise<void> {
async startAiderChat(model: string, apiKey: string | undefined): Promise<void> {
return new Promise(async (resolve, reject) => {
let currentDir: string;
if (this.getCurrentDirectory) {
console.log("Current directory function isNOT null");
currentDir = await this.getCurrentDirectory();
console.log("Current directory:", currentDir);
} else {
console.log("Current directory function is null");
currentDir = "/Users/nang/Documents/ebook-generator-site/"; // Todo: make this the dir that is currently open
currentDir = "";
}
console.log("Current directory:", currentDir);


let command: string;
switch (model) {
Expand All @@ -86,18 +133,64 @@ class Aider extends BaseLLM {
command = `export OPENAI_API_KEY=${apiKey}; /opt/homebrew/bin/aider`;
break;
case "pearai_model":
default:
command = '/opt/homebrew/bin/aider --openai-api-key 8888 --openai-api-base http://localhost:8000/integrations/aider';
break;
default:
await this.credentials.checkAndUpdateCredentials();
const accessToken = this.credentials.getAccessToken();
command = `aider --openai-api-key ${accessToken} --openai-api-base http://localhost:8000/integrations/aider`;
break;
}

const userPath = this.getUserPath();
const userShell = this.getUserShell();
console.log('User PATH:', userPath);
console.log('User shell:', userShell);

let spawnArgs: string[];
if (os.platform() === 'win32') {
spawnArgs = ['/c', command];
} else {
spawnArgs = ['-c', command];
}

this.aiderProcess = cp.spawn('bash', ['-c', command], {
this.aiderProcess = cp.spawn(userShell, spawnArgs, {
stdio: ['pipe', 'pipe', 'pipe'],
cwd: currentDir,
env: {
...process.env,
PATH: userPath, // Use the user's PATH here
}
});
const spawnAiderProcess = () => {
return cp.spawn(userShell, spawnArgs, {
stdio: ['pipe', 'pipe', 'pipe'],
cwd: currentDir,
env: {
...process.env,
PATH: userPath,
}
});
};

try {
this.aiderProcess = spawnAiderProcess();

if (!this.aiderProcess.pid) {
console.log('Aider not found. Attempting to install...');
execSync('python -m pip install -U --upgrade-strategy only-if-needed aider-chat', { stdio: 'inherit' });
console.log('Aider installed successfully. Retrying...');
this.aiderProcess = spawnAiderProcess();
}

if (!this.aiderProcess.pid) {
throw new Error('Failed to start Aider after installation');
}
} catch (error) {
console.error('Failed to start or install Aider:', error);
reject(error);
return;
}

console.log('Spawned Aider process with PATH:', userPath);

if (this.aiderProcess.stdout && this.aiderProcess.stderr) {
this.aiderProcess.stdout.on('data', (data: Buffer) => {
Expand Down

0 comments on commit f6b3445

Please sign in to comment.