Skip to content

Commit

Permalink
fix: update new api from cortex to support 0.5.0 (#3221)
Browse files Browse the repository at this point in the history
* fix: update new api from cortex to support 0.5.0

Signed-off-by: James <namnh0122@gmail.com>

* fix stop button for streaming

Signed-off-by: James <namnh0122@gmail.com>

* fix stop inference for nonstreaming

Signed-off-by: James <namnh0122@gmail.com>

* chore: remove umami prevent tracking call to vercel

Signed-off-by: James <namnh0122@gmail.com>

* add warning modal when running more than 2 model concurrently

Signed-off-by: James <namnh0122@gmail.com>

* fix: skip summarize if abort

Signed-off-by: James <namnh0122@gmail.com>

* 0.5.0-3

* add inference error popup

Signed-off-by: James <namnh0122@gmail.com>

* add back import local model

Signed-off-by: James <namnh0122@gmail.com>

* fix: max token issue (#3225)

Signed-off-by: James <namnh0122@gmail.com>

* format status

Signed-off-by: James <namnh0122@gmail.com>

* fix migration missing instructions

Signed-off-by: James <namnh0122@gmail.com>

* fix: wait for cortex process overlay should be on top (#3224)

* fix: wait for cortex process overlay should be on top

* chore: update cortex.js

* Cortex 0.5.0-5

* add import model to my model screen

Signed-off-by: James <namnh0122@gmail.com>

* fix: should migrate symlink models (#3226)

* fix import on windows (#3229)

Signed-off-by: James <namnh0122@gmail.com>

* fix yarn lint

Signed-off-by: James <namnh0122@gmail.com>

* fix: clean up port before start jan (#3232)

Signed-off-by: James <namnh0122@gmail.com>

---------

Signed-off-by: James <namnh0122@gmail.com>
Co-authored-by: Van Pham <64197333+Van-QA@users.noreply.github.com>
Co-authored-by: Louis <louis@jan.ai>
  • Loading branch information
3 people committed Aug 2, 2024
1 parent e8ee694 commit ec9b5bf
Show file tree
Hide file tree
Showing 54 changed files with 1,117 additions and 835 deletions.
5 changes: 5 additions & 0 deletions core/src/types/file/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ export interface DownloadState2 {
*/
type: DownloadType2

/**
* Percentage of the download.
*/
progress: number

/**
* The status of the download.
*/
Expand Down
2 changes: 1 addition & 1 deletion electron/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ module.exports = {
{ name: 'Link', linkAttribute: 'to' },
],
},
ignorePatterns: ['build', 'renderer', 'node_modules', '@global'],
ignorePatterns: ['build', 'renderer', 'node_modules', '@global', 'playwright-report'],
}
22 changes: 20 additions & 2 deletions electron/download.bat
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
@echo off
setlocal

:: Read the version from the version.txt file
set /p CORTEX_VERSION=<./resources/version.txt

:: Set the download URL
set DOWNLOAD_URL=https://github.com/janhq/cortex/releases/download/v%CORTEX_VERSION%/cortex-%CORTEX_VERSION%-amd64-windows.tar.gz
echo Downloading from %DOWNLOAD_URL%

.\node_modules\.bin\download %DOWNLOAD_URL% -e -o ./resources/win
:: Set the output directory and file name
set OUTPUT_DIR=./resources/win
set OUTPUT_FILE=%OUTPUT_DIR%/cortex.exe

echo %OUTPUT_FILE%

:: Check if the file already exists
if exist %OUTPUT_FILE% (
echo File %OUTPUT_FILE% already exists. Skipping download.
) else (
echo Downloading from %DOWNLOAD_URL%
.\node_modules\.bin\download %DOWNLOAD_URL% -e -o %OUTPUT_DIR%
)

endlocal
98 changes: 61 additions & 37 deletions electron/handlers/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
writeFileSync,
readFileSync,
existsSync,
mkdirSync,
mkdirSync
} from 'fs'
import { dump } from 'js-yaml'
import os from 'os'
Expand Down Expand Up @@ -229,25 +229,20 @@ export function handleAppIPCs() {

const cortexHomeDir = join(os.homedir(), 'cortex')
const cortexModelFolderPath = join(cortexHomeDir, 'models')

if(!existsSync(cortexModelFolderPath))
mkdirSync(cortexModelFolderPath)
console.log('cortexModelFolderPath', cortexModelFolderPath)
const reflect = require('@alumna/reflect')

for (const modelName of allModelFolders) {
const modelFolderPath = join(janModelFolderPath, modelName)
const filesInModelFolder = readdirSync(modelFolderPath)
if (filesInModelFolder.length <= 1) {
// if only have model.json file or empty folder, we skip it
continue
}
try {

const destinationPath = join(cortexModelFolderPath, modelName)
const filesInModelFolder = readdirSync(modelFolderPath)

// create folder if not exist
if (!existsSync(destinationPath)) {
mkdirSync(destinationPath, { recursive: true })
}
const destinationPath = join(cortexModelFolderPath, modelName)

try {
const modelJsonFullPath = join(
janModelFolderPath,
modelName,
Expand All @@ -256,12 +251,25 @@ export function handleAppIPCs() {

const model = JSON.parse(readFileSync(modelJsonFullPath, 'utf-8'))
const fileNames: string[] = model.sources.map((x: any) => x.filename)
// prepend fileNames with cortexModelFolderPath
const files = fileNames.map((x: string) =>
join(cortexModelFolderPath, model.id, x)
)
let files: string[] = []

const engine = 'cortex.llamacpp'
if(filesInModelFolder.length > 1) {
// prepend fileNames with cortexModelFolderPath
files = fileNames.map((x: string) =>
join(cortexModelFolderPath, model.id, x)
)
} else if(model.sources.length && !/^(http|https):\/\/[^/]+\/.*/.test(model.sources[0].url)) {
// Symlink case
files = [ model.sources[0].url ]
} else continue;

// create folder if not exist
// only for local model files
if (!existsSync(destinationPath) && filesInModelFolder.length > 1) {
mkdirSync(destinationPath, { recursive: true })
}

const engine = (model.engine === 'nitro' || model.engine === 'cortex') ? 'cortex.llamacpp' : (model.engine ?? 'cortex.llamacpp')

const updatedModelFormat = {
id: model.id,
Expand All @@ -288,24 +296,27 @@ export function handleAppIPCs() {
max_tokens: model.parameters?.max_tokens ?? 2048,
stream: model.parameters?.stream ?? true,
}

const { err } = await reflect({
src: modelFolderPath,
dest: destinationPath,
recursive: true,
exclude: ['model.json'],
delete: false,
overwrite: true,
errorOnExist: false,
})
if (err) console.error(err)
else {
// create the model.yml file
const modelYamlData = dump(updatedModelFormat)
const modelYamlPath = join(cortexModelFolderPath, `${modelName}.yaml`)

writeFileSync(modelYamlPath, modelYamlData)
if(filesInModelFolder.length > 1 ) {
const { err } = await reflect({
src: modelFolderPath,
dest: destinationPath,
recursive: true,
exclude: ['model.json'],
delete: false,
overwrite: true,
errorOnExist: false,
})

if (err) {
console.error(err);
continue;
}
}
// create the model.yml file
const modelYamlData = dump(updatedModelFormat)
const modelYamlPath = join(cortexModelFolderPath, `${modelName}.yaml`)

writeFileSync(modelYamlPath, modelYamlData)
} catch (err) {
console.error(err)
}
Expand All @@ -316,6 +327,13 @@ export function handleAppIPCs() {
NativeRoute.getAllMessagesAndThreads,
async (_event): Promise<any> => {
const janThreadFolderPath = join(getJanDataFolderPath(), 'threads')
// check if exist
if (!existsSync(janThreadFolderPath)) {
return {
threads: [],
messages: [],
}
}
// get children of thread folder
const allThreadFolders = readdirSync(janThreadFolderPath)
const threads: any[] = []
Expand All @@ -335,10 +353,12 @@ export function handleAppIPCs() {
threadFolder,
'messages.jsonl'
)

if(!existsSync(messageFullPath)) continue;
const lines = readFileSync(messageFullPath, 'utf-8')
.toString()
.split('\n')
.filter((line: any) => line !== '')
.toString()
.split('\n')
.filter((line: any) => line !== '')
for (const line of lines) {
messages.push(JSON.parse(line))
}
Expand All @@ -357,6 +377,10 @@ export function handleAppIPCs() {
NativeRoute.getAllLocalModels,
async (_event): Promise<boolean> => {
const janModelsFolderPath = join(getJanDataFolderPath(), 'models')

if (!existsSync(janModelsFolderPath)) {
return false
}
// get children of thread folder
const allModelsFolders = readdirSync(janModelsFolderPath)
let hasLocalModels = false
Expand Down
119 changes: 109 additions & 10 deletions electron/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { app, BrowserWindow } from 'electron'

import { join, resolve } from 'path'
import { exec } from 'child_process'
import { exec, execSync, ChildProcess } from 'child_process'
import { cortexPath } from './cortex-runner'

/**
Expand Down Expand Up @@ -56,13 +56,18 @@ log.info('Log from the main process')
// replace all console.log to log
Object.assign(console, log.functions)

let cortexService: ChildProcess | undefined = undefined

app
.whenReady()
.then(() => killProcessesOnPort(3929))
.then(() => killProcessesOnPort(1337))
.then(() => {
log.info('Starting cortex with path:', cortexPath)
const command = `${cortexPath} -a 127.0.0.1 -p 1337`

log.info('Starting cortex with command:', command)
// init cortex
// running shell command cortex init -s
exec(`${cortexPath}`, (error, stdout, stderr) => {
cortexService = exec(`${command}`, (error, stdout, stderr) => {
if (error) {
log.error(`error: ${error.message}`)
return
Expand Down Expand Up @@ -123,25 +128,37 @@ app.on('open-url', (_event, url) => {
})

app.once('quit', async () => {
await stopApiServer()
cleanUpAndQuit()
})

app.once('window-all-closed', async () => {
await stopApiServer()
await stopCortexService()
cleanUpAndQuit()
})

async function stopCortexService() {
try {
const pid = cortexService?.pid
if (!pid) {
console.log('No cortex service to stop.')
return
}
process.kill(pid)
console.log(`Service with PID ${pid} has been terminated.`)
} catch (error) {
console.error('Error killing service:', error)
}
}

async function stopApiServer() {
// this function is not meant to be success. It will throw an error.
try {
console.log('Stopping API server')
const response = await fetch('http://localhost:1337/v1/process', {
await fetch('http://localhost:1337/v1/system', {
method: 'DELETE',
})

console.log('Response status:', response.status)
} catch (error) {
console.error('Error stopping API server:', error)
// do nothing
}
}

Expand All @@ -154,6 +171,88 @@ function handleIPCs() {
handleAppIPCs()
}

function killProcessesOnPort(port: number): void {
try {
console.log(`Killing processes on port ${port}...`)
if (process.platform === 'win32') {
killProcessesOnWindowsPort(port)
} else {
killProcessesOnUnixPort(port)
}
} catch (error) {
console.error(
`Failed to kill process(es) on port ${port}: ${(error as Error).message}`
)
}
}

function killProcessesOnWindowsPort(port: number): void {
let result: string
try {
result = execSync(`netstat -ano | findstr :${port}`).toString()
} catch (error) {
console.log(`No processes found on port ${port}.`)
return
}

const lines = result.split('\n').filter(Boolean)

if (lines.length === 0) {
console.log(`No processes found on port ${port}.`)
return
}

const pids = lines
.map((line) => {
const parts = line.trim().split(/\s+/)
return parts[parts.length - 1]
})
.filter((pid): pid is string => Boolean(pid) && !isNaN(Number(pid)))

if (pids.length === 0) {
console.log(`No valid PIDs found for port ${port}.`)
return
}
const uniquePids = Array.from(new Set(pids))
console.log('uniquePids', uniquePids)

uniquePids.forEach((pid) => {
try {
execSync(`taskkill /PID ${pid} /F`)
console.log(
`Process with PID ${pid} on port ${port} has been terminated.`
)
} catch (error) {
console.error(
`Failed to kill process with PID ${pid}: ${(error as Error).message}`
)
}
})
}

function killProcessesOnUnixPort(port: number): void {
let pids: string[]

try {
pids = execSync(`lsof -ti tcp:${port}`)
.toString()
.trim()
.split('\n')
.filter(Boolean)
} catch (error) {
if ((error as { status?: number }).status === 1) {
console.log(`No processes found on port ${port}.`)
return
}
throw error // Re-throw if it's not the "no processes found" error
}

pids.forEach((pid) => {
process.kill(parseInt(pid), 'SIGTERM')
console.log(`Process with PID ${pid} on port ${port} has been terminated.`)
})
}

/**
* Suppress Node error messages
*/
Expand Down
1 change: 1 addition & 0 deletions electron/managers/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class WindowManager {
x: bounds.x,
y: bounds.y,
webPreferences: {
allowRunningInsecureContent: true,
nodeIntegration: true,
preload: preloadPath,
webSecurity: false,
Expand Down
2 changes: 1 addition & 1 deletion electron/resources/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.5.0-1
0.5.0-5
Loading

0 comments on commit ec9b5bf

Please sign in to comment.