|
|
@ -1,9 +1,14 @@ |
|
|
|
import * as packageJson from '../../../../../package.json' |
|
|
|
import * as packageJson from '../../../../../package.json' |
|
|
|
import { ViewPlugin } from '@remixproject/engine-web' |
|
|
|
import { ViewPlugin } from '@remixproject/engine-web' |
|
|
|
import { Plugin } from '@remixproject/engine'; |
|
|
|
import { Plugin } from '@remixproject/engine'; |
|
|
|
import { RemixAITab } from '@remix-ui/remix-ai' |
|
|
|
import { RemixAITab, ChatApi } from '@remix-ui/remix-ai' |
|
|
|
import React from 'react'; |
|
|
|
import React, { useCallback } from 'react'; |
|
|
|
import { ICompletions, IModel, RemoteInferencer, IRemoteModel } from '@remix/remix-ai-core'; |
|
|
|
import { ICompletions, IModel, RemoteInferencer, IRemoteModel, IParams, GenerationParams, CodeExplainAgent } from '@remix/remix-ai-core'; |
|
|
|
|
|
|
|
import { CustomRemixApi } from '@remix-api' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type chatRequestBufferT<T> = { |
|
|
|
|
|
|
|
[key in keyof T]: T[key] |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const profile = { |
|
|
|
const profile = { |
|
|
|
name: 'remixAI', |
|
|
|
name: 'remixAI', |
|
|
@ -11,39 +16,52 @@ const profile = { |
|
|
|
methods: ['code_generation', 'code_completion', |
|
|
|
methods: ['code_generation', 'code_completion', |
|
|
|
"solidity_answer", "code_explaining", |
|
|
|
"solidity_answer", "code_explaining", |
|
|
|
"code_insertion", "error_explaining", |
|
|
|
"code_insertion", "error_explaining", |
|
|
|
"initialize"], |
|
|
|
"initialize", 'chatPipe', 'ProcessChatRequestBuffer', 'isChatRequestPending'], |
|
|
|
events: [], |
|
|
|
events: [], |
|
|
|
icon: 'assets/img/remix-logo-blue.png', |
|
|
|
icon: 'assets/img/remix-logo-blue.png', |
|
|
|
description: 'RemixAI provides AI services to Remix IDE.', |
|
|
|
description: 'RemixAI provides AI services to Remix IDE.', |
|
|
|
kind: '', |
|
|
|
kind: '', |
|
|
|
// location: 'sidePanel',
|
|
|
|
location: 'sidePanel', |
|
|
|
documentation: 'https://remix-ide.readthedocs.io/en/latest/remixai.html', |
|
|
|
documentation: 'https://remix-ide.readthedocs.io/en/latest/remixai.html', |
|
|
|
version: packageJson.version, |
|
|
|
version: packageJson.version, |
|
|
|
maintainedBy: 'Remix' |
|
|
|
maintainedBy: 'Remix' |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export class RemixAIPlugin extends Plugin { |
|
|
|
// add Plugin<any, CustomRemixApi>
|
|
|
|
|
|
|
|
export class RemixAIPlugin extends ViewPlugin { |
|
|
|
isOnDesktop:boolean = false |
|
|
|
isOnDesktop:boolean = false |
|
|
|
aiIsActivated:boolean = false |
|
|
|
aiIsActivated:boolean = false |
|
|
|
readonly remixDesktopPluginName = 'remixAID' |
|
|
|
readonly remixDesktopPluginName = 'remixAID' |
|
|
|
remoteInferencer:RemoteInferencer = null |
|
|
|
remoteInferencer:RemoteInferencer = null |
|
|
|
isInferencing: boolean = false |
|
|
|
isInferencing: boolean = false |
|
|
|
|
|
|
|
chatRequestBuffer: chatRequestBufferT<any> = null |
|
|
|
|
|
|
|
agent: CodeExplainAgent |
|
|
|
|
|
|
|
useRemoteInferencer:boolean = false |
|
|
|
|
|
|
|
|
|
|
|
constructor(inDesktop:boolean) { |
|
|
|
constructor(inDesktop:boolean) { |
|
|
|
super(profile) |
|
|
|
super(profile) |
|
|
|
this.isOnDesktop = inDesktop |
|
|
|
this.isOnDesktop = inDesktop |
|
|
|
|
|
|
|
this.agent = new CodeExplainAgent(this) |
|
|
|
// user machine dont use ressource for remote inferencing
|
|
|
|
// user machine dont use ressource for remote inferencing
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
onActivation(): void { |
|
|
|
onActivation(): void { |
|
|
|
this.initialize(null, null, null, false) |
|
|
|
if (this.isOnDesktop) { |
|
|
|
|
|
|
|
console.log('Activating RemixAIPlugin on desktop') |
|
|
|
|
|
|
|
// this.on(this.remixDesktopPluginName, 'activated', () => {
|
|
|
|
|
|
|
|
this.useRemoteInferencer = true |
|
|
|
|
|
|
|
this.initialize(null, null, null, this.useRemoteInferencer); |
|
|
|
|
|
|
|
// })
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
console.log('Activating RemixAIPlugin on browser') |
|
|
|
|
|
|
|
this.useRemoteInferencer = true |
|
|
|
|
|
|
|
this.initialize() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async initialize(model1?:IModel, model2?:IModel, remoteModel?:IRemoteModel, useRemote?:boolean){ |
|
|
|
async initialize(model1?:IModel, model2?:IModel, remoteModel?:IRemoteModel, useRemote?:boolean){ |
|
|
|
if (this.isOnDesktop) { |
|
|
|
if (this.isOnDesktop && !this.useRemoteInferencer) { |
|
|
|
// on desktop use remote inferencer -> false
|
|
|
|
// on desktop use remote inferencer -> false
|
|
|
|
console.log('initialize on desktop') |
|
|
|
|
|
|
|
const res = await this.call(this.remixDesktopPluginName, 'initializeModelBackend', useRemote, model1, model2) |
|
|
|
const res = await this.call(this.remixDesktopPluginName, 'initializeModelBackend', useRemote, model1, model2) |
|
|
|
if (res) { |
|
|
|
if (res) { |
|
|
|
this.on(this.remixDesktopPluginName, 'onStreamResult', (value) => { |
|
|
|
this.on(this.remixDesktopPluginName, 'onStreamResult', (value) => { |
|
|
@ -60,7 +78,6 @@ export class RemixAIPlugin extends Plugin { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// on browser
|
|
|
|
|
|
|
|
this.remoteInferencer = new RemoteInferencer(remoteModel?.apiUrl, remoteModel?.completionUrl) |
|
|
|
this.remoteInferencer = new RemoteInferencer(remoteModel?.apiUrl, remoteModel?.completionUrl) |
|
|
|
this.remoteInferencer.event.on('onInference', () => { |
|
|
|
this.remoteInferencer.event.on('onInference', () => { |
|
|
|
this.isInferencing = true |
|
|
|
this.isInferencing = true |
|
|
@ -80,7 +97,7 @@ export class RemixAIPlugin extends Plugin { |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (this.isOnDesktop) { |
|
|
|
if (this.isOnDesktop && !this.useRemoteInferencer) { |
|
|
|
return await this.call(this.remixDesktopPluginName, 'code_generation', prompt) |
|
|
|
return await this.call(this.remixDesktopPluginName, 'code_generation', prompt) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return await this.remoteInferencer.code_generation(prompt) |
|
|
|
return await this.remoteInferencer.code_generation(prompt) |
|
|
@ -88,82 +105,109 @@ export class RemixAIPlugin extends Plugin { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async code_completion(prompt: string, promptAfter: string): Promise<any> { |
|
|
|
async code_completion(prompt: string, promptAfter: string): Promise<any> { |
|
|
|
if (this.isOnDesktop) { |
|
|
|
if (this.isOnDesktop && !this.useRemoteInferencer) { |
|
|
|
return await this.call(this.remixDesktopPluginName, 'code_completion', prompt, promptAfter) |
|
|
|
return await this.call(this.remixDesktopPluginName, 'code_completion', prompt, promptAfter) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return await this.remoteInferencer.code_completion(prompt, promptAfter) |
|
|
|
return await this.remoteInferencer.code_completion(prompt, promptAfter) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async solidity_answer(prompt: string): Promise<any> { |
|
|
|
async solidity_answer(prompt: string, params: IParams=GenerationParams): Promise<any> { |
|
|
|
if (this.isInferencing) { |
|
|
|
if (this.isInferencing) { |
|
|
|
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) |
|
|
|
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: `\n\nWaiting for RemixAI answer...` }) |
|
|
|
const newPrompt = await this.agent.chatCommand(prompt) |
|
|
|
|
|
|
|
|
|
|
|
let result |
|
|
|
let result |
|
|
|
if (this.isOnDesktop) { |
|
|
|
if (this.isOnDesktop && !this.useRemoteInferencer) { |
|
|
|
result = await this.call(this.remixDesktopPluginName, 'solidity_answer', prompt) |
|
|
|
result = await this.call(this.remixDesktopPluginName, 'solidity_answer', newPrompt) |
|
|
|
} else { |
|
|
|
} 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 }) |
|
|
|
if (result && params.terminal_output) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result }) |
|
|
|
// this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI Done" })
|
|
|
|
|
|
|
|
return 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) { |
|
|
|
if (this.isInferencing) { |
|
|
|
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) |
|
|
|
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: `\n\nWaiting for RemixAI answer...` }) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let result |
|
|
|
let result |
|
|
|
if (this.isOnDesktop) { |
|
|
|
if (this.isOnDesktop && !this.useRemoteInferencer) { |
|
|
|
result = await this.call(this.remixDesktopPluginName, 'code_explaining', prompt) |
|
|
|
result = await this.call(this.remixDesktopPluginName, 'code_explaining', prompt, context, params) |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
} 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 }) |
|
|
|
if (result && params.terminal_output) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result }) |
|
|
|
// this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI Done" })
|
|
|
|
|
|
|
|
return 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) { |
|
|
|
if (this.isInferencing) { |
|
|
|
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) |
|
|
|
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: `\n\nWaiting for RemixAI answer...` }) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let result |
|
|
|
let result |
|
|
|
if (this.isOnDesktop) { |
|
|
|
if (this.isOnDesktop && !this.useRemoteInferencer) { |
|
|
|
result = await this.call(this.remixDesktopPluginName, 'error_explaining', prompt) |
|
|
|
result = await this.call(this.remixDesktopPluginName, 'error_explaining', prompt) |
|
|
|
} else { |
|
|
|
} 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 }) |
|
|
|
if (result && params.terminal_output) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result }) |
|
|
|
// this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI Done" })
|
|
|
|
|
|
|
|
return result |
|
|
|
return result |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async code_insertion(msg_pfx: string, msg_sfx: string): Promise<any> { |
|
|
|
async code_insertion(msg_pfx: string, msg_sfx: string): Promise<any> { |
|
|
|
if (this.isOnDesktop) { |
|
|
|
if (this.isOnDesktop && !this.useRemoteInferencer) { |
|
|
|
return await this.call(this.remixDesktopPluginName, 'code_insertion', msg_pfx, msg_sfx) |
|
|
|
return await this.call(this.remixDesktopPluginName, 'code_insertion', msg_pfx, msg_sfx) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return await this.remoteInferencer.code_insertion(msg_pfx, msg_sfx) |
|
|
|
return await this.remoteInferencer.code_insertion(msg_pfx, msg_sfx) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// render() {
|
|
|
|
chatPipe(fn, prompt: string, context?: string, pipeMessage?: string){ |
|
|
|
// return (
|
|
|
|
if (this.chatRequestBuffer == null){ |
|
|
|
// <RemixAITab plugin={this}></RemixAITab>
|
|
|
|
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> |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|