Fix local models (#176)

Fixes #167
This commit is contained in:
Will Chen
2025-05-15 16:46:29 -07:00
committed by GitHub
parent 56900ebe9a
commit 9a288fd82e
2 changed files with 94 additions and 62 deletions

View File

@@ -102,66 +102,68 @@ export function ProviderSettingsGrid() {
<div className="p-6"> <div className="p-6">
<h2 className="text-2xl font-bold mb-6">AI Providers</h2> <h2 className="text-2xl font-bold mb-6">AI Providers</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{providers?.map((provider: LanguageModelProvider) => { {providers
const isCustom = provider.type === "custom"; ?.filter((p) => p.type !== "local")
.map((provider: LanguageModelProvider) => {
const isCustom = provider.type === "custom";
return ( return (
<Card <Card
key={provider.id} key={provider.id}
className="relative transition-all hover:shadow-md border-border" className="relative transition-all hover:shadow-md border-border"
>
<CardHeader
className="p-4 cursor-pointer"
onClick={() => handleProviderClick(provider.id)}
> >
<CardTitle className="text-xl flex items-center justify-between"> <CardHeader
{provider.name} className="p-4 cursor-pointer"
{isProviderSetup(provider.id) ? ( onClick={() => handleProviderClick(provider.id)}
<span className="ml-3 text-sm font-medium text-green-500 bg-green-50 dark:bg-green-900/30 border border-green-500/50 dark:border-green-500/50 px-2 py-1 rounded-full">
Ready
</span>
) : (
<span className="text-sm text-gray-500 bg-gray-50 dark:bg-gray-900 dark:text-gray-300 px-2 py-1 rounded-full">
Needs Setup
</span>
)}
</CardTitle>
<CardDescription>
{provider.hasFreeTier && (
<span className="text-blue-600 mt-2 dark:text-blue-400 text-sm font-medium bg-blue-100 dark:bg-blue-900/30 px-2 py-1 rounded-full inline-flex items-center">
<GiftIcon className="w-4 h-4 mr-1" />
Free tier available
</span>
)}
</CardDescription>
</CardHeader>
{isCustom && (
<div
className="absolute top-2 right-2"
onClick={(e) => e.stopPropagation()}
> >
<DropdownMenu> <CardTitle className="text-xl flex items-center justify-between">
<DropdownMenuTrigger className="focus:outline-none"> {provider.name}
<div className="p-1 hover:bg-muted rounded-full"> {isProviderSetup(provider.id) ? (
<MoreVertical className="h-4 w-4 text-muted-foreground" /> <span className="ml-3 text-sm font-medium text-green-500 bg-green-50 dark:bg-green-900/30 border border-green-500/50 dark:border-green-500/50 px-2 py-1 rounded-full">
</div> Ready
</DropdownMenuTrigger> </span>
<DropdownMenuContent align="end"> ) : (
<DropdownMenuItem <span className="text-sm text-gray-500 bg-gray-50 dark:bg-gray-900 dark:text-gray-300 px-2 py-1 rounded-full">
className="text-destructive focus:text-destructive" Needs Setup
onClick={() => setProviderToDelete(provider.id)} </span>
> )}
<Trash2 className="h-4 w-4 mr-2" /> </CardTitle>
Delete Provider <CardDescription>
</DropdownMenuItem> {provider.hasFreeTier && (
</DropdownMenuContent> <span className="text-blue-600 mt-2 dark:text-blue-400 text-sm font-medium bg-blue-100 dark:bg-blue-900/30 px-2 py-1 rounded-full inline-flex items-center">
</DropdownMenu> <GiftIcon className="w-4 h-4 mr-1" />
</div> Free tier available
)} </span>
</Card> )}
); </CardDescription>
})} </CardHeader>
{isCustom && (
<div
className="absolute top-2 right-2"
onClick={(e) => e.stopPropagation()}
>
<DropdownMenu>
<DropdownMenuTrigger className="focus:outline-none">
<div className="p-1 hover:bg-muted rounded-full">
<MoreVertical className="h-4 w-4 text-muted-foreground" />
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
className="text-destructive focus:text-destructive"
onClick={() => setProviderToDelete(provider.id)}
>
<Trash2 className="h-4 w-4 mr-2" />
Delete Provider
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
)}
</Card>
);
})}
{/* Add custom provider button */} {/* Add custom provider button */}
<Card <Card

View File

@@ -113,7 +113,7 @@ export const PROVIDER_TO_ENV_VAR: Record<string, string> = {
openrouter: "OPENROUTER_API_KEY", openrouter: "OPENROUTER_API_KEY",
}; };
export const PROVIDERS: Record< export const CLOUD_PROVIDERS: Record<
string, string,
{ {
displayName: string; displayName: string;
@@ -153,6 +153,23 @@ export const PROVIDERS: Record<
}, },
}; };
const LOCAL_PROVIDERS: Record<
string,
{
displayName: string;
hasFreeTier: boolean;
}
> = {
ollama: {
displayName: "Ollama",
hasFreeTier: true,
},
lmstudio: {
displayName: "LM Studio",
hasFreeTier: true,
},
};
/** /**
* Fetches language model providers from both the database (custom) and hardcoded constants (cloud), * Fetches language model providers from both the database (custom) and hardcoded constants (cloud),
* merging them with custom providers taking precedence. * merging them with custom providers taking precedence.
@@ -181,11 +198,11 @@ export async function getLanguageModelProviders(): Promise<
// Get hardcoded cloud providers // Get hardcoded cloud providers
const hardcodedProviders: LanguageModelProvider[] = []; const hardcodedProviders: LanguageModelProvider[] = [];
for (const providerKey in PROVIDERS) { for (const providerKey in CLOUD_PROVIDERS) {
if (Object.prototype.hasOwnProperty.call(PROVIDERS, providerKey)) { if (Object.prototype.hasOwnProperty.call(CLOUD_PROVIDERS, providerKey)) {
// Ensure providerKey is a key of PROVIDERS // Ensure providerKey is a key of PROVIDERS
const key = providerKey as keyof typeof PROVIDERS; const key = providerKey as keyof typeof CLOUD_PROVIDERS;
const providerDetails = PROVIDERS[key]; const providerDetails = CLOUD_PROVIDERS[key];
if (providerDetails) { if (providerDetails) {
// Ensure providerDetails is not undefined // Ensure providerDetails is not undefined
hardcodedProviders.push({ hardcodedProviders.push({
@@ -202,6 +219,19 @@ export async function getLanguageModelProviders(): Promise<
} }
} }
for (const providerKey in LOCAL_PROVIDERS) {
if (Object.prototype.hasOwnProperty.call(LOCAL_PROVIDERS, providerKey)) {
const key = providerKey as keyof typeof LOCAL_PROVIDERS;
const providerDetails = LOCAL_PROVIDERS[key];
hardcodedProviders.push({
id: key,
name: providerDetails.displayName,
hasFreeTier: providerDetails.hasFreeTier,
type: "local",
});
}
}
return [...hardcodedProviders, ...customProvidersMap.values()]; return [...hardcodedProviders, ...customProvidersMap.values()];
} }