Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [alpha] Providers grid refactor #1345

Merged
merged 16 commits into from
Feb 24, 2025
6 changes: 6 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ run-ui:
@echo "Running UI..."
cd ui/desktop && npm install && npm run start-gui

# Run UI with alpha changes
run-ui-alpha:
@just release-binary
@echo "Running UI..."
cd ui/desktop && npm install && ALPHA=true npm run start-alpha-gui

# Run UI with latest (Windows version)
run-ui-windows:
@just release-windows
Expand Down
40 changes: 40 additions & 0 deletions ui/desktop/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion ui/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"lint:check": "eslint \"src/**/*.{ts,tsx}\"",
"format": "prettier --write \"src/**/*.{ts,tsx,css,json}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx,css,json}\"",
"prepare": "cd ../.. && husky install"
"prepare": "cd ../.. && husky install",
"start-alpha-gui": "ALPHA=true npm start-gui"
},
"devDependencies": {
"@electron-forge/cli": "^7.5.0",
Expand Down
7 changes: 6 additions & 1 deletion ui/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import SettingsView, { type SettingsViewOptions } from './components/settings/SettingsView';
import MoreModelsView from './components/settings/models/MoreModelsView';
import ConfigureProvidersView from './components/settings/providers/ConfigureProvidersView';
import ProviderSettings from './components/settings/providers/providers/NewProviderSettingsPage';

import 'react-toastify/dist/ReactToastify.css';

Expand All @@ -26,11 +27,12 @@
| 'settings'
| 'moreModels'
| 'configureProviders'
| 'configPage';
| 'configPage'
| 'alphaConfigureProviders';

export type ViewConfig = {
view: View;
viewOptions?: SettingsViewOptions | Record<any, any>;

Check warning on line 35 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type

Check warning on line 35 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
};

export default function App() {
Expand All @@ -46,7 +48,7 @@

const { switchModel } = useModel();
const { addRecentModel } = useRecentModels();
const setView = (view: View, viewOptions: Record<any, any> = {}) => {

Check warning on line 51 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type

Check warning on line 51 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
setInternalView({ view, viewOptions });
};

Expand All @@ -59,7 +61,7 @@
}

useEffect(() => {
const handleAddExtension = (_: any, link: string) => {

Check warning on line 64 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
const command = extractCommand(link);
const extName = extractExtensionName(link);
window.electron.logInfo(`Adding extension from deep link ${link}`);
Expand Down Expand Up @@ -133,10 +135,10 @@
};

setupStoredProvider();
}, []);

Check warning on line 138 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

React Hook useEffect has missing dependencies: 'addRecentModel' and 'switchModel'. Either include them or remove the dependency array

useEffect(() => {
const handleFatalError = (_: any, errorMessage: string) => {

Check warning on line 141 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
setFatalError(errorMessage);
};

Expand Down Expand Up @@ -224,6 +226,9 @@
}}
/>
)}
{view === 'alphaConfigureProviders' && (
<ProviderSettings onClose={() => setView('chat')} />
)}
{view === 'chat' && <ChatView setView={setView} />}
</div>
</div>
Expand Down
14 changes: 12 additions & 2 deletions ui/desktop/src/components/MoreMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Popover, PopoverContent, PopoverTrigger, PopoverPortal } from '@radix-ui/react-popover';
import React, { useEffect, useState } from 'react';
import { More } from './icons';
import type { View } from '../ChatWindow';

import { View } from '../App';
interface VersionInfo {
current_version: string;
available_versions: string[];
Expand Down Expand Up @@ -260,6 +259,17 @@ export default function MoreMenu({ setView }: { setView: (view: View) => void })
>
Reset Provider
</button>
{process.env.ALPHA && (
<button
onClick={() => {
setOpen(false);
setView('alphaConfigureProviders');
}}
className="w-full text-left p-2 text-sm hover:bg-bgSubtle transition-colors text-indigo-800"
>
See new providers grid
</button>
)}
</div>
</PopoverContent>
</PopoverPortal>
Expand Down
28 changes: 14 additions & 14 deletions ui/desktop/src/components/settings/api_keys/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,22 @@ export async function getActiveProviders(): Promise<string[]> {
const configSettings = await getConfigSettings();

const activeProviders = Object.values(configSettings)
.filter((provider) => {
// 1. Get provider's config_status
const configStatus = provider.config_status ?? {};
.filter((provider) => {
// 1. Get provider's config_status
const configStatus = provider.config_status ?? {};

// 2. Collect only the keys *not* in default_key_value
const requiredKeyEntries = Object.entries(configStatus).filter(([k]) => isRequiredKey(k));
// 2. Collect only the keys *not* in default_key_value
const requiredKeyEntries = Object.entries(configStatus).filter(([k]) => isRequiredKey(k));

// 3. If there are *no* non-default keys, it is NOT active
if (requiredKeyEntries.length === 0) {
return false;
}
// 3. If there are *no* non-default keys, it is NOT active
if (requiredKeyEntries.length === 0) {
return false;
}

// 4. Otherwise, all non-default keys must be `is_set`
return requiredKeyEntries.every(([_, value]) => value?.is_set);
})
.map((provider) => provider.name || 'Unknown Provider');
// 4. Otherwise, all non-default keys must be `is_set`
return requiredKeyEntries.every(([_, value]) => value?.is_set);
})
.map((provider) => provider.name || 'Unknown Provider');

console.log('[GET ACTIVE PROVIDERS]:', activeProviders);
return activeProviders;
Expand Down Expand Up @@ -93,4 +93,4 @@ export async function getProvidersList(): Promise<Provider[]> {
models: item.details?.models || [], // Nested models array
requiredKeys: item.details?.required_keys || [], // Nested required keys array
}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ export function ConfigureProvidersGrid() {
<div className="relative z-[9999]">
<ProviderSetupModal
provider={providers.find((p) => p.id === selectedForSetup)?.name || ''}
model="Example Model"
endpoint="Example Endpoint"
_model="Example Model"
_endpoint="Example Endpoint"
title={
modalMode === 'edit'
? `Edit ${providers.find((p) => p.id === selectedForSetup)?.name} Configuration`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';
import { ScrollArea } from '../../../ui/scroll-area';
import BackButton from '../../../ui/BackButton';
import ProviderGrid from './ProviderGrid';
import ProviderState from './interfaces/ProviderState';

const fakeProviderState: ProviderState[] = [
{
id: 'openai',
name: 'OpenAI',
isConfigured: true,
metadata: null,
},
{
id: 'anthropic',
name: 'Anthropic',
isConfigured: false,
metadata: null,
},
{
id: 'groq',
name: 'Groq',
isConfigured: false,
metadata: null,
},
{
id: 'google',
name: 'Google',
isConfigured: false,
metadata: null,
},
{
id: 'openrouter',
name: 'OpenRouter',
isConfigured: false,
metadata: null,
},
{
id: 'databricks',
name: 'Databricks',
isConfigured: false,
metadata: null,
},
{
id: 'ollama',
name: 'Ollama',
isConfigured: false,
metadata: { location: null },
},
];

export default function ProviderSettings({ onClose }: { onClose: () => void }) {
return (
<div className="h-screen w-full">
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle"></div>

<ScrollArea className="h-full w-full">
<div className="px-8 pt-6 pb-4">
<BackButton onClick={onClose} />
<h1 className="text-3xl font-medium text-textStandard mt-1">Configure</h1>
</div>

<div className=" py-8 pt-[20px]">
<div className="flex justify-between items-center mb-6 border-b border-borderSubtle px-8">
<h2 className="text-xl font-medium text-textStandard">Providers</h2>
</div>

{/* Content Area */}
<div className="max-w-5xl pt-4 px-8">
<div className="relative z-10">
<ProviderGrid providers={fakeProviderState} />
</div>
</div>
</div>
</ScrollArea>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import { ProviderCard } from './subcomponents/ProviderCard';
import ProviderState from './interfaces/ProviderState';
import OnShowModal from './callbacks/ShowModal';
import OnAdd from './callbacks/AddProviderParameters';
import OnDelete from './callbacks/DeleteProviderParameters';
import OnShowSettings from './callbacks/UpdateProviderParameters';
import OnRefresh from './callbacks/RefreshActiveProviders';
import DefaultProviderActions from './subcomponents/actions/DefaultProviderActions';

function GridLayout({ children }: { children: React.ReactNode }) {
return (
<div className="grid grid-cols-3 sm:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-7 gap-3 auto-rows-fr max-w-full [&_*]:z-20">
{children}
</div>
);
}

function ProviderCards({ providers }: { providers: ProviderState[] }) {
const providerCallbacks = {
onShowModal: OnShowModal,
onAdd: OnAdd,
onDelete: OnDelete,
onShowSettings: OnShowSettings,
onRefresh: OnRefresh,
};
return (
<>
{providers.map((provider) => (
<ProviderCard
key={provider.name} // helps React efficiently update and track components when rendering lists
provider={provider}
providerCallbacks={providerCallbacks}
/>
))}
</>
);
}

export default function ProviderGrid({ providers }: { providers: ProviderState[] }) {
console.log('got these providers', providers);
return (
<GridLayout>
<ProviderCards providers={providers} />
</GridLayout>
);
}
Loading
Loading