pull/5098/head
Stéphane Tetsing 4 months ago
parent 52f594e04d
commit 10128bf873
  1. 19
      apps/remix-ide/src/app/plugins/remixAIPlugin.tsx
  2. 53
      apps/remixdesktop/src/lib/InferenceServerManager.ts
  3. 7
      libs/remix-ai-core/src/index.ts
  4. 51
      libs/remix-ai-core/src/inferencers/remote/remoteInference.ts
  5. 21
      libs/remix-ai-core/src/prompts/chat.ts
  6. 21
      libs/remix-ai-core/src/prompts/promptBuilder.ts
  7. 11
      libs/remix-ai-core/src/types/models.ts
  8. 2
      libs/remix-ai-core/src/types/types.ts

@ -43,6 +43,7 @@ export class RemixAIPlugin extends ViewPlugin {
} else {
console.log('Activating RemixAIPlugin on browser')
}
this.initialize()
}
async initialize(model1?:IModel, model2?:IModel, remoteModel?:IRemoteModel){
@ -66,17 +67,17 @@ export class RemixAIPlugin extends ViewPlugin {
async code_generation(prompt: string): Promise<any> {
console.log('code_generation')
if (this.isOnDesktop) {
return this.call(this.remixDesktopPluginName, 'code_generation', prompt)
return await this.call(this.remixDesktopPluginName, 'code_generation', prompt)
} else {
return this.remoteInferencer.code_generation(prompt)
return await this.remoteInferencer.code_generation(prompt)
}
}
async code_completion(prompt: string): Promise<any> {
if (this.isOnDesktop) {
return this.call(this.remixDesktopPluginName, 'code_completion', prompt)
return await this.call(this.remixDesktopPluginName, 'code_completion', prompt)
} else {
return this.remoteInferencer.code_completion(prompt)
return await this.remoteInferencer.code_completion(prompt)
}
}
@ -85,9 +86,9 @@ export class RemixAIPlugin extends ViewPlugin {
let result
if (this.isOnDesktop) {
result = this.call(this.remixDesktopPluginName, 'solidity_answer', prompt)
result = await this.call(this.remixDesktopPluginName, 'solidity_answer', prompt)
} else {
result = this.remoteInferencer.solidity_answer(prompt)
result = await this.remoteInferencer.solidity_answer(prompt)
}
this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result })
@ -101,7 +102,7 @@ export class RemixAIPlugin extends ViewPlugin {
result = await this.call(this.remixDesktopPluginName, 'code_explaining', prompt)
} else {
result = this.remoteInferencer.code_explaining(prompt)
result = await this.remoteInferencer.code_explaining(prompt)
}
if (result) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result })
}
@ -120,9 +121,9 @@ export class RemixAIPlugin extends ViewPlugin {
async code_insertion(msg_pfx: string, msg_sfx: string): Promise<any> {
if (this.isOnDesktop) {
return this.call(this.remixDesktopPluginName, 'code_insertion', msg_pfx, msg_sfx)
return await this.call(this.remixDesktopPluginName, 'code_insertion', msg_pfx, msg_sfx)
} else {
return this.remoteInferencer.code_insertion(msg_pfx, msg_sfx)
return await this.remoteInferencer.code_insertion(msg_pfx, msg_sfx)
}
}

@ -4,8 +4,9 @@ import fs from 'fs';
import axios from "axios";
import { EventEmitter } from 'events';
import { ICompletions, IModel, IParams, InsertionParams,
CompletionParams, GenerationParams, ModelType,
IStreamResponse } from "../../../../libs/remix-ai-core/src/index"
CompletionParams, GenerationParams, ModelType, AIRequestType,
IStreamResponse, ChatHistory,
buildSolgptPromt } from "../../../../libs/remix-ai-core/src/index"
class ServerStatusTimer {
private intervalId: NodeJS.Timeout | null = null;
@ -127,7 +128,7 @@ export class InferenceManager implements ICompletions {
console.log('Inference server not running')
InferenceManager.instance = null
this.stateTimer.interval += this.stateTimer.interval
if (this.stateTimer.interval >= 60000) {
// attempt to restart the server
console.log('Attempting to restart the server')
@ -257,17 +258,27 @@ export class InferenceManager implements ICompletions {
}
}
private async _makeInferenceRequest(endpoint, payload){
private async _makeInferenceRequest(endpoint, payload, rType:AIRequestType){
try {
this.event.emit('onInference')
const options = { headers: { 'Content-Type': 'application/json', } }
const response = await axios.post(`${this.inferenceURL}/${endpoint}`, payload, options)
const userPrompt = payload[Object.keys(payload)[0]]
this.event.emit('onInferenceDone')
console.log('response', response)
console.log('userprompt', userPrompt)
console.log('chat history:', ChatHistory.getHistory())
if (response.data?.generatedText) {
if (rType === AIRequestType.GENERAL) {
ChatHistory.pushHistory(userPrompt, response.data.generatedText)
}
return response.data.generatedText
} else { return "" }
} catch (error) {
ChatHistory.clearHistory()
console.error('Error making request to Inference server:', error.message);
}
}
@ -286,22 +297,31 @@ export class InferenceManager implements ICompletions {
}
, responseType: 'stream' });
const userPrompt = payload[Object.keys(payload)[0]]
let resultText = ""
response.data.on('data', (chunk: Buffer) => {
try {
const parsedData = JSON.parse(chunk.toString());
if (parsedData.isGenerating) {
this.event.emit('onStreamResult', parsedData.generatedText);
resultText = resultText + parsedData.generatedText
} else {
resultText = resultText + parsedData.generatedText
// no additional check for streamed results
ChatHistory.pushHistory(userPrompt, resultText)
return parsedData.generatedText
}
} catch (error) {
ChatHistory.clearHistory()
console.error('Error parsing JSON:', error);
}
});
return "" // return empty string for now as payload already handled in event
return "" // return empty string for now as payload is/will be handled in event
} catch (error) {
ChatHistory.clearHistory()
console.error('Error making stream request to Inference server:', error.message);
}
finally {
@ -310,6 +330,7 @@ export class InferenceManager implements ICompletions {
}
private async _makeRequest(endpoint, payload){
// make a simple request to the inference server
try {
const options = { headers: { 'Content-Type': 'application/json', } }
const response = await axios.post(`${this.inferenceURL}/${endpoint}`, payload, options)
@ -329,7 +350,7 @@ export class InferenceManager implements ICompletions {
// as of now no prompt required
const payload = { context_code: context, ...params }
return this._makeInferenceRequest('code_completion', payload)
return this._makeInferenceRequest('code_completion', payload, AIRequestType.COMPLETION)
}
async code_insertion(msg_pfx: string, msg_sfx: string, params:IParams=InsertionParams): Promise<any> {
@ -338,7 +359,7 @@ export class InferenceManager implements ICompletions {
return
}
const payload = { code_pfx:msg_pfx, code_sfx:msg_sfx, ...params }
return this._makeInferenceRequest('code_insertion', payload)
return this._makeInferenceRequest('code_insertion', payload, AIRequestType.COMPLETION)
}
@ -347,7 +368,7 @@ export class InferenceManager implements ICompletions {
console.log('model not ready yet')
return
}
return this._makeInferenceRequest('code_generation', { prompt, ...params })
return this._makeInferenceRequest('code_generation', { prompt, ...params }, AIRequestType.GENERAL)
}
async code_explaining(code:string, context:string, params:IParams=GenerationParams): Promise<any> {
@ -358,7 +379,7 @@ export class InferenceManager implements ICompletions {
if (GenerationParams.stream_result) {
return this._streamInferenceRequest('code_explaining', { code, context, ...params })
} else {
return this._makeInferenceRequest('code_explaining', { code, context, ...params })
return this._makeInferenceRequest('code_explaining', { code, context, ...params }, AIRequestType.GENERAL)
}
}
@ -370,19 +391,27 @@ export class InferenceManager implements ICompletions {
if (GenerationParams.stream_result) {
return this._streamInferenceRequest('error_explaining', { prompt, ...params })
} else {
return this._makeInferenceRequest('error_explaining', { prompt, ...params })
return this._makeInferenceRequest('error_explaining', { prompt, ...params }, AIRequestType.GENERAL)
}
}
async solidity_answer(prompt: string, params:IParams=GenerationParams): Promise<any> {
async solidity_answer(userPrompt: string, params:IParams=GenerationParams): Promise<any> {
if (!this.isReady) {
console.log('model not ready yet')
return
}
let modelOP = undefined
for (const model of this.selectedModels) {
if (model?.modelOP) {
modelOP = model.modelOP
}
}
const prompt = buildSolgptPromt(userPrompt, this.selectedModels[0]?.modelOP)
if (GenerationParams.stream_result) {
return this._streamInferenceRequest('solidity_answer', { prompt, ...params })
} else {
return this._makeInferenceRequest('solidity_answer', { prompt, ...params })
return this._makeInferenceRequest('solidity_answer', { prompt, ...params }, AIRequestType.GENERAL)
}
}

@ -6,13 +6,14 @@ import { IModel, IModelResponse, IModelRequest, InferenceModel, ICompletions,
import { ModelType } from './types/constants'
import { DefaultModels, InsertionParams, CompletionParams, GenerationParams } from './types/models'
import { getCompletionPrompt, getInsertionPrompt } from './prompts/completionPrompts'
import { PromptBuilder } from './prompts/promptBuilder'
import { buildSolgptPromt, PromptBuilder } from './prompts/promptBuilder'
import { RemoteInferencer } from './inferencers/remote/remoteInference'
import { ChatHistory } from './prompts/chat'
export {
IModel, IModelResponse, IModelRequest, InferenceModel,
ModelType, DefaultModels, ICompletions, IParams, IRemoteModel,
getCompletionPrompt, getInsertionPrompt, IStreamResponse,
getCompletionPrompt, getInsertionPrompt, IStreamResponse, buildSolgptPromt,
RemoteInferencer, InsertionParams, CompletionParams, GenerationParams,
ChatEntry, AIRequestType, RemoteBackendOPModel, PromptBuilder
ChatEntry, AIRequestType, RemoteBackendOPModel, ChatHistory
}

@ -1,14 +1,14 @@
import { ICompletions, IParams, ChatEntry, AIRequestType, RemoteBackendOPModel } from "../../types/types";
import { PromptBuilder } from "../../prompts/promptBuilder";
import { ICompletions, IParams, AIRequestType, RemoteBackendOPModel } from "../../types/types";
import { buildSolgptPromt } from "../../prompts/promptBuilder";
import axios from "axios";
import EventEmitter from "events";
import { ChatHistory } from "../../prompts/chat";
const defaultErrorMessage = `Unable to get a response from AI server`
export class RemoteInferencer implements ICompletions {
api_url: string
completion_url: string
solgpt_chat_history:ChatEntry[]
max_history = 7
model_op = RemoteBackendOPModel.DEEPSEEK
event: EventEmitter
@ -16,20 +16,14 @@ export class RemoteInferencer implements ICompletions {
constructor(apiUrl?:string, completionUrl?:string) {
this.api_url = apiUrl!==undefined ? apiUrl: "https://solcoder.remixproject.org"
this.completion_url = completionUrl!==undefined ? completionUrl : "https://completion.remixproject.org"
this.solgpt_chat_history = []
this.event = new EventEmitter()
}
private pushChatHistory(prompt, result){
const chat:ChatEntry = [prompt, result.data[0]]
this.solgpt_chat_history.push(chat)
if (this.solgpt_chat_history.length > this.max_history){this.solgpt_chat_history.shift()}
}
private async _makeRequest(data, rType:AIRequestType){
this.event.emit("onInference")
const requesURL = rType === AIRequestType.COMPLETION ? this.completion_url : this.api_url
console.log("requesting on ", requesURL, rType, data.data[1])
const userPrompt = data.data[0]
console.log('userPrompt reuesting...')
try {
const result = await axios(requesURL, {
@ -51,7 +45,7 @@ export class RemoteInferencer implements ICompletions {
case AIRequestType.GENERAL:
if (result.statusText === "OK") {
const resultText = result.data.data[0]
this.pushChatHistory(prompt, resultText)
ChatHistory.pushHistory(userPrompt, resultText)
return resultText
} else {
return defaultErrorMessage
@ -59,7 +53,8 @@ export class RemoteInferencer implements ICompletions {
}
} catch (e) {
this.solgpt_chat_history = []
ChatHistory.clearHistory()
console.error('Error making request to Inference server:', e.message)
return e
}
finally {
@ -71,32 +66,37 @@ export class RemoteInferencer implements ICompletions {
try {
this.event.emit('onInference')
const requesURL = rType === AIRequestType.COMPLETION ? this.completion_url : this.api_url
const options = { headers: { 'Content-Type': 'application/json', "Accept": "text/event-stream" } }
const userPrompt = data.data[0]
const response = await axios({
method: 'post',
url: rType === AIRequestType.COMPLETION ? this.completion_url : this.api_url,
url: requesURL,
data: data,
headers: { 'Content-Type': 'application/json', "Accept": "text/event-stream" },
responseType: 'stream'
});
let resultText = ""
response.data.on('data', (chunk: Buffer) => {
try {
const parsedData = JSON.parse(chunk.toString());
if (parsedData.isGenerating) {
this.event.emit('onStreamResult', parsedData.generatedText);
resultText = resultText + parsedData.generatedText
} else {
// stream generation is complete
resultText = resultText + parsedData.generatedText
ChatHistory.pushHistory(userPrompt, resultText)
return parsedData.generatedText
}
} catch (error) {
console.error('Error parsing JSON:', error);
ChatHistory.clearHistory()
}
});
return "" // return empty string for now as handled in event
} catch (error) {
ChatHistory.clearHistory()
console.error('Error making stream request to Inference server:', error.message);
}
finally {
@ -125,7 +125,7 @@ export class RemoteInferencer implements ICompletions {
}
async solidity_answer(prompt): Promise<any> {
const main_prompt = this._build_solgpt_promt(prompt)
const main_prompt = buildSolgptPromt(prompt, this.model_op)
const payload = { "data":[main_prompt, "solidity_answer", false,2000,0.9,0.8,50]}
return this._makeRequest(payload, AIRequestType.GENERAL)
}
@ -139,19 +139,4 @@ export class RemoteInferencer implements ICompletions {
const payload = { "data":[prompt, "error_explaining", false,2000,0.9,0.8,50]}
return this._makeRequest(payload, AIRequestType.GENERAL)
}
private _build_solgpt_promt(user_promt:string){
if (this.solgpt_chat_history.length === 0){
return user_promt
} else {
let new_promt = ""
for (const [question, answer] of this.solgpt_chat_history) {
new_promt += PromptBuilder(question.split('sol-gpt')[1], answer, this.model_op)
}
// finaly
new_promt = "sol-gpt " + new_promt + PromptBuilder(user_promt.split('sol-gpt')[1], "", this.model_op)
return new_promt
}
}
}

@ -0,0 +1,21 @@
import { ChatEntry } from "../types/types"
export abstract class ChatHistory{
private static chatEntries:ChatEntry[] = []
static queuSize:number = 7 // change the queue size wrt the GPU size
public static pushHistory(prompt, result){
const chat:ChatEntry = [prompt, result]
this.chatEntries.push(chat)
if (this.chatEntries.length > this.queuSize){this.chatEntries.shift()}
}
public static getHistory(){
return this.chatEntries
}
public static clearHistory(){
this.chatEntries = []
}
}

@ -1,7 +1,28 @@
import { RemoteBackendOPModel } from "../types/types"
import { ChatHistory } from "./chat"
export const PromptBuilder = (inst, answr, modelop) => {
if (modelop === RemoteBackendOPModel.CODELLAMA) return ""
if (modelop === RemoteBackendOPModel.DEEPSEEK) return "\n### INSTRUCTION:\n" + inst + "\n### RESPONSE:\n" + answr
if (modelop === RemoteBackendOPModel.MISTRAL) return ""
}
export const buildSolgptPromt = (userPrompt:string, modelOP:RemoteBackendOPModel) => {
if (modelOP === undefined) {
console.log('WARNING: modelOP is undefined. Provide a valide model OP for chat history')
return userPrompt
}
if (ChatHistory.getHistory().length === 0){
return userPrompt
} else {
let newPrompt = ""
for (const [question, answer] of ChatHistory.getHistory()) {
if (question.startsWith('sol-gpt')) newPrompt += PromptBuilder(question.split('sol-gpt')[1], answer, modelOP)
else if (question.startsWith('gpt')) newPrompt += PromptBuilder(question.split('sol-gpt')[1], answer, modelOP)
else newPrompt += PromptBuilder(question, answer, modelOP)
}
// finaly
newPrompt = "sol-gpt " + newPrompt + PromptBuilder(userPrompt.split('sol-gpt')[1], "", modelOP)
return newPrompt
}
}

@ -2,12 +2,14 @@
// create a function getModels returning a list of all supported models
// create a function getModel returning a model by its name
import { IModel, IParams } from './types';
import { IModel, IParams, RemoteBackendOPModel } from './types';
import { ModelType } from './constants';
const DefaultModels = (): IModel[] => {
const model1:IModel = {
name: 'DeepSeek',
modelOP: RemoteBackendOPModel.DEEPSEEK,
task: 'text-generation',
modelName: 'deepseek-coder-1.3b-instruct.gguf',
downloadUrl: 'https://huggingface.co/TheBloke/deepseek-coder-1.3b-instruct-GGUF/resolve/main/deepseek-coder-1.3b-instruct.Q4_K_M.gguf?download=true',
@ -16,6 +18,7 @@ const DefaultModels = (): IModel[] => {
};
const model2: IModel = {
name: 'DeepSeek',
modelOP: RemoteBackendOPModel.DEEPSEEK,
task: 'text-generation',
modelName: 'deepseek-coder-6.7b-instruct.gguf',
downloadUrl: 'https://huggingface.co/TheBloke/deepseek-coder-6.7B-instruct-GGUF/resolve/main/deepseek-coder-6.7b-instruct.Q4_K_M.gguf?download=true',
@ -24,6 +27,7 @@ const DefaultModels = (): IModel[] => {
};
const model3: IModel = {
name: 'DeepSeekTransformer',
modelOP: RemoteBackendOPModel.DEEPSEEK,
task: 'text-generation',
modelName: 'Xenova/deepseek-coder-1.3b-base',
downloadUrl: 'Xenova/deepseek-coder-1.3b-base',
@ -32,6 +36,7 @@ const DefaultModels = (): IModel[] => {
};
const model4: IModel = {
name: 'DeepSeek',
modelOP: RemoteBackendOPModel.DEEPSEEK,
task: 'text-generation',
modelName: 'deepseek-coder-1.3b-base.gguf',
downloadUrl: 'https://huggingface.co/TheBloke/deepseek-coder-1.3b-base-GGUF/resolve/main/deepseek-coder-1.3b-base.Q4_K_M.gguf?download=true',
@ -41,6 +46,7 @@ const DefaultModels = (): IModel[] => {
const model5: IModel = {
name: 'DeepSeek',
modelOP: RemoteBackendOPModel.DEEPSEEK,
task: 'text-generation',
modelName: 'deepseek-coder-6.7B-base-GGUF',
downloadUrl: 'https://huggingface.co/TheBloke/deepseek-coder-6.7B-base-GGUF/resolve/main/deepseek-coder-6.7b-base.Q4_K_M.gguf?download=true',
@ -50,6 +56,7 @@ const DefaultModels = (): IModel[] => {
const model6: IModel = {
name: 'DeepSeek',
modelOP: RemoteBackendOPModel.DEEPSEEK,
task: 'text-generation',
modelName: 'DeepSeek-Coder-V2-Lite-Base.Q2_K.gguf',
downloadUrl: 'https://huggingface.co/QuantFactory/DeepSeek-Coder-V2-Lite-Base-GGUF/resolve/main/DeepSeek-Coder-V2-Lite-Base.Q2_K.gguf?download=true',
@ -87,7 +94,7 @@ const GenerationParams:IParams = {
topK: 40,
topP: 0.92,
max_new_tokens: 2000,
stream_result: true,
stream_result: false,
}
export { DefaultModels, CompletionParams, InsertionParams, GenerationParams }

@ -18,6 +18,8 @@ export interface IModel {
modelType: ModelType;
modelReqs: IModelRequirements;
downloadPath?: string;
modelOP?: RemoteBackendOPModel;
}
export interface IRemoteModel {
completionUrl: string;

Loading…
Cancel
Save