Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions cortex-js/src/infrastructure/commanders/init.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ export class InitCommand extends CommandRunner {

async run(passedParams: string[], options?: InitOptions): Promise<void> {
if (options?.silent) {
const installationOptions =
await this.initUsecases.defaultInstallationOptions();
await this.initUsecases.installEngine(installationOptions);
await this.initUsecases.installEngine(undefined);
} else {
options = await this.inquirerService.ask(
'init-run-mode-questions',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,7 @@ export class ModelPullCommand extends CommandRunner {
!existsSync(join(await this.fileService.getCortexCppEnginePath(), engine))
) {
console.log('\n');
await this.initUsecases.installEngine(
await this.initUsecases.defaultInstallationOptions(),
'latest',
engine,
);
await this.initUsecases.installEngine(undefined, 'latest', engine);
}
this.telemetryUsecases.sendEvent(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { CortexUsecases } from '@/usecases/cortex/cortex.usecases';
import { SetCommandContext } from '../decorators/CommandContext';
import { ContextService } from '@/infrastructure/services/context/context.service';
import { InitCliUsecases } from '../usecases/init.cli.usecases';
import { existsSync } from 'node:fs';
import { createReadStream, existsSync, statSync, watchFile } from 'node:fs';
import { FileManagerService } from '@/infrastructure/services/file-manager/file-manager.service';
import { join } from 'node:path';
import { Engines } from '../types/engine.interface';
Expand Down Expand Up @@ -61,7 +61,9 @@ export class ModelStartCommand extends CommandRunner {
!Array.isArray(existingModel.files) ||
/^(http|https):\/\/[^/]+\/.*/.test(existingModel.files[0])
) {
checkingSpinner.fail(`Model ${modelId} not found on filesystem.\nPlease try 'cortex pull ${modelId}' first.`);
checkingSpinner.fail(
`Model ${modelId} not found on filesystem.\nPlease try 'cortex pull ${modelId}' first.`,
);
process.exit(1);
}

Expand All @@ -74,18 +76,19 @@ export class ModelStartCommand extends CommandRunner {
!existsSync(join(await this.fileService.getCortexCppEnginePath(), engine))
) {
const engineSpinner = ora('Installing engine...').start();
await this.initUsecases.installEngine(
await this.initUsecases.defaultInstallationOptions(),
'latest',
engine,
);
await this.initUsecases.installEngine(undefined, 'latest', engine);
engineSpinner.succeed();
}

// Attached - stdout logs
if (options.attach) {
this.attachLogWatch();
}

await this.cortexUsecases
.startCortex(options.attach)
.startCortex()
.then(() => this.modelsCliUsecases.startModel(modelId, options.preset))
.then(console.log)
.then(() => !options.attach && process.exit(0));
.then(() => options.attach && ora('Model is running...').start());
}

modelInquiry = async () => {
Expand Down Expand Up @@ -120,4 +123,38 @@ export class ModelStartCommand extends CommandRunner {
parseTemplate(value: string) {
return value;
}

/**
* Attach to the log file and watch for changes
*/
private async attachLogWatch() {
const logPath = await this.fileService.getLogPath();
const initialSize = statSync(logPath).size;
const logStream = createReadStream(logPath, {
start: initialSize,
encoding: 'utf-8',
autoClose: false,
});
logStream.on('data', (chunk) => {
console.log(chunk);
});
watchFile(logPath, (curr, prev) => {
// Check if the file size has increased
if (curr.size > prev.size) {
// Calculate the position to start reading from
const position = prev.size;

// Create a new read stream from the updated position
const updateStream = createReadStream(logPath, {
encoding: 'utf8',
start: position,
});

// Read the newly written content
updateStream.on('data', (chunk) => {
console.log(chunk);
});
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,12 @@ export class RunCommand extends CommandRunner {
!existsSync(join(await this.fileService.getCortexCppEnginePath(), engine))
) {
const engineSpinner = ora('Installing engine...').start();
await this.initUsecases.installEngine(
await this.initUsecases.defaultInstallationOptions(),
'latest',
engine,
);
await this.initUsecases.installEngine(undefined, 'latest', engine);
engineSpinner.succeed('Engine installed');
}

return this.cortexUsecases
.startCortex(false)
.startCortex()
.then(() => this.modelsCliUsecases.startModel(modelId, options.preset))
.then(() => this.chatCliUsecases.chat(modelId, options.threadId));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@ export class InitCliUsecases {
* @param version
*/
installEngine = async (
options: InitOptions,
options?: InitOptions,
version: string = 'latest',
engine: string = 'default',
force: boolean = true,
): Promise<any> => {
// Use default option if not defined
if (!options) {
options = await this.defaultInstallationOptions();
}
const configs = await this.fileManagerService.getConfig();

if (configs.initialized && !force) return;
Expand Down Expand Up @@ -271,7 +275,7 @@ export class InitCliUsecases {
private detectInstructions = (): Promise<
'AVX' | 'AVX2' | 'AVX512' | undefined
> => {
const cpuInstruction = cpuInfo.cpuInfo()[0]?? 'AVX'
const cpuInstruction = cpuInfo.cpuInfo()[0] ?? 'AVX';
console.log(cpuInstruction, 'CPU instructions detected');
return Promise.resolve(cpuInstruction);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ export class ModelsCliUsecases {
.catch(async (e) => {
console.error('Model start failed with reason:', e.message);

printLastErrorLines(await this.fileService.getDataFolderPath(), 5);
printLastErrorLines(await this.fileService.getLogPath());

console.log(
'For more information, please check the logs at: %s',
join(await this.fileService.getDataFolderPath(), 'cortex.log'),
await this.fileService.getLogPath(),
);
process.exit(1);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,14 @@ export class FileManagerService {
return join(await this.getDataFolderPath(), 'cortex-cpp', 'engines');
}

/**
* Get log path
* @returns the path to the cortex engines folder
*/
async getLogPath(): Promise<string> {
return join(await this.getDataFolderPath(), 'cortex.log');
}

async createFolderIfNotExistInDataFolder(folderName: string): Promise<void> {
const dataFolderPath = await this.getDataFolderPath();
const folderPath = join(dataFolderPath, folderName);
Expand Down
17 changes: 6 additions & 11 deletions cortex-js/src/usecases/cortex/cortex.usecases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ export class CortexUsecases {
* @param attach
* @returns
*/
async startCortex(
attach: boolean = false,
): Promise<CortexOperationSuccessfullyDto> {
async startCortex(): Promise<CortexOperationSuccessfullyDto> {
const configs = await this.fileManagerService.getConfig();
const host = configs.cortexCppHost;
const port = configs.cortexCppPort;
Expand All @@ -56,14 +54,11 @@ export class CortexUsecases {
'cortex-cpp',
);

const writer = openSync(
join(await this.fileManagerService.getDataFolderPath(), 'cortex.log'),
'a+',
);
const writer = openSync(await this.fileManagerService.getLogPath(), 'a+');

// go up one level to get the binary folder, have to also work on windows
this.cortexProcess = spawn(cortexCppPath, args, {
detached: !attach,
detached: true,
cwd: cortexCppFolderPath,
stdio: [0, writer, writer],
env: {
Expand Down Expand Up @@ -143,9 +138,9 @@ export class CortexUsecases {

/**
* Check whether the Cortex CPP is healthy
* @param host
* @param port
* @returns
* @param host
* @param port
* @returns
*/
healthCheck(host: string, port: number): Promise<boolean> {
return fetch(CORTEX_CPP_HEALTH_Z_URL(host, port))
Expand Down
1 change: 1 addition & 0 deletions cortex-js/src/usecases/models/models.usecases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ export class ModelsUsecases {
};
this.eventEmitter.emit('model.event', modelEvent);
if (e.code === AxiosError.ERR_BAD_REQUEST) {
loadingModelSpinner.succeed('Model loaded');
return {
message: 'Model already loaded',
modelId,
Expand Down
6 changes: 3 additions & 3 deletions cortex-js/src/utils/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { createInterface } from 'readline';
* @param numLines
*/
export async function printLastErrorLines(
dataFolderPath: string,
numLines: number = 5,
logPath: string,
numLines: number = 10,
): Promise<void> {
const errorLines: string[] = [];

const fileStream = createReadStream(join(dataFolderPath, 'cortex.log'));
const fileStream = createReadStream(logPath);
const rl = createInterface({
input: fileStream,
crlfDelay: Infinity,
Expand Down