Skip to content

Commit

Permalink
fix: Propagate code selection location to Navie
Browse files Browse the repository at this point in the history
Fixes #2188
  • Loading branch information
kgilpin committed Jan 21, 2025
1 parent b83566e commit 6380cab
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 42 deletions.
47 changes: 31 additions & 16 deletions packages/components/src/components/chat/Chat.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,14 @@

<script lang="ts">
//@ts-nocheck
import Vue, { PropType } from 'vue';
import Vue from 'vue';
import VUserMessage from '@/components/chat/UserMessage.vue';
import VChatInput from '@/components/chat/ChatInput.vue';
import VAppMapNavieLogo from '@/assets/appmap-full-logo.svg';
import VButton from '@/components/Button.vue';
import { AI } from '@appland/client';
import { ExplainRpc, NavieRpc } from '@appland/rpc';
export type CodeSelection = {
path: string;
lineStart: number;
lineEnd: number;
code: string;
language: string;
};
import { CodeSelection } from './CodeSelection';
export interface ITool {
title: string;
Expand Down Expand Up @@ -237,8 +230,34 @@ export default {
const userMessage = this.addUserMessage(content);
if (codeSelection && !populatedCodeSelection) {
populatedCodeSelection = true;
// TODO: There's some mismatch here between what the UI shows & what's in the thread data.
userMessage.codeSelections = [codeSelection];
let parsedCodeSelections: CodeSelection[];
try {
// Code selections are stored natively as a CodeSelection object, but restored from
// serialization as a string.
parsedCodeSelections = JSON.parse(codeSelection);
} catch {
// Issue a warning
console.warn('Code selection is not valid JSON');
// If the codeSelection is not valid JSON, it's likely a string.
// This is a temporary fix to handle the string case.
parsedCodeSelections = [
{
content: codeSelection,
},
];
}
userMessage.codeSelections = parsedCodeSelections;
if (typeof codeSelection === 'string') {
const codeSelectionObj: CodeSelection = {
content: codeSelection,
};
userMessage.codeSelections = [codeSelectionObj];
} else {
userMessage.codeSelections = [codeSelection];
}
}
}
if (exchange.answer) {
Expand Down Expand Up @@ -310,11 +329,7 @@ export default {
const userMessage = this.addUserMessage(message);
userMessage.codeSelections = this.codeSelections;
this.sendMessage(
message,
this.codeSelections.map((s) => s.code),
this.appmaps
);
this.sendMessage(message, this.codeSelections, this.appmaps);
this.codeSelections = [];
},
Expand Down
7 changes: 7 additions & 0 deletions packages/components/src/components/chat/CodeSelection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type CodeSelection = {
path: string;
lineStart: number;
lineEnd: number;
code: string;
language: string;
};
71 changes: 46 additions & 25 deletions packages/components/src/pages/ChatSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ import VPopper from '@/components/Popper.vue';
import VContext from '@/components/chat-search/Context.vue';
import VLlmConfiguration from '@/components/chat-search/LlmConfiguration.vue';
import VPinnedItems from '@/components/chat-search/PinnedItems.vue';
import type { CodeSelection, ITool } from '@/components/chat/Chat.vue';
import type { ITool } from '@/components/chat/Chat.vue';
import type { CodeSelection } from '@/components/chat/CodeSelection';
import VChat from '@/components/chat/Chat.vue';
import { getNextHandle } from '@/components/chat/Handle';
import type { PinEvent, PinFile } from '@/components/chat/PinEvent';
Expand Down Expand Up @@ -469,7 +470,11 @@ export default {
// and emit a stop event.
this.ask?.stop();
},
async sendMessage(message: string, codeSelections: string[] = [], appmaps: string[] = []) {
async sendMessage(
message: string,
codeSelections: CodeSelection[] = [],
appmaps: string[] = []
) {
this.ask = this.rpcClient.explain();
this.searching = true;
this.lastStatusLabel = undefined;
Expand Down Expand Up @@ -562,35 +567,51 @@ export default {
const userProvidedContext: ExplainRpc.UserContextItem[] = [];
if (this.pinnedItems.length > 0) {
userProvidedContext.push(
...this.pinnedItems.map((p) => {
if (p.type === 'file') {
return {
type: 'file',
location: p.location,
};
} else {
return {
type: 'code-snippet',
location: p.location,
content: p.content,
};
}
})
);
const pinnedItemContextItems = this.pinnedItems.map((p) => {
if (p.type === 'file') {
return {
type: 'file',
location: p.location,
};
} else {
return {
type: 'code-snippet',
location: p.location,
content: p.content,
};
}
});
userProvidedContext.push(...pinnedItemContextItems);
}
if (codeSelections.length > 0) {
userProvidedContext.push(
...codeSelections.map((c) => ({
const codeSelectionContextItems = codeSelections.map((c) => {
const result: ExplainRpc.UserContextItem = {
type: 'code-selection',
content: c,
}))
);
}
if (userProvidedContext.length > 0) {
explainRequest.codeSelection = userProvidedContext;
};
if (c.path) {
const tokens = [c.path];
const range = [];
if (c.lineStart) {
range.push(c.lineStart);
if (c.lineEnd && c.lineEnd !== c.lineStart) {
range.push(c.lineEnd);
}
}
if (range) {
tokens.push(range.join('-'));
}
result.location = tokens.join(':');
}
return result;
});
userProvidedContext.push(...codeSelectionContextItems);
}
if (userProvidedContext.length > 0) explainRequest.codeSelection = userProvidedContext;
const threadId = this.$refs.vchat.threadId || this.threadId;
this.ask.explain(explainRequest, threadId).catch(onError);
});
Expand Down
13 changes: 12 additions & 1 deletion packages/components/tests/unit/chat/ChatComponent.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,18 @@ describe('components/Chat.vue', () => {

await wrapper.vm.$nextTick();

expect(sendMessage).toBeCalledWith('Hello from the user', [codeSelection.code], []);
expect(sendMessage).toBeCalledWith(
'Hello from the user',
[
{
code: '...',
lineEnd: 17,
lineStart: 6,
path: 'app/controllers/users_controller.rb',
},
],
[]
);
});

it('includes pending code snippets in the input area', async () => {
Expand Down

0 comments on commit 6380cab

Please sign in to comment.