+ );
+};
\ No newline at end of file
diff --git a/app/components/chat/ProviderSelector.tsx b/app/components/chat/ProviderSelector.tsx
new file mode 100644
index 000000000..a59a0830d
--- /dev/null
+++ b/app/components/chat/ProviderSelector.tsx
@@ -0,0 +1,33 @@
+// This is a new component to handle provider selection
+// Based on existing patterns in the codebase
+
+import { Button } from "@convex-dev/design-system";
+import { useStore } from "@nanostores/react";
+import { $selectedProvider, setProvider } from "../stores/providerStore";
+
+export const ProviderSelector = () => {
+ const selectedProvider = useStore($selectedProvider);
+
+ return (
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/app/config/providers.ts b/app/config/providers.ts
new file mode 100644
index 000000000..805223f13
--- /dev/null
+++ b/app/config/providers.ts
@@ -0,0 +1,18 @@
+export const SUPPORTED_PROVIDERS = [
+ {
+ id: 'openai',
+ name: 'OpenAI',
+ apiKeyEnvVar: 'OPENAI_API_KEY',
+ },
+ {
+ id: 'anthropic',
+ name: 'Anthropic',
+ apiKeyEnvVar: 'ANTHROPIC_API_KEY',
+ },
+ {
+ id: 'openrouter',
+ name: 'OpenRouter',
+ apiKeyEnvVar: 'OPENROUTER_API_KEY',
+ isAggregator: true,
+ },
+] as const;
\ No newline at end of file
diff --git a/app/routes/settings.tsx b/app/routes/settings.tsx
index a0625e1cc..f8bf05c72 100644
--- a/app/routes/settings.tsx
+++ b/app/routes/settings.tsx
@@ -1,32 +1,23 @@
-import { useTeamsInitializer } from '~/lib/stores/startup/useTeamsInitializer';
-import { ChefAuthProvider } from '~/components/chat/ChefAuthWrapper';
-import { json } from '@vercel/remix';
-import type { LoaderFunctionArgs, MetaFunction } from '@vercel/remix';
-import { SettingsContent } from '~/components/SettingsContent.client';
-import { ClientOnly } from 'remix-utils/client-only';
-
-export const meta: MetaFunction = () => {
- return [{ title: 'Settings | Chef' }];
-};
-
-export const loader = async (args: LoaderFunctionArgs) => {
- const url = new URL(args.request.url);
- let code: string | null = url.searchParams.get('code');
- const state = url.searchParams.get('state');
- // If state is also set, this is probably the GitHub OAuth login flow finishing.
- // The code is probably not for us.
- if (state) {
- code = null;
- }
- return json({ code });
-};
-
-export default function Settings() {
- useTeamsInitializer();
+import { ApiKeyForm } from "../components/ApiKeyForm";
+import { $apiKeys } from "../stores/apiKeyStore";
+export const SettingsRoute = () => {
return (
-
- {() => }
-
+
+
API Keys
+
+
+
+
);
-}
+};
\ No newline at end of file
diff --git a/app/services/openrouterService.ts b/app/services/openrouterService.ts
new file mode 100644
index 000000000..f4a3f3e52
--- /dev/null
+++ b/app/services/openrouterService.ts
@@ -0,0 +1,31 @@
+import { createOpenRouter } from '@ai-sdk/openrouter';
+import { ApiKeyManager } from '../utils/apiKeyManager';
+
+export class OpenRouterService {
+ private apiKey: string;
+
+ constructor(apiKey: string) {
+ this.apiKey = apiKey;
+ }
+
+ async createCompletion(messages: any[]) {
+ const response = await fetch('https://openrouter.com/api/v1/chat/completions', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${this.apiKey}`,
+ },
+ body: JSON.stringify({
+ model: 'openrouter',
+ messages,
+ temperature: 0.7,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error('OpenRouter API request failed');
+ }
+
+ return response.json();
+ }
+}
\ No newline at end of file
diff --git a/app/stores/providerStore.ts b/app/stores/providerStore.ts
new file mode 100644
index 000000000..06b976499
--- /dev/null
+++ b/app/stores/providerStore.ts
@@ -0,0 +1,7 @@
+import { atom } from 'nanostores';
+
+export const $selectedProvider = atom<'openai' | 'anthropic' | 'openrouter'>('openai');
+
+export const setProvider = (provider: 'openai' | 'anthropic' | 'openrouter') => {
+ $selectedProvider.set(provider);
+};
\ No newline at end of file