-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add chat agent with online help contextual hints
Gordon Smith <[email protected]>
- Loading branch information
1 parent
e1bb74d
commit 1da7edc
Showing
16 changed files
with
759 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import * as vscode from "vscode"; | ||
|
||
export const ECL_COMMAND_ID = "ecl"; | ||
export const PROCESS_COPILOT_CREATE_CMD = "ecl.createFiles"; | ||
export const PROCESS_COPILOT_CREATE_CMD_TITLE = "Create ECL file"; | ||
export const COPILOT_CREATE_CMD = "ECL file"; | ||
|
||
export const OWNER = "hpcc-systems"; | ||
export const REPO = "HPCC-Platform"; | ||
export const BRANCH = "master"; | ||
export const SAMPLE_COLLECTION_URL = `https://cdn.jsdelivr.net/gh/${OWNER}/${REPO}@${BRANCH}/`; | ||
|
||
export const MODEL_VENDOR: string = "copilot"; | ||
|
||
enum LANGUAGE_MODEL_ID { | ||
GPT_3 = "gpt-3.5-turbo", | ||
GPT_4 = "gpt-4", | ||
GPT_4o = "gpt-4o" | ||
} | ||
|
||
export const MODEL_SELECTOR: vscode.LanguageModelChatSelector = { vendor: MODEL_VENDOR, family: LANGUAGE_MODEL_ID.GPT_4o }; | ||
|
||
export const FETCH_ISSUE_DETAIL_CMD = "Fetch Issue Details Command"; | ||
|
||
export enum commands { | ||
DOCS = "docs", | ||
ISSUES = "issues", | ||
} | ||
|
||
const GREETINGS = [ | ||
"Let me think how I can assist you... 🤔", | ||
"Just a moment, I'm pondering... 💭", | ||
"Give me a second, I'm working on it... ⏳", | ||
"Hold on, let me figure this out... 🧐", | ||
"One moment, I'm processing your request... ⏲️", | ||
"Checking inside Gavins brain... 💭", | ||
"Dans the man for this... 🧐", | ||
"Working on your request... 🚀", | ||
"Lets see what schmoo can do... 🕵️♂️", | ||
"Let's see what we can do... 🕵️♂️", | ||
"Let's get this sorted... 🗂️", | ||
"Calling Jake for an answer... 💭", | ||
"Hang tight, I'm on the case... 🕵️♀️", | ||
"Analyzing the situation... 📊", | ||
"Preparing the solution... 🛠️", | ||
"Searching for the answer... 🔍", | ||
"Maybe Mark knows... 🤔", | ||
"Investigating the problem... 🕵️♂️" | ||
]; | ||
|
||
export const getRandomGreeting = () => { | ||
return GREETINGS[Math.floor(Math.random() * GREETINGS.length)]; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import * as vscode from "vscode"; | ||
import { commands, getRandomGreeting } from "./constants"; | ||
import localize from "../../util/localize"; | ||
import { handleDocsCommand } from "./prompts/docs"; | ||
import { handleIssueManagement } from "./prompts/issues"; | ||
|
||
const ECL_PARTICIPANT_ID = "chat.ecl"; | ||
|
||
interface IECLChatResult extends vscode.ChatResult { | ||
metadata: { | ||
command: string; | ||
} | ||
} | ||
|
||
function handleError(logger: vscode.TelemetryLogger, err: any, stream: vscode.ChatResponseStream): void { | ||
logger.logError(err); | ||
|
||
if (err instanceof vscode.LanguageModelError) { | ||
console.log(err.message, err.code, err.cause); | ||
if (err.cause instanceof Error && err.cause.message.includes("off_topic")) { | ||
stream.markdown(localize("I'm sorry, I can only explain ECL related topics.")); | ||
} | ||
} else { | ||
throw err; | ||
} | ||
} | ||
|
||
let eclChat: ECLChat; | ||
|
||
export class ECLChat { | ||
protected constructor(ctx: vscode.ExtensionContext) { | ||
const handler: vscode.ChatRequestHandler = async (request: vscode.ChatRequest, ctx: vscode.ChatContext, stream: vscode.ChatResponseStream, token: vscode.CancellationToken): Promise<IECLChatResult> => { | ||
let cmdResult: any; | ||
stream.progress(localize(getRandomGreeting())); | ||
try { | ||
if (request.command === commands.ISSUES) { | ||
cmdResult = await handleIssueManagement(request, stream, token); | ||
logger.logUsage("request", { kind: commands.ISSUES }); | ||
} else { | ||
cmdResult = await handleDocsCommand(request, stream, token); | ||
} | ||
} catch (err) { | ||
handleError(logger, err, stream); | ||
} | ||
|
||
return { | ||
metadata: { | ||
command: request.command || "", | ||
}, | ||
}; | ||
|
||
}; | ||
|
||
const chatParticipant = vscode.chat.createChatParticipant(ECL_PARTICIPANT_ID, handler); | ||
chatParticipant.iconPath = vscode.Uri.joinPath(ctx.extensionUri, "resources/hpcc-icon.png"); | ||
|
||
chatParticipant.followupProvider = { | ||
provideFollowups(result: IECLChatResult, context: vscode.ChatContext, token: vscode.CancellationToken) { | ||
return []; | ||
} | ||
}; | ||
|
||
const logger = vscode.env.createTelemetryLogger({ | ||
sendEventData(eventName, data) { | ||
// Capture event telemetry | ||
console.log(`Event: ${eventName}`); | ||
console.log(`Data: ${JSON.stringify(data)}`); | ||
}, | ||
sendErrorData(error, data) { | ||
console.error(`Error: ${error}`); | ||
console.error(`Data: ${JSON.stringify(data)}`); | ||
} | ||
}); | ||
|
||
ctx.subscriptions.push(chatParticipant.onDidReceiveFeedback((feedback: vscode.ChatResultFeedback) => { | ||
logger.logUsage("chatResultFeedback", { | ||
kind: feedback.kind | ||
}); | ||
})); | ||
} | ||
|
||
static attach(ctx: vscode.ExtensionContext): ECLChat { | ||
if (!eclChat) { | ||
eclChat = new ECLChat(ctx); | ||
} | ||
return eclChat; | ||
} | ||
} | ||
|
||
export function deactivate() { } | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import * as vscode from "vscode"; | ||
import { AssistantMessage, BasePromptElementProps, PromptElement, PromptSizing, TextChunk, UserMessage, } from "@vscode/prompt-tsx"; | ||
import { commands, MODEL_SELECTOR } from "../constants"; | ||
import { getChatResponse } from "../utils/index"; | ||
import { fetchContext, fetchIndexes, Hit, matchTopics } from "../../docs"; | ||
import * as prompts from "./templates/default"; | ||
|
||
export interface PromptProps extends BasePromptElementProps { | ||
userQuery: string; | ||
} | ||
|
||
export interface DocsPromptProps extends PromptProps { | ||
hits: Hit[] | ||
} | ||
|
||
export class DocsPrompt extends PromptElement<DocsPromptProps, any> { | ||
|
||
render(state: void, sizing: PromptSizing) { | ||
return ( | ||
<> | ||
<AssistantMessage priority={1000}>{prompts.SYSTEM_MESSAGE}</AssistantMessage> | ||
<UserMessage priority={500}> | ||
{this.props.hits.map((hit, idx) => ( | ||
<TextChunk breakOn=' '> | ||
{JSON.stringify(hit)} | ||
</TextChunk> | ||
))} | ||
</UserMessage> | ||
<UserMessage priority={1000}>{this.props.userQuery}</UserMessage> | ||
</> | ||
); | ||
} | ||
} | ||
|
||
export async function handleDocsCommand(request: vscode.ChatRequest, stream: vscode.ChatResponseStream, token: vscode.CancellationToken): Promise<{ metadata: { command: string, hits: Hit[] } }> { | ||
const [model] = await vscode.lm.selectChatModels(MODEL_SELECTOR); | ||
if (model) { | ||
const hits = await fetchContext(request.prompt); | ||
let promptProps: DocsPromptProps; | ||
if (!hits.length) { | ||
promptProps = { | ||
userQuery: `Suggest several (more 3 or more) web links that exist in the previous html content above that might help with the following question "${request.prompt}". The user can not see the above content. Explain why they might be helpful`, | ||
hits: await fetchIndexes() | ||
}; | ||
} else { | ||
promptProps = { | ||
userQuery: request.prompt, | ||
hits, | ||
}; | ||
} | ||
|
||
const chatResponse = await getChatResponse(DocsPrompt, promptProps, token); | ||
for await (const fragment of chatResponse.text) { | ||
stream.markdown(fragment); | ||
} | ||
|
||
return { | ||
metadata: { | ||
command: commands.DOCS, | ||
hits: promptProps.hits, | ||
}, | ||
}; | ||
} | ||
} | ||
|
Oops, something went wrong.