Skip to content

Commit 23d3c86

Browse files
committed
refactor: streamline chat API imports and enhance DocsAssistant with settings provider
1 parent 77d5085 commit 23d3c86

4 files changed

Lines changed: 152 additions & 73 deletions

File tree

app/api/chat/route.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { openai, createOpenAI } from "@ai-sdk/openai";
2-
import { google, createGoogleGenerativeAI } from "@ai-sdk/google";
1+
import { createOpenAI } from "@ai-sdk/openai";
2+
import { createGoogleGenerativeAI } from "@ai-sdk/google";
33
import { streamText, UIMessage, convertToModelMessages } from "ai";
44

55
// Allow streaming responses up to 30 seconds
@@ -15,7 +15,7 @@ export async function POST(req: Request) {
1515
}: {
1616
messages: UIMessage[];
1717
system?: string; // System message forwarded from AssistantChatTransport
18-
tools?: any; // Frontend tools forwarded from AssistantChatTransport
18+
tools?: unknown; // Frontend tools forwarded from AssistantChatTransport
1919
pageContext?: {
2020
title?: string;
2121
description?: string;
@@ -67,13 +67,13 @@ export async function POST(req: Request) {
6767
const customGoogle = createGoogleGenerativeAI({
6868
apiKey: apiKey,
6969
});
70-
model = customGoogle("models/gemini-1.5-pro-latest");
70+
model = customGoogle("models/gemini-2.0-flash");
7171
} else {
7272
// Default to OpenAI
7373
const customOpenAI = createOpenAI({
7474
apiKey: apiKey,
7575
});
76-
model = customOpenAI("gpt-4o-mini");
76+
model = customOpenAI("gpt-4.1-nano");
7777
}
7878

7979
const result = streamText({

app/components/DocsAssistant.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import { useAISDKRuntime } from "@assistant-ui/react-ai-sdk";
55
import { useChat } from "@ai-sdk/react";
66
import { DefaultChatTransport } from "ai";
77
import { AssistantModal } from "@/app/components/assistant-ui/assistant-modal";
8-
import { useAssistantSettings } from "@/app/hooks/useAssistantSettings";
8+
import {
9+
AssistantSettingsProvider,
10+
useAssistantSettings,
11+
} from "@/app/hooks/useAssistantSettings";
912

1013
interface PageContext {
1114
title?: string;
@@ -19,13 +22,19 @@ interface DocsAssistantProps {
1922
}
2023

2124
export function DocsAssistant({ pageContext }: DocsAssistantProps) {
25+
return (
26+
<AssistantSettingsProvider>
27+
<DocsAssistantInner pageContext={pageContext} />
28+
</AssistantSettingsProvider>
29+
);
30+
}
31+
32+
function DocsAssistantInner({ pageContext }: DocsAssistantProps) {
2233
const { provider, openaiApiKey, geminiApiKey } = useAssistantSettings();
2334

24-
// Use DefaultChatTransport with request-level body configuration
2535
const chat = useChat({
2636
transport: new DefaultChatTransport({
2737
api: "/api/chat",
28-
// Use function to ensure dynamic values are captured at request time
2938
body: () => {
3039
const apiKey = provider === "openai" ? openaiApiKey : geminiApiKey;
3140
return {

app/hooks/useAssistantSettings.ts

Lines changed: 0 additions & 65 deletions
This file was deleted.

app/hooks/useAssistantSettings.tsx

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"use client";
2+
3+
import { createContext, useContext, useEffect, useMemo, useState } from "react";
4+
import type { ReactNode } from "react";
5+
6+
type Provider = "openai" | "gemini";
7+
8+
interface AssistantSettingsState {
9+
provider: Provider;
10+
openaiApiKey: string;
11+
geminiApiKey: string;
12+
}
13+
14+
interface AssistantSettingsContextValue extends AssistantSettingsState {
15+
setProvider: (provider: Provider) => void;
16+
setOpenaiApiKey: (key: string) => void;
17+
setGeminiApiKey: (key: string) => void;
18+
}
19+
20+
const SETTINGS_KEY = "assistant-settings-storage";
21+
22+
const defaultSettings: AssistantSettingsState = {
23+
provider: "openai",
24+
openaiApiKey: "",
25+
geminiApiKey: "",
26+
};
27+
28+
const AssistantSettingsContext = createContext<
29+
AssistantSettingsContextValue | undefined
30+
>(undefined);
31+
32+
const parseStoredSettings = (raw: string | null): AssistantSettingsState => {
33+
if (!raw) {
34+
return { ...defaultSettings };
35+
}
36+
37+
try {
38+
const parsed = JSON.parse(raw) as Partial<AssistantSettingsState>;
39+
return {
40+
provider: parsed.provider === "gemini" ? "gemini" : "openai",
41+
openaiApiKey:
42+
typeof parsed.openaiApiKey === "string" ? parsed.openaiApiKey : "",
43+
geminiApiKey:
44+
typeof parsed.geminiApiKey === "string" ? parsed.geminiApiKey : "",
45+
};
46+
} catch (error) {
47+
console.error(
48+
"Failed to parse assistant settings from localStorage",
49+
error,
50+
);
51+
return { ...defaultSettings };
52+
}
53+
};
54+
55+
const readStoredSettings = (): AssistantSettingsState => {
56+
if (typeof window === "undefined") {
57+
return { ...defaultSettings };
58+
}
59+
60+
const raw = window.localStorage.getItem(SETTINGS_KEY);
61+
return parseStoredSettings(raw);
62+
};
63+
64+
export const AssistantSettingsProvider = ({
65+
children,
66+
}: {
67+
children: ReactNode;
68+
}) => {
69+
const [settings, setSettings] = useState<AssistantSettingsState>(() =>
70+
readStoredSettings(),
71+
);
72+
73+
useEffect(() => {
74+
if (typeof window === "undefined") {
75+
return;
76+
}
77+
78+
try {
79+
window.localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
80+
} catch (error) {
81+
console.error("Failed to save assistant settings to localStorage", error);
82+
}
83+
}, [settings]);
84+
85+
useEffect(() => {
86+
if (typeof window === "undefined") {
87+
return;
88+
}
89+
90+
const handleStorage = (event: StorageEvent) => {
91+
if (event.key !== SETTINGS_KEY) {
92+
return;
93+
}
94+
95+
setSettings(parseStoredSettings(event.newValue));
96+
};
97+
98+
window.addEventListener("storage", handleStorage);
99+
return () => window.removeEventListener("storage", handleStorage);
100+
}, []);
101+
102+
const value = useMemo(
103+
(): AssistantSettingsContextValue => ({
104+
...settings,
105+
setProvider: (provider: Provider) => {
106+
setSettings((prev) => ({ ...prev, provider }));
107+
},
108+
setOpenaiApiKey: (key: string) => {
109+
setSettings((prev) => ({ ...prev, openaiApiKey: key }));
110+
},
111+
setGeminiApiKey: (key: string) => {
112+
setSettings((prev) => ({ ...prev, geminiApiKey: key }));
113+
},
114+
}),
115+
[settings],
116+
);
117+
118+
return (
119+
<AssistantSettingsContext.Provider value={value}>
120+
{children}
121+
</AssistantSettingsContext.Provider>
122+
);
123+
};
124+
125+
export const useAssistantSettings = () => {
126+
const context = useContext(AssistantSettingsContext);
127+
128+
if (!context) {
129+
throw new Error(
130+
"useAssistantSettings must be used within an AssistantSettingsProvider",
131+
);
132+
}
133+
134+
return context;
135+
};

0 commit comments

Comments
 (0)