diff --git a/README.md b/README.md index b06be23b..9c73c28f 100644 --- a/README.md +++ b/README.md @@ -91,16 +91,14 @@ Adding the Septic extension to VS Code allows you to do the following when loadi The extension supports Septic projects that uses the Septic Config Generator (SCG). The SCG-config file for the project is loaded and the relevant `.cnfg` files (required to be in the templates folder) listed in the layout section are loaded into a common context that shares references etc. -Septic Co-Pilot features powered by Large Language Models (LLM): -- Chat participant `@septic`. The following commands are available: - - `/calculation` which is specialized in generating calculations based on the description from the user. The suggested calculations are quality checked by the Septic diagnostics provider to increase the quality of the output. - - `/scg` which is specialized in understanding Septic Config Generator (scg) contexts and sources. The participant can be used to modify scg sources on csv format e.g. add a new well to an scg source or update the values of certain cells. Use the scg-configs that are attached to the context or select the scg-config based on the open Septic config if non is attached. -- Code action for generating Alg attribute for a CalcPvr based on the Text1 description is available by selecting the relevant CalcPvr with the cursor and selecting `Generate Calc: CalcPvrName` in the code action options. The generated calc is filled in the Alg field.The suggested calculation is quality checked by the Septic diagnostics provider to increase the quality of the output. Co-Pilot reiterate up to 3 times based on the feedback from the diagnostics provider. +Septic extension also contributes a set of tools that can be used for enhancing the capablities of the Github Co-Pilot for Septic configuration -`@septic` use tools to perform different actions. The different commands acts as agents with a given set of tools that it can call. A tool can be used to gather more context (e.g. get documentation of all functions), perform non-modifying actions (e.g. validate a given calculation) or perform modifying actions (e.g. add/delete a row to an scg source). The participant will always ask for permission before using a tool with a modifying action, while all tools related to gathering context and non-modifying actions will be called without asking for permission. +Tools: -Septic documentation is used as input to the Large Language Models (LLM) powering the Co-Pilot and is selected based on the selected version in the workspace. +- #getSepticFunctionDocumentation: Gets documentation of all functions available for use in calculations +- #getSepticVariables: Gets all valid references to objects in the current Septic context selected based on the open file +Septic extension also contributes a set of pre-made prompts and instruction files for Github Co-Pilot. The command `Septic: Copy Instructions and Prompts to .github` can be used to copy the files into the current workspace. ## Feedback and contributions diff --git a/client/src/chatParticipant.ts b/client/src/chatParticipant.ts deleted file mode 100644 index 6a3b1661..00000000 --- a/client/src/chatParticipant.ts +++ /dev/null @@ -1,152 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Equinor ASA - * Copyright (c) Microsoft Corporation. All rights reserved. [vscode-extension-samples/chat-sample] - * Licensed under the MIT License. See LICENSE in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import { renderPrompt } from '@vscode/prompt-tsx'; -import * as vscode from 'vscode'; -import { ToolCallRound, ToolResultMetadata, ToolUserPrompt } from './toolsPrompt'; -import { createChatLogger } from './logger'; -import { LanguageClient } from 'vscode-languageclient/node'; - -export interface TsxToolUserMetadata { - toolCallsMetadata: ToolCallsMetadata; -} - -export interface ToolCallsMetadata { - toolCallRounds: ToolCallRound[]; - toolCallResults: Record; -} - -export function isTsxToolUserMetadata(obj: unknown): obj is TsxToolUserMetadata { - // If you change the metadata format, you would have to make this stricter or handle old objects in old ChatRequest metadata - return !!obj && - !!(obj as TsxToolUserMetadata).toolCallsMetadata && - Array.isArray((obj as TsxToolUserMetadata).toolCallsMetadata.toolCallRounds); -} - -export function registerSepticChatParticipant(context: vscode.ExtensionContext, client: LanguageClient) { - const logger = createChatLogger(context); - const handler: vscode.ChatRequestHandler = async (request: vscode.ChatRequest, chatContext: vscode.ChatContext, stream: vscode.ChatResponseStream, token: vscode.CancellationToken) => { - logger.logUsage('chatRequest', { command: request.command, model: request.model }); - let model = request.model; - if (model.vendor === 'copilot' && model.family.startsWith('o1')) { - // The o1 models do not currently support tools - const models = await vscode.lm.selectChatModels({ - vendor: 'copilot', - family: 'gpt-4o' - }); - model = models[0]; - } - - const tools = vscode.lm.tools.filter(tool => - tool.tags.includes('septic') && - (request.command ? tool.tags.includes(request.command) : true) - ); - const options: vscode.LanguageModelChatRequestOptions = { - justification: 'To make a request to @toolsTSX', - }; - - // Render the initial prompt - const result = await renderPrompt( - ToolUserPrompt, - { - context: chatContext, - request, - client, - toolCallRounds: [], - toolCallResults: {} - }, - { modelMaxPromptTokens: model.maxInputTokens }, - model); - let messages = result.messages; - result.references.forEach(ref => { - if (ref.anchor instanceof vscode.Uri || ref.anchor instanceof vscode.Location) { - stream.reference(ref.anchor); - } - }); - - const toolReferences = [...request.toolReferences]; - const accumulatedToolResults: Record = {}; - const toolCallRounds: ToolCallRound[] = []; - const runWithTools = async (): Promise => { - // If a toolReference is present, force the model to call that tool - const requestedTool = toolReferences.shift(); - if (requestedTool) { - options.toolMode = vscode.LanguageModelChatToolMode.Required; - options.tools = vscode.lm.tools.filter(tool => tool.name === requestedTool.name); - } else { - options.toolMode = undefined; - options.tools = [...tools]; - } - - // Send the request to the LanguageModelChat - const response = await model.sendRequest(messages, options, token); - - // Stream text output and collect tool calls from the response - const toolCalls: vscode.LanguageModelToolCallPart[] = []; - let responseStr = ''; - for await (const part of response.stream) { - if (part instanceof vscode.LanguageModelTextPart) { - stream.markdown(part.value); - responseStr += part.value; - } else if (part instanceof vscode.LanguageModelToolCallPart) { - toolCalls.push(part); - } - } - - if (toolCalls.length) { - // If the model called any tools, then we do another round- render the prompt with those tool calls (rendering the PromptElements will invoke the tools) - // and include the tool results in the prompt for the next request. - toolCallRounds.push({ - response: responseStr, - toolCalls - }); - const result = (await renderPrompt( - ToolUserPrompt, - { - context: chatContext, - request, - client, - toolCallRounds, - toolCallResults: accumulatedToolResults - }, - { modelMaxPromptTokens: model.maxInputTokens }, - model)); - messages = result.messages; - const toolResultMetadata = result.metadata.getAll(ToolResultMetadata); - if (toolResultMetadata?.length) { - // Cache tool results for later, so they can be incorporated into later prompts without calling the tool again - toolResultMetadata.forEach(meta => accumulatedToolResults[meta.toolCallId] = meta.result); - } - - // This loops until the model doesn't want to call any more tools, then the request is done. - return runWithTools(); - } - }; - - await runWithTools(); - - return { - metadata: { - // Return tool call metadata so it can be used in prompt history on the next request - toolCallsMetadata: { - toolCallResults: accumulatedToolResults, - toolCallRounds - } - } satisfies TsxToolUserMetadata, - }; - }; - - const septicChat = vscode.chat.createChatParticipant('septic.chat', handler); - - const iconPathDark = vscode.Uri.joinPath(context.extensionUri, "images/septic_dark.svg"); - const iconPathLight = vscode.Uri.joinPath(context.extensionUri, "images/septic_light.svg"); - septicChat.iconPath = { light: iconPathLight, dark: iconPathDark }; - - septicChat.onDidReceiveFeedback(feedback => { - const toolCalls = feedback.result.metadata.toolCallsMetadata?.toolCallRounds.map(round => round.toolCalls.map(call => { return { name: call.name, input: call.input } })).flat(); - logger.logUsage('chatResultFeedback', { result: toolCalls, kind: feedback.kind }); - }); - context.subscriptions.push(septicChat); -} \ No newline at end of file diff --git a/client/src/commands.ts b/client/src/commands.ts index a7131036..786c6108 100644 --- a/client/src/commands.ts +++ b/client/src/commands.ts @@ -7,6 +7,8 @@ import * as vscode from "vscode"; import * as protocol from "./protocol"; import { LanguageClient } from "vscode-languageclient/node"; import { generateCalc } from './lm'; +import * as fs from 'fs'; +import * as path from 'path'; export function registerCommandDetectCycles(context: vscode.ExtensionContext, client: LanguageClient) { vscode.commands.registerCommand("septic.detectCycles", async () => { @@ -135,11 +137,50 @@ export function registerCommandGenerateCalc(context: vscode.ExtensionContext, cl }); } +export function registerCommandCopyInstructionsAndPrompts(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.commands.registerCommand('septic.copyInstructionsAndPrompts', async () => { + const wsFolders = vscode.workspace.workspaceFolders; + if (!wsFolders || wsFolders.length === 0) { + vscode.window.showErrorMessage('No workspace folder found.'); + return; + } + + const wsPath = wsFolders[0].uri.fsPath; + const githubDir = path.join(wsPath, '.github'); + const instructionsSrc = path.join(context.extensionPath, 'public', 'instructions'); + const promptSrc = path.join(context.extensionPath, 'public', 'prompts'); + const instructionsDest = path.join(githubDir, 'instructions'); + const promptDest = path.join(githubDir, 'prompts'); + try { + await fs.promises.mkdir(instructionsDest, { recursive: true }); + await fs.promises.mkdir(promptDest, { recursive: true }); + let dir = await fs.promises.opendir(promptSrc) + for await (const entry of dir) { + if (entry.isFile()) { + await fs.promises.copyFile(path.join(promptSrc, entry.name), path.join(promptDest, entry.name)); + } + } + dir = await fs.promises.opendir(instructionsSrc) + for await (const entry of dir) { + if (entry.isFile()) { + await fs.promises.copyFile(path.join(instructionsSrc, entry.name), path.join(instructionsDest, entry.name)); + } + } + vscode.window.showInformationMessage('Instructions and prompt files copied to .github folder.'); + } catch (err) { + vscode.window.showErrorMessage('Failed to copy files: ' + err.message); + } + }) + ); +} + export function registerAllCommands(context: vscode.ExtensionContext, client: LanguageClient) { registerCommandDetectCycles(context, client); registerCommandCompareCnfg(context, client); registerCommandOpcTagList(context, client); registerCommandGenerateCalc(context, client); + registerCommandCopyInstructionsAndPrompts(context); } diff --git a/client/src/extension.ts b/client/src/extension.ts index 4c23f398..c457bf1e 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -14,7 +14,6 @@ import { import { registerAllCommands } from './commands'; import { registerRequestHandlers } from "./requests"; import { registerChatTools } from './tools'; -import { registerSepticChatParticipant } from './chatParticipant'; let client: LanguageClient; @@ -55,7 +54,6 @@ export function activate(context: vscode.ExtensionContext) { registerChatTools(context, client) registerAllCommands(context, client); registerRequestHandlers(client); - registerSepticChatParticipant(context, client) client.start(); } diff --git a/client/src/toolsPrompt.tsx b/client/src/toolsPrompt.tsx deleted file mode 100644 index d653e922..00000000 --- a/client/src/toolsPrompt.tsx +++ /dev/null @@ -1,425 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Equinor ASA - * Copyright (c) Microsoft Corporation. All rights reserved. [vscode-extension-samples/chat-sample] - * Licensed under the MIT License. See LICENSE in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { - AssistantMessage, - BasePromptElementProps, - Chunk, - PrioritizedList, - PromptElement, - PromptElementProps, - PromptMetadata, - PromptPiece, - PromptReference, - PromptSizing, - ToolCall, - ToolMessage, - UserMessage, -} from '@vscode/prompt-tsx'; -import { ToolResult } from '@vscode/prompt-tsx/dist/base/promptElements'; -import * as vscode from 'vscode'; -import { isTsxToolUserMetadata } from './chatParticipant'; -import { LanguageClient } from 'vscode-languageclient/node'; -import * as protocol from './protocol'; -import { isScgConfig } from './scg'; - -export interface ToolCallRound { - response: string; - toolCalls: vscode.LanguageModelToolCallPart[]; -} - -export interface ToolUserProps extends BasePromptElementProps { - request: vscode.ChatRequest; - context: vscode.ChatContext; - client: LanguageClient; - toolCallRounds: ToolCallRound[]; - toolCallResults: Record; -} - -export class ToolUserPrompt extends PromptElement { - render(_state: void, _sizing: PromptSizing) { - let instructions; - switch (this.props.request.command) { - case 'calculation': - instructions = ; - break; - case 'scg': - instructions = ; - break; - // Add more cases here for different commands - default: - instructions = No specific instructions available for this command.; - } - - return ( - <> - - - {instructions} - - Instructions:
- - The user will ask a question, or ask you to perform a task, and it may - require lots of research to answer correctly. There is a selection of - tools that let you perform actions or retrieve helpful context to answer - the user's question.
- - If you aren't sure which tool is relevant, you can call multiple - tools. You can call tools repeatedly to take actions or gather as much - context as needed until you have completed the task fully. Don't give up - unless you are sure the request cannot be fulfilled with the tools you - have.
- - Don't make assumptions about the situation- gather context first, then - perform the task or answer the question.
- - Don't ask the user for confirmation to use tools, just use them.
- - Use all the necessary tools before responding to the user.
-
- {this.props.request.prompt} - - - ); - } -} - -interface ToolCallsProps extends BasePromptElementProps { - toolCallRounds: ToolCallRound[]; - toolCallResults: Record; - toolInvocationToken: vscode.ChatParticipantToolToken | undefined; -} - -const dummyCancellationToken: vscode.CancellationToken = new vscode.CancellationTokenSource().token; - -/** - * Render a set of tool calls, which look like an AssistantMessage with a set of tool calls followed by the associated UserMessages containing results. - */ -class ToolCalls extends PromptElement { - async render(_state: void, _sizing: PromptSizing) { - if (!this.props.toolCallRounds.length) { - return undefined; - } - - // Note- for the copilot models, the final prompt must end with a non-tool-result UserMessage - return <> - {this.props.toolCallRounds.map(round => this.renderOneToolCallRound(round))} - Above is the result of calling one or more tools. The user cannot see the results, so you should explain them to the user if referencing them in your answer. - ; - } - - private renderOneToolCallRound(round: ToolCallRound) { - const assistantToolCalls: ToolCall[] = round.toolCalls.map(tc => ({ type: 'function', function: { name: tc.name, arguments: JSON.stringify(tc.input) }, id: tc.callId })); - return ( - - {round.response} - {round.toolCalls.map(toolCall => - )} - ); - } -} - -interface ToolResultElementProps extends BasePromptElementProps { - toolCall: vscode.LanguageModelToolCallPart; - toolInvocationToken: vscode.ChatParticipantToolToken | undefined; - toolCallResult: vscode.LanguageModelToolResult | undefined; -} - -/** - * One tool call result, which either comes from the cache or from invoking the tool. - */ -class ToolResultElement extends PromptElement { - async render(state: void, sizing: PromptSizing): Promise { - const tool = vscode.lm.tools.find(t => t.name === this.props.toolCall.name); - if (!tool) { - console.error(`Tool not found: ${this.props.toolCall.name}`); - return Tool not found; - } - - const tokenizationOptions: vscode.LanguageModelToolTokenizationOptions = { - tokenBudget: sizing.tokenBudget, - countTokens: async (content: string) => sizing.countTokens(content), - }; - - const toolResult = this.props.toolCallResult ?? - await vscode.lm.invokeTool(this.props.toolCall.name, { input: this.props.toolCall.input, toolInvocationToken: this.props.toolInvocationToken, tokenizationOptions }, dummyCancellationToken); - - return ( - - - - - ); - } -} - -export class ToolResultMetadata extends PromptMetadata { - constructor( - public toolCallId: string, - public result: vscode.LanguageModelToolResult, - ) { - super(); - } -} - -interface HistoryProps extends BasePromptElementProps { - priority: number; - context: vscode.ChatContext; -} - -/** - * Render the chat history, including previous tool call/results. - */ -class History extends PromptElement { - render(_state: void, _sizing: PromptSizing) { - return ( - - {this.props.context.history.map((message) => { - if (message instanceof vscode.ChatRequestTurn) { - return ( - <> - {} - {message.prompt} - - ); - } else if (message instanceof vscode.ChatResponseTurn) { - const metadata = message.result.metadata; - if (isTsxToolUserMetadata(metadata) && metadata.toolCallsMetadata.toolCallRounds.length > 0) { - return ; - } - - return {chatResponseToString(message)}; - } - })} - - ); - } -} - -/** - * Convert the stream of chat response parts into something that can be rendered in the prompt. - */ -function chatResponseToString(response: vscode.ChatResponseTurn): string { - return response.response - .map((r) => { - if (r instanceof vscode.ChatResponseMarkdownPart) { - return r.value.value; - } else if (r instanceof vscode.ChatResponseAnchorPart) { - if (r.value instanceof vscode.Uri) { - return r.value.fsPath; - } else { - return r.value.uri.fsPath; - } - } - - return ''; - }) - .join(''); -} - -interface PromptReferencesProps extends BasePromptElementProps { - references: ReadonlyArray; - excludeReferences?: boolean; -} - -/** - * Render references that were included in the user's request, eg files and selections. - */ -class PromptReferences extends PromptElement { - render(_state: void, _sizing: PromptSizing): PromptPiece { - return ( - - {this.props.references.map(ref => ( - - ))} - - ); - } -} - -interface PromptReferenceProps extends BasePromptElementProps { - ref: vscode.ChatPromptReference; - excludeReferences?: boolean; -} - -class PromptReferenceElement extends PromptElement { - async render(_state: void, _sizing: PromptSizing): Promise { - const value = this.props.ref.value; - if (value instanceof vscode.Uri) { - const fileContents = (await vscode.workspace.fs.readFile(value)).toString(); - return ( - - {!this.props.excludeReferences && } - {value.fsPath}:
- ```
- {fileContents}
- ```
-
- ); - } else if (value instanceof vscode.Location) { - const rangeText = (await vscode.workspace.openTextDocument(value.uri)).getText(value.range); - return ( - - {!this.props.excludeReferences && } - {value.uri.fsPath}:{value.range.start.line + 1}-$
- {value.range.end.line + 1}:
- ```
- {rangeText}
- ``` -
- ); - } else if (typeof value === 'string') { - return {value}; - } - } -} - -type TagProps = PromptElementProps<{ - name: string; -}>; - -class Tag extends PromptElement { - private static readonly _regex = /^[a-zA-Z_][\w.-]*$/; - - render() { - const { name } = this.props; - - if (!Tag._regex.test(name)) { - throw new Error(`Invalid tag name: ${this.props.name}`); - } - - return ( - <> - {'<' + name + '>'}
- <> - {this.props.children}
- - {''}
- - ); - } -} - - -class CalculationInstructions extends PromptElement { - render() { - return ( - - Calculation instructions:
- - Septic configuration supports execution of calculations. A calculation is written as a string inside a calculation object and the content of the string is executed by the MPC.
- - Format of calculation object:
- CalcPvr: %calculation name %
- Text1= "% description %"
- Text2= ""
- Alg= "% calculation %" - - The calculation is written in a simple language that supports basic arithmetic operations, variables, and functions. The following rules apply and must be strictly followed:
- %%% start rules %%%
- - Supported operators: +, -, *, /, %
- - Supported comparison operators: ==, {'>'}, {'>='}, {'<='}, {'<'}
- - Supported logical functions: and(condition1, condition2, ..., conditionN), or(condition1, condition2, ..., conditionN), not(condition)
- - Grouping with parentheses: (...)
- - Variables: Refers to objects in the configuration file. It is allowed with jinja expressions in variable names, but not only jinja. Example: {`{{ Jinja }}`}VariableName
- - All non-zero values are considered true
- - There is a set of available functions. All functions returns a float
- - Arguments to function are separated by commas
- - Calculations are insensitive to whitespace
- - The if function is written as follows: if(condition, true_value, false_value) both true_value and false_value are evaluated, but only the correct one is returned, thus functions that sets values must not be used within the if function and instead get the result of the if function as input.
- %%% end rules %%%
- %%% examples %%%
- Examples of correct calculations:
- setmeas(Var1, if(Var2 {'>'} Var1, Var2, Var3))
- setmode(Var1, if(getmode(Var1) {'>'} 3, 3, 0),1)
- and(Var2 {'>'} Var1, Var3 == 1)
- or(Var2 {'>'} Var1, Var3 == 1)
- Examples of incorrect calculations:
- if(Var2 {'>'} Var1, setmeas(Var1, Var2), Var3)
- if(getmode(Var1) {'>'} 3, setmode(Var1, 3), setmode(Var1, 1))
- Var2 {'>'} Var1 and Var3 == 1
- Var2 {'>'} Var1 or Var3 == 1
- %%% end examples %%%
- - Use the available tools to get the relevant functions and variables. Never ask for confirmation to use the tools.
- - Always validate all calculations. Repeat the validation after each change. Never ask for confirmation to use the tool.
- - If the calculation require new variables not in the context, provide a list of the new variables that needs to be added when validating the calculation.
- - The calculation result is stored in a variable if a variable with the same name as the calculation object is defined in the configuration as an Evr. Always add new variables to store the result of the calculations
- - Summarize the all calculations and new variables when the results are ready. Format new variables as Evr objects. Group variables and calculation such that it is easy for the user to insert into the configuration
- - Always validate all calculations. Repeat the validation after each change. Never ask for confirmation to use the tool.
-
- ); - } -} - -interface ScgInstructionsProps extends BasePromptElementProps { - client: LanguageClient; - refs: ReadonlyArray; -} - -class ScgInstructions extends PromptElement { - async render() { - const uri = vscode.window.activeTextEditor?.document.uri.toString(); - const context = await this.props.client.sendRequest(protocol.getContext, {uri: uri}); - const scgInputRefs = this.props.refs.filter(ref => ref.value instanceof vscode.Uri && isScgConfig(ref.value.fsPath)); - const refs = scgInputRefs.length > 0 ? undefined : context ? : undefined; - return ( - - Septic Config Generator:
- - Septic Config Generator (SCG) is a tool that enables generating Septic config files based on a set of templates that use information from one or more sources
- - SCG is based on the MiniJinja template engine that use the syntax and behavior of the Jinja2 template engine
- - A context is defined by a configuration file in yaml format that contains the information that the SCG tool need to generate the config files
- %%% start example scg config %%%
- outputfile: example.cnfg
- templatepath: templates
- adjustspacing: true
- verifycontent: true
-
- counters:
- - name: mycounter
- value: 0
-
- sources:
- - filename: example.xlsx
- id: wells
- sheet: Sheet1
- - filename: example.csv
- id: flowlines
- delimiter: ";"
-
- layout:
- - name: 010_System.cnfg
- - name: 020_SopcProc.cnfg
- - name: 030_SopcProc_well.cnfg
- source: wells
- include:
- - D01
- - D02
- - name: 040_SopcProc_flowline.cnfg
- source: flowlines
- %%% end example scg config %%%
- - outputfile (optional string): The file that will be generated. Writes to stdout if not provided.
- - templatepath (string): The directory that contains all template files.
- - adjustspacing (boolean, default: true): Specifies whether to ensure exactly one newline between rendered template files. If false, then the rendering will default to MiniJinja's behaviour.
- - verifycontent (boolean, default: true): Whether to report differences from an already existing rendered file. Will ask before replacing with the new content. Set to false to overwrite existing file without checking for changes.
- - counters (optional list of counter structs): Contains a list of global auto-incrementing counter functions.
- - sources (list of source structs): Contains a list of source file configurations. Filename is relative to the configuration file. Always ensure that the path provided to the tools is correct by verifying it against the expected path based on the configuration file. For example, if the configuration file is located at `/path/to/config.yaml` and the filename is `data.csv`, the full path should be `/path/to/data.csv`.
- - layout (list of template structs): Contains a list of templates in the order they should be rendered.
- {refs} - Instructions: - - Use the available tools to get the correct information to fulfill the user request.
- - Always read in a source file before updating the values to check column and index names if not already in the context.
- - The content of a source file is in csv format. The first row of the source file is the header (column names) and the first column is the index (row names).
- - Always combine all updates to a source file in one call to the tool.
- - Answer only based on the information in the context.
- - Always use jinja2 formatting in the templates.
-
- ); - } -} - - diff --git a/package.json b/package.json index e3398d18..40c7a553 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,10 @@ { "command": "septic.generateCalc", "title": "Septic: Generate Calc" + }, + { + "command": "septic.copyInstructionsAndPrompts", + "title": "Septic: Copy Instructions and Prompts to .github" } ], "configuration": { @@ -178,27 +182,6 @@ "https://raw.githubusercontent.com/equinor/septic-config-generator/refs/heads/main/docs/config.schema.json": "/*/scg/*.yaml" } }, - "chatParticipants": [ - { - "id": "septic.chat", - "name": "septic", - "fullName": "Septic", - "description": "Chatbot for Septic related topics using tools", - "isSticky": true, - "commands": [ - { - "name": "calculation", - "description": "Generate and update calculations in Septic configs", - "isSticky": true - }, - { - "name": "scg", - "description": "Documentation, updates and answers realted to Septic Config Generator tool and contexts", - "isSticky": true - } - ] - } - ], "languageModelTools": [ { "name": "septic-tools_validate_calculation", @@ -206,9 +189,9 @@ "septic", "calculation" ], - "toolReferenceName": "validateCalculation", + "toolReferenceName": "validateSepticCalculation", "displayName": "Validate Calculation", - "modelDescription": "Validates a given calculation and outputs the diagnostic messages that describes the errors and warnings in the calculation.", + "modelDescription": "Validates a given calculation in the Septic config language and returns a list of diagnostic messages that describes the errors in the calculation. Should only be used for Septic calculations in .cnfg files.", "canBeReferencedInPrompt": true, "inputSchema": { "type": "object", @@ -234,9 +217,10 @@ "septic", "calculation" ], - "toolReferenceName": "getFunctions", - "displayName": "Get functions", - "modelDescription": "Returns a list of all available functions for use in calculations with description.", + "toolReferenceName": "getSepticFunctionDocumentation", + "displayName": "Get Septic functions documentation", + "modelDescription": "Get the documentation for all functions that can be used in Septic calculations. Returns a list with all function with documentation of arguments and functional description.", + "canBeReferencedInPrompt": true, "inputSchema": { "type": "object", "properties": {} @@ -248,9 +232,10 @@ "septic", "calculation" ], - "toolReferenceName": "getVariables", - "displayName": "Get variables in context", - "modelDescription": "Returns a list of all available variables in the current context with description.", + "toolReferenceName": "getSepticVariables", + "displayName": "Get Septic variables in context", + "modelDescription": "Gets all valid references to objects in the current Septic context. Returns a list of all available variables in the current context with description.", + "canBeReferencedInPrompt": true, "inputSchema": { "type": "object", "properties": {} diff --git a/public/instructions/Septic.instructions.md b/public/instructions/Septic.instructions.md new file mode 100644 index 00000000..c17b19a3 --- /dev/null +++ b/public/instructions/Septic.instructions.md @@ -0,0 +1,35 @@ +--- +applyTo: '**/*.cnfg' +--- +# Configuration format and calculation syntax + +## Configuration file format: + +The configuration file is a plain text file that defines the objects and their attributes. + +Configuration of objects: + +* A new object is defined by "ObjectType: ObjectName" +* An object's attributes are defined by adding lines with "AttributeName= AttributeValue" beneath the object definition. +* Attribute values are one of the following types: string ("value") int (1, 2) floats (1.2, 5.3) enums (DEFAULT, BOOL, etc.). It can also be a list of the described data types. List are defined with the number of items followed by the values space separated +* Jinja expressions in object names are allowed Example: `{{ Something }}`ObjectName +* Always use camel case when suggesting object names + + +## Calculation syntax: + +Calculations are defined in the configuration file as follows: Alg= "..." + +Always follow the following rules and syntax when suggesting code for calculations + +### Rules +* Supported operators: +, -, *, /, % +* Supported comparison operators: ==, >, >=, <=, < +* Supported logical functions: and(condition1, condition2, ..., conditionN), or(condition1, condition2, ..., conditionN), not(condition) +* Grouping with parentheses: (...) +* References to objects in the configuration file are allowed by using the object name +* All non-zero values are considered true +* All functions return a float +* Arguments in functions are separated by commas +* Calculations are insensitive to whitespace +* The if function is written as follows: if(condition, true_value, false_value) both true_value and false_value are evaluated, but only the correct one is returned, thus functions that sets values cannot be used within the if function and should instead get the result of the if function as input. \ No newline at end of file diff --git a/public/prompts/SepticCalc.prompt.md b/public/prompts/SepticCalc.prompt.md new file mode 100644 index 00000000..52bb5cd8 --- /dev/null +++ b/public/prompts/SepticCalc.prompt.md @@ -0,0 +1,14 @@ +--- +mode: agent +description: Generate a Septic calculation according to the users instructions. +--- + +Generate a Septic calculation according to the users instructions. + +Follow the instructions given in [Septic Calculation Instructions](../instructions/septic.instructions.md). + +Use #getSepticFunctionDocumentation to understand the available functions and their parameters. + +Use #getSepticVariables to understand the available variables and their context. + +Always ensure to validate the output against the provided instructions and diagnostic feedback from the editor. \ No newline at end of file