Skip to content

Commit 1d8723b

Browse files
committed
Fix: Fix the support for custom provider
1 parent cc07bf1 commit 1d8723b

22 files changed

+339
-669
lines changed

src/App.tsx

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,9 @@ import { useEffect } from 'react';
22
import { ChatPage } from './pages/ChatPage';
33
import MainLayout from './components/layout/MainLayout';
44
import DatabaseInitializer from './components/core/DatabaseInitializer';
5-
import { AIService } from './services/ai-service';
65

76
function App() {
87

9-
// Initialize AI service and cache models at startup
10-
useEffect(() => {
11-
const aiService = AIService.getInstance();
12-
13-
// Start caching models
14-
const cacheModels = async () => {
15-
try {
16-
await aiService.refreshModels();
17-
} catch (error) {
18-
console.error('Error caching models:', error);
19-
}
20-
};
21-
22-
cacheModels();
23-
24-
return () => {};
25-
}, []);
26-
278
// Handle link clicks
289
useEffect(() => {
2910
const handleLinkClick = (e: MouseEvent) => {

src/components/chat/ChatMessageArea.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ export const ChatMessageArea: React.FC<ChatMessageAreaProps> = ({
5151

5252
if(activeConversation) {
5353
const messagesList = MessageHelper.mapMessagesTreeToList(activeConversation);
54-
console.log(activeConversation);
55-
console.log(messagesList);
5654
setMessagesList(messagesList);
5755
}
5856
}, [activeConversation, activeConversation?.messages]);

src/components/layout/MainLayout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
2828

2929
// Handle selecting a model
3030
const handleSelectModel = (modelId: string, provider: string) => {
31+
console.log('--------------------Selecting Model: ', modelId, ' for Provider: ', provider);
3132
SettingsService.getInstance().setSelectedModel(modelId);
3233
SettingsService.getInstance().setSelectedProvider(provider);
3334
};

src/components/layout/TopBar.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ChevronDown, Cpu, Settings } from 'lucide-react';
22
import React, { useEffect, useState } from 'react';
33
import { SelectModelDialog } from '../models/SelectModelDialog';
4-
import { ModelOption } from '../../services/ai-service';
4+
import { AIService, ModelOption } from '../../services/ai-service';
55
import { SettingsService } from '../../services/settings-service';
66

77
interface TopBarProps {
@@ -12,11 +12,18 @@ interface TopBarProps {
1212
const TopBar: React.FC<TopBarProps> = ({ onSelectModel, onOpenSettingsDialog }) => {
1313
const [isModelDialogOpen, setIsModelDialogOpen] = useState(false);
1414
const [selectedModel, setSelectedModel] = useState('');
15+
const [selectedModelName, setSelectedModelName] = useState('');
1516
const [selectedProvider, setSelectedProvider] = useState('');
1617

1718
useEffect(() => {
1819
const settingsService = SettingsService.getInstance();
1920
setSelectedModel(settingsService.getSelectedModel());
21+
AIService.getInstance().getModelsForProvider(settingsService.getSelectedProvider()).then((models) => {
22+
const model = models.find((model) => model.id === settingsService.getSelectedModel());
23+
if (model) {
24+
setSelectedModelName(model.name);
25+
}
26+
});
2027
setSelectedProvider(settingsService.getSelectedProvider());
2128
}, [SettingsService.getInstance().getSelectedModel(), SettingsService.getInstance().getSelectedProvider()]);
2229

@@ -42,7 +49,7 @@ const TopBar: React.FC<TopBarProps> = ({ onSelectModel, onOpenSettingsDialog })
4249
aria-label="Select AI model"
4350
>
4451
<Cpu className="w-5 h-5 p-0.5 text-gray-600" />
45-
<span className='text-center truncate max-w-[200px]'>{selectedModel}</span>
52+
<span className='text-center truncate max-w-[200px]'>{selectedModelName}</span>
4653
<ChevronDown className="w-5 h-5 text-gray-600" />
4754
</button>
4855
</div>
@@ -56,7 +63,7 @@ const TopBar: React.FC<TopBarProps> = ({ onSelectModel, onOpenSettingsDialog })
5663
onClose={handleCloseModelDialog}
5764
onSelectModel={handleSelectModel}
5865
currentModelId={selectedModel}
59-
currentProviderName={selectedProvider}
66+
currentProviderId={selectedProvider}
6067
/>
6168
</div>
6269
);

src/components/models/SelectModelDialog.tsx

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
import React, { useState, useEffect } from 'react';
22
import { Search, Loader, Check, ChevronDown, ChevronUp } from 'lucide-react';
33
import { AIService, ModelOption } from '../../services/ai-service';
4+
import { SettingsService } from '../../services/settings-service';
45

56
interface SelectModelDialogProps {
67
isOpen: boolean;
78
onClose: () => void;
89
onSelectModel: (model: ModelOption, provider: string) => void;
910
currentModelId?: string;
10-
currentProviderName?: string;
11+
currentProviderId?: string;
1112
}
1213

1314
export const SelectModelDialog: React.FC<SelectModelDialogProps> = ({
1415
isOpen,
1516
onClose,
1617
onSelectModel,
1718
currentModelId,
18-
currentProviderName
19+
currentProviderId
1920
}) => {
2021
const [models, setModels] = useState<ModelOption[]>([]);
2122
const [isLoading, setIsLoading] = useState<boolean>(false);
2223
const [searchQuery, setSearchQuery] = useState<string>('');
2324
const [selectedModelId, setSelectedModelId] = useState<string | undefined>(currentModelId);
24-
const [selectedProviderName, setSelectedProviderName] = useState<string | undefined>(currentProviderName);
25+
const [selectedProviderId, setSelectedProviderId] = useState<string | undefined>(currentProviderId);
2526
const [collapsedList, setCollapsedList] = useState<Map<string, boolean>>(new Map());
2627
const [aiService] = useState(() => AIService.getInstance());
2728

@@ -43,8 +44,8 @@ export const SelectModelDialog: React.FC<SelectModelDialogProps> = ({
4344
}, [currentModelId]);
4445

4546
useEffect(() => {
46-
setSelectedProviderName(currentProviderName);
47-
}, [currentProviderName]);
47+
setSelectedProviderId(currentProviderId);
48+
}, [currentProviderId]);
4849

4950
const loadModels = async () => {
5051
try {
@@ -70,10 +71,10 @@ export const SelectModelDialog: React.FC<SelectModelDialogProps> = ({
7071
setSearchQuery(e.target.value);
7172
};
7273

73-
const handleSelectModel = (model: ModelOption, provider: string) => {
74+
const handleSelectModel = (model: ModelOption, providerId: string) => {
7475
setSelectedModelId(model.id);
75-
setSelectedProviderName(provider);
76-
onSelectModel(model, provider);
76+
setSelectedProviderId(providerId);
77+
onSelectModel(model, providerId);
7778
};
7879

7980
const handleCollapseProvider = (provider: string) => {
@@ -100,6 +101,12 @@ export const SelectModelDialog: React.FC<SelectModelDialogProps> = ({
100101
return groups;
101102
}, {} as Record<string, ModelOption[]>);
102103

104+
const getProviderName = (providerId: string) => {
105+
const settingsService = SettingsService.getInstance();
106+
const providerSettings = settingsService.getProviderSettings(providerId);
107+
return providerSettings.providerName;
108+
}
109+
103110
if (!isOpen) return null;
104111

105112
return (
@@ -155,26 +162,26 @@ export const SelectModelDialog: React.FC<SelectModelDialogProps> = ({
155162
</div>
156163
) : (
157164
<div className="space-y-6">
158-
{Object.entries(groupedModels).map(([provider, providerModels]) => (
159-
<div key={provider} className="mb-6">
165+
{Object.entries(groupedModels).map(([providerId, providerModels]) => (
166+
<div key={providerId} className="mb-6">
160167

161168
<div className="flex items-start justify-start">
162-
<h3 className="mb-2 text-lg font-medium text-white">{provider}</h3>
169+
<h3 className="mb-2 text-lg font-medium text-white">{getProviderName(providerId)}</h3>
163170
<button
164-
onClick={() => handleCollapseProvider(provider)}
171+
onClick={() => handleCollapseProvider(providerId)}
165172
className="p-1 ml-2 text-gray-400 bg-gray-800 rounded-md hover:text-white"
166173
>
167-
{collapsedList.get(provider) ? <ChevronDown size={16} /> : <ChevronUp size={16} />}
174+
{collapsedList.get(providerId) ? <ChevronDown size={16} /> : <ChevronUp size={16} />}
168175
</button>
169176
</div>
170177

171-
<div className="space-y-2" style={{display: (collapsedList.has(provider) && collapsedList.get(provider)) ? 'none' : 'block'}}>
178+
<div className="space-y-2" style={{display: (collapsedList.has(providerId) && collapsedList.get(providerId)) ? 'none' : 'block'}}>
172179
{providerModels.map(model => (
173180
<div
174181
key={model.id}
175-
onClick={() => handleSelectModel(model, provider)}
182+
onClick={() => handleSelectModel(model, providerId)}
176183
className={`flex items-center justify-between p-3 rounded-lg cursor-pointer ${
177-
(selectedModelId === model.id && selectedProviderName === model.provider)
184+
(selectedModelId === model.id && selectedProviderId === providerId)
178185
? 'bg-blue-600 text-white'
179186
: 'bg-gray-800 text-gray-200 hover:bg-gray-700'
180187
}`}
@@ -187,7 +194,7 @@ export const SelectModelDialog: React.FC<SelectModelDialogProps> = ({
187194
</div>
188195
)}
189196
</div>
190-
{(selectedModelId === model.id && selectedProviderName === model.provider) && (
197+
{(selectedModelId === model.id && selectedProviderId === providerId) && (
191198
<div className="ml-4 text-white">
192199
<Check size={16} />
193200
</div>

src/components/settings/ApiManagement.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ export const ApiManagement: React.FC<ApiManagementProps> = ({
6060
setCurrentProviderSettings({ ...currentProviderSettings, baseUrl: value });
6161
}
6262

63-
const handleEndpointChange = (endpoint: string, value: string) => {
64-
setCurrentProviderSettings({ ...currentProviderSettings, [endpoint]: value });
65-
}
63+
// const handleEndpointChange = (endpoint: string, value: string) => {
64+
// setCurrentProviderSettings({ ...currentProviderSettings, [endpoint]: value });
65+
// }
6666

6767
const handleOnEndEditing = () => {
6868
onProviderSettingsChange(currentProviderSettings);
@@ -265,7 +265,7 @@ export const ApiManagement: React.FC<ApiManagementProps> = ({
265265
/>
266266
</div>
267267

268-
<div>
268+
{/* <div>
269269
<label className="block mb-2 text-sm font-medium text-gray-700">
270270
Model Description
271271
</label>
@@ -276,7 +276,7 @@ export const ApiManagement: React.FC<ApiManagementProps> = ({
276276
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
277277
rows={3}
278278
></textarea>
279-
</div>
279+
</div> */}
280280

281281
<div>
282282
<label className="block mb-2 text-sm font-medium text-gray-700">
@@ -462,15 +462,20 @@ export const ApiManagement: React.FC<ApiManagementProps> = ({
462462
value={currentProviderSettings.baseUrl || ''}
463463
onChange={(e) => handleBaseUrlChange(e.target.value)}
464464
onBlur={handleOnEndEditing}
465-
placeholder="https://your-custom-api.com/v1"
465+
placeholder="https://your-custom-api.com/"
466466
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
467467
/>
468-
<p className="mt-2 text-xs text-gray-500">
469-
The base URL for your custom OpenAI-compatible API
470-
</p>
468+
<div className="flex flex-row items-center justify-between">
469+
<p className="mt-2 text-xs text-gray-500">
470+
The base URL for your custom OpenAI-compatible API
471+
</p>
472+
<p className="mt-2 overflow-hidden text-xs text-gray-500">
473+
{currentProviderSettings.baseUrl}/v1/chat/completions
474+
</p>
475+
</div>
471476
</div>
472477

473-
<div className="pt-6 mt-6 border-t border-gray-200">
478+
{/* <div className="pt-6 mt-6 border-t border-gray-200">
474479
<h4 className="mb-4 font-medium text-md">API Endpoints</h4>
475480
476481
<div className="space-y-4">
@@ -502,7 +507,7 @@ export const ApiManagement: React.FC<ApiManagementProps> = ({
502507
/>
503508
</div>
504509
</div>
505-
</div>
510+
</div> */}
506511
</>
507512
)}
508513

src/pages/ChatPage.tsx

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
1-
import React, { useState, useEffect, useCallback, useRef } from 'react';
1+
import { useState, useEffect, useCallback, useRef } from 'react';
22
import ChatHistoryList from '../components/chat/ChatHistoryList';
33
import ChatMessageArea from '../components/chat/ChatMessageArea';
44
import { Conversation, ConversationFolder } from '../types/chat';
55
import { SETTINGS_CHANGE_EVENT, SettingsService } from '../services/settings-service';
66
import { ChatService } from '../services/chat-service';
77
import { AIService } from '../services/ai-service';
8-
interface ChatPageProps {
9-
initialSelectedModel?: string;
10-
}
118

12-
export const ChatPage: React.FC<ChatPageProps> = ({
13-
initialSelectedModel = '',
14-
}) => {
9+
export const ChatPage = () => {
1510
const [conversations, setConversations] = useState<Conversation[]>([]);
1611
const [folders, setFolders] = useState<ConversationFolder[]>([]);
1712
const [activeConversationId, setActiveConversationId] = useState<string | null>(null);
@@ -53,20 +48,8 @@ export const ChatPage: React.FC<ChatPageProps> = ({
5348
setActiveConversationId(activeId);
5449
}
5550

56-
// Get streaming settings
57-
const settingsService = SettingsService.getInstance();
58-
5951
setIsServiceInitialized(true);
6052

61-
// Get saved model from settings if no initial model provided
62-
if (!initialSelectedModel) {
63-
const settings = settingsService.getSettings();
64-
if (settings.selectedModel) {
65-
settingsService.setSelectedModel(settings.selectedModel);
66-
}
67-
aiService.refreshModels();
68-
}
69-
7053
return () => {
7154
unsubscribe(); // Clean up the subscription
7255
};
@@ -79,7 +62,7 @@ export const ChatPage: React.FC<ChatPageProps> = ({
7962
initServices();
8063
}
8164

82-
}, [initialSelectedModel, isServiceInitialized]);
65+
}, [isServiceInitialized]);
8366

8467
// Load active conversation details when selected
8568
useEffect(() => {

src/services/ai-service.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,11 @@ export class AIService {
6363
private addProviders(): void {
6464
const settingsService = SettingsService.getInstance();
6565
const settings = settingsService.getSettings();
66+
6667
for (const provider of Object.keys(settings.providers)) {
6768
const providerSettings = settings.providers[provider];
68-
69-
// console.log('Provider: ', provider, ' Provider settings: ', providerSettings);
70-
7169
if (!this.providers.has(provider) && providerSettings && providerSettings.apiKey && providerSettings.apiKey.length > 0) {
70+
7271
const providerInstance = ProviderFactory.getNewProvider(provider);
7372
if (providerInstance) {
7473
this.providers.set(provider, providerInstance);
@@ -115,16 +114,6 @@ export class AIService {
115114
this.notifyListeners();
116115
}
117116

118-
/**
119-
* Start a new AI request
120-
*/
121-
private startRequest(): void {
122-
this.setState({
123-
status: 'loading',
124-
error: null
125-
});
126-
}
127-
128117
/**
129118
* Handle request success
130119
*/
@@ -333,7 +322,7 @@ export class AIService {
333322
const providerPromises = [];
334323

335324
for (const provider of this.getAllProviders()) {
336-
providerPromises.push(this.getModelsForProvider(provider.name));
325+
providerPromises.push(this.getModelsForProvider(provider.id));
337326
}
338327

339328
const results = await Promise.all(providerPromises);
@@ -378,9 +367,9 @@ export class AIService {
378367

379368
// Convert to ModelOption format
380369
const modelOptions: ModelOption[] = models.map(model => ({
381-
id: model,
382-
name: model,
383-
provider: providerName
370+
id: model.modelId,
371+
name: model.modelName,
372+
provider: providerName,
384373
}));
385374

386375
// Cache results

0 commit comments

Comments
 (0)