Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion core/src/browser/extensions/engines/AIEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ export abstract class AIEngine extends BaseExtension {
/**
* Loads a model into memory
*/
abstract load(modelId: string): Promise<SessionInfo>
abstract load(modelId: string, settings?: any): Promise<SessionInfo>

/**
* Unloads a model from memory
Expand Down
10 changes: 7 additions & 3 deletions web-app/src/containers/ModelSetting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ export function ModelSetting({
...(params as unknown as object),
})

// Call debounced stopModel after updating the model
debouncedStopModel(model.id)
// Call debounced stopModel only when updating ctx_len or ngl
if (key === 'ctx_len' || key === 'ngl') {
debouncedStopModel(model.id)
}
}
}

Expand All @@ -106,7 +108,9 @@ export function ModelSetting({
</SheetTrigger>
<SheetContent className="h-[calc(100%-8px)] top-1 right-1 rounded-e-md overflow-y-auto">
<SheetHeader>
<SheetTitle>{t('common:modelSettings.title', { modelId: model.id })}</SheetTitle>
<SheetTitle>
{t('common:modelSettings.title', { modelId: model.id })}
</SheetTitle>
<SheetDescription>
{t('common:modelSettings.description')}
</SheetDescription>
Expand Down
24 changes: 23 additions & 1 deletion web-app/src/hooks/useChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,14 +261,36 @@ export const useChat = () => {
!abortController.signal.aborted &&
activeProvider
) {
const modelConfig = activeProvider.models.find(
(m) => m.id === selectedModel?.id
)

const modelSettings = modelConfig?.settings
? Object.fromEntries(
Object.entries(modelConfig.settings)
.filter(
([key, value]) =>
key !== 'ctx_len' &&
key !== 'ngl' &&
value.controller_props?.value !== undefined &&
value.controller_props?.value !== null &&
value.controller_props?.value !== ''
)
.map(([key, value]) => [key, value.controller_props?.value])
)
: undefined

const completion = await sendCompletion(
activeThread,
activeProvider,
builder.getMessages(),
abortController,
availableTools,
currentAssistant.parameters?.stream === false ? false : true,
currentAssistant.parameters as unknown as Record<string, object>
{
...modelSettings,
...currentAssistant.parameters,
} as unknown as Record<string, object>
)

if (!completion) throw new Error('No completion received')
Expand Down
12 changes: 6 additions & 6 deletions web-app/src/services/__tests__/models.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'

import {
fetchModels,
fetchModelCatalog,
Expand All @@ -10,9 +11,8 @@
stopModel,
stopAllModels,
startModel,
configurePullOptions,
} from '../models'
import { EngineManager } from '@janhq/core'
import { EngineManager, Model } from '@janhq/core'

// Mock EngineManager
vi.mock('@janhq/core', () => ({
Expand Down Expand Up @@ -118,7 +118,7 @@
settings: [{ key: 'temperature', value: 0.7 }],
}

await updateModel(model)
await updateModel(model as any)

expect(mockEngine.updateSettings).toHaveBeenCalledWith(model.settings)
})
Expand Down Expand Up @@ -209,7 +209,7 @@

describe('startModel', () => {
it('should start model successfully', async () => {
const provider = { provider: 'openai', models: [] } as ProviderObject
const provider = { provider: 'openai', models: [] } as any
const model = 'model1'
const mockSession = { id: 'session1' }

Expand All @@ -221,11 +221,11 @@
const result = await startModel(provider, model)

expect(result).toEqual(mockSession)
expect(mockEngine.load).toHaveBeenCalledWith(model)

Check failure on line 224 in web-app/src/services/__tests__/models.test.ts

View workflow job for this annotation

GitHub Actions / test-on-windows-pr

src/services/__tests__/models.test.ts > models service > startModel > should start model successfully

AssertionError: expected "spy" to be called with arguments: [ 'model1' ] Received: 1st spy call: [ "model1", + undefined, ] Number of calls: 1 ❯ src/services/__tests__/models.test.ts:224:31

Check failure on line 224 in web-app/src/services/__tests__/models.test.ts

View workflow job for this annotation

GitHub Actions / test-on-macos

src/services/__tests__/models.test.ts > models service > startModel > should start model successfully

AssertionError: expected "spy" to be called with arguments: [ 'model1' ] Received: 1st spy call: [ "model1", + undefined, ] Number of calls: 1 ❯ src/services/__tests__/models.test.ts:224:31

Check failure on line 224 in web-app/src/services/__tests__/models.test.ts

View workflow job for this annotation

GitHub Actions / test-on-ubuntu

src/services/__tests__/models.test.ts > models service > startModel > should start model successfully

AssertionError: expected "spy" to be called with arguments: [ 'model1' ] Received: 1st spy call: [ "model1", + undefined, ] Number of calls: 1 ❯ src/services/__tests__/models.test.ts:224:31

Check failure on line 224 in web-app/src/services/__tests__/models.test.ts

View workflow job for this annotation

GitHub Actions / coverage-check

src/services/__tests__/models.test.ts > models service > startModel > should start model successfully

AssertionError: expected "spy" to be called with arguments: [ 'model1' ] Received: 1st spy call: [ "model1", + undefined, ] Number of calls: 1 ❯ src/services/__tests__/models.test.ts:224:31
})

it('should handle start model error', async () => {
const provider = { provider: 'openai', models: [] } as ProviderObject
const provider = { provider: 'openai', models: [] } as any
const model = 'model1'
const error = new Error('Failed to start model')

Expand All @@ -237,7 +237,7 @@
await expect(startModel(provider, model)).rejects.toThrow(error)
})
it('should not load model again', async () => {
const provider = { provider: 'openai', models: [] } as ProviderObject
const provider = { provider: 'openai', models: [] } as any
const model = 'model1'

mockEngine.getLoadedModels.mockResolvedValue({
Expand Down
24 changes: 23 additions & 1 deletion web-app/src/services/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,29 @@ export const startModel = async (
if (!engine) return undefined

if ((await engine.getLoadedModels()).includes(model)) return undefined
return engine.load(model).catch((error) => {

// Find the model configuration to get settings
const modelConfig = provider.models.find((m) => m.id === model)

// Key mapping function to transform setting keys
const mapSettingKey = (key: string): string => {
const keyMappings: Record<string, string> = {
ctx_len: 'ctx_size',
ngl: 'n_gpu_layers',
}
return keyMappings[key] || key
}

const settings = modelConfig?.settings
? Object.fromEntries(
Object.entries(modelConfig.settings).map(([key, value]) => [
mapSettingKey(key),
value.controller_props?.value,
])
)
: undefined

return engine.load(model, settings).catch((error) => {
console.error(
`Failed to start model ${model} for provider ${provider.provider}:`,
error
Expand Down
Loading