-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4175 from ethereum/inlinecompletion
provide Inlinecompletion using an huggingface model
- Loading branch information
Showing
15 changed files
with
800 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
apps/remix-ide/src/app/plugins/copilot/suggestion-service/copilot-suggestion.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
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', 'init', 'uninstall', 'status'] | ||
} | ||
|
||
export class CopilotSuggestion extends Plugin { | ||
service: SuggestionService | ||
context: string | ||
ready: boolean | ||
constructor() { | ||
super(profile) | ||
this.service = new SuggestionService() | ||
this.context = '' | ||
this.service.events.on('progress', (data) => { | ||
this.emit('loading', data) | ||
}) | ||
this.service.events.on('done', (data) => { | ||
}) | ||
this.service.events.on('ready', (data) => { | ||
this.ready = true | ||
}) | ||
} | ||
|
||
status () { | ||
return this.ready | ||
} | ||
|
||
async suggest(content: string) { | ||
if (!await this.call('settings', 'get', 'settings/copilot/suggest/activate')) return { output: [{ generated_text: ''}]} | ||
|
||
const max_new_tokens = await this.call('settings', 'get', 'settings/copilot/suggest/max_new_tokens') | ||
const temperature = await this.call('settings', 'get', 'settings/copilot/suggest/temperature') | ||
console.log('suggest', max_new_tokens, temperature) | ||
const options: SuggestOptions = { | ||
do_sample: false, | ||
top_k: 0, | ||
temperature, | ||
max_new_tokens | ||
} | ||
return this.service.suggest(this.context ? this.context + '\n\n' + content : content, options) | ||
} | ||
|
||
async loadModeContent() { | ||
let importsContent = '' | ||
const imports = await this.call('codeParser', 'getImports') | ||
for (const imp of imports.modules) { | ||
try { | ||
importsContent += '\n\n' + (await this.call('contentImport', 'resolve', imp)).content | ||
} catch (e) { | ||
console.log(e) | ||
} | ||
} | ||
return importsContent | ||
} | ||
|
||
async init() { | ||
return this.service.init() | ||
} | ||
|
||
async uninstall() { | ||
this.service.terminate() | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
apps/remix-ide/src/app/plugins/copilot/suggestion-service/suggestion-service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import EventEmitter from 'events' | ||
|
||
export type SuggestOptions = { max_new_tokens: number, temperature: number, top_k: number, do_sample: boolean } | ||
|
||
export class SuggestionService { | ||
worker: Worker | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
responses: { [key: number]: Function } | ||
events: EventEmitter | ||
current: number | ||
constructor() { | ||
this.worker = new Worker(new URL('./worker.js', import.meta.url), { | ||
type: 'module' | ||
}); | ||
this.events = new EventEmitter() | ||
this.responses = {} | ||
this.current | ||
} | ||
|
||
terminate(): void { | ||
this.worker.terminate() | ||
this.worker = new Worker(new URL('./worker.js', import.meta.url), { | ||
type: 'module' | ||
}); | ||
} | ||
|
||
async init() { | ||
const onMessageReceived = (e) => { | ||
switch (e.data.status) { | ||
case 'initiate': | ||
console.log(e.data) | ||
this.events.emit(e.data.status, e.data) | ||
// Model file start load: add a new progress item to the list. | ||
break; | ||
|
||
case 'progress': | ||
this.events.emit(e.data.status, e.data) | ||
console.log(e.data) | ||
// Model file progress: update one of the progress items. | ||
break; | ||
|
||
case 'done': | ||
this.events.emit(e.data.status, e.data) | ||
console.log(e.data) | ||
// Model file loaded: remove the progress item from the list. | ||
break; | ||
|
||
case 'ready': | ||
this.events.emit(e.data.status, e.data) | ||
console.log(e.data) | ||
// Pipeline ready: the worker is ready to accept messages. | ||
break; | ||
|
||
case 'update': | ||
this.events.emit(e.data.status, e.data) | ||
console.log(e.data) | ||
// Generation update: update the output text. | ||
break; | ||
|
||
case 'complete': | ||
console.log(e.data) | ||
if (this.responses[e.data.id]) { | ||
if (this.current === e.data.id) { | ||
this.responses[e.data.id](null, e.data) | ||
} else { | ||
this.responses[e.data.id]('aborted') | ||
} | ||
delete this.responses[e.data.id] | ||
this.current = null | ||
} else { | ||
console.log('no callback for', e.data) | ||
} | ||
|
||
// Generation complete: re-enable the "Generate" button | ||
break; | ||
} | ||
}; | ||
|
||
// Attach the callback function as an event listener. | ||
this.worker.addEventListener('message', onMessageReceived) | ||
|
||
this.worker.postMessage({ | ||
cmd: 'init', | ||
model: 'Pipper/finetuned_sol' | ||
}) | ||
} | ||
|
||
suggest (content: string, options: SuggestOptions) { | ||
return new Promise((resolve, reject) => { | ||
if (this.current) return reject(new Error('already running')) | ||
const timespan = Date.now() | ||
this.current = timespan | ||
this.worker.postMessage({ | ||
id: timespan, | ||
cmd: 'suggest', | ||
text: content, | ||
max_new_tokens: options.max_new_tokens, | ||
temperature: options.temperature, | ||
top_k: options.top_k, | ||
do_sample: options.do_sample | ||
}) | ||
this.responses[timespan] = (error, result) => { | ||
if (error) return reject(error) | ||
resolve(result) | ||
} | ||
}) | ||
} | ||
} |
90 changes: 90 additions & 0 deletions
90
apps/remix-ide/src/app/plugins/copilot/suggestion-service/worker.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
|
||
import { pipeline, env } from '@xenova/transformers'; | ||
|
||
env.allowLocalModels = true; | ||
|
||
const instance = null | ||
/** | ||
* This class uses the Singleton pattern to ensure that only one instance of the pipeline is loaded. | ||
*/ | ||
class CodeCompletionPipeline { | ||
static task = 'text-generation'; | ||
static model = null | ||
static instance = null; | ||
|
||
static async getInstance(progress_callback = null) { | ||
if (this.instance === null) { | ||
this.instance = pipeline(this.task, this.model, { progress_callback }); | ||
} | ||
|
||
return this.instance; | ||
} | ||
} | ||
|
||
// Listen for messages from the main thread | ||
self.addEventListener('message', async (event) => { | ||
const { | ||
id, model, text, max_new_tokens, cmd, | ||
|
||
// Generation parameters | ||
temperature, | ||
top_k, | ||
do_sample, | ||
} = event.data; | ||
|
||
if (cmd === 'init') { | ||
// Retrieve the code-completion pipeline. When called for the first time, | ||
// this will load the pipeline and save it for future use. | ||
CodeCompletionPipeline.model = model | ||
await CodeCompletionPipeline.getInstance(x => { | ||
// We also add a progress callback to the pipeline so that we can | ||
// track model loading. | ||
self.postMessage(x); | ||
}); | ||
return | ||
} | ||
|
||
if (!CodeCompletionPipeline.instance) { | ||
// Send the output back to the main thread | ||
self.postMessage({ | ||
id, | ||
status: 'error', | ||
message: 'model not yet loaded' | ||
}); | ||
} | ||
|
||
if (cmd === 'suggest') { | ||
// Retrieve the code-completion pipeline. When called for the first time, | ||
// this will load the pipeline and save it for future use. | ||
let generator = await CodeCompletionPipeline.getInstance(x => { | ||
// We also add a progress callback to the pipeline so that we can | ||
// track model loading. | ||
self.postMessage(x); | ||
}); | ||
|
||
// Actually perform the code-completion | ||
let output = await generator(text, { | ||
max_new_tokens, | ||
temperature, | ||
top_k, | ||
do_sample, | ||
|
||
// Allows for partial output | ||
callback_function: x => { | ||
/*self.postMessage({ | ||
id, | ||
status: 'update', | ||
output: generator.tokenizer.decode(x[0].output_token_ids, { skip_special_tokens: true }) | ||
}); | ||
*/ | ||
} | ||
}); | ||
|
||
// Send the output back to the main thread | ||
self.postMessage({ | ||
id, | ||
status: 'complete', | ||
output: output, | ||
}); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.