added model download

pull/4956/head
Stéphane Tetsing 5 months ago
parent 8ed1077cb2
commit ea506080a8
  1. 7
      apps/remix-ide/src/app.js
  2. 51
      apps/remix-ide/src/app/plugins/electron/remixAIDesktopPlugin.tsx
  3. 3
      apps/remix-ide/src/app/plugins/remixAIPlugin.tsx
  4. 5
      apps/remix-ide/src/remixAppManager.js
  5. 2
      apps/remix-ide/src/remixEngine.js
  6. 78
      apps/remixdesktop/src/plugins/remixAIDektop.ts
  7. 5
      apps/remixdesktop/tsconfig.json
  8. 5
      libs/remix-ai-core/src/index.ts
  9. 1
      libs/remix-ai-core/src/types/constants.ts
  10. 33
      libs/remix-ai-core/src/types/models.ts
  11. 26
      libs/remix-ai-core/src/types/types.ts
  12. 13
      libs/remix-ui/remix-ai/src/lib/components/Default.tsx
  13. 3
      libs/remix-ui/remix-ai/src/lib/components/RemixAI.tsx

@ -371,13 +371,14 @@ class AppComponent {
const remixAIDesktop = new remixAIDesktopPlugin()
this.engine.register([remixAIDesktop])
}
const remixAI = new RemixAIPlugin(isElectron())
this.engine.register([remixAI])
const compilerloader = isElectron()? new compilerLoaderPluginDesktop(): new compilerLoaderPlugin()
this.engine.register([compilerloader])
const remixAI = new RemixAIPlugin(isElectron())
this.engine.register([remixAI])
// LAYOUT & SYSTEM VIEWS
const appPanel = new MainPanel()
Registry.getInstance().put({api: this.mainview, name: 'mainview'})

@ -1,56 +1,41 @@
import { ElectronPlugin } from '@remixproject/engine-electron'
import { IModel, ModelType, DefaultModels } from '@remix/remix-ai-core';
import axios from 'axios';
import fs from 'fs';
import { Model } from '@remix/remix-ai-core';
const desktop_profile = {
name: 'remixAID',
displayName: 'RemixAI Desktop',
maintainedBy: 'Remix',
description: 'RemixAI provides AI services to Remix IDE Desktop.',
documentation: 'https://remix-ide.readthedocs.io/en/latest/remixai.html',
icon: 'assets/img/remix-logo-blue.png',
methods: ['downloadModel'],
}
export class remixAIDesktopPlugin extends ElectronPlugin {
selectedModel: IModel | null = DefaultModels()[0]
constructor() {
console.log('remixAIDesktopPlugin')
console.log('remixAIDesktopPlugin loaded')
super(desktop_profile)
this
}
onActivation(): void {
this.on('remixAI', 'enabled', () => {console.log('someone enable the remixAI desktop plugin')} )
console.log('remixAIDesktopPlugin ---------------------- activated')
console.log('remixAIDesktopPlugin Model: ', this.selectedModel)
}
async downloadModel(model: Model, outputLocationPath: string): Promise<void> {
console.log(`Downloading model ${model.name}`)
// Make a HEAD request to get the file size
const { headers } = await axios.head(model.download_url);
const totalSize = parseInt(headers['content-length'], 10);
// Create a write stream to save the file
const writer = fs.createWriteStream(outputLocationPath);
// Start the file download
const response = await axios({
method: 'get',
url: model.download_url,
responseType: 'stream'
});
let downloadedSize = 0;
response.data.on('data', (chunk: Buffer) => {
downloadedSize += chunk.length;
const progress = (downloadedSize / totalSize) * 100;
this.emit('download_progress', progress);
});
response.data.pipe(writer);
}
return new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});
}
}
// class RemixAIPlugin extends ElectronPlugin {
// constructor() {
// super(dek)
// this.methods = ['downloadModel']
// }
// }

@ -23,7 +23,7 @@ export class RemixAIPlugin extends ViewPlugin {
isOnDesktop:boolean = false
aiIsActivated:boolean = false
constructor(inDesktop:boolean) {
console.log('remixAIPlugin')
console.log('remixAIPlugin loaded')
super(profile)
this.isOnDesktop = inDesktop
}
@ -32,7 +32,6 @@ export class RemixAIPlugin extends ViewPlugin {
if (this.isOnDesktop) {
console.log('Activating RemixAIPlugin on desktop')
// Do some desktop specific stuff
}
}

@ -76,7 +76,8 @@ let requiredModules = [ // services + layout views + system views
'home',
'doc-viewer',
'doc-gen',
'remix-templates'
'remix-templates',
'remixAID'
]
@ -153,7 +154,7 @@ export class RemixAppManager extends PluginManager {
this.pluginsDirectory = 'https://raw.githubusercontent.com/ethereum/remix-plugins-directory/master/build/metadata.json'
this.pluginLoader = new PluginLoader()
if (Registry.getInstance().get('platform').api.isDesktop()) {
requiredModules = [...requiredModules, 'fs', 'electronTemplates', 'isogit', 'remix-templates', 'electronconfig', 'xterm', 'compilerloader', 'ripgrep']
requiredModules = [...requiredModules, 'fs', 'electronTemplates', 'isogit', 'remix-templates', 'electronconfig', 'xterm', 'compilerloader', 'ripgrep', 'remixAID']
}
}

@ -26,8 +26,8 @@ export class RemixEngine extends Engine {
if (name === 'compilerloader') return { queueTimeout: 60000 * 4 }
if (name === 'filePanel') return { queueTimeout: 60000 * 20 }
if (name === 'fileManager') return { queueTimeout: 60000 * 20 }
if (name === 'openaigpt') return { queueTimeout: 60000 * 2 }
if (name === 'solcoder') return { queueTimeout: 60000 * 2 }
if (name === 'remixAID') return { queueTimeout: 60000 * 20 }
if (name === 'cookbookdev') return { queueTimeout: 60000 * 3 }
return { queueTimeout: 10000 }
}

@ -1,14 +1,20 @@
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron"
import { Profile } from "@remixproject/plugin-utils"
import { app } from 'electron';
// import { Model, ModelType, DefaultModels } from '@remix/remix-ai-core';
import axios from "axios";
import fs from 'fs';
import path from 'path';
// import { isE2E } from "../main";
const profile = {
name: 'remixAID',
displayName: 'RemixAI desktop',
methods: [''],
description: 'RemixAI provides AI services to Remix IDE Desktop.',
displayName: 'RemixAI Desktop',
maintainedBy: 'Remix',
description: 'RemixAI provides AI services to Remix IDE Desktop.',
kind: '',
documentation: 'https://remix-ide.readthedocs.io/en/latest/remixai.html',
}
export class RemixAIDesktopPlugin extends ElectronBasePlugin {
@ -27,10 +33,12 @@ export class RemixAIDesktopPlugin extends ElectronBasePlugin {
const clientProfile: Profile = {
name: 'remixAID',
displayName: 'RemixAI desktop',
methods: ['enable', 'downloadModel'],
description: 'RemixAI provides AI services to Remix IDE Desktop.',
displayName: 'RemixAI Desktop',
maintainedBy: 'Remix',
description: 'RemixAI provides AI services to Remix IDE Desktop.',
kind: '',
documentation: 'https://remix-ide.readthedocs.io/en/latest/remixai.html',
methods: ['downloadModel']
}
class RemixAIDesktopPluginClient extends ElectronBasePluginClient {
@ -40,10 +48,10 @@ class RemixAIDesktopPluginClient extends ElectronBasePluginClient {
}
async onActivation(): Promise<void> {
console.log("Activation", "loaded the remix plugin client")
console.log("Activation", "loaded the remix plugin client application side")
this.onload(() => {
this.emit('loaded')
console.log("loaded the remix plugin client")
console.log("loaded the remix plugin client application side")
})
}
async listAvailableModels(){
@ -54,6 +62,60 @@ class RemixAIDesktopPluginClient extends ElectronBasePluginClient {
console.log('Remix AI desktop plugin enabled')
this.emit('enabled')
}
async downloadModel(model): Promise<void> {
console.log('Downloading the model model', model)
console.log('Downloading model', model.downloadUrl)
const wdir = await this.call('fs' as any, 'getWorkingDir');
console.log('working dir is', wdir)
const outputLocationDir = await this.call('fs' as any, 'selectFolder', wdir);
console.log('output location dir is', outputLocationDir)
if (outputLocationDir === undefined) {
console.log('No output location selected');
return;
}
const outputLocationPath = path.join(outputLocationDir, model.modelName);
console.log('output location path is', outputLocationDir)
if (fs.existsSync(outputLocationPath)) {
console.log('Model already exists in the output location', outputLocationPath);
return;
}
// Make a HEAD request to get the file size
const { headers } = await axios.head(model.downloadUrl);
const totalSize = parseInt(headers['content-length'], 10);
// Create a write stream to save the file
const writer = fs.createWriteStream(outputLocationPath);
// Start the file download
const response = await axios({
method: 'get',
url: model.downloadUrl,
responseType: 'stream'
});
let downloadedSize = 0;
response.data.on('data', (chunk: Buffer) => {
downloadedSize += chunk.length;
const progress = (downloadedSize / totalSize) * 100;
console.log(`Downloaded ${progress}%`);
this.emit('download_progress', progress);
});
response.data.pipe(writer);
console.log('Download complete');
return new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});
}
}

@ -13,7 +13,10 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"paths": {
"*": ["node_modules/*"]
"*": ["node_modules/*"],
// "@remix/remix-ai-core": [
// "../../libs/remix-ai-core/src/index.ts"
// ],
},
"typeRoots": ["src/**/*.d.ts", "node_modules/@types", "test/**/*.d.ts", "../remix-ide-e2e/src/**/*.d.ts"]
},

@ -1,7 +1,8 @@
'use strict'
import { Model, ModelResponse, ModelRequest, InferenceModel} from './types/types'
import { IModel, IModelResponse, IModelRequest, InferenceModel} from './types/types'
import { ModelType } from './types/constants'
import { DefaultModels } from './types/models'
export { Model, ModelResponse, ModelRequest, InferenceModel }
export { IModel, IModelResponse, IModelRequest, InferenceModel, ModelType, DefaultModels}

@ -4,4 +4,3 @@ export enum ModelType {
CODE_COMPLETION = 'code_completion',
GENERAL = 'general',
}

@ -2,19 +2,34 @@
// create a function getModels returning a list of all supported models
// create a function getModel returning a model by its name
import { Model } from './types';
import { IModel} from './types';
import { ModelType} from './constants';
const supportedModels: Model[] = [];
const getModels = async (): Promise<Model[]> => {
return supportedModels;
const DefaultModels = (): IModel[] => {
const model1:IModel = {
name: 'DeepSeek',
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',
modelType: ModelType.CODE_COMPLETION,
modelReqs: { backend: 'llamacpp', minSysMemory: 2, GPURequired: false, MinGPUVRAM: 2}
};
const model2: IModel = {
name: 'DeepSeek',
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',
modelType: ModelType.GENERAL,
modelReqs: { backend: 'llamacpp', minSysMemory: 8, GPURequired: true, MinGPUVRAM: 8}
};
return [model1, model2];
}
const getModel = async (name: string): Promise<Model | undefined> => {
return supportedModels.find(model => model.name === name);
const getModel = async (name: string): Promise<IModel | undefined> => {
return DefaultModels().find(model => model.name === name);
}
const loadModel = async (modelname: string): Promise<void> => {
console.log(`Loading model ${modelname}`);
}
}
export { DefaultModels}

@ -3,27 +3,35 @@
import exp from 'constants';
import { ModelType } from './constants';
export interface Model {
export interface IModelRequirements{
backend: string,
minSysMemory: number,
GPURequired: boolean,
MinGPUVRAM: number,
}
export interface IModel {
name: string;
download_url: string;
type: ModelType;
url: string;
downloadUrl: string;
modelName?: string;
modelType: ModelType;
modelReqs: IModelRequirements;
}
export interface ModelResponse {
export interface IModelResponse {
output: string;
error: string;
success: boolean;
model: Model;
model: IModel;
}
export interface ModelRequest {
export interface IModelRequest {
input: string;
model: Model;
model: IModel;
}
export interface InferenceModel {
model: Model;
model: IModel;
location: string;
isRemote: boolean;
}

@ -1,9 +1,11 @@
import React, { useContext, useEffect, useState } from 'react'
import '../remix-ai.css'
import { DefaultModels } from '@remix/remix-ai-core';
export const Default = () => {
export const Default = (props) => {
const [searchText, setSearchText] = useState('');
const [resultText, setResultText] = useState('');
const pluginName = props.plugin.isOnDesktop ? 'remixAID' : 'remixAI'
return (
<div>
@ -27,6 +29,15 @@ export const Default = () => {
style={{fontSize: "x-small", alignSelf: "end"}}>Search</span>
</button>
<button className="remix_ai_plugin_download_button text-ai pl-2 pr-0 py-0 d-flex"
onClick={async () => {
if (props.plugin.isOnDesktop ) {
await props.plugin.call(pluginName, 'downloadModel', DefaultModels()[0]);
}
}}
> Download Model </button>
</div>
<div className="remix_ai_plugin_find_container_internal">
<textarea

@ -3,11 +3,12 @@ import '../remix-ai.css'
import { Default } from './Default'
export const RemixAITab = (props) => {
const plugin = props.plugin
return (
<>
<div id="remixAITab pr-4 px-2 pb-4">
<Default></Default>
<Default plugin={plugin}></Default>
</div>
</>
)

Loading…
Cancel
Save