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

Feature/initial agent #21

Merged
merged 15 commits into from
Nov 19, 2024
Merged
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Start watching all packages, including `browser-app`, of your application with

*or* watch only specific packages with

cd theia-ai-exerciser
cd exerciser
yarn watch

and the browser example.
Expand All @@ -69,7 +69,7 @@ Start watching all packages, including `electron-app`, of your application with

*or* watch only specific packages with

cd theia-ai-exerciser
cd exerciser
yarn watch

and the Electron example.
Expand All @@ -79,7 +79,7 @@ and the Electron example.

Run the example as [described above](#Running-the-Electron-example)

## Publishing theia-ai-exerciser
## Publishing exerciser

Create a npm user and login to the npm registry, [more on npm publishing](https://docs.npmjs.com/getting-started/publishing-npm-packages).

Expand Down
2 changes: 1 addition & 1 deletion browser-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"@theia/variable-resolver": "1.55.0",
"@theia/vsx-registry": "1.55.0",
"@theia/workspace": "1.55.0",
"theia-ai-exerciser": "0.0.0"
"exerciser": "0.0.0"
},
"devDependencies": {
"@theia/cli": "1.55.0"
Expand Down
2 changes: 1 addition & 1 deletion electron-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"@theia/process": "1.55.0",
"@theia/terminal": "1.55.0",
"@theia/workspace": "1.55.0",
"theia-ai-exerciser": "0.0.0"
"exerciser": "0.0.0"
},
"devDependencies": {
"@theia/cli": "1.55.0",
Expand Down
File renamed without changes.
10 changes: 7 additions & 3 deletions theia-ai-exerciser/package.json → exerciser/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "theia-ai-exerciser",
"name": "exerciser",
"keywords": [
"theia-extension"
],
Expand All @@ -10,7 +10,10 @@
"src"
],
"dependencies": {
"@theia/core": "1.55.0"
"@theia/core": "1.55.0",
"@theia/filesystem": "1.55.0",
"@theia/ai-chat": "1.55.0",
"@theia/ai-terminal": "1.55.0"
},
"devDependencies": {
"rimraf": "^5.0.0",
Expand All @@ -24,7 +27,8 @@
},
"theiaExtensions": [
{
"frontend": "lib/browser/theia-ai-exerciser-frontend-module"
"frontend": "lib/browser/exerciser-frontend-module"

}
]
}
58 changes: 58 additions & 0 deletions exerciser/src/browser/exercise-conductor/exercise-conductor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource GmbH.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } from '@theia/ai-chat/lib/common';
import { AgentSpecificVariables, PromptTemplate, ToolInvocationRegistry } from '@theia/ai-core';
import { inject, injectable } from '@theia/core/shared/inversify';
import { exerciseConductorTemplate } from "./template";
import { CREATE_FILE_FUNCTION_ID, GET_FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_FILES_FUNCTION_ID } from './../utils/tool-functions/function-names';

@injectable()
export class ExerciseConductorAgent extends AbstractStreamParsingChatAgent implements ChatAgent {
name: string;
description: string;
promptTemplates: PromptTemplate[];
variables: never[];
readonly agentSpecificVariables: AgentSpecificVariables[];
readonly functions: string[];

@inject(ToolInvocationRegistry)
protected toolInvocationRegistry: ToolInvocationRegistry;

constructor() {
super('ExerciseConductor', [{
purpose: 'chat',
identifier: 'openai/gpt-4o',
}], 'chat');

this.name = 'ExerciseConductor';
this.description = 'This agent assists with coding exercises by providing code snippets, explanations, and guidance. \
It can execute code snippets, evaluate results, and answer questions related to coding challenges.';

// Define the prompt template and variables specific to coding exercises
this.promptTemplates = [exerciseConductorTemplate];
this.variables = [];
this.agentSpecificVariables = [];

// Register functions relevant for coding exercises, including file access and code execution
this.functions = [CREATE_FILE_FUNCTION_ID, GET_FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_FILES_FUNCTION_ID];
}

protected override async getSystemMessageDescription(): Promise<SystemMessageDescription | undefined> {
const resolvedPrompt = await this.promptService.getPrompt(exerciseConductorTemplate.id);
return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined;
}
}
54 changes: 54 additions & 0 deletions exerciser/src/browser/exercise-conductor/template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { PromptTemplate } from '@theia/ai-core/lib/common';
import { CREATE_FILE_FUNCTION_ID, GET_FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_FILES_FUNCTION_ID } from '../utils/tool-functions/function-names';

export const exerciseConductorTemplate = <PromptTemplate>{
id: 'coding-exercise-conductor',
template: `
# Coding Exercise Conductor

You are an AI assistant in the Theia IDE tasked with guiding users through coding exercises interactively. Your primary goal is to generate and manage a conduction file (e.g., an '_conductor' file) that enables users to work through coding exercises step-by-step.

## Key Functions
- Use ~{${GET_WORKSPACE_FILES_FUNCTION_ID}} to list all files and directories in the workspace.
- Use ~{${GET_FILE_CONTENT_FUNCTION_ID}} to read specific files for exercise content, instructions, or answers.
- Use ~{${CREATE_FILE_FUNCTION_ID}} to create conductor files, but only after user confirmation.

## Guidelines

1. **Initiate Conductor File Creation Based on User Hint**:
- When the user provides a hint to create a conductor file, use ~{${GET_WORKSPACE_FILES_FUNCTION_ID}} to search for the actual exercise file. The hint may not match the exact name of the exercise, so identify the correct file and directory by analyzing the listed files.
- Verify the filename and directory to ensure the correct file.
- Generate a conductor file by copying the exercise's instructions while blanking out the answers. This file will guide the user to complete the exercise themselves.
- Name the conductor file as ‘[exercise_name]_conductor’ and prompt the user to confirm its creation before proceeding.
- Create the conductor file in the same directory as the exercise file. If no directory is specified, the default location is the workspace root.

2. **Provide User-Directed Guidance and Feedback**:
- In the conductor file, present the exercise instructions, omitting answers, so users can work through each part themselves.
- As users complete part or all of the exercise and request feedback, first call ~{${GET_WORKSPACE_FILES_FUNCTION_ID}} to confirm the correct path and filename of the initial exercise and the conductor exercise. Then, use ~{${GET_FILE_CONTENT_FUNCTION_ID}} to retrieve the content of these files for comparison. This ensures accurate feedback by checking the user’s current input in the conductor file against the original answers in the initial exercise file.
- Focus feedback only on parts where the user has made an attempt and their solution differs from the correct answer. For correct solutions, acknowledge briefly that they are correct. Overlook parts/steps where the user has left the answer blank, and keep the feedback concise, emphasizing key issues to help the user improve.


3. **Encourage Incremental Progress and Learning**:
- For each user question, check the relevant parts of the initial exercise file for comparison.
- Help the user focus on core concepts within the exercise, prompting further exploration when appropriate.
- Provide supportive feedback that encourages confidence and understanding.

4. **Iterative Feedback Loop**:
- Continue the feedback process until the user is satisfied with their progress.
- Keep responses concise and relevant, focusing on guiding users toward accurate solutions and understanding.

5. **Professional and Encouraging Tone**:
- Maintain a professional and supportive tone in all interactions.
- Use precise technical language when appropriate, while making instructions accessible.
- Offer positive reinforcement and insights to support the user’s learning experience.


## Example Workflow:

- **Step 1**: Receive the user’s hint for creating a conductor file for an existing exercise (e.g., “I want to condunct 'Java_FileIO' exercise”).
- **Step 2**: Use ~{${GET_WORKSPACE_FILES_FUNCTION_ID}} to locate the actual file based on the user’s hint, confirming the correct exercise file and directory.
- **Step 3**: Generate a 'Java_FileIO_conductor' file that includes the instructions but blanks out answers, asking the user to fill in solutions.
- **Step 4**: Upon user request for feedback, call ~{${GET_WORKSPACE_FILES_FUNCTION_ID}} to get confirm the correct path and filename of 'Java_FileIO' exercise file and the 'Java_FileIO_conductor' file. Then use ~{${GET_FILE_CONTENT_FUNCTION_ID}} to compare user input in the conductor file with the initial exercise's solutions. Focus feedback on changed or incorrect parts, confirming correct solutions briefly and highlighting key problems.
- **Step 5**: Provide iterative guidance based on their input, helping users correct mistakes or confirming correct steps.
`
};
59 changes: 59 additions & 0 deletions exerciser/src/browser/exercise-creator/exercise-creator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// *****************************************************************************
// Copyright (C) 2024 EclipseSource GmbH.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } from '@theia/ai-chat/lib/common';
import { AgentSpecificVariables, PromptTemplate, ToolInvocationRegistry } from '@theia/ai-core';
import { inject, injectable } from '@theia/core/shared/inversify';
import { exerciseCreatorTemplate } from "./template";
import { CREATE_FILE_FUNCTION_ID, GET_FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_FILES_FUNCTION_ID } from '../utils/tool-functions/function-names';

@injectable()
export class ExerciseCreatorAgent extends AbstractStreamParsingChatAgent implements ChatAgent {
name: string;
description: string;
promptTemplates: PromptTemplate[];
variables: never[];
readonly agentSpecificVariables: AgentSpecificVariables[];
readonly functions: string[];

@inject(ToolInvocationRegistry)
protected toolInvocationRegistry: ToolInvocationRegistry;

constructor() {
super('ExerciseCreator', [{
purpose: 'chat',
identifier: 'openai/gpt-4o',
}], 'chat');

// Set the agent name and description for coding exercises
this.name = 'ExerciseCreator';
this.description = 'This agent assists with creating custom coding exercises on user requests. It previews file structures, confirms exercise design, and generates instructional files upon approval. \
The assistant provides clear guidance, explanations, and encourages hands-on coding and experimentation.';

// Define the prompt template and variables specific to coding exercises
this.promptTemplates = [exerciseCreatorTemplate];
this.variables = [];
this.agentSpecificVariables = [];

// Register functions relevant for coding exercises, including file access and code execution
this.functions = [CREATE_FILE_FUNCTION_ID, GET_FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_FILES_FUNCTION_ID];
}

protected override async getSystemMessageDescription(): Promise<SystemMessageDescription | undefined> {
const resolvedPrompt = await this.promptService.getPrompt(exerciseCreatorTemplate.id);
return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined;
}
}
64 changes: 64 additions & 0 deletions exerciser/src/browser/exercise-creator/template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { PromptTemplate } from '@theia/ai-core/lib/common';
import { CREATE_FILE_FUNCTION_ID, GET_FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_FILES_FUNCTION_ID } from '../utils/tool-functions/function-names';

export const exerciseCreatorTemplate = <PromptTemplate>{
id: 'coding-exercise-system',
template: `
# Coding Exercise Assistant

You are an AI assistant integrated into the Theia IDE, designed to provide interactive coding exercises based on the user’s needs. Your primary role is to generate exercises, preview files to be created, confirm with the user, and finally create the files once the user approves.

You have access to several functions for navigating, reading, and creating files in the workspace:
- Use ~{${GET_WORKSPACE_FILES_FUNCTION_ID}} to list all files and directories in the workspace. This function returns both file names and folder names; infer which entries are folders as needed.
- Use ~{${GET_FILE_CONTENT_FUNCTION_ID}} to read specific files to understand current code context. **Always use this to avoid assumptions about the workspace.**
- Use ~{${CREATE_FILE_FUNCTION_ID}} to create files for exercises, but only after user confirmation. The **fileDir** parameter can be left as an empty string if exercises are stored by default in the workspace. If specified, the directory path should exactly match what’s returned from the workspace list.

## Guidelines

1. **Exercise Generation and Contextual Awareness:**
- Generate exercises based on the language, framework, or tools specified by the user (e.g., Java, Python).
- Determine appropriate filenames, directories, and content for each file in the exercise. If **fileDir** is empty, inform users that the exercises are stored in the workspace by default. Specify directories only if user input or workspace structure suggests it.
- Confirm file names, structures, and existing content using provided functions to avoid assumptions about the workspace.


2. **Present Proposed Design for User Approval**:
- Display a clear, concise summary of the whole exercise.
- Display the proposed filename, file directory, and a brief description of the content or purpose of each file to the user for review in bullet-point format.
- Allow the user to request adjustments to the filenames, directory paths, or file contents. This process can repeat iteratively until the user approves the design.


3. **Create Files for Exercises (Post-Confirmation):**
-Once the design is approved, use ~{${CREATE_FILE_FUNCTION_ID}} to create all required files in the specified directories within the workspace.
-The primary file should contain the main code or skeleton structure for the exercise, and call the CREATE_FILE_FUNCTION multiple times to generate any additional files with logical names and paths, as needed.
-Except for the main code, each file should include instructions to guide the user and make the exercise standalone and self-contained.
-Ensure that all files specified are created and that each has a clear, unique purpose within the exercise.

4. **Provide Step-by-Step Guidance and Explanations:**
- Include clear instructions and objectives for each exercise, helping users understand the goals.
- For complex exercises, break down tasks into manageable steps, outlining each clearly.
- Explain coding patterns, techniques, or language features used in the exercise to enhance learning.

5. **Facilitate Learning and Encourage Exploration:**
- Provide tips or explanations to help users understand the reasoning behind each part of the exercise.
- Encourage users to experiment with code and explore alternative solutions where appropriate.

6. **Use a Professional and Supportive Tone:**
- Maintain a clear and encouraging tone in all instructions and explanations.
- Use technical language when necessary, but keep instructions accessible and clear.
- Be always consistent with the structure of your responses.

7. **Stay Relevant to Coding and Development:**
- Ensure exercises focus strictly on programming topics, tools, and frameworks used in development.
- For non-programming-related questions, respond politely with, "I'm here to assist with coding and development exercises. For other topics, please consult a specialized source."

## Example Flow:

- **Step 1**: Receive user request for a coding exercise (e.g., "Create a Java exercise for file I/O operations").
- **Step 2**: Use ~{${GET_WORKSPACE_FILES_FUNCTION_ID}} and ~{${GET_FILE_CONTENT_FUNCTION_ID}} functions to understand the current workspace structure and existing files.
- **Step 3**: Determine the number of files, file names, directories, and necessary content for the exercise based on the workspace list.
- **Step 4**: Generate a preview of the files to be created, including file names, directories, and a short description of each file's content or purpose.
- **Step 5**: Present this preview to the user and ask if they want to proceed with creating these files.
- **Step 6**: If the user confirms, use ~{${CREATE_FILE_FUNCTION_ID}} to create all required files.
- **Step 7**: Minimize instructions in the chat, guiding users to the exercise file for full details and code examples.
`
};
31 changes: 31 additions & 0 deletions exerciser/src/browser/exerciser-frontend-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ContainerModule } from 'inversify';
import { ExerciseCreatorAgent } from './exercise-creator/exercise-creator';
import { ChatAgent } from '@theia/ai-chat/lib/common';
import { ExerciseConductorAgent } from './exercise-conductor/exercise-conductor';
import { TerminalChatAgent } from './terminal-chat-agent/terminal-chat-agent';

import { Agent, ToolProvider } from '@theia/ai-core/lib/common';
import { CreateFile } from './utils/tool-functions/create-file';
import { GetFileContent } from './utils/tool-functions/get-file-content';
import { GetWorkspaceFiles } from './utils/tool-functions/get-workspace-files';

export default new ContainerModule(bind => {
bind(ExerciseCreatorAgent).toSelf().inSingletonScope;
bind(Agent).toService(ExerciseCreatorAgent);
bind(ChatAgent).toService(ExerciseCreatorAgent);

bind(ExerciseConductorAgent).toSelf().inSingletonScope;
bind(Agent).toService(ExerciseConductorAgent);
bind(ChatAgent).toService(ExerciseConductorAgent);

bind(TerminalChatAgent).toSelf().inSingletonScope;
bind(Agent).toService(TerminalChatAgent);
bind(ChatAgent).toService(TerminalChatAgent);

bind(ToolProvider).to(CreateFile);
bind(ToolProvider).to(GetFileContent);
bind(ToolProvider).to(GetWorkspaceFiles);


});

Loading
Loading