diff --git a/package.json b/package.json index f7deec99..a8e0baee 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,10 @@ } ], "commands": [ + { + "command": "appmap.openAiHelp", + "title": "AppMap AI: Help" + }, { "command": "appmap.getAppmapState", "title": "AppMap: Copy Current AppMap State to Clipboard" @@ -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", @@ -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", diff --git a/src/configuration/extensionSettings.ts b/src/configuration/extensionSettings.ts index 88939f51..1bc38251 100644 --- a/src/configuration/extensionSettings.ts +++ b/src/configuration/extensionSettings.ts @@ -1,3 +1,4 @@ +import { DefaultApiURL } from '@appland/client'; import * as vscode from 'vscode'; export default class ExtensionSettings { @@ -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; + } } diff --git a/src/extension.ts b/src/extension.ts index a9f68758..46afc512 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -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 { Telemetry.register(context); + ChatHelpWebview.register(context); + const workspaceServices = initializeWorkspaceServices(); context.subscriptions.push(workspaceServices); diff --git a/src/services/processWatcher.ts b/src/services/processWatcher.ts index f752eb35..4ec4399d 100644 --- a/src/services/processWatcher.ts +++ b/src/services/processWatcher.ts @@ -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. @@ -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; } diff --git a/src/webviews/chatHelpWebview.ts b/src/webviews/chatHelpWebview.ts new file mode 100644 index 00000000..24af61bc --- /dev/null +++ b/src/webviews/chatHelpWebview.ts @@ -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 + } + } + }); + }) + ); + } +} diff --git a/src/webviews/chatSearchWebview.ts b/src/webviews/chatSearchWebview.ts index 490605a0..2e91b24a 100644 --- a/src/webviews/chatSearchWebview.ts +++ b/src/webviews/chatSearchWebview.ts @@ -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(); @@ -104,6 +106,8 @@ export default class ChatSearchWebview { appmapRpcPort, question, savedFilters: this.filterStore.getSavedFilters(), + apiUrl: ExtensionSettings.apiUrl, + apiKey: await getApiKey(false), }); break; } diff --git a/src/webviews/getWebviewContent.ts b/src/webviews/getWebviewContent.ts index b00d048e..fabd5e5e 100644 --- a/src/webviews/getWebviewContent.ts +++ b/src/webviews/getWebviewContent.ts @@ -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'; @@ -24,6 +26,9 @@ export default function getWebviewContent( vscode.Uri.file(path.join(context.extensionPath, 'out', 'app.css')) ); + const { apiUrl } = ExtensionSettings; + const wsUrl = apiUrl.replace(/^http/, 'ws'); + return ` @@ -31,7 +36,7 @@ export default function getWebviewContent( + 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'); +} diff --git a/web/src/chatSearchView.js b/web/src/chatSearchView.js index 3729be5a..2df65218 100644 --- a/web/src/chatSearchView.js +++ b/web/src/chatSearchView.js @@ -17,6 +17,8 @@ export default function mountChatSearchView() { appmapRpcPort: initialData.appmapRpcPort, question: initialData.question, savedFilters: initialData.savedFilters, + apiUrl: initialData.apiUrl, + apiKey: initialData.apiKey, }, }); }, diff --git a/web/src/installGuideView.js b/web/src/installGuideView.js index f6bf5a80..accb5d38 100644 --- a/web/src/installGuideView.js +++ b/web/src/installGuideView.js @@ -23,6 +23,7 @@ export default function mountInstallGuide() { userAuthenticated: this.userAuthenticated, javaAgentStatus: this.javaAgentStatus, featureFlags: new Set(['ar-python']), + displayAiHelp: true, }, }); }, @@ -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); }); diff --git a/yarn.lock b/yarn.lock index 7622800a..e0c67d90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -147,7 +147,7 @@ __metadata: languageName: node linkType: hard -"@appland/components@npm:^3.21.2, @appland/components@npm:^3.25.0": +"@appland/components@npm:^3.21.2": version: 3.25.0 resolution: "@appland/components@npm:3.25.0" dependencies: @@ -174,6 +174,33 @@ __metadata: languageName: node linkType: hard +"@appland/components@npm:^3.27.0": + version: 3.27.0 + resolution: "@appland/components@npm:3.27.0" + dependencies: + "@appland/client": ^1.12.0 + "@appland/diagrams": ^1.7.0 + "@appland/models": ^2.10.0 + "@appland/rpc": ^1.1.0 + "@appland/sequence-diagram": ^1.11.0 + buffer: ^6.0.3 + d3: ^7.8.4 + d3-flame-graph: ^4.1.3 + diff: ^5.1.0 + dom-to-svg: ^0.12.2 + dompurify: ^3.0.6 + events: ^3.3.0 + highlight.js: ^11.9.0 + jayson: ^4.1.0 + marked: ^10.0.0 + marked-highlight: ^2.0.7 + sql-formatter: ^4.0.2 + vue: ^2.7.14 + vuex: ^3.6.0 + checksum: 4173690f8c8189e33d4a4da82be89ac3f47e637dde39d57147f2e82fe590c07209b61bff6d6207e0a07048a321cca6bc4810ff6becc66155b16a34ee6754a801 + languageName: node + linkType: hard + "@appland/diagrams@npm:^1.7.0, @appland/diagrams@npm:^1.8.0": version: 1.8.0 resolution: "@appland/diagrams@npm:1.8.0" @@ -729,11 +756,11 @@ __metadata: linkType: hard "@langchain/community@npm:~0.0.3": - version: 0.0.9 - resolution: "@langchain/community@npm:0.0.9" + version: 0.0.15 + resolution: "@langchain/community@npm:0.0.15" dependencies: - "@langchain/core": ~0.1.3 - "@langchain/openai": ~0.0.7 + "@langchain/core": ~0.1.9 + "@langchain/openai": ~0.0.10 flat: ^5.0.2 langsmith: ~0.0.48 uuid: ^9.0.0 @@ -749,6 +776,7 @@ __metadata: "@aws-sdk/credential-provider-node": ^3.388.0 "@clickhouse/client": ^0.2.5 "@cloudflare/ai": ^1.0.12 + "@datastax/astra-db-ts": 0.1.2 "@elastic/elasticsearch": ^8.4.0 "@getmetal/metal-sdk": "*" "@getzep/zep-js": ^0.9.0 @@ -798,6 +826,7 @@ __metadata: jsdom: "*" llmonitor: ^0.5.9 lodash: ^4.17.21 + lunary: ^0.6.11 mongodb: ^5.2.0 mysql2: ^3.3.3 neo4j-driver: "*" @@ -837,6 +866,8 @@ __metadata: optional: true "@cloudflare/ai": optional: true + "@datastax/astra-db-ts": + optional: true "@elastic/elasticsearch": optional: true "@getmetal/metal-sdk": @@ -935,6 +966,8 @@ __metadata: optional: true lodash: optional: true + lunary: + optional: true mongodb: optional: true mysql2: @@ -971,13 +1004,13 @@ __metadata: optional: true ws: optional: true - checksum: 0b3cb1ab6589e80fcb5588fd0d27fe4fdf7f6b8c016135b6bf3642f4579985dad787bbd964ee10ba198d2fc44f20b19a0523491e8427dd9b599d0777a971129b + checksum: fac8b668e5a8883051b3c1b328b069e23626c6a1d5000b6caf0fc740e8da6f5dba4a59d2d8b5da49c26277a310f302e16a845e56542b7537ba526984b4246077 languageName: node linkType: hard -"@langchain/core@npm:~0.1.0, @langchain/core@npm:~0.1.5": - version: 0.1.7 - resolution: "@langchain/core@npm:0.1.7" +"@langchain/core@npm:~0.1.0, @langchain/core@npm:~0.1.9": + version: 0.1.11 + resolution: "@langchain/core@npm:0.1.11" dependencies: ansi-styles: ^5.0.0 camelcase: 6 @@ -989,13 +1022,13 @@ __metadata: p-retry: 4 uuid: ^9.0.0 zod: ^3.22.3 - checksum: 395c00b323bff662eacf3f7cedbad1f70a0409c748e035d6af5ce026f3422c67188e6ba07db80146156b683020a16f024f70915a407679ea8aeace0f1dcc00ab + checksum: 8157968b0ba8c75acf70471a04dcf9be650874c34dd9d61bbb6c6b0cbef17a72bb098d21559c9875c637051bb00afa9ac5c36a9602d7534ebb94d71aee3c89a9 languageName: node linkType: hard -"@langchain/core@npm:~0.1.3": - version: 0.1.4 - resolution: "@langchain/core@npm:0.1.4" +"@langchain/core@npm:~0.1.5": + version: 0.1.7 + resolution: "@langchain/core@npm:0.1.7" dependencies: ansi-styles: ^5.0.0 camelcase: 6 @@ -1007,33 +1040,33 @@ __metadata: p-retry: 4 uuid: ^9.0.0 zod: ^3.22.3 - checksum: 85f5a23a3e96cac6a8345c083df925339a6a3f9aa620f9faad295a62b992b0345f61fe7d649c27ff5058c86cdec84b3a503462ccdd759c6f46c4fa418255d16d + checksum: 395c00b323bff662eacf3f7cedbad1f70a0409c748e035d6af5ce026f3422c67188e6ba07db80146156b683020a16f024f70915a407679ea8aeace0f1dcc00ab languageName: node linkType: hard -"@langchain/openai@npm:~0.0.5": - version: 0.0.9 - resolution: "@langchain/openai@npm:0.0.9" +"@langchain/openai@npm:~0.0.10": + version: 0.0.10 + resolution: "@langchain/openai@npm:0.0.10" dependencies: - "@langchain/core": ~0.1.5 + "@langchain/core": ~0.1.9 js-tiktoken: ^1.0.7 openai: ^4.19.0 zod: ^3.22.3 zod-to-json-schema: 3.20.3 - checksum: 89f9d6f30d724e7e007bee95a21afd1a9a6a2c0b4aa2f22d420c8f33a478ec429447e357e9a822cbc181770665e1eb85f2c611b23e25434c6a45af761986cdcd + checksum: 1bee02b30ee99f6f7dd0d83ed471bfa19245ff1621116725623a8fa2b7566288006613497a5df2b2309d77569fc974d6b9ce2c174f00c3edcdac6e73b462bddf languageName: node linkType: hard -"@langchain/openai@npm:~0.0.7": - version: 0.0.7 - resolution: "@langchain/openai@npm:0.0.7" +"@langchain/openai@npm:~0.0.5": + version: 0.0.9 + resolution: "@langchain/openai@npm:0.0.9" dependencies: - "@langchain/core": ~0.1.3 + "@langchain/core": ~0.1.5 js-tiktoken: ^1.0.7 openai: ^4.19.0 zod: ^3.22.3 zod-to-json-schema: 3.20.3 - checksum: e2cd0aa59229fcb7217110a95b61035b9e889ea74461c96f141eb2313738bc00f7ff102f85df5144552c103df2c9932a796d219cde1f7f22a637ea432b608cc6 + checksum: 89f9d6f30d724e7e007bee95a21afd1a9a6a2c0b4aa2f22d420c8f33a478ec429447e357e9a822cbc181770665e1eb85f2c611b23e25434c6a45af761986cdcd languageName: node linkType: hard @@ -2267,12 +2300,12 @@ __metadata: linkType: hard "@types/node-fetch@npm:^2.6.4": - version: 2.6.9 - resolution: "@types/node-fetch@npm:2.6.9" + version: 2.6.10 + resolution: "@types/node-fetch@npm:2.6.10" dependencies: "@types/node": "*" form-data: ^4.0.0 - checksum: 212269aff4b251477c13c33cee6cea23e4fd630be6c0bfa3714968cce7efd7055b52f2f82aab3394596d8c758335cc802e7c5fa3f775e7f2a472fa914c90dc15 + checksum: e0c9a6023752ff6c744a33a3045b9adb11fd1882997ef891bf7ce91f937ddab91c0acee4c8e806a8a5aec0e8d8c132709141e8512fec28030d7cc9ef92c7ff1e languageName: node linkType: hard @@ -2298,11 +2331,11 @@ __metadata: linkType: hard "@types/node@npm:^18.11.18": - version: 18.18.11 - resolution: "@types/node@npm:18.18.11" + version: 18.19.6 + resolution: "@types/node@npm:18.19.6" dependencies: undici-types: ~5.26.4 - checksum: e4c2c5b792023c86248013589820d157ced46a35dcd60b15a3133a8f67f6b54c8870f245ecccbe7a12c49f1991ea84401946ddbb3f44894ec042642233e616d5 + checksum: f0f3739f2d0e5be7b0b41a1fb149f554b1d6cb6c61c68e316c01ec2605559314f5434c170fb8e1a32f6c96a08ac752baa00ffe4451c154e37c77f76b70cf3d5b languageName: node linkType: hard @@ -3136,7 +3169,7 @@ __metadata: 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 @@ -5811,9 +5844,9 @@ __metadata: linkType: hard "dompurify@npm:^3.0.6": - version: 3.0.6 - resolution: "dompurify@npm:3.0.6" - checksum: e5c6cdc5fe972a9d0859d939f1d86320de275be00bbef7bd5591c80b1e538935f6ce236624459a1b0c84ecd7c6a1e248684aa4637512659fccc0ce7c353828a6 + version: 3.0.8 + resolution: "dompurify@npm:3.0.8" + checksum: cac660ccae15a9603f06a85344da868a4c3732d8b57f7998de0f421eb4b9e67d916be52e9bb2a57b2f95b49e994cc50bcd06bb87f2cb2849cf058bdf15266237 languageName: node linkType: hard @@ -8914,8 +8947,8 @@ __metadata: linkType: hard "langsmith@npm:~0.0.48": - version: 0.0.52 - resolution: "langsmith@npm:0.0.52" + version: 0.0.56 + resolution: "langsmith@npm:0.0.56" dependencies: "@types/uuid": ^9.0.1 commander: ^10.0.1 @@ -8924,7 +8957,7 @@ __metadata: uuid: ^9.0.0 bin: langsmith: dist/cli/main.cjs - checksum: b782836e07568f833ef56cab4a1404911b1395ec1619e8d2378839324328c5fb5e50ba6fc5839e93c40e99c6198942cae48bd890f95d6433ae3897b60a15f62c + checksum: 78f8f97bceb8d198bb507b49869ce3c1392bfb400dd5411bedeaa11d9263287ecce5dfc95464215fe81f9bd9a1e3273e16092fdac0d396f30bb5c7aff0bd1c11 languageName: node linkType: hard @@ -9545,11 +9578,11 @@ __metadata: linkType: hard "marked-highlight@npm:^2.0.7": - version: 2.0.9 - resolution: "marked-highlight@npm:2.0.9" + version: 2.1.0 + resolution: "marked-highlight@npm:2.1.0" peerDependencies: marked: ">=4 <12" - checksum: e28324d4854d31af4ebb1a136a965f13573f261debcb891442cc7c316e68ced53e07c191f8824561f21669dec341f89b52b9adcb5ce113a7f0c5672b26a84ed0 + checksum: 96e7e3d494b10b990a9cc36b87c0539495f2c612d1067fe648a6840af9296f1435ddc683c0f9472299f3339c75e69fe4a528a6d4fc7f99ecfa7e12119238d48e languageName: node linkType: hard @@ -10809,8 +10842,8 @@ __metadata: linkType: hard "openai@npm:^4.19.0": - version: 4.24.0 - resolution: "openai@npm:4.24.0" + version: 4.24.2 + resolution: "openai@npm:4.24.2" dependencies: "@types/node": ^18.11.18 "@types/node-fetch": ^2.6.4 @@ -10823,7 +10856,7 @@ __metadata: web-streams-polyfill: ^3.2.1 bin: openai: bin/cli - checksum: 5a65897daadbac30b452c16b8d7342db14e701feafa6f2294dde05ec95e404dd5b93b8ade0c4a39667819e6ec00215c248889bb69767730e656442b0d6c68ce3 + checksum: 95be77f624a682be3bb3c789ef46fd1b85e3d96c7e759010803fc94c596cdb5b4334d96579de6eead6710981bc23cdc98ac201be1d1cb99bb1d32c4ebb998cf5 languageName: node linkType: hard @@ -12865,14 +12898,14 @@ resolve@^2.0.0-next.3: linkType: hard "socket.io-client@npm:^4.7.2": - version: 4.7.2 - resolution: "socket.io-client@npm:4.7.2" + version: 4.7.3 + resolution: "socket.io-client@npm:4.7.3" dependencies: "@socket.io/component-emitter": ~3.1.0 debug: ~4.3.2 engine.io-client: ~6.5.2 socket.io-parser: ~4.2.4 - checksum: 8f0ab6b623e014d889bae0cd847ef7826658e8f131bd9367ee5ae4404bb52a6d7b1755b8fbe8e68799b60e92149370a732b381f913b155e40094facb135cd088 + checksum: fd27f2e64db7a137eb2cf256ea1bde8625f127f208be69c28c5d6f2d8c8c4a6af736fb0f8b23317353b00ccf4637dbb72a59d4066d9e5bb171b7ffd0f18c5b93 languageName: node linkType: hard @@ -14405,9 +14438,9 @@ typescript@^3.9.3: linkType: hard "web-streams-polyfill@npm:^3.2.1": - version: 3.2.1 - resolution: "web-streams-polyfill@npm:3.2.1" - checksum: b119c78574b6d65935e35098c2afdcd752b84268e18746606af149e3c424e15621b6f1ff0b42b2676dc012fc4f0d313f964b41a4b5031e525faa03997457da02 + version: 3.3.2 + resolution: "web-streams-polyfill@npm:3.3.2" + checksum: 0292f4113c1bda40d8e8ecebee39eb14cc2e2e560a65a6867980e394537a2645130e2c73f5ef6e641fd3697d2f71720ccf659aebaf69a9d5a773f653a0fdf39d languageName: node linkType: hard