Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add chat interface #844

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
}
],
"commands": [
{
"command": "appmap.openAiHelp",
"title": "AppMap AI: Help"
},
{
"command": "appmap.getAppmapState",
"title": "AppMap: Copy Current AppMap State to Clipboard"
Expand Down Expand Up @@ -220,7 +224,12 @@
"appMap.applandUrl": {
"type": "string",
"default": "https://getappmap.com",
"description": "URL of an AppLand cloud instance for AppMap storage"
"description": "URL of AppMap"
},
"appMap.apiUrl": {
"type": "string",
"default": "https://api.getappmap.com",
"description": "URL of the AppMap API"
},
"appMap.viewConfiguration": {
"type": "string",
Expand Down Expand Up @@ -493,7 +502,7 @@
"dependencies": {
"@appland/appmap": "^3.117.0",
"@appland/client": "^1.14.1",
"@appland/components": "^3.25.0",
"@appland/components": "^3.27.0",
"@appland/diagrams": "^1.8.0",
"@appland/models": "^2.10.0",
"@appland/scanner": "^1.86.0",
Expand Down
5 changes: 5 additions & 0 deletions src/configuration/extensionSettings.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DefaultApiURL } from '@appland/client';
import * as vscode from 'vscode';

export default class ExtensionSettings {
Expand Down Expand Up @@ -27,4 +28,8 @@ export default class ExtensionSettings {
public static get appMapCommandLineToolsPath(): string | undefined {
return vscode.workspace.getConfiguration('appMap').get('commandLineToolsPath');
}

public static get apiUrl(): string {
return vscode.workspace.getConfiguration('appMap').get('apiUrl') || DefaultApiURL;
}
}
3 changes: 3 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,13 @@ import Watcher from './services/watcher';
import ChatSearchWebview from './webviews/chatSearchWebview';
import quickSearch from './commands/quickSearch';
import appmapState from './commands/appmapState';
import ChatHelpWebview from './webviews/chatHelpWebview';

export async function activate(context: vscode.ExtensionContext): Promise<AppMapService> {
Telemetry.register(context);

ChatHelpWebview.register(context);

const workspaceServices = initializeWorkspaceServices();
context.subscriptions.push(workspaceServices);

Expand Down
2 changes: 2 additions & 0 deletions src/services/processWatcher.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import { ChildProcess, OutputStream, spawn, SpawnOptions } from './nodeDependencyProcess';
import { getApiKey } from '../authentication';
import ExtensionSettings from '../configuration/extensionSettings';

export type RetryOptions = {
// The number of retries made before declaring the process as failed.
Expand Down Expand Up @@ -198,6 +199,7 @@ export class ProcessWatcher implements vscode.Disposable {
if (accessToken) {
env.APPMAP_API_KEY = accessToken;
}
env.APPMAP_API_URL = ExtensionSettings.apiUrl;
return env;
}

Expand Down
62 changes: 62 additions & 0 deletions src/webviews/chatHelpWebview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as vscode from 'vscode';
import getWebviewContent from './getWebviewContent';
import { getApiKey } from '../authentication';
import ExtensionSettings from '../configuration/extensionSettings';

export default class AiHelpWebview {
public static viewType = 'appmap.openAiHelp';
private static existingPanel?: vscode.WebviewPanel;

public static register(context: vscode.ExtensionContext): void {
context.subscriptions.push(
vscode.commands.registerCommand('appmap.openAiHelp', async () => {
// Attempt to re-use an existing webview for this project if one exists
if (this.existingPanel) {
this.existingPanel.reveal(vscode.ViewColumn.One);
return;
}

const panel = vscode.window.createWebviewPanel(
this.viewType,
'Ask AppMap AI',
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true,
}
);

this.existingPanel = panel;
panel.webview.html = getWebviewContent(
panel.webview,
context,
'Ask AppMap AI',
'chat-help',
{ htmlStyle: 'height: 100%; width: 100%;' }
);

panel.onDidDispose(() => {
this.existingPanel = undefined;
});

panel.webview.onDidReceiveMessage(async (message) => {
switch (message.command) {
case 'ready': {
console.log('got ready message');
panel.webview.postMessage({
type: 'init',
apiUrl: ExtensionSettings.apiUrl,
apiKey: await getApiKey(false),
});
break;
}

default: {
// nop
}
}
});
})
);
}
}
4 changes: 4 additions & 0 deletions src/webviews/chatSearchWebview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { ProcessId } from '../services/processWatcher';
import appmapMessageHandler from './appmapMessageHandler';
import FilterStore, { SavedFilter } from './filterStore';
import WebviewList from './WebviewList';
import { getApiKey } from '../authentication';
import ExtensionSettings from '../configuration/extensionSettings';

export default class ChatSearchWebview {
private webviewList = new WebviewList();
Expand Down Expand Up @@ -104,6 +106,8 @@ export default class ChatSearchWebview {
appmapRpcPort,
question,
savedFilters: this.filterStore.getSavedFilters(),
apiUrl: ExtensionSettings.apiUrl,
apiKey: await getApiKey(false),
});
break;
}
Expand Down
7 changes: 6 additions & 1 deletion src/webviews/getWebviewContent.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import path from 'path';
import * as vscode from 'vscode';
import ExtensionSettings from '../configuration/extensionSettings';

type AppmapModule =
| 'app'
| 'install-guide'
| 'chat-search'
| 'chat-help'
| 'findings-view'
| 'finding-info-view'
| 'sign-in-view';
Expand All @@ -24,14 +26,17 @@ export default function getWebviewContent(
vscode.Uri.file(path.join(context.extensionPath, 'out', 'app.css'))
);

const { apiUrl } = ExtensionSettings;
const wsUrl = apiUrl.replace(/^http/, 'ws');

return ` <!DOCTYPE html>
<html style="${htmlStyle ?? ''}" lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="
default-src 'none';
connect-src ${rpcPort ? `http://localhost:${rpcPort}` : "'none'"};
connect-src ${rpcPort ? `http://localhost:${rpcPort}` : ''} ${apiUrl} ${wsUrl};
img-src ${webview.cspSource} data:;
script-src ${webview.cspSource} 'unsafe-eval';
style-src ${webview.cspSource} 'unsafe-inline';
Expand Down
4 changes: 4 additions & 0 deletions src/webviews/installGuideWebview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ export default class InstallGuideWebView {
vscode.commands.executeCommand('appmap.openFindingsOverview');
break;

case 'ai-help':
vscode.commands.executeCommand('appmap.openAiHelp');
break;

default:
break;
}
Expand Down
2 changes: 2 additions & 0 deletions web/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import mountFindingsView from './findingsView';
import mountFindingInfoView from './findingsInfo';
import mountSignInView from './signInView';
import mountChatSearch from './chatSearchView';
import mountChatHelp from './chatHelp';

Vue.use(plugin);

Expand All @@ -16,6 +17,7 @@ const modules = {
'finding-info-view': mountFindingInfoView,
'sign-in-view': mountSignInView,
'chat-search': mountChatSearch,
'chat-help': mountChatHelp,
};

const { body } = document;
Expand Down
61 changes: 61 additions & 0 deletions web/src/chatHelp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import Vue from 'vue';
import { VChat } from '@appland/components';
import MessagePublisher from './messagePublisher';

export default function mountChatHelp() {
document.body.style = 'height: 100%; margin: 0; overflow-y: scroll;';

const vscode = window.acquireVsCodeApi();
const messages = new MessagePublisher(vscode);

messages.on(
'init',
({ apiKey }) =>
new Vue({
el: '#app',
render(h) {
return h(VChat, {
ref: 'chat',
props: {
apiKey,
apiUrl: 'https://api.getappmap.com', // TODO: this should not be needed
suggestions: [
{
title: 'How to Install AppMap',
subTitle: 'Step-by-Step Installation Guide',
prompt:
'Ask me how to install AppMap in your development environment for a detailed guide.',
},
{
title: 'Setting Up AppMap',
subTitle: 'Configuring for the First Time',
prompt: 'Need help configuring AppMap after installation? Just ask me!',
},
{
title: 'Integrating AppMap with Your Projects',
subTitle: 'Effective Integration Tips',
prompt:
'Inquire about integrating AppMap into your existing projects for successful implementation.',
},
{
title: 'Solving AppMap Installation Issues',
subTitle: 'Overcoming Common Challenges',
prompt:
'Encountering installation issues with AppMap? Ask me for solutions to common problems.',
},
{
title: 'Enhancing AppMap Performance',
subTitle: 'Tips for Large-Scale Projects',
prompt:
'Want to optimize AppMap for large projects? I can provide tips on enhancing its performance.',
},
],
},
});
},
})
);

vscode.postMessage({ command: 'ready' });
console.log('emitting ready');
}
2 changes: 2 additions & 0 deletions web/src/chatSearchView.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export default function mountChatSearchView() {
appmapRpcPort: initialData.appmapRpcPort,
question: initialData.question,
savedFilters: initialData.savedFilters,
apiUrl: initialData.apiUrl,
apiKey: initialData.apiKey,
},
});
},
Expand Down
5 changes: 5 additions & 0 deletions web/src/installGuideView.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default function mountInstallGuide() {
userAuthenticated: this.userAuthenticated,
javaAgentStatus: this.javaAgentStatus,
featureFlags: new Set(['ar-python']),
displayAiHelp: true,
},
});
},
Expand Down Expand Up @@ -101,6 +102,10 @@ export default function mountInstallGuide() {
vscode.postMessage({ command: 'view-output' });
});

app.$on('ai-help', () => {
vscode.postMessage({ command: 'ai-help' });
});

messages.on('page', ({ page }) => {
app.$refs.ui.jumpTo(page);
});
Expand Down
Loading
Loading