How to Add New Features or Models to Lobe Chat: A Complete Developer Guide
Adding new features or models to Lobe Chat involves creating a ModelProviderCard in packages/model-bank/src/modelProviders/ for backend model support, or building React components in src/features/ for UI functionality, then wiring them through the Zustand store and i18n system.
Lobe Chat is a modern AI chat application built by lobehub that separates model provider definitions from UI features through a type-safe TypeScript architecture. Whether you need to integrate a new LLM provider or build a custom interface component, understanding how to add new features or models to Lobe Chat requires navigating its provider card system, state management, and React component structure.
Understanding Lobe Chat's Architecture
Lobe Chat's architecture separates model providers (the back-end definitions of AI models) from UI features (React components that surface functionality). Adding a new model or a new front-end feature follows a predictable, type-safe flow:
- Model side – create a provider card, register it, and expose it through the parsing utilities.
- State side – the provider list is loaded into the Zustand store so the UI can enumerate it.
- UI side – build a feature component, add a route (if needed), and optionally expose i18n strings.
All of these steps are pure TypeScript/React; no runtime code execution is required.
Adding a New Model Provider
Step 1: Define the Provider Card
Each provider is described by a ModelProviderCard (type defined in packages/types/src/llm.ts). It contains the provider id, displayName, description, and an array of chatModels.
// packages/types/src/llm.ts
export interface ModelProviderCard {
id: string;
displayName: string;
description?: string;
chatModels: ChatModelCard[];
// …other optional fields (e.g., `enabled`, `icon`)
}
Create a provider file under packages/model-bank/src/modelProviders/. Use an existing provider as a template (e.g., openai.ts):
// packages/model-bank/src/modelProviders/mynewprovider.ts
import type { ModelProviderCard } from '@/types/llm';
import { createChatModel } from '@/utils/model';
const MyNewProvider: ModelProviderCard = {
id: 'mynewprovider',
displayName: 'My New Provider',
description: 'A custom LLM provider for demo purposes',
chatModels: [
createChatModel({
id: 'my-model-1',
displayName: 'My Model 1',
// token limit, abilities, etc.
contextWindowTokens: 4096,
abilities: {
reasoning: true,
vision: false,
functionCall: true,
files: false,
},
}),
// …add more models if needed
],
};
export default MyNewProvider;
The helper createChatModel lives in packages/model-bank/src/utils/model.ts (you can also construct the object manually).
Step 2: Register the Provider
Export the provider in the central index:
// packages/model-bank/src/modelProviders/index.ts
import MyNewProvider from './mynewprovider';
/* …other imports… */
export const DEFAULT_MODEL_PROVIDER_LIST = [
/* existing providers … */
MyNewProvider, // ← add here
];
Run the type-checking script (bun run type-check) to ensure the new card complies with ModelProviderCard.
Step 3: Verify Model Parsing
When a user enters a model selector string (e.g., gpt-4<8192:vision>), Lobe Chat parses it with parseModels.ts. The new provider's models are automatically recognized as long as they appear in DEFAULT_MODEL_PROVIDER_LIST.
// src/utils/server/parseModels.ts
export const parseModelString = async (
providerId: string,
modelString: string = '',
withDeploymentName = false,
) => {
// …
const modelType = await getModelPropertyWithFallback<AiModelType>(
id,
'type',
providerId,
);
// …
};
No further changes are needed – the parser looks up the model type via @lobechat/model-runtime, which in turn reads the provider card you added.
Step 4: State Integration
The provider list is loaded into the Zustand store (aiProvider slice). Adding a new provider automatically makes it selectable in the UI.
// src/store/aiInfra/slices/aiProvider/action.ts
const [{ LOBE_DEFAULT_MODEL_LIST }, { DEFAULT_MODEL_PROVIDER_LIST }] =
await Promise.all([
import('model-bank'),
import('model-bank/modelProviders'),
]);
// …
const enabledAiProviders: EnabledProvider[] = DEFAULT_MODEL_PROVIDER_LIST.filter(
(p) => p.enabled !== false,
);
Adding a New Front-End Feature
Step 1: Create the Feature Component
All UI pieces live under src/features/. A feature typically consists of a React component (TSX), optional hooks, and i18n entries.
// src/features/MyNewFeature.tsx
import React from 'react';
import { Card, Button } from 'antd';
import { useTranslation } from 'react-i18next';
import { useChatStore } from '@/store/chat';
export const MyNewFeature = () => {
const { t } = useTranslation('default');
const startChat = useChatStore((s) => s.startChat);
return (
<Card title={t('myNewFeature.title')}>
<p>{t('myNewFeature.description')}</p>
<Button type="primary" onClick={() => startChat('mynewprovider', 'my-model-1')}>
{t('myNewFeature.startButton')}
</Button>
</Card>
);
};
Step 2: Add Internationalization
Add i18n strings under src/locales/default/:
// src/locales/default/myNewFeature.ts
export default {
title: 'Demo Feature',
description: 'A quick way to start a chat with My New Model.',
startButton: 'Start Demo Chat',
};
Re-export it in the locale index:
// src/locales/default/index.ts
import myNewFeature from './myNewFeature';
export default {
/* …existing imports… */
myNewFeature,
};
Step 3: Wire Into Routing
If the feature should have its own page, add a folder under src/routes/(main)/:
// src/routes/(main)/my-demo/page.tsx
import { MyNewFeature } from '@/features/MyNewFeature';
export default function Page() {
return <MyNewFeature />;
}
The route automatically picks up the Next.js file-system routing.
Step 4: Testing Your Feature
Lobe Chat uses Vitest for unit tests. Add a test under src/features/__tests__/MyNewFeature.test.tsx:
import { render, screen, fireEvent } from '@testing-library/react';
import { MyNewFeature } from '@/features/MyNewFeature';
import { useChatStore } from '@/store/chat';
vi.mock('@/store/chat', () => ({
useChatStore: vi.fn(() => ({
startChat: vi.fn(),
})),
}));
test('starts chat with the new model', () => {
render(<MyNewFeature />);
fireEvent.click(screen.getByRole('button'));
expect(useChatStore().startChat).toHaveBeenCalledWith('mynewprovider', 'my-model-1');
});
Run the test: bunx vitest src/features/__tests__/MyNewFeature.test.tsx.
Complete Integration Example
Below is a minimal, end-to-end snippet showing the three main files you need to modify/create.
// packages/model-bank/src/modelProviders/mynewprovider.ts
import type { ModelProviderCard } from '@/types/llm';
import { createChatModel } from '@/utils/model';
const MyNewProvider: ModelProviderCard = {
id: 'mynewprovider',
displayName: 'My New Provider',
chatModels: [
createChatModel({
id: 'my-model-1',
displayName: 'My Model 1',
contextWindowTokens: 4096,
abilities: { reasoning: true, functionCall: true },
}),
],
};
export default MyNewProvider;
// packages/model-bank/src/modelProviders/index.ts (excerpt)
import MyNewProvider from './mynewprovider';
/* …existing imports… */
export const DEFAULT_MODEL_PROVIDER_LIST = [
/* …existing providers… */
MyNewProvider,
];
// src/features/MyNewFeature.tsx
import React from 'react';
import { Card, Button } from 'antd';
import { useTranslation } from 'react-i18next';
import { useChatStore } from '@/store/chat';
export const MyNewFeature = () => {
const { t } = useTranslation('default');
const startChat = useChatStore((s) => s.startChat);
return (
<Card title={t('myNewFeature.title')}>
<p>{t('myNewFeature.description')}</p>
<Button type="primary" onClick={() => startChat('mynewprovider', 'my-model-1')}>
{t('myNewFeature.startButton')}
</Button>
</Card>
);
};
After adding these files, run bun run type-check to verify TypeScript compliance, then start the dev server with bun dev to see the new provider and feature in action.
Key Files Reference
| Area | File | Purpose |
|---|---|---|
| Model definition | packages/model-bank/src/modelProviders/<provider>.ts |
Holds the ModelProviderCard for each provider. |
| Provider registry | packages/model-bank/src/modelProviders/index.ts |
DEFAULT_MODEL_PROVIDER_LIST is the source of truth for all providers. |
| Model parsing | src/utils/server/parseModels.ts |
Translates user-entered strings into concrete model objects. |
| State loading | src/store/aiInfra/slices/aiProvider/action.ts |
Pulls the provider list into the Zustand store. |
| Feature component | src/features/<Feature>.tsx |
UI implementation of a new capability. |
| Routing | src/routes/(main)/<slug>/page.tsx |
Optional page wrapper for a feature. |
| i18n | src/locales/default/<feature>.ts |
Adds localized strings for the UI. |
| Tests | src/**/*.test.tsx |
Vitest unit tests for components and logic. |
Summary
- Model providers are defined as
ModelProviderCardobjects inpackages/model-bank/src/modelProviders/and exported viaDEFAULT_MODEL_PROVIDER_LISTin the index file. - Model parsing happens automatically through
src/utils/server/parseModels.tsonce the provider is registered, requiring no manual parser updates. - State management integrates new providers via the Zustand store in
src/store/aiInfra/slices/aiProvider/action.ts, making them immediately selectable in the UI. - Front-end features are React components in
src/features/that consume the store and require i18n entries insrc/locales/default/for localization. - Testing uses Vitest; run
bun run type-checkandbunx vitestto validate changes before deployment.
Frequently Asked Questions
Do I need to modify the database schema to add a new provider?
No, Lobe Chat uses a runtime provider card system where models are defined in TypeScript files under packages/model-bank/src/modelProviders/. The DEFAULT_MODEL_PROVIDER_LIST array is loaded into the Zustand store at runtime, so no database migrations are required to add or update model providers.
How do I test a new model provider without API credentials?
You can verify the provider integration using bun run type-check to ensure TypeScript compliance and run bunx vitest to execute unit tests. The provider will appear in the UI's model selector even without valid API keys, allowing you to test the parsing logic and state integration; only the actual chat completion will fail when the backend attempts to call the provider's API.
Can I add a feature without adding a new provider?
Yes, front-end features in src/features/ are completely independent of the model provider system. You can build React components that utilize existing models through the useChatStore hook or implement entirely new UI capabilities such as dashboards, tools, or widgets without modifying any files in packages/model-bank/.
What testing framework does Lobe Chat use?
Lobe Chat uses Vitest for unit testing. Test files are typically co-located with source files or placed in __tests__ directories throughout the src/ folder. You can run the full test suite with bunx vitest or target specific files to validate that new features and providers integrate correctly with existing state management and UI components.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →