-
-
Notifications
You must be signed in to change notification settings - Fork 95
feat/optional key support #16
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { useEffect } from 'react'; | ||
| import { useAPIKeyStore } from '@/frontend/stores/APIKeyStore'; | ||
| import { useModelStore } from '@/frontend/stores/ModelStore'; | ||
| import { AI_MODELS, getModelConfig } from '@/lib/models'; | ||
|
|
||
| export const useAutoSelectModel = () => { | ||
| const getKey = useAPIKeyStore((state) => state.getKey); | ||
| const { selectedModel, setModel } = useModelStore(); | ||
|
|
||
| useEffect(() => { | ||
| // Check if the currently selected model has a valid API key | ||
| const currentModelConfig = getModelConfig(selectedModel); | ||
| const currentApiKey = getKey(currentModelConfig.provider); | ||
|
|
||
| if (!currentApiKey) { | ||
| // Find the first model with an available API key | ||
| const firstAvailableModel = AI_MODELS.find((model) => { | ||
| const modelConfig = getModelConfig(model); | ||
| const apiKey = getKey(modelConfig.provider); | ||
| return !!apiKey; | ||
| }); | ||
|
|
||
| if (firstAvailableModel && firstAvailableModel !== selectedModel) { | ||
| setModel(firstAvailableModel); | ||
| } | ||
| } | ||
| }, [getKey, selectedModel, setModel]); | ||
| }; | ||
mihirpenugonda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,9 @@ | ||||||||||||||||||||||||||||||||||||||||||
| import { useCompletion } from '@ai-sdk/react'; | ||||||||||||||||||||||||||||||||||||||||||
| import { useAPIKeyStore } from '@/frontend/stores/APIKeyStore'; | ||||||||||||||||||||||||||||||||||||||||||
| import { toast } from 'sonner'; | ||||||||||||||||||||||||||||||||||||||||||
| import { createMessageSummary, updateThread } from '@/frontend/dexie/queries'; | ||||||||||||||||||||||||||||||||||||||||||
| import { useCompletion } from "@ai-sdk/react"; | ||||||||||||||||||||||||||||||||||||||||||
| import { useAPIKeyStore } from "@/frontend/stores/APIKeyStore"; | ||||||||||||||||||||||||||||||||||||||||||
| import { toast } from "sonner"; | ||||||||||||||||||||||||||||||||||||||||||
| import { createMessageSummary, updateThread } from "@/frontend/dexie/queries"; | ||||||||||||||||||||||||||||||||||||||||||
| import { useTitleLoadingStore } from "@/frontend/stores/TitleLoadingStore"; | ||||||||||||||||||||||||||||||||||||||||||
| import { getProviderHeaderKey } from "@/lib/models"; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| interface MessageSummaryPayload { | ||||||||||||||||||||||||||||||||||||||||||
| title: string; | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -11,13 +13,20 @@ interface MessageSummaryPayload { | |||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| export const useMessageSummary = () => { | ||||||||||||||||||||||||||||||||||||||||||
| const getKey = useAPIKeyStore((state) => state.getKey); | ||||||||||||||||||||||||||||||||||||||||||
| const getFirstAvailableKey = useAPIKeyStore( | ||||||||||||||||||||||||||||||||||||||||||
| (state) => state.getFirstAvailableKey | ||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||
| const setLoading = useTitleLoadingStore((state) => state.setLoading); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const availableKey = getFirstAvailableKey(); | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add validation for available API keys before making requests. The hook retrieves the first available key but doesn't validate that one exists before proceeding with API calls. This could lead to failed requests with empty headers. const availableKey = getFirstAvailableKey();
+
+ if (!availableKey) {
+ toast.error("No API key available. Please configure at least one API key.");
+ return {
+ complete: () => {},
+ isLoading: false,
+ };
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const { complete, isLoading } = useCompletion({ | ||||||||||||||||||||||||||||||||||||||||||
| api: '/api/completion', | ||||||||||||||||||||||||||||||||||||||||||
| ...(getKey('google') && { | ||||||||||||||||||||||||||||||||||||||||||
| headers: { 'X-Google-API-Key': getKey('google')! }, | ||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||
| api: "/api/completion", | ||||||||||||||||||||||||||||||||||||||||||
| headers: availableKey | ||||||||||||||||||||||||||||||||||||||||||
| ? { | ||||||||||||||||||||||||||||||||||||||||||
| [getProviderHeaderKey(availableKey.provider)]: availableKey.key, | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| : {}, | ||||||||||||||||||||||||||||||||||||||||||
| onResponse: async (response) => { | ||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||
| const payload: MessageSummaryPayload = await response.json(); | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -28,20 +37,33 @@ export const useMessageSummary = () => { | |||||||||||||||||||||||||||||||||||||||||
| if (isTitle) { | ||||||||||||||||||||||||||||||||||||||||||
| await updateThread(threadId, title); | ||||||||||||||||||||||||||||||||||||||||||
| await createMessageSummary(threadId, messageId, title); | ||||||||||||||||||||||||||||||||||||||||||
| setLoading(threadId, false); | ||||||||||||||||||||||||||||||||||||||||||
mihirpenugonda marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||
| await createMessageSummary(threadId, messageId, title); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||
| toast.error('Failed to generate a summary for the message'); | ||||||||||||||||||||||||||||||||||||||||||
| toast.error("Failed to generate a summary for the message"); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||
| console.error(error); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const completeWithLoading = ( | ||||||||||||||||||||||||||||||||||||||||||
| prompt: string, | ||||||||||||||||||||||||||||||||||||||||||
| options: { | ||||||||||||||||||||||||||||||||||||||||||
| body: { threadId: string; messageId: string; isTitle?: boolean }; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| ) => { | ||||||||||||||||||||||||||||||||||||||||||
| if (options.body.isTitle) { | ||||||||||||||||||||||||||||||||||||||||||
| setLoading(options.body.threadId, true); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| return complete(prompt, options); | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
mihirpenugonda marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||
| complete, | ||||||||||||||||||||||||||||||||||||||||||
| complete: completeWithLoading, | ||||||||||||||||||||||||||||||||||||||||||
| isLoading, | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import { create } from 'zustand'; | ||
|
|
||
| type TitleLoadingStore = { | ||
| loadingThreads: Set<string>; | ||
| setLoading: (threadId: string, isLoading: boolean) => void; | ||
| isLoading: (threadId: string) => boolean; | ||
| }; | ||
|
|
||
| export const useTitleLoadingStore = create<TitleLoadingStore>((set, get) => ({ | ||
| loadingThreads: new Set(), | ||
|
|
||
| setLoading: (threadId, isLoading) => { | ||
| set((state) => { | ||
| const newSet = new Set(state.loadingThreads); | ||
| if (isLoading) { | ||
| newSet.add(threadId); | ||
| } else { | ||
| newSet.delete(threadId); | ||
| } | ||
| return { loadingThreads: newSet }; | ||
| }); | ||
| }, | ||
|
|
||
| isLoading: (threadId) => { | ||
| return get().loadingThreads.has(threadId); | ||
| }, | ||
| })); |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve consistency and model selection logic.
The provider selection logic works but has some inconsistencies:
Hardcoded model IDs: Each provider uses a hardcoded model ID instead of leveraging the model configuration system used elsewhere in the codebase.
OpenRouter setup: Uses
createOpenAIwith a custom base URL, which is correct but could be more explicit.Consider using the existing model configuration system for consistency:
This approach would be more maintainable and consistent with the model configuration pattern used in
app/api/chat/route.ts.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this cleanup should happen in a separate pr where we improve model registration. currently following whats already being done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mihirpenugonda That makes perfect sense! Keeping this PR focused on the optional key support and handling the model registration improvements separately is a good approach for maintaining clean, focused changes.
Would you like me to create a GitHub issue to track the model registration cleanup for the future PR? This could help ensure the refactoring doesn't get forgotten and provide a place to document the specific improvements needed.
🐰
┌─────────────────┐
│ ◕ ◕ │
│ ω │
│ Noted! 📝 │
└─────────────────┘