Skip to content

Commit

Permalink
Refactor copilot chat participant and add icon (#808)
Browse files Browse the repository at this point in the history
* refactor: ♻️ update and rename copilot chat participant

* add icon

* add run command

* more docs

* more upates

* tuned copilot

* more docs

* avoid duplicate prompt
  • Loading branch information
pelikhan authored Oct 31, 2024
1 parent 28ffa62 commit 9109632
Show file tree
Hide file tree
Showing 20 changed files with 188 additions and 155 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
script({
title: "Image Alt Text generator",
description: "Generate alt text for images",
model: "openai:gpt-4o",
model: "large",
group: "docs",
maxTokens: 4000,
temperature: 0,
Expand Down
Binary file added docs/src/assets/chat-participant.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/src/assets/chat-participant.png.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A screenshot of the chat participant window.
78 changes: 0 additions & 78 deletions docs/src/content/docs/reference/scripts/system.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,84 +78,6 @@ script({ ...,
GenAIScript comes with a number of system prompt that support features like creating files, extracting diffs or
generating annotations. If unspecified, GenAIScript looks for specific keywords to activate the various system prompts.

### `copilot_chat_participant`







`````js wrap title="copilot_chat_participant"
script({
system: [
// List of system components and tools available for the script
"system",
"system.tools",
"system.files",
"system.diagrams",
"system.annotations",
"system.git_info",
"system.github_info",
"system.safety_harmful_content",
],
tools: ["agent"], // Tools that the script can use
group: "infrastructure", // Group categorization for the script
parameters: {
question: {
type: "string", // Type of the parameter
description: "the user question", // Description of the parameter
},
"copilot.editor": {
type: "string",
description: "the content of the opened editor",
default: "",
},
"copilot.selection": {
type: "string",
description: "the content of the opened editor",
default: "",
},
},
flexTokens: 20000, // Flexible token limit for the script
})

// Extract the 'question' parameter from the environment variables
const { question } = env.vars
const editor = env.vars["copilot.editor"]
const selection = env.vars["copilot.selection"]

$`## task
- make a plan to answer the QUESTION step by step using the information in the Context section
- answer the QUESTION
## output
- The final output will be inserted into the Visual Studio Code Copilot Chat window.
- do NOT include the plan in the output
## guidance:
- use the agent tools to help you
- do NOT be lazy, always finish the tasks
- do NOT skip any steps
`

// Define a variable QUESTION with the value of 'question'
def("QUESTION", question, { lineNumbers: false })

$`## Context`

// Define a variable FILE with the file data from the environment variables
// The { ignoreEmpty: true, flex: 1 } options specify to ignore empty files and to use flexible token allocation
def("FILE", env.files, { lineNumbers: false, ignoreEmpty: true, flex: 1 })

if (editor) writeText(editor, { flex: 4 })
if (selection) writeText(selection, { flex: 5 })

`````


### `system`

Base system prompt
Expand Down
53 changes: 53 additions & 0 deletions docs/src/content/docs/reference/vscode/github-copilot-chat.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
title: GitHub Copilot Chat
sidebar:
order: 3
---

import { Image } from "astro:assets"
import { Code } from "@astrojs/starlight/components"
import scriptSource from "../../../../../../packages/vscode/genaisrc/copilotchat.genai.mjs?raw"
import src from "../../../../assets/chat-participant.png"
import alt from "../../../../assets/chat-participant.png.txt?raw"

The `@genaiscript` [chat participant](https://code.visualstudio.com/api/extension-guides/chat#parts-of-the-chat-user-experience) lets your run scripts without the context
of a [GitHub Copilot Chat](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat) conversation.
This is useful for leverage existing scripts in an interactive chat session.

<Image src={src} alt={alt} />

## Choosing which script to run

The `/run` command expects a script id as the first argument (e.g., `/run poem`). The rest of the query is
passed to the script as the `env.vars.question` variable.

```sh
@genaiscript /run summarize
```

If you omit the `/run` command, GenAIScript will look for a script named `copilotchat`. If it finds one, it will run it.
Otherwise, it will propose you to create a new script.

```sh
@genaiscript add comments to the current editor
```

## Context

The context selected by the user in Copilot Chat is converted to variables and passed to the script:

- the prompt content is passed in `env.vars.question`. The script id is removed in the case of `/run`.
- the current editor text is passed in `env.vars["copilot.editor"]`
- the current editor selection is passed in `env.vars["copilot.selection"]`
- the file references are passed in `env.files`

## Default script <a id="copilotchat" href="" />

The following script can used as a starter template to create the default script when the user does not use the `/run` command.

<Code
code={scriptSource}
wrap={true}
lang="ts"
title="genaisrc/copilotchat.genai.mts"
/>
1 change: 1 addition & 0 deletions docs/src/content/docs/reference/vscode/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ contains the latest stable release of the [extension](https://marketplace.visual

- [Download](https://marketplace.visualstudio.com/items?itemName=genaiscript.genaiscript-vscode)
- [Installation instructions](/genaiscript/getting-started/installation/#visual-studio-code-extension)
- [Copilot Chat Integration](/genaiscript/reference/copilot-chat/)
34 changes: 25 additions & 9 deletions packages/core/src/annotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ const AZURE_DEVOPS_ANNOTATIONS_RX =
// Example: foo.ts:10:error TS1005: ';' expected.
const TYPESCRIPT_ANNOTATIONS_RX =
/^(?<file>[^:\s].*?):(?<line>\d+)(?::(?<endLine>\d+))?(?::\d+)?\s+-\s+(?<severity>error|warning)\s+(?<code>[^:]+)\s*:\s*(?<message>.*)$/gim
// Maps severity strings to `DiagnosticSeverity`.
const SEV_MAP: Record<string, DiagnosticSeverity> = Object.freeze({
["info"]: "info",
["notice"]: "info", // Maps 'notice' to 'info' severity
["warning"]: "warning",
["error"]: "error",
})

/**
* Parses annotations from TypeScript, GitHub Actions, and Azure DevOps.
Expand All @@ -28,20 +35,12 @@ const TYPESCRIPT_ANNOTATIONS_RX =
export function parseAnnotations(text: string): Diagnostic[] {
if (!text) return []

// Maps severity strings to `DiagnosticSeverity`.
const sevMap: Record<string, DiagnosticSeverity> = {
["info"]: "info",
["notice"]: "info", // Maps 'notice' to 'info' severity
["warning"]: "warning",
["error"]: "error",
}

// Helper function to add an annotation to the set.
// Extracts groups from the regex match and constructs a `Diagnostic` object.
const addAnnotation = (m: RegExpMatchArray) => {
const { file, line, endLine, severity, code, message } = m.groups
const annotation: Diagnostic = {
severity: sevMap[severity?.toLowerCase()] ?? "info", // Default to "info" if severity is missing
severity: SEV_MAP[severity?.toLowerCase()] ?? "info", // Default to "info" if severity is missing
filename: file,
range: [
[parseInt(line) - 1, 0], // Start of range, 0-based index
Expand Down Expand Up @@ -72,6 +71,23 @@ export function eraseAnnotations(text: string) {
].reduce((t, rx) => t.replace(rx, ""), text)
}

export function convertAnnotationsToItems(text: string) {
return [
TYPESCRIPT_ANNOTATIONS_RX,
GITHUB_ANNOTATIONS_RX,
AZURE_DEVOPS_ANNOTATIONS_RX,
].reduce(
(t, rx) =>
t.replace(rx, (s) => {
const m = rx.exec(s)
if (!m) return s
const { file, line, severity, code, message } = m.groups
return `- ${SEV_MAP[severity?.toLowerCase()] ?? "info"}: ${message} (${file}#L${line} ${code || ""})`
}),
text
)
}

/**
* Converts a `Diagnostic` to a GitHub Action command string.
*
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ export function appendSystemMessage(
if (last?.role !== "system") {
last = {
role: "system",
content,
content: "",
} as ChatCompletionSystemMessageParam
messages.unshift(last)
}
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ export const CACHE_LLMREQUEST_PREFIX = "genaiscript/cache/llm/"
export const CACHE_AIREQUEST_PREFIX = "genaiscript/cache/ai/"
export const TRACE_NODE_PREFIX = "genaiscript/trace/"
export const EXTENSION_ID = "genaiscript.genaiscript-vscode"
export const CHAT_PARTICIPANT_ID = TOOL_ID
export const COPILOT_CHAT_PARTICIPANT_ID = TOOL_ID
export const COPILOT_CHAT_PARTICIPANT_SCRIPT_ID = "copilotchat"
export const BING_SEARCH_ENDPOINT = "https://api.bing.microsoft.com/v7.0/search"
export const SYSTEM_FENCE = "\n"
export const MAX_DATA_REPAIRS = 1
Expand Down
26 changes: 8 additions & 18 deletions packages/core/src/promptdom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,19 @@ import {
MARKDOWN_PROMPT_FENCE,
PROMPT_FENCE,
PROMPTY_REGEX,
SYSTEM_FENCE,
TEMPLATE_ARG_DATA_SLICE_SAMPLE,
TEMPLATE_ARG_FILE_MAX_TOKENS,
} from "./constants"
import { parseModelIdentifier } from "./models"
import { appendAssistantMessage, appendUserMessage } from "./chat"
import {
appendAssistantMessage,
appendSystemMessage,
appendUserMessage,
} from "./chat"
import { errorMessage } from "./error"
import { tidyData } from "./tidy"
import { dedent } from "./indent"
import {
ChatCompletionMessageParam,
ChatCompletionSystemMessageParam,
ChatCompletionUserMessageParam,
} from "./chattypes"
import { ChatCompletionMessageParam } from "./chattypes"
import { resolveTokenEncoder } from "./encoders"
import { expandFiles } from "./fs"
import { interpolateVariables } from "./mustache"
Expand Down Expand Up @@ -938,17 +937,8 @@ export async function renderPromptNode(
if (truncated) await tracePromptNode(trace, node, { label: "truncated" })

const messages: ChatCompletionMessageParam[] = []
const appendSystem = (content: string) => {
const last = messages.find(
({ role }) => role === "system"
) as ChatCompletionSystemMessageParam
if (last) last.content += content + SYSTEM_FENCE
else
messages.push({
role: "system",
content,
} as ChatCompletionSystemMessageParam)
}
const appendSystem = (content: string) =>
appendSystemMessage(messages, content)
const appendUser = (content: string) => appendUserMessage(messages, content)
const appendAssistant = (content: string) =>
appendAssistantMessage(messages, content)
Expand Down
3 changes: 2 additions & 1 deletion packages/sample/genaisrc/bicep-best-practices.genai.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
script({
title: "Bicep Best Practices",
temperature: 0,
system: ["system", "system.annotations"]
})

def("FILE", env.files, { endsWith: ".bicep" })

$`You are an expert at Azure Bicep.
Review the bicep in FILE and generate annotations to enhance the script base on best practices
Review the bicep in FILE and generate errors to enhance the script base on best practices
(https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/best-practices).
- Generate the top 3 most important annotations.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
script({
model: "large",
system: [
// List of system components and tools available for the script
"system",
"system.safety_harmful_content",
"system.safety_jailbreak",
"system.safety_protected_material",
"system.tools",
"system.files",
"system.files_schema",
"system.diagrams",
"system.annotations",
"system.git_info",
"system.github_info",
"system.safety_harmful_content",
"system.agent_fs",
"system.agent_git",
"system.agent_github",
"system.agent_interpreter",
"system.agent_docs",
],
tools: ["agent"], // Tools that the script can use
group: "infrastructure", // Group categorization for the script
group: "copilot", // Group categorization for the script
parameters: {
question: {
type: "string", // Type of the parameter
description: "the user question", // Description of the parameter
type: "string",
description: "the user question",
},
"copilot.editor": {
type: "string",
description: "the content of the opened editor",
description: "the content of the opened editor, if any",
default: "",
},
"copilot.selection": {
type: "string",
description: "the content of the opened editor",
description: "the content of the opened editor, if any",
default: "",
},
},
Expand Down Expand Up @@ -60,6 +69,5 @@ $`## Context`
// Define a variable FILE with the file data from the environment variables
// The { ignoreEmpty: true, flex: 1 } options specify to ignore empty files and to use flexible token allocation
def("FILE", env.files, { lineNumbers: false, ignoreEmpty: true, flex: 1 })

if (editor) writeText(editor, { flex: 4 })
if (selection) writeText(selection, { flex: 5 })
def("EDITOR", editor, { flex: 4, ignoreEmpty: true })
def("SELECTION", selection, { flex: 5, ignoreEmpty: true })
13 changes: 9 additions & 4 deletions packages/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@
"name": "genaiscript",
"fullName": "GenAIScript",
"isSticky": false,
"disambiguation": [
"commands": [
{
"category": "workspace_question",
"description": "Runs GenAIScript agents defined in the current respository.",
"examples": []
"name": "run",
"description": "Runs a GenAIScript script. The query should start with the script filename without the extension.",
"isSticky": false,
"sampleRequest": "/run poem"
}
]
}
Expand Down Expand Up @@ -249,6 +250,10 @@
{
"command": "genaiscript.request.status",
"when": "false"
},
{
"command": "genaiscript.samples.download",
"when": "false"
}
],
"view/title": [
Expand Down
2 changes: 0 additions & 2 deletions packages/vscode/postpackage.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import "zx/globals"

const pkg = await fs.readJSON("./package.json")
pkg.enabledApiProposals = pkg._enabledApiProposals
pkg.contributes.chatParticipants = pkg._chatParticipants
pkg.displayName = "GenAIScript Insiders"
delete pkg._enabledApiProposals
delete pkg._chatParticipants
await fs.writeJSON("./package.json", pkg, { spaces: 4 })
console.log(`cleaned package.json`)
1 change: 0 additions & 1 deletion packages/vscode/prepackage.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import "zx/globals"

const pkg = await fs.readJSON("./package.json")
pkg._enabledApiProposals = pkg.enabledApiProposals
pkg._chatParticipants = pkg.contributes.chatParticipants
pkg.displayName = "GenAIScript"
delete pkg.enabledApiProposals
await fs.writeJSON("./package.json", pkg, { spaces: 4 })
Expand Down
Loading

0 comments on commit 9109632

Please sign in to comment.