feat: Cortex API Authorization

This commit is contained in:
Louis 2025-03-20 19:00:29 +07:00
parent 09877c94a2
commit 821036945f
No known key found for this signature in database
GPG Key ID: 44FA9F4D33C37DE2
27 changed files with 221 additions and 348 deletions

View File

@ -28,6 +28,7 @@ export default defineConfig([
}, },
define: { define: {
CORTEX_API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`), CORTEX_API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
}, },
platform: 'node', platform: 'node',
}, },

View File

@ -1,3 +1,4 @@
declare const NODE: string declare const NODE: string
declare const VERSION: string declare const VERSION: string
declare const CORTEX_API_URL: string declare const CORTEX_API_URL: string
declare const CORTEX_API_KEY: string

View File

@ -27,7 +27,7 @@ export class Retrieval {
// declare time-weighted retriever and storage // declare time-weighted retriever and storage
this.timeWeightedVectorStore = new MemoryVectorStore( this.timeWeightedVectorStore = new MemoryVectorStore(
new OpenAIEmbeddings( new OpenAIEmbeddings(
{ openAIApiKey: 'cortex-embedding' }, { openAIApiKey: CORTEX_API_KEY },
{ basePath: `${CORTEX_API_URL}/v1` } { basePath: `${CORTEX_API_URL}/v1` }
) )
) )
@ -49,7 +49,7 @@ export class Retrieval {
public updateEmbeddingEngine(model: string, engine: string): void { public updateEmbeddingEngine(model: string, engine: string): void {
this.embeddingModel = new OpenAIEmbeddings( this.embeddingModel = new OpenAIEmbeddings(
{ openAIApiKey: 'cortex-embedding', model }, { openAIApiKey: CORTEX_API_KEY, model },
// TODO: Raw settings // TODO: Raw settings
{ basePath: `${CORTEX_API_URL}/v1` } { basePath: `${CORTEX_API_URL}/v1` }
) )

View File

@ -9,5 +9,6 @@ export default defineConfig({
platform: 'browser', platform: 'browser',
define: { define: {
API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`), API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
}, },
}) })

View File

@ -1,4 +1,5 @@
declare const API_URL: string declare const API_URL: string
declare const CORTEX_API_KEY: string
interface Core { interface Core {
api: APIFunctions api: APIFunctions

View File

@ -22,6 +22,17 @@ type MessageList = {
export default class CortexConversationalExtension extends ConversationalExtension { export default class CortexConversationalExtension extends ConversationalExtension {
queue = new PQueue({ concurrency: 1 }) queue = new PQueue({ concurrency: 1 })
/**
* Extended API instance for making requests to the Cortex API.
* @returns
*/
api = () =>
ky.extend({
prefixUrl: API_URL,
headers: {
Authorization: `Bearer ${CORTEX_API_KEY}`,
},
})
/** /**
* Called when the extension is loaded. * Called when the extension is loaded.
*/ */
@ -39,8 +50,8 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async listThreads(): Promise<Thread[]> { async listThreads(): Promise<Thread[]> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.get(`${API_URL}/v1/threads?limit=-1`) .get('v1/threads?limit=-1')
.json<ThreadList>() .json<ThreadList>()
.then((e) => e.data) .then((e) => e.data)
) as Promise<Thread[]> ) as Promise<Thread[]>
@ -52,7 +63,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async createThread(thread: Thread): Promise<Thread> { async createThread(thread: Thread): Promise<Thread> {
return this.queue.add(() => return this.queue.add(() =>
ky.post(`${API_URL}/v1/threads`, { json: thread }).json<Thread>() this.api().post('v1/threads', { json: thread }).json<Thread>()
) as Promise<Thread> ) as Promise<Thread>
} }
@ -63,7 +74,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
async modifyThread(thread: Thread): Promise<void> { async modifyThread(thread: Thread): Promise<void> {
return this.queue return this.queue
.add(() => .add(() =>
ky.patch(`${API_URL}/v1/threads/${thread.id}`, { json: thread }) this.api().patch(`v1/threads/${thread.id}`, { json: thread })
) )
.then() .then()
} }
@ -74,7 +85,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async deleteThread(threadId: string): Promise<void> { async deleteThread(threadId: string): Promise<void> {
return this.queue return this.queue
.add(() => ky.delete(`${API_URL}/v1/threads/${threadId}`)) .add(() => this.api().delete(`v1/threads/${threadId}`))
.then() .then()
} }
@ -85,8 +96,8 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async createMessage(message: ThreadMessage): Promise<ThreadMessage> { async createMessage(message: ThreadMessage): Promise<ThreadMessage> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.post(`${API_URL}/v1/threads/${message.thread_id}/messages`, { .post(`v1/threads/${message.thread_id}/messages`, {
json: message, json: message,
}) })
.json<ThreadMessage>() .json<ThreadMessage>()
@ -100,9 +111,9 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async modifyMessage(message: ThreadMessage): Promise<ThreadMessage> { async modifyMessage(message: ThreadMessage): Promise<ThreadMessage> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.patch( .patch(
`${API_URL}/v1/threads/${message.thread_id}/messages/${message.id}`, `v1/threads/${message.thread_id}/messages/${message.id}`,
{ {
json: message, json: message,
} }
@ -120,7 +131,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
async deleteMessage(threadId: string, messageId: string): Promise<void> { async deleteMessage(threadId: string, messageId: string): Promise<void> {
return this.queue return this.queue
.add(() => .add(() =>
ky.delete(`${API_URL}/v1/threads/${threadId}/messages/${messageId}`) this.api().delete(`v1/threads/${threadId}/messages/${messageId}`)
) )
.then() .then()
} }
@ -132,8 +143,8 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async listMessages(threadId: string): Promise<ThreadMessage[]> { async listMessages(threadId: string): Promise<ThreadMessage[]> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.get(`${API_URL}/v1/threads/${threadId}/messages?order=asc&limit=-1`) .get(`v1/threads/${threadId}/messages?order=asc&limit=-1`)
.json<MessageList>() .json<MessageList>()
.then((e) => e.data) .then((e) => e.data)
) as Promise<ThreadMessage[]> ) as Promise<ThreadMessage[]>
@ -147,8 +158,8 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async getThreadAssistant(threadId: string): Promise<ThreadAssistantInfo> { async getThreadAssistant(threadId: string): Promise<ThreadAssistantInfo> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.get(`${API_URL}/v1/assistants/${threadId}?limit=-1`) .get(`v1/assistants/${threadId}?limit=-1`)
.json<ThreadAssistantInfo>() .json<ThreadAssistantInfo>()
) as Promise<ThreadAssistantInfo> ) as Promise<ThreadAssistantInfo>
} }
@ -163,8 +174,8 @@ export default class CortexConversationalExtension extends ConversationalExtensi
assistant: ThreadAssistantInfo assistant: ThreadAssistantInfo
): Promise<ThreadAssistantInfo> { ): Promise<ThreadAssistantInfo> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.post(`${API_URL}/v1/assistants/${threadId}`, { json: assistant }) .post(`v1/assistants/${threadId}`, { json: assistant })
.json<ThreadAssistantInfo>() .json<ThreadAssistantInfo>()
) as Promise<ThreadAssistantInfo> ) as Promise<ThreadAssistantInfo>
} }
@ -180,8 +191,8 @@ export default class CortexConversationalExtension extends ConversationalExtensi
assistant: ThreadAssistantInfo assistant: ThreadAssistantInfo
): Promise<ThreadAssistantInfo> { ): Promise<ThreadAssistantInfo> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.patch(`${API_URL}/v1/assistants/${threadId}`, { json: assistant }) .patch(`v1/assistants/${threadId}`, { json: assistant })
.json<ThreadAssistantInfo>() .json<ThreadAssistantInfo>()
) as Promise<ThreadAssistantInfo> ) as Promise<ThreadAssistantInfo>
} }
@ -191,8 +202,8 @@ export default class CortexConversationalExtension extends ConversationalExtensi
* @returns * @returns
*/ */
async healthz(): Promise<void> { async healthz(): Promise<void> {
return ky return this.api()
.get(`${API_URL}/healthz`, { .get('healthz', {
retry: { limit: 20, delay: () => 500, methods: ['get'] }, retry: { limit: 20, delay: () => 500, methods: ['get'] },
}) })
.then(() => {}) .then(() => {})

View File

@ -28,6 +28,7 @@ export default defineConfig([
'Authorization: Bearer {{api_key}}' 'Authorization: Bearer {{api_key}}'
), ),
VERSION: JSON.stringify(pkgJson.version ?? '0.0.0'), VERSION: JSON.stringify(pkgJson.version ?? '0.0.0'),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
}, },
}, },
{ {

View File

@ -6,6 +6,7 @@ declare const DEFAULT_REQUEST_PAYLOAD_TRANSFORM: string
declare const DEFAULT_RESPONSE_BODY_TRANSFORM: string declare const DEFAULT_RESPONSE_BODY_TRANSFORM: string
declare const DEFAULT_REQUEST_HEADERS_TRANSFORM: string declare const DEFAULT_REQUEST_HEADERS_TRANSFORM: string
declare const VERSION: string declare const VERSION: string
declare const CORTEX_API_KEY: string
declare const DEFAULT_REMOTE_ENGINES: ({ declare const DEFAULT_REMOTE_ENGINES: ({
id: string id: string

View File

@ -31,6 +31,17 @@ interface ModelList {
export default class JanEngineManagementExtension extends EngineManagementExtension { export default class JanEngineManagementExtension extends EngineManagementExtension {
queue = new PQueue({ concurrency: 1 }) queue = new PQueue({ concurrency: 1 })
/**
* Extended API instance for making requests to the Cortex API.
* @returns
*/
api = () =>
ky.extend({
prefixUrl: API_URL,
headers: {
Authorization: `Bearer ${CORTEX_API_KEY}`,
},
})
/** /**
* Called when the extension is loaded. * Called when the extension is loaded.
*/ */
@ -59,8 +70,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async getEngines(): Promise<Engines> { async getEngines(): Promise<Engines> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.get(`${API_URL}/v1/engines`) .get('v1/engines')
.json<Engines>() .json<Engines>()
.then((e) => e) .then((e) => e)
) as Promise<Engines> ) as Promise<Engines>
@ -70,8 +81,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
* @returns A Promise that resolves to an object of list engines. * @returns A Promise that resolves to an object of list engines.
*/ */
async getRemoteModels(name: string): Promise<any> { async getRemoteModels(name: string): Promise<any> {
return ky return this.api()
.get(`${API_URL}/v1/models/remote/${name}`) .get(`v1/models/remote/${name}`)
.json<ModelList>() .json<ModelList>()
.catch(() => ({ .catch(() => ({
data: [], data: [],
@ -84,8 +95,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async getInstalledEngines(name: InferenceEngine): Promise<EngineVariant[]> { async getInstalledEngines(name: InferenceEngine): Promise<EngineVariant[]> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.get(`${API_URL}/v1/engines/${name}`) .get(`v1/engines/${name}`)
.json<EngineVariant[]>() .json<EngineVariant[]>()
.then((e) => e) .then((e) => e)
) as Promise<EngineVariant[]> ) as Promise<EngineVariant[]>
@ -103,8 +114,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
platform?: string platform?: string
) { ) {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.get(`${API_URL}/v1/engines/${name}/releases/${version}`) .get(`v1/engines/${name}/releases/${version}`)
.json<EngineReleased[]>() .json<EngineReleased[]>()
.then((e) => .then((e) =>
platform ? e.filter((r) => r.name.includes(platform)) : e platform ? e.filter((r) => r.name.includes(platform)) : e
@ -119,8 +130,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async getLatestReleasedEngine(name: InferenceEngine, platform?: string) { async getLatestReleasedEngine(name: InferenceEngine, platform?: string) {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.get(`${API_URL}/v1/engines/${name}/releases/latest`) .get(`v1/engines/${name}/releases/latest`)
.json<EngineReleased[]>() .json<EngineReleased[]>()
.then((e) => .then((e) =>
platform ? e.filter((r) => r.name.includes(platform)) : e platform ? e.filter((r) => r.name.includes(platform)) : e
@ -134,8 +145,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async installEngine(name: string, engineConfig: EngineConfig) { async installEngine(name: string, engineConfig: EngineConfig) {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.post(`${API_URL}/v1/engines/${name}/install`, { json: engineConfig }) .post(`v1/engines/${name}/install`, { json: engineConfig })
.then((e) => e) .then((e) => e)
) as Promise<{ messages: string }> ) as Promise<{ messages: string }>
} }
@ -167,7 +178,9 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
engineConfig.metadata.header_template = DEFAULT_REQUEST_HEADERS_TRANSFORM engineConfig.metadata.header_template = DEFAULT_REQUEST_HEADERS_TRANSFORM
return this.queue.add(() => return this.queue.add(() =>
ky.post(`${API_URL}/v1/engines`, { json: engineConfig }).then((e) => { this.api()
.post('v1/engines', { json: engineConfig })
.then((e) => {
if (persistModels && engineConfig.metadata?.get_models_url) { if (persistModels && engineConfig.metadata?.get_models_url) {
// Pull /models from remote models endpoint // Pull /models from remote models endpoint
return this.populateRemoteModels(engineConfig) return this.populateRemoteModels(engineConfig)
@ -185,8 +198,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async uninstallEngine(name: InferenceEngine, engineConfig: EngineConfig) { async uninstallEngine(name: InferenceEngine, engineConfig: EngineConfig) {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.delete(`${API_URL}/v1/engines/${name}/install`, { json: engineConfig }) .delete(`v1/engines/${name}/install`, { json: engineConfig })
.then((e) => e) .then((e) => e)
) as Promise<{ messages: string }> ) as Promise<{ messages: string }>
} }
@ -198,8 +211,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
async addRemoteModel(model: Model) { async addRemoteModel(model: Model) {
return this.queue return this.queue
.add(() => .add(() =>
ky this.api()
.post(`${API_URL}/v1/models/add`, { .post('v1/models/add', {
json: { json: {
inference_params: { inference_params: {
max_tokens: 4096, max_tokens: 4096,
@ -223,8 +236,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async getDefaultEngineVariant(name: InferenceEngine) { async getDefaultEngineVariant(name: InferenceEngine) {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.get(`${API_URL}/v1/engines/${name}/default`) .get(`v1/engines/${name}/default`)
.json<{ messages: string }>() .json<{ messages: string }>()
.then((e) => e) .then((e) => e)
) as Promise<DefaultEngineVariant> ) as Promise<DefaultEngineVariant>
@ -240,8 +253,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
engineConfig: EngineConfig engineConfig: EngineConfig
) { ) {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.post(`${API_URL}/v1/engines/${name}/default`, { json: engineConfig }) .post(`v1/engines/${name}/default`, { json: engineConfig })
.then((e) => e) .then((e) => e)
) as Promise<{ messages: string }> ) as Promise<{ messages: string }>
} }
@ -251,8 +264,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async updateEngine(name: InferenceEngine, engineConfig?: EngineConfig) { async updateEngine(name: InferenceEngine, engineConfig?: EngineConfig) {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.post(`${API_URL}/v1/engines/${name}/update`, { json: engineConfig }) .post(`v1/engines/${name}/update`, { json: engineConfig })
.then((e) => e) .then((e) => e)
) as Promise<{ messages: string }> ) as Promise<{ messages: string }>
} }
@ -262,8 +275,8 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
* @returns * @returns
*/ */
async healthz(): Promise<void> { async healthz(): Promise<void> {
return ky return this.api()
.get(`${API_URL}/healthz`, { .get('healthz', {
retry: { limit: 20, delay: () => 500, methods: ['get'] }, retry: { limit: 20, delay: () => 500, methods: ['get'] },
}) })
.then(() => { .then(() => {
@ -390,7 +403,6 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
const version = await this.getSetting<string>('version', '0.0.0') const version = await this.getSetting<string>('version', '0.0.0')
const engines = await this.getEngines() const engines = await this.getEngines()
if (version < VERSION) { if (version < VERSION) {
console.log('Migrating engine settings...') console.log('Migrating engine settings...')
// Migrate engine settings // Migrate engine settings
await Promise.all( await Promise.all(

View File

@ -29,12 +29,10 @@
}, },
"dependencies": { "dependencies": {
"@janhq/core": "../../core/package.tgz", "@janhq/core": "../../core/package.tgz",
"cpu-instructions": "^0.0.13",
"ky": "^1.7.2", "ky": "^1.7.2",
"p-queue": "^8.0.1" "p-queue": "^8.0.1"
}, },
"bundledDependencies": [ "bundledDependencies": [
"cpu-instructions",
"@janhq/core" "@janhq/core"
], ],
"hardwares": { "hardwares": {

View File

@ -11,6 +11,7 @@ export default defineConfig([
define: { define: {
NODE: JSON.stringify(`${pkgJson.name}/${pkgJson.node}`), NODE: JSON.stringify(`${pkgJson.name}/${pkgJson.node}`),
API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`), API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
}, },
}, },
]) ])

View File

@ -1,5 +1,6 @@
declare const API_URL: string declare const API_URL: string
declare const NODE: string declare const NODE: string
declare const CORTEX_API_KEY: string
interface Core { interface Core {
api: APIFunctions api: APIFunctions

View File

@ -9,6 +9,17 @@ import PQueue from 'p-queue'
export default class JSONHardwareManagementExtension extends HardwareManagementExtension { export default class JSONHardwareManagementExtension extends HardwareManagementExtension {
queue = new PQueue({ concurrency: 1 }) queue = new PQueue({ concurrency: 1 })
/**
* Extended API instance for making requests to the Cortex API.
* @returns
*/
api = () =>
ky.extend({
prefixUrl: API_URL,
headers: {
'Authorization': `Bearer ${CORTEX_API_KEY}`,
},
});
/** /**
* Called when the extension is loaded. * Called when the extension is loaded.
*/ */
@ -27,8 +38,8 @@ export default class JSONHardwareManagementExtension extends HardwareManagementE
* @returns * @returns
*/ */
async healthz(): Promise<void> { async healthz(): Promise<void> {
return ky return this.api()
.get(`${API_URL}/healthz`, { .get('healthz', {
retry: { limit: 20, delay: () => 500, methods: ['get'] }, retry: { limit: 20, delay: () => 500, methods: ['get'] },
}) })
.then(() => {}) .then(() => {})
@ -39,8 +50,8 @@ export default class JSONHardwareManagementExtension extends HardwareManagementE
*/ */
async getHardware(): Promise<HardwareInformation> { async getHardware(): Promise<HardwareInformation> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.get(`${API_URL}/v1/hardware`) .get('v1/hardware')
.json<HardwareInformation>() .json<HardwareInformation>()
.then((e) => e) .then((e) => e)
) as Promise<HardwareInformation> ) as Promise<HardwareInformation>
@ -54,7 +65,7 @@ export default class JSONHardwareManagementExtension extends HardwareManagementE
activated_gpus: number[] activated_gpus: number[]
}> { }> {
return this.queue.add(() => return this.queue.add(() =>
ky.post(`${API_URL}/v1/hardware/activate`, { json: data }).then((e) => e) this.api().post('v1/hardware/activate', { json: data }).then((e) => e)
) as Promise<{ ) as Promise<{
message: string message: string
activated_gpus: number[] activated_gpus: number[]

View File

@ -1 +1 @@
1.0.11 1.0.12-rc3

View File

@ -5,11 +5,11 @@ set /p CORTEX_VERSION=<./bin/version.txt
set ENGINE_VERSION=0.1.55 set ENGINE_VERSION=0.1.55
@REM Download cortex.llamacpp binaries @REM Download cortex.llamacpp binaries
set DOWNLOAD_URL=https://github.com/janhq/cortex.llamacpp/releases/download/v%ENGINE_VERSION%/cortex.llamacpp-%ENGINE_VERSION%-windows-amd64 set DOWNLOAD_URL=https://github.com/menloresearch/cortex.llamacpp/releases/download/v%ENGINE_VERSION%/cortex.llamacpp-%ENGINE_VERSION%-windows-amd64
set CUDA_DOWNLOAD_URL=https://github.com/janhq/cortex.llamacpp/releases/download/v%ENGINE_VERSION% set CUDA_DOWNLOAD_URL=https://github.com/menloresearch/cortex.llamacpp/releases/download/v%ENGINE_VERSION%
set SUBFOLDERS=windows-amd64-noavx-cuda-12-0 windows-amd64-noavx-cuda-11-7 windows-amd64-avx2-cuda-12-0 windows-amd64-avx2-cuda-11-7 windows-amd64-noavx windows-amd64-avx windows-amd64-avx2 windows-amd64-avx512 windows-amd64-vulkan set SUBFOLDERS=windows-amd64-noavx-cuda-12-0 windows-amd64-noavx-cuda-11-7 windows-amd64-avx2-cuda-12-0 windows-amd64-avx2-cuda-11-7 windows-amd64-noavx windows-amd64-avx windows-amd64-avx2 windows-amd64-avx512 windows-amd64-vulkan
call .\node_modules\.bin\download -e --strip 1 -o %BIN_PATH% https://github.com/janhq/cortex.cpp/releases/download/v%CORTEX_VERSION%/cortex-%CORTEX_VERSION%-windows-amd64.tar.gz call .\node_modules\.bin\download -e --strip 1 -o %BIN_PATH% https://github.com/menloresearch/cortex.cpp/releases/download/v%CORTEX_VERSION%/cortex-%CORTEX_VERSION%-windows-amd64.tar.gz
call .\node_modules\.bin\download %DOWNLOAD_URL%-avx2-cuda-12-0.tar.gz -e --strip 1 -o %SHARED_PATH%/engines/cortex.llamacpp/windows-amd64-avx2-cuda-12-0/v%ENGINE_VERSION% call .\node_modules\.bin\download %DOWNLOAD_URL%-avx2-cuda-12-0.tar.gz -e --strip 1 -o %SHARED_PATH%/engines/cortex.llamacpp/windows-amd64-avx2-cuda-12-0/v%ENGINE_VERSION%
call .\node_modules\.bin\download %DOWNLOAD_URL%-avx2-cuda-11-7.tar.gz -e --strip 1 -o %SHARED_PATH%/engines/cortex.llamacpp/windows-amd64-avx2-cuda-11-7/v%ENGINE_VERSION% call .\node_modules\.bin\download %DOWNLOAD_URL%-avx2-cuda-11-7.tar.gz -e --strip 1 -o %SHARED_PATH%/engines/cortex.llamacpp/windows-amd64-avx2-cuda-11-7/v%ENGINE_VERSION%
call .\node_modules\.bin\download %DOWNLOAD_URL%-noavx-cuda-12-0.tar.gz -e --strip 1 -o %SHARED_PATH%/engines/cortex.llamacpp/windows-amd64-noavx-cuda-12-0/v%ENGINE_VERSION% call .\node_modules\.bin\download %DOWNLOAD_URL%-noavx-cuda-12-0.tar.gz -e --strip 1 -o %SHARED_PATH%/engines/cortex.llamacpp/windows-amd64-noavx-cuda-12-0/v%ENGINE_VERSION%

View File

@ -3,9 +3,9 @@
# Read CORTEX_VERSION # Read CORTEX_VERSION
CORTEX_VERSION=$(cat ./bin/version.txt) CORTEX_VERSION=$(cat ./bin/version.txt)
ENGINE_VERSION=0.1.55 ENGINE_VERSION=0.1.55
CORTEX_RELEASE_URL="https://github.com/janhq/cortex.cpp/releases/download" CORTEX_RELEASE_URL="https://github.com/menloresearch/cortex.cpp/releases/download"
ENGINE_DOWNLOAD_URL="https://github.com/janhq/cortex.llamacpp/releases/download/v${ENGINE_VERSION}/cortex.llamacpp-${ENGINE_VERSION}" ENGINE_DOWNLOAD_URL="https://github.com/menloresearch/cortex.llamacpp/releases/download/v${ENGINE_VERSION}/cortex.llamacpp-${ENGINE_VERSION}"
CUDA_DOWNLOAD_URL="https://github.com/janhq/cortex.llamacpp/releases/download/v${ENGINE_VERSION}" CUDA_DOWNLOAD_URL="https://github.com/menloresearch/cortex.llamacpp/releases/download/v${ENGINE_VERSION}"
BIN_PATH=./bin BIN_PATH=./bin
SHARED_PATH="../../electron/shared" SHARED_PATH="../../electron/shared"
# Detect platform # Detect platform

View File

@ -19,12 +19,13 @@ export default defineConfig([
CORTEX_SOCKET_URL: JSON.stringify( CORTEX_SOCKET_URL: JSON.stringify(
`ws://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}` `ws://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}`
), ),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
CORTEX_ENGINE_VERSION: JSON.stringify('v0.1.55'), CORTEX_ENGINE_VERSION: JSON.stringify('v0.1.55'),
}, },
}, },
{ {
input: 'src/node/index.ts', input: 'src/node/index.ts',
external: ['@janhq/core/node', 'cpu-instructions'], external: ['@janhq/core/node'],
output: { output: {
format: 'cjs', format: 'cjs',
file: 'dist/node/index.cjs.js', file: 'dist/node/index.cjs.js',
@ -35,6 +36,7 @@ export default defineConfig([
extensions: ['.js', '.ts', '.json'], extensions: ['.js', '.ts', '.json'],
}, },
define: { define: {
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
CORTEX_API_URL: JSON.stringify( CORTEX_API_URL: JSON.stringify(
`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}` `http://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}`
), ),

View File

@ -3,3 +3,4 @@ declare const CORTEX_API_URL: string
declare const CORTEX_SOCKET_URL: string declare const CORTEX_SOCKET_URL: string
declare const CORTEX_ENGINE_VERSION: string declare const CORTEX_ENGINE_VERSION: string
declare const SETTINGS: any declare const SETTINGS: any
declare const CORTEX_API_KEY: string

View File

@ -75,6 +75,28 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
abortControllers = new Map<string, AbortController>() abortControllers = new Map<string, AbortController>()
/**
* Extended API instance for making requests to the Cortex API.
* @returns
*/
api = () =>
ky.extend({
prefixUrl: CORTEX_API_URL,
headers: {
Authorization: `Bearer ${CORTEX_API_KEY}`,
},
})
/**
* Authorization headers for the API requests.
* @returns
*/
headers(): Promise<HeadersInit> {
return Promise.resolve({
Authorization: `Bearer ${CORTEX_API_KEY}`,
})
}
/** /**
* Subscribes to events emitted by the @janhq/core package. * Subscribes to events emitted by the @janhq/core package.
*/ */
@ -153,8 +175,8 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
this.abortControllers.set(model.id, controller) this.abortControllers.set(model.id, controller)
return await this.queue.add(() => return await this.queue.add(() =>
ky this.api()
.post(`${CORTEX_API_URL}/v1/models/start`, { .post('v1/models/start', {
json: { json: {
...extractModelLoadParams(model.settings), ...extractModelLoadParams(model.settings),
model: model.id, model: model.id,
@ -183,8 +205,8 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
} }
override async unloadModel(model: Model): Promise<void> { override async unloadModel(model: Model): Promise<void> {
return ky return this.api()
.post(`${CORTEX_API_URL}/v1/models/stop`, { .post('v1/models/stop', {
json: { model: model.id }, json: { model: model.id },
}) })
.json() .json()
@ -199,8 +221,8 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
* @returns * @returns
*/ */
private async healthz(): Promise<void> { private async healthz(): Promise<void> {
return ky return this.api()
.get(`${CORTEX_API_URL}/healthz`, { .get('healthz', {
retry: { retry: {
limit: 20, limit: 20,
delay: () => 500, delay: () => 500,
@ -215,8 +237,8 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
* @returns * @returns
*/ */
private async clean(): Promise<any> { private async clean(): Promise<any> {
return ky return this.api()
.delete(`${CORTEX_API_URL}/processmanager/destroy`, { .delete('processmanager/destroy', {
timeout: 2000, // maximum 2 seconds timeout: 2000, // maximum 2 seconds
retry: { retry: {
limit: 0, limit: 0,

View File

@ -44,8 +44,9 @@ function run(): Promise<any> {
`${path.join(dataFolderPath, '.janrc')}`, `${path.join(dataFolderPath, '.janrc')}`,
'--data_folder_path', '--data_folder_path',
dataFolderPath, dataFolderPath,
'--loglevel', 'config',
'INFO', '--api_keys',
CORTEX_API_KEY ?? 'cortexserver',
], ],
{ {
env: { env: {

View File

@ -11,7 +11,8 @@ export default defineConfig({
platform: 'browser', platform: 'browser',
define: { define: {
SETTINGS: JSON.stringify(settingJson), SETTINGS: JSON.stringify(settingJson),
API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`), CORTEX_API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`),
DEFAULT_MODEL_SOURCES: JSON.stringify(modelSources), DEFAULT_MODEL_SOURCES: JSON.stringify(modelSources),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
}, },
}) })

View File

@ -1,7 +1,8 @@
declare const NODE: string declare const NODE: string
declare const API_URL: string declare const CORTEX_API_URL: string
declare const SETTINGS: SettingComponentProps[] declare const SETTINGS: SettingComponentProps[]
declare const DEFAULT_MODEL_SOURCES: any declare const DEFAULT_MODEL_SOURCES: any
declare const CORTEX_API_KEY: string
interface Core { interface Core {
api: APIFunctions api: APIFunctions

View File

@ -32,6 +32,19 @@ type Data<T> = {
*/ */
export default class JanModelExtension extends ModelExtension { export default class JanModelExtension extends ModelExtension {
queue = new PQueue({ concurrency: 1 }) queue = new PQueue({ concurrency: 1 })
/**
* Extended API instance for making requests to the Cortex API.
* @returns
*/
api = () =>
ky.extend({
prefixUrl: CORTEX_API_URL,
headers: {
Authorization: `Bearer ${CORTEX_API_KEY}`,
},
})
/** /**
* Called when the extension is loaded. * Called when the extension is loaded.
* @override * @override
@ -82,8 +95,8 @@ export default class JanModelExtension extends ModelExtension {
* Sending POST to /models/pull/{id} endpoint to pull the model * Sending POST to /models/pull/{id} endpoint to pull the model
*/ */
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.post(`${API_URL}/v1/models/pull`, { json: { model, id, name } }) .post('v1/models/pull', { json: { model, id, name } })
.json() .json()
.catch(async (e) => { .catch(async (e) => {
throw (await e.response?.json()) ?? e throw (await e.response?.json()) ?? e
@ -103,8 +116,8 @@ export default class JanModelExtension extends ModelExtension {
* Sending DELETE to /models/pull/{id} endpoint to cancel a model pull * Sending DELETE to /models/pull/{id} endpoint to cancel a model pull
*/ */
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.delete(`${API_URL}/v1/models/pull`, { json: { taskId: model } }) .delete('v1/models/pull', { json: { taskId: model } })
.json() .json()
.then() .then()
) )
@ -117,7 +130,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async deleteModel(model: string): Promise<void> { async deleteModel(model: string): Promise<void> {
return this.queue return this.queue
.add(() => ky.delete(`${API_URL}/v1/models/${model}`).json().then()) .add(() => this.api().delete(`v1/models/${model}`).json().then())
.catch((e) => console.debug(e)) .catch((e) => console.debug(e))
.finally(async () => { .finally(async () => {
// Delete legacy model files // Delete legacy model files
@ -219,8 +232,8 @@ export default class JanModelExtension extends ModelExtension {
async updateModel(model: Partial<Model>): Promise<Model> { async updateModel(model: Partial<Model>): Promise<Model> {
return this.queue return this.queue
.add(() => .add(() =>
ky this.api()
.patch(`${API_URL}/v1/models/${model.id}`, { json: { ...model } }) .patch(`v1/models/${model.id}`, { json: { ...model } })
.json() .json()
.then() .then()
) )
@ -233,8 +246,8 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async getModel(model: string): Promise<Model> { async getModel(model: string): Promise<Model> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.get(`${API_URL}/v1/models/${model}`) .get(`v1/models/${model}`)
.json() .json()
.then((e) => this.transformModel(e)) .then((e) => this.transformModel(e))
) as Promise<Model> ) as Promise<Model>
@ -252,8 +265,8 @@ export default class JanModelExtension extends ModelExtension {
option?: OptionType option?: OptionType
): Promise<void> { ): Promise<void> {
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.post(`${API_URL}/v1/models/import`, { .post('v1/models/import', {
json: { model, modelPath, name, option }, json: { model, modelPath, name, option },
}) })
.json() .json()
@ -269,7 +282,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async getSources(): Promise<ModelSource[]> { async getSources(): Promise<ModelSource[]> {
const sources = await this.queue const sources = await this.queue
.add(() => ky.get(`${API_URL}/v1/models/sources`).json<Data<ModelSource>>()) .add(() => this.api().get('v1/models/sources').json<Data<ModelSource>>())
.then((e) => (typeof e === 'object' ? (e.data as ModelSource[]) : [])) .then((e) => (typeof e === 'object' ? (e.data as ModelSource[]) : []))
.catch(() => []) .catch(() => [])
return sources.concat( return sources.concat(
@ -283,7 +296,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async addSource(source: string): Promise<any> { async addSource(source: string): Promise<any> {
return this.queue.add(() => return this.queue.add(() =>
ky.post(`${API_URL}/v1/models/sources`, { this.api().post('v1/models/sources', {
json: { json: {
source, source,
}, },
@ -297,7 +310,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async deleteSource(source: string): Promise<any> { async deleteSource(source: string): Promise<any> {
return this.queue.add(() => return this.queue.add(() =>
ky.delete(`${API_URL}/v1/models/sources`, { this.api().delete('v1/models/sources', {
json: { json: {
source, source,
}, },
@ -312,7 +325,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async isModelLoaded(model: string): Promise<boolean> { async isModelLoaded(model: string): Promise<boolean> {
return this.queue return this.queue
.add(() => ky.get(`${API_URL}/v1/models/status/${model}`)) .add(() => this.api().get(`v1/models/status/${model}`))
.then((e) => true) .then((e) => true)
.catch(() => false) .catch(() => false)
} }
@ -331,7 +344,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async fetchModels(): Promise<Model[]> { async fetchModels(): Promise<Model[]> {
return this.queue return this.queue
.add(() => ky.get(`${API_URL}/v1/models?limit=-1`).json<Data<Model>>()) .add(() => this.api().get('v1/models?limit=-1').json<Data<Model>>())
.then((e) => .then((e) =>
typeof e === 'object' ? e.data.map((e) => this.transformModel(e)) : [] typeof e === 'object' ? e.data.map((e) => this.transformModel(e)) : []
) )
@ -371,7 +384,9 @@ export default class JanModelExtension extends ModelExtension {
}): Promise<void> { }): Promise<void> {
return this.queue return this.queue
.add(() => .add(() =>
ky.patch(`${API_URL}/v1/configs`, { json: body }).then(() => {}) this.api()
.patch('v1/configs', { json: body })
.then(() => {})
) )
.catch((e) => console.debug(e)) .catch((e) => console.debug(e))
} }
@ -381,8 +396,8 @@ export default class JanModelExtension extends ModelExtension {
* @returns * @returns
*/ */
private healthz(): Promise<void> { private healthz(): Promise<void> {
return ky return this.api()
.get(`${API_URL}/healthz`, { .get('healthz', {
retry: { retry: {
limit: 20, limit: 20,
delay: () => 500, delay: () => 500,
@ -401,8 +416,8 @@ export default class JanModelExtension extends ModelExtension {
const models = await this.fetchModels() const models = await this.fetchModels()
return this.queue.add(() => return this.queue.add(() =>
ky this.api()
.get(`${API_URL}/v1/models/hub?author=cortexso&tag=cortex.cpp`) .get('v1/models/hub?author=cortexso&tag=cortex.cpp')
.json<Data<string>>() .json<Data<string>>()
.then((e) => { .then((e) => {
e.data?.forEach((model) => { e.data?.forEach((model) => {

View File

@ -3633,238 +3633,6 @@
}, },
"tags": ["Files"] "tags": ["Files"]
} }
},
"/configs": {
"get": {
"summary": "Get Configurations",
"description": "Retrieves the current configuration settings of the Cortex server.",
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"allowed_origins": {
"type": "array",
"items": {
"type": "string"
},
"example": ["http://127.0.0.1:39281", "https://cortex.so"]
},
"cors": {
"type": "boolean",
"example": false
},
"proxy_username": {
"type": "string",
"example": "username"
},
"proxy_password": {
"type": "string",
"example": "password"
},
"proxy_url": {
"type": "string",
"example": "http://proxy.example.com:8080"
},
"verify_proxy_ssl": {
"type": "boolean",
"description": "test",
"example": false
},
"verify_proxy_host_ssl": {
"type": "boolean",
"example": false
},
"verify_peer_ssl": {
"type": "boolean",
"example": false
},
"verify_host_ssl": {
"type": "boolean",
"example": false
},
"no_proxy": {
"type": "string",
"example": "localhost"
},
"huggingface_token": {
"type": "string",
"example": "your_token"
}
}
},
"example": {
"allowed_origins": [
"http://127.0.0.1:39281",
"https://cortex.so"
],
"cors": false,
"proxy_username": "username",
"proxy_password": "password",
"proxy_url": "http://proxy.example.com:8080",
"verify_proxy_ssl": false,
"verify_proxy_host_ssl": false,
"verify_peer_ssl": false,
"verify_host_ssl": false,
"no_proxy": "localhost",
"huggingface_token": "your_token"
}
}
}
}
},
"tags": ["Configurations"]
},
"patch": {
"tags": ["Configurations"],
"summary": "Update configuration settings",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"cors": {
"type": "boolean",
"description": "Indicates whether CORS is enabled.",
"example": false
},
"allowed_origins": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of allowed origins.",
"example": ["http://127.0.0.1:39281", "https://cortex.so"]
},
"proxy_username": {
"type": "string",
"description": "Username for the proxy server.",
"example": "username"
},
"proxy_password": {
"type": "string",
"description": "Password for the proxy server.",
"example": "password"
},
"proxy_url": {
"type": "string",
"description": "URL for the proxy server.",
"example": "http://proxy.example.com:8080"
},
"verify_proxy_ssl": {
"type": "boolean",
"description": "Indicates whether to verify the SSL certificate of the proxy server.",
"example": false
},
"verify_proxy_host_ssl": {
"type": "boolean",
"description": "Indicates whether to verify the SSL certificate of the proxy server host.",
"example": false
},
"verify_peer_ssl": {
"type": "boolean",
"description": "Indicates whether to verify the SSL certificate of the peer.",
"example": false
},
"verify_host_ssl": {
"type": "boolean",
"description": "Indicates whether to verify the SSL certificate of the host.",
"example": false
},
"no_proxy": {
"type": "string",
"description": "List of hosts that should not be proxied.",
"example": "localhost"
},
"huggingface_token": {
"type": "string",
"description": "HuggingFace token to pull models.",
"example": "your_token"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Configuration updated successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"config": {
"type": "object",
"properties": {
"allowed_origins": {
"type": "array",
"items": {
"type": "string"
},
"example": [
"http://127.0.0.1:39281",
"https://cortex.so"
]
},
"cors": {
"type": "boolean",
"example": false
},
"proxy_username": {
"type": "string",
"example": "username"
},
"proxy_password": {
"type": "string",
"example": "password"
},
"proxy_url": {
"type": "string",
"example": "http://proxy.example.com:8080"
},
"verify_proxy_ssl": {
"type": "boolean",
"example": false
},
"verify_proxy_host_ssl": {
"type": "boolean",
"example": false
},
"verify_peer_ssl": {
"type": "boolean",
"example": false
},
"verify_host_ssl": {
"type": "boolean",
"example": false
},
"no_proxy": {
"type": "string",
"example": "localhost"
},
"huggingface_token": {
"type": "string",
"example": "your_token"
}
}
},
"message": {
"type": "string",
"example": "Configuration updated successfully"
}
}
}
}
}
}
}
}
} }
}, },
"info": { "info": {

1
server/global.d.ts vendored
View File

@ -1 +1,2 @@
declare const CORTEX_API_URL: string declare const CORTEX_API_URL: string
declare const CORTEX_API_KEY: string

View File

@ -86,6 +86,14 @@ export const startServer = async (configs?: ServerConfig): Promise<boolean> => {
}, },
}) })
const rewriteRequestHeaders = (req: any, headers: any) => {
if (req.url.includes('/configs')) return headers
return {
...headers,
authorization: `Bearer ${CORTEX_API_KEY}`, // Add or modify Authorization header
}
}
// Register Swagger UI // Register Swagger UI
await server.register(require('@fastify/swagger-ui'), { await server.register(require('@fastify/swagger-ui'), {
routePrefix: '/', routePrefix: '/',
@ -102,24 +110,36 @@ export const startServer = async (configs?: ServerConfig): Promise<boolean> => {
upstream: `${CORTEX_API_URL}/v1`, upstream: `${CORTEX_API_URL}/v1`,
prefix: configs?.prefix ?? '/v1', prefix: configs?.prefix ?? '/v1',
http2: false, http2: false,
}) replyOptions: {
rewriteRequestHeaders,
server.register(proxy, { },
upstream: `${CORTEX_API_URL}/system`,
prefix:'/system',
http2: false,
}) })
server.register(proxy, { server.register(proxy, {
upstream: `${CORTEX_API_URL}/processManager`, upstream: `${CORTEX_API_URL}/processManager`,
prefix: '/processManager', prefix: '/processManager',
http2: false, http2: false,
replyOptions: {
rewriteRequestHeaders,
},
})
server.register(proxy, {
upstream: `${CORTEX_API_URL}/system`,
prefix: '/system',
http2: false,
replyOptions: {
rewriteRequestHeaders,
},
}) })
server.register(proxy, { server.register(proxy, {
upstream: `${CORTEX_API_URL}/healthz`, upstream: `${CORTEX_API_URL}/healthz`,
prefix: '/healthz', prefix: '/healthz',
http2: false, http2: false,
replyOptions: {
rewriteRequestHeaders,
},
}) })
// Start listening for requests // Start listening for requests

View File

@ -16,6 +16,7 @@ export default defineConfig([
platform: 'node', platform: 'node',
define: { define: {
CORTEX_API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`), CORTEX_API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
} }
}, },
]) ])