Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add diff context to any question
Browse files Browse the repository at this point in the history
kgilpin committed Jan 22, 2025
1 parent 7b54baa commit 0d16426
Showing 9 changed files with 84 additions and 15 deletions.
18 changes: 17 additions & 1 deletion packages/cli/src/cmds/navie/projectInfo.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ProjectInfo } from '@appland/navie';
import { collectStats } from '../../rpc/appmap/stats';
import configuration from '../../rpc/configuration';
import { getDiffLog, getWorkingDiff } from '../../lib/git';

export default async function collectProjectInfos(
codeEditor?: string
codeEditor: string | undefined,
params: ProjectInfo.ProjectInfoRequest = { type: 'projectInfo' }
): Promise<ProjectInfo.ProjectInfo[]> {
const projectInfoByPath = new Map<string, ProjectInfo.ProjectInfo>();

@@ -38,6 +40,20 @@ export default async function collectProjectInfos(
};
});

if (params.includeDiff) {
const { baseBranch } = params;

for (const [directory, info] of projectInfoByPath) {
const diffContent = (
await Promise.all([getWorkingDiff(directory), getDiffLog(undefined, baseBranch, directory)])
)
.filter(Boolean)
.join('\n\n');

info.diff = diffContent;
}
}

const { projectDirectories } = configuration();
if (codeEditor) {
projectDirectories.forEach((dir) => {
6 changes: 4 additions & 2 deletions packages/cli/src/rpc/explain/explain.ts
Original file line number Diff line number Diff line change
@@ -183,8 +183,10 @@ export class Explain extends EventEmitter {
return searchResult.context;
}

async projectInfoContext(): Promise<ProjectInfo.ProjectInfoResponse> {
return await collectProjectInfos(this.codeEditor);
async projectInfoContext(
data: ProjectInfo.ProjectInfoRequest
): Promise<ProjectInfo.ProjectInfoResponse> {
return await collectProjectInfos(this.codeEditor, data);
}

helpContext(data: Help.HelpRequest): Promise<Help.HelpResponse> {
1 change: 1 addition & 0 deletions packages/cli/src/rpc/explain/review.ts
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ export default async function handleReview(

const result = parseOptions(question);
const base = result.options.stringValue('base');
// Why not diff all of them?
const cwd = result.options.stringValue('project', configuration().projectDirectories[0]);
const diffContent = await Promise.all([getWorkingDiff(cwd), getDiffLog(undefined, base, cwd)]);
return {
3 changes: 2 additions & 1 deletion packages/navie/src/agent.ts
Original file line number Diff line number Diff line change
@@ -22,7 +22,8 @@ export class AgentOptions {
public chatHistory: Message[],
public projectInfo: ProjectInfo[],
public codeSelection?: UserContext.Context,
public contextLabels?: ContextV2.ContextLabel[]
public contextLabels?: ContextV2.ContextLabel[],
public diff?: string
) {}

get hasAppMaps() {
23 changes: 16 additions & 7 deletions packages/navie/src/commands/explain-command.ts
Original file line number Diff line number Diff line change
@@ -58,8 +58,14 @@ export default class ExplainCommand implements Command {
});

let projectInfo: ProjectInfo[] = [];
if (request.userOptions.isEnabled('projectinfo', true)) {
const projectInfoResponse = await this.projectInfoService.lookupProjectInfo();
if (isProjectInfoEnabled(request.userOptions)) {
const baseBranch = request.userOptions.stringValue('base');
const diffEnabled = !!(baseBranch || request.userOptions.isEnabled('diff', false));

const projectInfoResponse = await this.projectInfoService.lookupProjectInfo(
diffEnabled,
baseBranch
);
projectInfo = Array.isArray(projectInfoResponse)
? projectInfoResponse
: [projectInfoResponse];
@@ -124,9 +130,7 @@ export default class ExplainCommand implements Command {
label.name === ContextV2.ContextLabelName.Overview
);

if (request.userOptions.isEnabled('projectinfo', true)) {
this.projectInfoService.promptProjectInfo(isArchitecture, projectInfo);
}
if (projectInfo) this.projectInfoService.promptProjectInfo(isArchitecture, projectInfo);

const agentResponse = await mode.perform(agentOptions, tokensAvailable);
if (agentResponse) {
@@ -158,7 +162,7 @@ export default class ExplainCommand implements Command {
if (codeSelection) this.codeSelectionService.applyCodeSelection(codeSelection);
mode.applyQuestionPrompt(question);

if (gathererEnabled(request.userOptions, agentMode, contextLabels)) {
if (isGathererEnabled(request.userOptions, agentMode, contextLabels)) {
yield* this.gatherAdditionalInformation();
}

@@ -194,7 +198,12 @@ export default class ExplainCommand implements Command {
}
}

function gathererEnabled(
function isProjectInfoEnabled(userOptions: UserOptions): boolean {
const isDiffRequested = userOptions.isEnabled('diff', false);
return isDiffRequested || userOptions.isEnabled('projectinfo', true);
}

function isGathererEnabled(
userOptions: UserOptions,
agentMode: AgentMode,
contextLabels: ContextV2.ContextLabel[]
7 changes: 6 additions & 1 deletion packages/navie/src/project-info.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export type ProjectInfoRequest = { [key: string]: unknown };
export type ProjectInfoRequest = {
type: 'projectInfo';
includeDiff?: boolean;
baseBranch?: string;
};

export type AppMapConfig = {
name: string;
@@ -23,6 +27,7 @@ export type ProjectInfo = {
appmapConfig?: AppMapConfig;
appmapStats?: AppMapStats;
codeEditor?: CodeEditorInfo;
diff?: string;
};

export type ProjectInfoResponse = ProjectInfo | Array<ProjectInfo>;
8 changes: 8 additions & 0 deletions packages/navie/src/prompt.ts
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ export enum PromptType {
HelpDoc = 'helpDoc',
CodeEditor = 'codeEditor',
ConversationSummary = 'conversationSummary',
Diff = 'Diff',
}

const PROMPT_NAMES: Record<PromptType, { singular: string; plural: string }> = {
@@ -31,6 +32,7 @@ const PROMPT_NAMES: Record<PromptType, { singular: string; plural: string }> = {
singular: 'conversation summary',
plural: 'conversation summary',
},
[PromptType.Diff]: { singular: 'diff', plural: 'diffs' },
};

export type Prompt = {
@@ -197,6 +199,12 @@ You're provided with a summary of the entire conversation history.`,
tagName: 'conversation-summary',
multiple: false,
},
[PromptType.Diff]: {
content: `**Diff**
You're provided with a diff of the changes that were made to the codebase.`,
tagName: 'diff',
},
};

export function buildPromptDescriptor(promptType: PromptType): string {
3 changes: 3 additions & 0 deletions packages/navie/src/services/context-service.ts
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import VectorTermsService from './vector-terms-service';
import { ContextV2 } from '../context';
import InteractionHistory, { ContextItemEvent, ContextItemRequestor } from '../interaction-history';
import ApplyContextService, { eventOfContextItem } from './apply-context-service';
import { ProjectInfo } from '../project-info';

export default class ContextService {
constructor(
@@ -40,6 +41,8 @@ export default class ContextService {
aggregateQuestion.push(event.content);
}

if (options.diff) aggregateQuestion.push(options.diff);

const searchTerms = await transformSearchTerms(
termsEnabled,
options.aggregateQuestion,
30 changes: 27 additions & 3 deletions packages/navie/src/services/project-info-service.ts
Original file line number Diff line number Diff line change
@@ -7,13 +7,14 @@ import {
CodeEditorInfo,
ProjectInfo,
ProjectInfoProvider,
ProjectInfoRequest,
} from '../project-info';
import InteractionHistory, {
ContextItemEvent,
ContextItemRequestor,
PromptInteractionEvent,
} from '../interaction-history';
import { PromptType, buildPromptDescriptor } from '../prompt';
import { PromptType, buildPromptDescriptor, buildPromptValue } from '../prompt';

type Test = () => boolean;

@@ -29,8 +30,16 @@ export default class ProjectInfoService {
public projectInfoProvider: ProjectInfoProvider
) {}

async lookupProjectInfo(): Promise<ProjectInfo[]> {
const response = await this.projectInfoProvider({ type: 'projectInfo' });
async lookupProjectInfo(diffEnabled?: boolean, baseBranch?: string): Promise<ProjectInfo[]> {
const projectInfoRequest: ProjectInfoRequest = {
type: 'projectInfo',
};
if (diffEnabled) {
projectInfoRequest.includeDiff = true;
if (baseBranch) projectInfoRequest.baseBranch = baseBranch;
}

const response = await this.projectInfoProvider(projectInfoRequest);
if (!response) {
this.interactionHistory.log('No project info found');
return [];
@@ -65,6 +74,7 @@ export default class ProjectInfoService {
const codeEditors = projectInfo
.map((info) => info.codeEditor)
.filter(Boolean) as CodeEditorInfo[];
const diffs = projectInfo.map((info) => info.diff).filter(Boolean);

this.interactionHistory.addEvent(
new PromptInteractionEvent(
@@ -91,6 +101,20 @@ export default class ProjectInfoService {
);
}

if (diffs.length > 0) {
this.interactionHistory.addEvent(
new PromptInteractionEvent(
PromptType.Diff,
'system',
buildPromptDescriptor(PromptType.Diff)
)
);

this.interactionHistory.addEvent(
new ContextItemEvent(PromptType.Diff, ContextItemRequestor.ProjectInfo, diffs.join('\n\n'))
);
}

this.interactionHistory.addEvent(
new PromptInteractionEvent(
PromptType.AppMapStats,

0 comments on commit 0d16426

Please sign in to comment.