-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Remixai chat #5241
Remixai chat #5241
Changes from 12 commits
edfb3bd
1e68ae0
118d66e
abeac7d
4c843e9
ad70e5e
39e89ab
52408f3
1348d1f
2695aaf
1743146
8c11a35
2b0f7a2
0d0dbd1
5b69872
b6eeacf
917f562
9d782ea
d2d68ce
bfa5eda
6e5ed32
707699b
b237be8
e89e938
dd061e5
2d4fbb5
8968ce6
51c4fbd
f964671
e33392f
5f0dc4d
0f0809e
d52765b
a18460d
09db2ad
f5e116e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,67 @@ | ||
import * as packageJson from '../../../../../package.json' | ||
import { ViewPlugin } from '@remixproject/engine-web' | ||
import { Plugin } from '@remixproject/engine'; | ||
import { RemixAITab } from '@remix-ui/remix-ai' | ||
import React from 'react'; | ||
import { ICompletions, IModel, RemoteInferencer, IRemoteModel } from '@remix/remix-ai-core'; | ||
import { RemixAITab, ChatApi } from '@remix-ui/remix-ai' | ||
import React, { useCallback } from 'react'; | ||
import { ICompletions, IModel, RemoteInferencer, IRemoteModel, IParams, GenerationParams, CodeExplainAgent } from '@remix/remix-ai-core'; | ||
|
||
type chatRequestBufferT<T> = { | ||
[key in keyof T]: T[key] | ||
} | ||
|
||
const profile = { | ||
name: 'remixAI', | ||
displayName: 'Remix AI', | ||
methods: ['code_generation', 'code_completion', | ||
"solidity_answer", "code_explaining", | ||
"code_insertion", "error_explaining", | ||
"initialize"], | ||
"initialize", 'chatPipe', 'ProcessChatRequestBuffer', 'isChatRequestPending'], | ||
events: [], | ||
icon: 'assets/img/remix-logo-blue.png', | ||
description: 'RemixAI provides AI services to Remix IDE.', | ||
kind: '', | ||
// location: 'sidePanel', | ||
location: 'sidePanel', | ||
documentation: 'https://remix-ide.readthedocs.io/en/latest/remixai.html', | ||
version: packageJson.version, | ||
maintainedBy: 'Remix' | ||
} | ||
|
||
export class RemixAIPlugin extends Plugin { | ||
export class RemixAIPlugin extends ViewPlugin { | ||
isOnDesktop:boolean = false | ||
aiIsActivated:boolean = false | ||
readonly remixDesktopPluginName = 'remixAID' | ||
remoteInferencer:RemoteInferencer = null | ||
isInferencing: boolean = false | ||
chatRequestBuffer: chatRequestBufferT<any> = null | ||
agent: CodeExplainAgent | ||
|
||
constructor(inDesktop:boolean) { | ||
super(profile) | ||
this.isOnDesktop = inDesktop | ||
|
||
this.agent = new CodeExplainAgent(this) | ||
// user machine dont use ressource for remote inferencing | ||
} | ||
|
||
onActivation(): void { | ||
this.initialize(null, null, null, false) | ||
if (this.isOnDesktop) { | ||
console.log('Activating RemixAIPlugin on desktop') | ||
this.on(this.remixDesktopPluginName, 'activated', () => { | ||
this.call("remixAI", 'initialize', null, null, null, false); | ||
}) | ||
} else { | ||
console.log('Activating RemixAIPlugin on browser') | ||
this.initialize() | ||
} | ||
this.setRemixAIOnSidePannel(false) | ||
} | ||
|
||
setRemixAIOnSidePannel(resize:boolean=false){ | ||
if (resize){ | ||
this.call('sidePanel', 'pinView', profile) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Waiting for some answers from David to resize the sidepanel There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we will remove it from there anyway, you don't need to do this @STetsing |
||
|
||
} else { | ||
this.call('sidePanel', 'pinView', profile) | ||
} | ||
} | ||
|
||
async initialize(model1?:IModel, model2?:IModel, remoteModel?:IRemoteModel, useRemote?:boolean){ | ||
|
@@ -60,7 +84,6 @@ export class RemixAIPlugin extends Plugin { | |
} | ||
|
||
} else { | ||
// on browser | ||
this.remoteInferencer = new RemoteInferencer(remoteModel?.apiUrl, remoteModel?.completionUrl) | ||
this.remoteInferencer.event.on('onInference', () => { | ||
this.isInferencing = true | ||
|
@@ -95,61 +118,53 @@ export class RemixAIPlugin extends Plugin { | |
} | ||
} | ||
|
||
async solidity_answer(prompt: string): Promise<any> { | ||
async solidity_answer(prompt: string, params: IParams=GenerationParams): Promise<any> { | ||
if (this.isInferencing) { | ||
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) | ||
return | ||
} | ||
|
||
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: `\n\nWaiting for RemixAI answer...` }) | ||
|
||
const newPrompt = await this.agent.chatCommand(prompt) | ||
let result | ||
if (this.isOnDesktop) { | ||
result = await this.call(this.remixDesktopPluginName, 'solidity_answer', prompt) | ||
result = await this.call(this.remixDesktopPluginName, 'solidity_answer', newPrompt) | ||
} else { | ||
result = await this.remoteInferencer.solidity_answer(prompt) | ||
result = await this.remoteInferencer.solidity_answer(newPrompt) | ||
} | ||
if (result) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result }) | ||
// this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI Done" }) | ||
if (result && params.terminal_output) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result }) | ||
return result | ||
} | ||
|
||
async code_explaining(prompt: string): Promise<any> { | ||
async code_explaining(prompt: string, context: string, params: IParams=GenerationParams): Promise<any> { | ||
if (this.isInferencing) { | ||
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) | ||
return | ||
} | ||
|
||
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: `\n\nWaiting for RemixAI answer...` }) | ||
|
||
let result | ||
if (this.isOnDesktop) { | ||
result = await this.call(this.remixDesktopPluginName, 'code_explaining', prompt) | ||
result = await this.call(this.remixDesktopPluginName, 'code_explaining', prompt, context, params) | ||
|
||
} else { | ||
result = await this.remoteInferencer.code_explaining(prompt) | ||
result = await this.remoteInferencer.code_explaining(prompt, context, params) | ||
} | ||
if (result) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result }) | ||
// this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI Done" }) | ||
if (result && params.terminal_output) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result }) | ||
return result | ||
} | ||
|
||
async error_explaining(prompt: string): Promise<any> { | ||
async error_explaining(prompt: string, context: string="", params: IParams=GenerationParams): Promise<any> { | ||
if (this.isInferencing) { | ||
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) | ||
return | ||
} | ||
|
||
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: `\n\nWaiting for RemixAI answer...` }) | ||
|
||
let result | ||
if (this.isOnDesktop) { | ||
result = await this.call(this.remixDesktopPluginName, 'error_explaining', prompt) | ||
} else { | ||
result = await this.remoteInferencer.error_explaining(prompt) | ||
result = await this.remoteInferencer.error_explaining(prompt, params) | ||
} | ||
if (result) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result }) | ||
// this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI Done" }) | ||
if (result && params.terminal_output) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result }) | ||
return result | ||
} | ||
|
||
|
@@ -161,9 +176,44 @@ export class RemixAIPlugin extends Plugin { | |
} | ||
} | ||
|
||
// render() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. need those? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This goes out from this PR |
||
// return ( | ||
// <RemixAITab plugin={this}></RemixAITab> | ||
// ) | ||
// } | ||
chatPipe(fn, prompt: string, context?: string, pipeMessage?: string){ | ||
if (this.chatRequestBuffer == null){ | ||
this.chatRequestBuffer = { | ||
fn_name: fn, | ||
prompt: prompt, | ||
context: context | ||
} | ||
if (pipeMessage) ChatApi.composer.send(pipeMessage) | ||
else { | ||
if (fn === "code_explaining") ChatApi.composer.send("Explain the current code") | ||
else if (fn === "error_explaining") ChatApi.composer.send("Explain the error") | ||
else if (fn === "solidity_answer") ChatApi.composer.send("Answer the following question") | ||
else console.log("chatRequestBuffer is not empty. First process the last request.") | ||
} | ||
} | ||
else { | ||
console.log("chatRequestBuffer is not empty. First process the last request.") | ||
} | ||
} | ||
|
||
async ProcessChatRequestBuffer(params:IParams=GenerationParams){ | ||
if (this.chatRequestBuffer != null){ | ||
const result = this[this.chatRequestBuffer.fn_name](this.chatRequestBuffer.prompt, this.chatRequestBuffer.context, params) | ||
this.chatRequestBuffer = null | ||
return result | ||
} | ||
else { | ||
console.log("chatRequestBuffer is empty.") | ||
return "" | ||
} | ||
} | ||
isChatRequestPending(){ | ||
return this.chatRequestBuffer != null | ||
} | ||
|
||
render() { | ||
return ( | ||
<RemixAITab plugin={this}></RemixAITab> | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,45 @@ | ||
// interactive code explaining and highlight security vunerabilities | ||
import * as fs from 'fs'; | ||
|
||
class CodeExplainAgent { | ||
export class CodeExplainAgent { | ||
private codebase: string[]; // list of code base file | ||
public currentFile: string; | ||
plugin | ||
|
||
constructor(codebasePath: string) { | ||
constructor(props) { | ||
this.plugin = props | ||
|
||
// git or fs | ||
this.codebase = this.loadCodebase(codebasePath); | ||
const codebase = this.loadCodebase("codebasePath"); | ||
} | ||
|
||
private loadCodebase(path: string): string[] { | ||
const files = fs.readdirSync(path); | ||
return files | ||
.filter(file => file.endsWith('.ts')) | ||
.flatMap(file => fs.readFileSync(`${path}/${file}`, 'utf-8').split('\n')); | ||
return [] | ||
} | ||
|
||
public update(currentFile, lineNumber){ | ||
|
||
} | ||
|
||
async chatCommand(prompt:string){ | ||
// change this function with indexer or related | ||
try{ | ||
if (prompt.includes('Explain briefly the current file')){ | ||
const file = await this.plugin.call('fileManager', 'getCurrentFile') | ||
const content = `Explain this code:\n ${await this.plugin.call('fileManager', 'readFile', file)}` | ||
return content | ||
} else return prompt | ||
} catch { | ||
console.log('There is No file selected') | ||
return 'There is No file selected' | ||
} | ||
} | ||
|
||
public getExplanations(currentLine: string, numSuggestions: number = 3): string[] { | ||
// process the code base explaining the current file and highlight some details | ||
const suggestions: string[] = []; | ||
return suggestions; | ||
} | ||
} | ||
|
||
// Handle file changed (significantly) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you remove all the comments?