Skip to content

Commit 5886a84

Browse files
nang-devnang-dev2Himanshu-Singh-Chauhan
authored
Aider part 2 (#32)
* Added command retries * barechatfilecontext * Added Reset Session * Added progress * Added working * Added cleanup --------- Co-authored-by: nang-dev <[email protected]> Co-authored-by: Himanshu <[email protected]>
1 parent ba37be1 commit 5886a84

File tree

12 files changed

+265
-149
lines changed

12 files changed

+265
-149
lines changed

core/config/default.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const FREE_TRIAL_MODELS: ModelDescription[] = [
3434
];
3535

3636
export const defaultContextProvidersVsCode: ContextProviderWithParams[] = [
37+
{ name: "file", params: {} },
3738
{ name: "directory", params: {} },
3839
{ name: "code", params: {} },
3940
{ name: "docs", params: {} },
@@ -42,6 +43,7 @@ export const defaultContextProvidersVsCode: ContextProviderWithParams[] = [
4243
{ name: "problems", params: {} },
4344
{ name: "folder", params: {} },
4445
{ name: "codebase", params: {} },
46+
{ name: "relativefilecontext", params: {} },
4547
];
4648

4749
export const defaultContextProvidersJetBrains: ContextProviderWithParams[] = [

core/config/load.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,9 +372,9 @@ async function intermediateToFinalConfig(
372372

373373
// These context providers are always included, regardless of what, if anything,
374374
// the user has configured in config.json
375-
const DEFAULT_CONTEXT_PROVIDERS = [
376-
new FileContextProvider({}),
377-
new CodebaseContextProvider({}),
375+
const DEFAULT_CONTEXT_PROVIDERS : any[] = [
376+
// new FileContextProvider({}),
377+
// new CodebaseContextProvider({}),
378378
];
379379

380380
const DEFAULT_CONTEXT_PROVIDERS_TITLES = DEFAULT_CONTEXT_PROVIDERS.map(

core/config/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ declare global {
453453
| "DraftIssueStep";
454454
455455
type ContextProviderName =
456+
| "file"
456457
| "diff"
457458
| "github"
458459
| "terminal"
@@ -471,7 +472,8 @@ declare global {
471472
| "code"
472473
| "docs"
473474
| "gitlab-mr"
474-
| "os";
475+
| "os"
476+
| "relativefilecontext";
475477
476478
type TemplateType =
477479
| "llama2"
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import {
2+
ContextItem,
3+
ContextProviderDescription,
4+
ContextProviderExtras,
5+
ContextSubmenuItem,
6+
LoadSubmenuItemsArgs,
7+
} from "../../index.js";
8+
import { walkDir } from "../../indexing/walkDir.js";
9+
import {
10+
getBasename,
11+
getUniqueFilePath,
12+
groupByLastNPathParts,
13+
} from "../../util/index.js";
14+
import { BaseContextProvider } from "../index.js";
15+
16+
const MAX_SUBMENU_ITEMS = 10_000;
17+
18+
class RelativeFileContextProvider extends BaseContextProvider {
19+
static description: ContextProviderDescription = {
20+
title: "relativefilecontext",
21+
displayTitle: "Files",
22+
description: "Add file to context.",
23+
type: "submenu",
24+
};
25+
26+
async getContextItems(
27+
query: string,
28+
extras: ContextProviderExtras,
29+
): Promise<ContextItem[]> {
30+
const workspaceDirs = await extras.ide.getWorkspaceDirs();
31+
const relativePath = this.normalizeRelativePath(query, workspaceDirs[0]);
32+
return [
33+
{
34+
name: getBasename(query),
35+
description: relativePath,
36+
content: relativePath,
37+
},
38+
];
39+
}
40+
41+
async loadSubmenuItems(
42+
args: LoadSubmenuItemsArgs,
43+
): Promise<ContextSubmenuItem[]> {
44+
const workspaceDirs = await args.ide.getWorkspaceDirs();
45+
const results = await Promise.all(
46+
workspaceDirs.map((dir) => {
47+
return walkDir(dir, args.ide);
48+
}),
49+
);
50+
const files = results.flat().slice(-MAX_SUBMENU_ITEMS);
51+
const fileGroups = groupByLastNPathParts(files, 2);
52+
53+
return files.map((file) => {
54+
const relativePath = this.normalizeRelativePath(file, workspaceDirs[0]);
55+
return {
56+
id: file,
57+
title: getBasename(file),
58+
description: relativePath,
59+
};
60+
});
61+
}
62+
63+
private normalizeRelativePath(path: string, workspaceDir: string): string {
64+
const relativePath = path.replace(workspaceDir, "").replace(/^[\/\\]/, "");
65+
return relativePath.replace(/\\/g, "/");
66+
}
67+
}
68+
69+
export default RelativeFileContextProvider;

core/context/providers/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import ProblemsContextProvider from "./ProblemsContextProvider.js";
2222
import SearchContextProvider from "./SearchContextProvider.js";
2323
import TerminalContextProvider from "./TerminalContextProvider.js";
2424
import URLContextProvider from "./URLContextProvider.js";
25+
import RelativeFileContextProvider from "./RelativeFileContextProvider.js";
26+
import FileContextProvider from "./FileContextProvider.js";
27+
2528

2629
/**
2730
* Note: We are currently omitting the following providers due to bugs:
@@ -31,6 +34,7 @@ import URLContextProvider from "./URLContextProvider.js";
3134
* See this issue for details: https://github.com/continuedev/continue/issues/1365
3235
*/
3336
const Providers: (typeof BaseContextProvider)[] = [
37+
FileContextProvider,
3438
DiffContextProvider,
3539
FileTreeContextProvider,
3640
GitHubIssuesContextProvider,
@@ -52,6 +56,7 @@ const Providers: (typeof BaseContextProvider)[] = [
5256
CurrentFileContextProvider,
5357
URLContextProvider,
5458
ContinueProxyContextProvider,
59+
RelativeFileContextProvider
5560
];
5661

5762
export function contextProviderClassFromName(

core/index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ type StepName =
550550
| "DraftIssueStep";
551551

552552
type ContextProviderName =
553+
| "file"
553554
| "diff"
554555
| "github"
555556
| "terminal"
@@ -569,7 +570,8 @@ type ContextProviderName =
569570
| "docs"
570571
| "gitlab-mr"
571572
| "os"
572-
| "currentFile";
573+
| "currentFile"
574+
| "relativefilecontext";
573575

574576
type TemplateType =
575577
| "llama2"

core/llm/llms/Aider.ts

Lines changed: 68 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -158,56 +158,74 @@ public async aiderResetSession(model: string, apiKey: string | undefined): Promi
158158

159159
let command: string[];
160160

161-
switch (model) {
162-
case "claude-3-5-sonnet-20240620":
163-
console.log("claude model chosen");
164-
command = ["aider --model claude-3-5-sonnet-20240620"];
165-
break;
166-
case "gpt-4o":
167-
command = ["aider --model gpt-4o"];
168-
break;
169-
case "pearai_model":
170-
default:
171-
await this.credentials.checkAndUpdateCredentials();
172-
const accessToken = this.credentials.getAccessToken();
173-
if (!accessToken) {
174-
let message = "PearAI token invalid. Please try logging in or contact PearAI support."
175-
vscode.window
176-
.showErrorMessage(
177-
message,
178-
'Login To PearAI',
179-
'Show Logs',
180-
)
181-
.then((selection: any) => {
182-
if (selection === 'Login To PearAI') {
183-
// Redirect to auth login URL
184-
vscode.env.openExternal(
185-
vscode.Uri.parse(
186-
'https://trypear.ai/signin?callback=pearai://pearai.pearai/auth',
187-
),
188-
);
189-
} else if (selection === 'Show Logs') {
190-
vscode.commands.executeCommand(
191-
'workbench.action.toggleDevTools',
192-
);
161+
const aiderFlags = "--no-pretty --yes-always --no-auto-commits";
162+
const aiderCommands = [
163+
`python -m aider ${aiderFlags}`,
164+
`python3 -m aider ${aiderFlags}`,
165+
`aider ${aiderFlags}`
166+
];
167+
let commandFound = false;
168+
169+
for (const aiderCommand of aiderCommands) {
170+
try {
171+
await execSync(`${aiderCommand} --version`, { stdio: 'ignore' });
172+
commandFound = true;
173+
174+
switch (model) {
175+
case model.includes("claude") && model:
176+
command = [`${aiderCommand} --model ${model}`];
177+
break;
178+
case "gpt-4o":
179+
command = [`${aiderCommand} --model gpt-4o`];
180+
break;
181+
case "pearai_model":
182+
default:
183+
await this.credentials.checkAndUpdateCredentials();
184+
const accessToken = this.credentials.getAccessToken();
185+
if (!accessToken) {
186+
let message = "PearAI token invalid. Please try logging in or contact PearAI support."
187+
vscode.window
188+
.showErrorMessage(
189+
message,
190+
'Login To PearAI',
191+
'Show Logs',
192+
)
193+
.then((selection: any) => {
194+
if (selection === 'Login To PearAI') {
195+
// Redirect to auth login URL
196+
vscode.env.openExternal(
197+
vscode.Uri.parse(
198+
'https://trypear.ai/signin?callback=pearai://pearai.pearai/auth',
199+
),
200+
);
201+
} else if (selection === 'Show Logs') {
202+
vscode.commands.executeCommand(
203+
'workbench.action.toggleDevTools',
204+
);
205+
}
206+
});
207+
throw new Error("User not logged in to PearAI.");
193208
}
194-
});
195-
throw new Error("User not logged in to PearAI.");
209+
command = [
210+
aiderCommand,
211+
"--openai-api-key",
212+
accessToken,
213+
"--openai-api-base",
214+
`${SERVER_URL}/integrations/aider`,
215+
];
216+
break;
196217
}
197-
command = [
198-
"aider",
199-
"--openai-api-key",
200-
accessToken,
201-
"--openai-api-base",
202-
`${SERVER_URL}/integrations/aider`,
203-
];
204-
break;
218+
break; // Exit the loop if a working command is found
219+
} catch (error) {
220+
console.log(`Command ${aiderCommand} not found or errored. Trying next...`);
221+
}
222+
}
223+
224+
if (!commandFound) {
225+
throw new Error("Aider command not found. Please ensure it's installed correctly.");
205226
}
206227

207-
// disable pretty printing
208-
command.push("--no-pretty");
209-
command.push("--yes-always");
210-
command.push("--no-auto-commits");
228+
211229

212230
const userPath = this.getUserPath();
213231
const userShell = this.getUserShell();
@@ -254,6 +272,8 @@ public async aiderResetSession(model: string, apiKey: string | undefined): Promi
254272
env: {
255273
...process.env,
256274
PATH: userPath,
275+
PYTHONIOENCODING: "utf-8",
276+
AIDER_SIMPLE_OUTPUT: "1",
257277
},
258278
windowsHide: true,
259279
});
@@ -343,7 +363,8 @@ public async aiderResetSession(model: string, apiKey: string | undefined): Promi
343363
this.aiderProcess.stdin &&
344364
!this.aiderProcess.killed
345365
) {
346-
this.aiderProcess.stdin.write(`${message}\n`);
366+
const formattedMessage = message.replace(/\n+/g, " ");
367+
this.aiderProcess.stdin.write(`${formattedMessage}\n`);
347368
} else {
348369
console.error("Aider process is not running");
349370
}
@@ -404,7 +425,6 @@ public async aiderResetSession(model: string, apiKey: string | undefined): Promi
404425
options: CompletionOptions,
405426
): AsyncGenerator<ChatMessage> {
406427
console.log("Inside Aider _streamChat");
407-
408428
const lastMessage = messages[messages.length - 1].content.toString();
409429
this.sendToAiderChat(lastMessage);
410430

gui/src/components/mainInput/ContinueInputBox.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import ContextItemsPeek from "./ContextItemsPeek";
1111
import TipTapEditor from "./TipTapEditor";
1212
import { useMemo } from "react";
1313
import { defaultModelSelector } from "../../redux/selectors/modelSelectors";
14-
import { isBareChatMode } from '../../util/bareChatMode';
14+
import { isBareChatMode } from "../../util/bareChatMode";
1515

1616
const gradient = keyframes`
1717
0% {
@@ -67,11 +67,19 @@ function ContinueInputBox(props: ContinueInputBoxProps) {
6767

6868
const active = useSelector((store: RootState) => store.state.active);
6969
const availableSlashCommands = useSelector(selectSlashCommands);
70-
const availableContextProviders = useSelector(
70+
let availableContextProviders = useSelector(
7171
(store: RootState) => store.state.config.contextProviders,
7272
);
73-
const bareChatMode = isBareChatMode();
74-
73+
const bareChatMode = isBareChatMode()
74+
const filteredContextProviders = useMemo(() => {
75+
return bareChatMode
76+
? availableContextProviders.filter(
77+
(provider) => provider.title === "relativefilecontext",
78+
)
79+
: availableContextProviders.filter(
80+
(provider) => provider.title !== "relativefilecontext",
81+
);
82+
}, [bareChatMode, availableContextProviders]);
7583

7684
useWebviewListener(
7785
"newSessionWithPrompt",
@@ -108,8 +116,10 @@ function ContinueInputBox(props: ContinueInputBoxProps) {
108116
editorState={props.editorState}
109117
onEnter={props.onEnter}
110118
isMainInput={props.isMainInput}
111-
availableContextProviders={bareChatMode ? undefined : availableContextProviders}
112-
availableSlashCommands={bareChatMode ? undefined : availableSlashCommands}
119+
availableContextProviders={filteredContextProviders}
120+
availableSlashCommands={
121+
bareChatMode ? undefined : availableSlashCommands
122+
}
113123
></TipTapEditor>
114124
</GradientBorder>
115125
<ContextItemsPeek contextItems={props.contextItems}></ContextItemsPeek>

gui/src/components/mainInput/TipTapEditor.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,7 @@ function TipTapEditor(props: TipTapEditorProps) {
211211
const defaultModel = useSelector(defaultModelSelector);
212212
const bareChatMode = isBareChatMode();
213213
const getSubmenuContextItemsRef = useUpdatingRef(getSubmenuContextItems);
214-
const availableContextProvidersRef = useUpdatingRef(
215-
props.availableContextProviders,
216-
);
214+
const availableContextProvidersRef = useUpdatingRef(props.availableContextProviders)
217215

218216
const historyLengthRef = useUpdatingRef(historyLength);
219217
const availableSlashCommandsRef = useUpdatingRef(

0 commit comments

Comments
 (0)