diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 2f3a862a07..d2c9d21bc6 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -56,6 +56,7 @@ const remixLib = require('@remix-project/remix-lib') import {QueryParams} from '@remix-project/remix-lib' import {SearchPlugin} from './app/tabs/search' +import { SuggestionService } from './app/plugins/copilot/suggestion-service/suggestion-service' const Storage = remixLib.Storage const RemixDProvider = require('./app/files/remixDProvider') @@ -186,8 +187,9 @@ class AppComponent { // ----------------- ContractFlattener ---------------------------- const contractFlattener = new ContractFlattener() - // ----------------- Open AI -------------------------------------- + // ----------------- AI -------------------------------------- const openaigpt = new OpenAIGpt() + const copilotSuggestion = new SuggestionService() // ----------------- import content service ------------------------ const contentImport = new CompilerImports() @@ -310,7 +312,8 @@ class AppComponent { compilationDetails, contractFlattener, solidityScript, - openaigpt + openaigpt, + copilotSuggestion ]) // LAYOUT & SYSTEM VIEWS diff --git a/apps/remix-ide/src/app/plugins/copilot/suggestion-service/copilot-suggestion.ts b/apps/remix-ide/src/app/plugins/copilot/suggestion-service/copilot-suggestion.ts new file mode 100644 index 0000000000..1d1c3caa49 --- /dev/null +++ b/apps/remix-ide/src/app/plugins/copilot/suggestion-service/copilot-suggestion.ts @@ -0,0 +1,44 @@ +import {Plugin} from '@remixproject/engine' +import {SuggestionService, SuggestOptions} from './suggestion-service' +const _paq = (window._paq = window._paq || []) //eslint-disable-line + +const profile = { + name: 'copilot-suggestion', + displayName: 'copilot-suggestion', + description: 'copilot-suggestion', + methods: ['suggest'] +} + +export class CopilotSuggestion extends Plugin { + service: SuggestionService + constructor() { + super(profile) + this.service = new SuggestionService() + this.service.events.on('progress', (data) => { + this.call('terminal', 'log', {type: 'info', value: `loading Solidity copilot: ${(data.loaded / data.total) * 100}% done.` }) + }) + this.service.events.on('done', (data) => { + this.call('terminal', 'log', { type: 'info', value: `Solidity copilot loaded.`}) + }) + this.service.events.on('ready', (data) => { + this.call('terminal', 'log', { type: 'info', value: `Solidity copilot ready to use.`}) + }) + } + + async suggest(content: string) { + const options: SuggestOptions = { + do_sample: false, + top_k: 0, + temperature: await this.call('settings', 'get', 'settings/copilot/suggest/temperature'), + max_new_tokens: await this.call('settings', 'get', 'settings/copilot/suggest/max_new_tokens') + } + return this.service.suggest(content, options) + } + + async init() { + return this.service.init() + } + + async uninstall() { + } +} \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/suggestion-service/suggestion-service.ts b/apps/remix-ide/src/app/plugins/copilot/suggestion-service/suggestion-service.ts similarity index 87% rename from libs/remix-ui/editor/src/lib/suggestion-service/suggestion-service.ts rename to apps/remix-ide/src/app/plugins/copilot/suggestion-service/suggestion-service.ts index 93d4048ac0..374b4434fe 100644 --- a/libs/remix-ui/editor/src/lib/suggestion-service/suggestion-service.ts +++ b/apps/remix-ide/src/app/plugins/copilot/suggestion-service/suggestion-service.ts @@ -1,15 +1,15 @@ import EventEmitter from 'events' +export type SuggestOptions = { max_new_tokens: number, temperature: number, top_k: number, do_sample: boolean } + export class SuggestionService { worker: Worker responses: Array events: EventEmitter constructor() { - console.log('SuggestionService instanciate worker') this.worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' }); - this.init() this.events = new EventEmitter() this.responses = [] } @@ -69,16 +69,16 @@ export class SuggestionService { }) } - suggest (content: string, max_new_tokens: number, temperature: number, top_k: number, do_sample: boolean) { + suggest (content: string, options: SuggestOptions) { return new Promise((resolve, reject) => { this.worker.postMessage({ id: this.responses.length, cmd: 'suggest', text: content, - max_new_tokens, - temperature, - top_k, - do_sample + max_new_tokens: options.max_new_tokens, + temperature: options.temperature, + top_k: options.top_k, + do_sample: options.do_sample }) this.responses.push((error, result) => { if (error) return reject(error) diff --git a/libs/remix-ui/editor/src/lib/suggestion-service/worker.js b/apps/remix-ide/src/app/plugins/copilot/suggestion-service/worker.js similarity index 100% rename from libs/remix-ui/editor/src/lib/suggestion-service/worker.js rename to apps/remix-ide/src/app/plugins/copilot/suggestion-service/worker.js diff --git a/apps/remix-ide/src/app/tabs/settings-tab.tsx b/apps/remix-ide/src/app/tabs/settings-tab.tsx index cfc168054a..8ba30c549c 100644 --- a/apps/remix-ide/src/app/tabs/settings-tab.tsx +++ b/apps/remix-ide/src/app/tabs/settings-tab.tsx @@ -63,6 +63,7 @@ module.exports = class SettingsTab extends ViewPlugin { updateComponent(state: any) { return ( { - this.props.plugin.call('terminal', 'log', {type: 'info', value: `loading Solidity copilot: ${(data.loaded / data.total) * 100}% done.` }) - }) - this.suggestionService.events.on('done', (data) => { - this.props.plugin.call('terminal', 'log', { type: 'info', value: `Solidity copilot loaded.`}) - }) - this.suggestionService.events.on('ready', (data) => { - this.props.plugin.call('terminal', 'log', { type: 'info', value: `Solidity copilot ready to use.`}) - }) + this.monaco = monaco } async provideInlineCompletions(model: monacoTypes.editor.ITextModel, position: monacoTypes.Position, context: monacoTypes.languages.InlineCompletionContext, token: monacoTypes.CancellationToken): Promise> { @@ -32,7 +20,7 @@ export class RemixInLineCompletionProvider implements monacoTypes.languages.Inli endColumn: position.column, }); - const result = await this.suggestionService.suggest(word, 3, 0.5, 5, false) + const result = await this.props.plugin.call('copilot-suggestion', 'suggest', word) const generatedText = (result as any).output[0].generated_text as string console.log(word, result) diff --git a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx index 91d423566f..bc1e4c6508 100644 --- a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx +++ b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx @@ -1,3 +1,4 @@ +import {ViewPlugin} from '@remixproject/engine-web' import React, {useState, useReducer, useEffect, useCallback} from 'react' // eslint-disable-line import {labels, textDark, textSecondary} from './constants' @@ -6,6 +7,9 @@ import './remix-ui-settings.css' import { generateContractMetadat, personal, + copilotActivate, + copilotMaxNewToken, + copilotTemperature, textWrapEventAction, useMatomoAnalytics, saveTokenToast, @@ -23,10 +27,10 @@ import {RemixUiLocaleModule, LocaleModule} from '@remix-ui/locale-module' import {FormattedMessage, useIntl} from 'react-intl' import {GithubSettings} from './github-settings' import {EtherscanSettings} from './etherscan-settings' -import {CustomTooltip} from '@remix-ui/helper' /* eslint-disable-next-line */ export interface RemixUiSettingsProps { + plugin: ViewPlugin config: any editor: any _deps: any @@ -122,6 +126,22 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => { textWrapEventAction(props.config, props.editor, event.target.checked, dispatch) } + const onchangeCopilotActivate = (event) => { + copilotActivate(props.config, event.target.checked, dispatch) + if (event.target.checked) props.plugin.call('copilot-suggestion', 'init') + else { + props.plugin.call('copilot-suggestion', 'uninstall') + } + } + + const onchangeCopilotMaxNewToken = (event) => { + copilotMaxNewToken(props.config, event.target.value, dispatch) + } + + const onchangeCopilotTemperature = (event) => { + copilotTemperature(props.config, event.target.value, dispatch) + } + const onchangePersonal = (event) => { personal(props.config, event.target.checked, dispatch) } @@ -377,6 +397,59 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => { saveIpfsSettingsToast(props.config, dispatchToast, ipfsUrl, ipfsProtocol, ipfsPort, ipfsProjectId, ipfsProjectSecret) } + const isCopilotActivated = props.config.get('settings/copilot/suggest/activate') || false + const copilotMaxnewToken = props.config.get('settings/copilot/suggest/max_new_tokens') || 5 + const copilotTemperatureValue = props.config.get('settings/copilot/suggest/temperature') || 0.5 + const copilotSettings = () => ( +
+
+
+ +
+ +
+
+
+
+ + +
+
+
+
+ +
+
+
+
+ + +
+
+
+
+ +
+
+
+
+ + +
+
+
+
+ +
+
+ ) + const ipfsSettings = () => (
@@ -481,6 +554,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => { {ipfsSettings()} + {copilotSettings()}
) } diff --git a/libs/remix-ui/settings/src/lib/settingsAction.ts b/libs/remix-ui/settings/src/lib/settingsAction.ts index 87ecd3c0d3..36ec1cbf0b 100644 --- a/libs/remix-ui/settings/src/lib/settingsAction.ts +++ b/libs/remix-ui/settings/src/lib/settingsAction.ts @@ -24,6 +24,21 @@ export const personal = (config, checked, dispatch) => { dispatch({ type: 'personal', payload: { isChecked: checked, textClass: checked ? textDark : textSecondary } }) } +export const copilotActivate = (config, checked, dispatch) => { + config.set('settings/copilot/suggest/activate', checked) + dispatch({ type: 'copilot/suggest/activate', payload: { isChecked: checked, textClass: checked ? textDark : textSecondary } }) +} + +export const copilotMaxNewToken = (config, checked, dispatch) => { + config.set('settings/copilot/suggest/max_new_tokens', checked) + dispatch({ type: 'copilot/suggest/max_new_tokens', payload: { isChecked: checked, textClass: checked ? textDark : textSecondary } }) +} + +export const copilotTemperature = (config, checked, dispatch) => { + config.set('settings/copilot/suggest/temperature', checked) + dispatch({ type: 'copilot/suggest/temperature', payload: { isChecked: checked, textClass: checked ? textDark : textSecondary } }) +} + export const useMatomoAnalytics = (config, checked, dispatch) => { config.set('settings/matomo-analytics', checked) dispatch({ type: 'useMatomoAnalytics', payload: { isChecked: checked, textClass: checked ? textDark : textSecondary } }) diff --git a/libs/remix-ui/settings/src/lib/settingsReducer.ts b/libs/remix-ui/settings/src/lib/settingsReducer.ts index 4c1db50ad2..abad510431 100644 --- a/libs/remix-ui/settings/src/lib/settingsReducer.ts +++ b/libs/remix-ui/settings/src/lib/settingsReducer.ts @@ -41,6 +41,21 @@ export const initialState = { name: 'displayErrors', isChecked: true, textClass: textSecondary + }, + { + name: 'copilot/suggest/activate', + isChecked: false, + textClass: textSecondary + }, + { + name: 'copilot/suggest/max_new_tokens', + value: 5, + textClass: textSecondary + }, + { + name: 'copilot/suggest/temperature', + value: 0.5, + textClass: textSecondary } ] } @@ -128,6 +143,36 @@ export const settingReducer = (state, action) => { return { ...state } + case 'copilot/suggest/activate': + state.elementState.map(element => { + if (element.name === 'copilot/suggest/activate') { + element.isChecked = action.payload.isChecked + element.textClass = action.payload.textClass + } + }) + return { + ...state + } + case 'copilot/suggest/max_new_tokens': + state.elementState.map(element => { + if (element.name === 'copilot/suggest/max_new_tokens') { + element.value = action.payload.value + element.textClass = action.payload.textClass + } + }) + return { + ...state + } + case 'copilot/suggest/temperature': + state.elementState.map(element => { + if (element.name === 'useShowGasInEditor') { + element.value = action.payload.value + element.textClass = action.payload.textClass + } + }) + return { + ...state + } default: return initialState }