@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
196 lines (165 loc) • 5.73 kB
text/typescript
// @vitest-environment node
import { ModelProvider } from 'model-bank';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { testProvider } from '../../providerTestUtils';
import { LobeLMStudioAI, params } from './index';
const provider = ModelProvider.LMStudio;
const defaultBaseURL = 'http://127.0.0.1:1234/v1';
testProvider({
Runtime: LobeLMStudioAI,
chatDebugEnv: 'DEBUG_LMSTUDIO_CHAT_COMPLETION',
chatModel: 'deepseek-r1',
defaultBaseURL,
provider,
test: {
skipAPICall: true,
},
});
describe('LobeLMStudioAI - custom features', () => {
let instance: InstanceType<typeof LobeLMStudioAI>;
beforeEach(() => {
instance = new LobeLMStudioAI({ apiKey: 'placeholder-to-avoid-error' });
vi.spyOn(instance['client'].chat.completions, 'create').mockResolvedValue(
new ReadableStream() as any,
);
});
describe('params export', () => {
it('should export params with correct structure', () => {
expect(params).toBeDefined();
expect(params.provider).toBe(ModelProvider.LMStudio);
expect(params.baseURL).toBe('http://127.0.0.1:1234/v1');
expect(params.apiKey).toBe('placeholder-to-avoid-error');
expect(params.debug).toBeDefined();
expect(params.models).toBeDefined();
});
it('should have debug.chatCompletion function', () => {
expect(typeof params.debug?.chatCompletion).toBe('function');
});
it('should return false when DEBUG_LMSTUDIO_CHAT_COMPLETION is not set', () => {
delete process.env.DEBUG_LMSTUDIO_CHAT_COMPLETION;
expect(params.debug?.chatCompletion()).toBe(false);
});
it('should return true when DEBUG_LMSTUDIO_CHAT_COMPLETION is set to 1', () => {
process.env.DEBUG_LMSTUDIO_CHAT_COMPLETION = '1';
expect(params.debug?.chatCompletion()).toBe(true);
delete process.env.DEBUG_LMSTUDIO_CHAT_COMPLETION;
});
});
describe('models function', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should fetch and process models successfully', async () => {
const mockClient = {
models: {
list: vi.fn().mockResolvedValue({
data: [{ id: 'deepseek-r1' }, { id: 'llama-3' }],
}),
},
};
const models = await params.models!({ client: mockClient as any });
expect(models).toBeDefined();
expect(mockClient.models.list).toHaveBeenCalled();
});
it('should handle known models from LOBE_DEFAULT_MODEL_LIST', async () => {
const mockClient = {
models: {
list: vi.fn().mockResolvedValue({
data: [{ id: 'gpt-4' }],
}),
},
};
const models = await params.models!({ client: mockClient as any });
expect(models).toBeDefined();
});
it('should handle case-insensitive model matching', async () => {
const mockClient = {
models: {
list: vi.fn().mockResolvedValue({
data: [{ id: 'GPT-4' }, { id: 'Claude-3-Sonnet' }],
}),
},
};
const models = await params.models!({ client: mockClient as any });
expect(models).toBeDefined();
});
it('should handle unknown models', async () => {
const mockClient = {
models: {
list: vi.fn().mockResolvedValue({
data: [{ id: 'unknown-model-123' }],
}),
},
};
const models = await params.models!({ client: mockClient as any });
expect(models).toBeDefined();
});
it('should merge abilities from known models', async () => {
const mockClient = {
models: {
list: vi.fn().mockResolvedValue({
data: [{ id: 'gpt-4' }],
}),
},
};
const models = await params.models!({ client: mockClient as any });
expect(models).toBeDefined();
});
it('should set enabled to false when not in known models', async () => {
const mockClient = {
models: {
list: vi.fn().mockResolvedValue({
data: [{ id: 'custom-local-model' }],
}),
},
};
const models = await params.models!({ client: mockClient as any });
expect(models).toBeDefined();
});
it('should handle empty model list', async () => {
const mockClient = {
models: {
list: vi.fn().mockResolvedValue({
data: [],
}),
},
};
const models = await params.models!({ client: mockClient as any });
expect(models).toEqual([]);
});
it('should handle models with abilities', async () => {
const mockClient = {
models: {
list: vi.fn().mockResolvedValue({
data: [{ id: 'deepseek-r1' }, { id: 'gpt-4-vision-preview' }, { id: 'claude-3-opus' }],
}),
},
};
const models = await params.models!({ client: mockClient as any });
expect(models).toBeDefined();
});
it('should handle models without contextWindowTokens', async () => {
const mockClient = {
models: {
list: vi.fn().mockResolvedValue({
data: [{ id: 'new-model-without-metadata' }],
}),
},
};
const models = await params.models!({ client: mockClient as any });
expect(models).toBeDefined();
});
it('should filter Boolean values correctly', async () => {
const mockClient = {
models: {
list: vi.fn().mockResolvedValue({
data: [{ id: 'model-1' }, { id: 'model-2' }],
}),
},
};
const models = await params.models!({ client: mockClient as any });
expect(models).toBeDefined();
expect(Array.isArray(models)).toBe(true);
});
});
});