Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
khaosans committed Oct 6, 2024
1 parent 6caa064 commit 845715d
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 194 deletions.
48 changes: 37 additions & 11 deletions app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,43 @@
import { NextRequest, NextResponse } from 'next/server';

// Remove the Edge runtime specification
// export const runtime = 'edge';
import { NextRequest } from 'next/server';

export async function POST(req: NextRequest) {
try {
const { message } = await req.json();
const { message, model } = await req.json();

// Process the chat message here
// For now, let's just echo the message back
const response = await fetch('http://localhost:11434/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model,
messages: [{ role: 'user', content: message }],
stream: true,
}),
});

return NextResponse.json({ reply: `You said: ${message}` });
} catch (error) {
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
if (!response.ok) {
throw new Error('Failed to fetch from Ollama');
}

const stream = new ReadableStream({
async start(controller) {
const reader = response.body?.getReader();
while (true) {
const { done, value } = await reader!.read();
if (done) break;
const chunk = new TextDecoder().decode(value);
try {
const parsed = JSON.parse(chunk);
if (parsed.message?.content) {
controller.enqueue(parsed.message.content);
}
} catch (e) {
console.error('Error parsing JSON:', e);
}
}
controller.close();
},
});

return new Response(stream, {
headers: { 'Content-Type': 'text/plain' },
});
}
16 changes: 16 additions & 0 deletions app/api/tags/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NextRequest, NextResponse } from 'next/server';

export async function GET(req: NextRequest) {
try {
const response = await fetch('http://localhost:11434/api/tags');
if (!response.ok) {
throw new Error('Failed to fetch models from Ollama');
}
const data = await response.json();
const models = data.models.map((model: { name: string }) => model.name);
return NextResponse.json({ models });
} catch (error) {
console.error('Error fetching models:', error);
return NextResponse.json({ error: 'Failed to fetch models' }, { status: 500 });
}
}
6 changes: 4 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
'use client';

import React, { ReactNode } from 'react';
import React from 'react';
import { ThemeProvider } from 'next-themes';
import '@/styles/globals.css';
import Layout from '@/components/Layout';

export default function RootLayout({
children,
Expand All @@ -12,7 +14,7 @@ export default function RootLayout({
<html lang="en">
<body>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
{children}
<Layout>{children}</Layout>
</ThemeProvider>
</body>
</html>
Expand Down
115 changes: 18 additions & 97 deletions app/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,24 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { Switch } from "@/components/ui/switch"
import { Button } from "@/components/ui/button"
import { toast } from "react-hot-toast"
import { Settings, defaultSettings } from '@/lib/settings'

export default function SettingsPage() {
const [settings, setSettings] = useState({
theme: 'light',
language: 'en',
notifications_enabled: true,
model: ''
})
const [settings, setSettings] = useState<Settings>(defaultSettings)
const [availableModels, setAvailableModels] = useState<string[]>([])

//DO NOT REMOVE curl http://localhost:11434/api/tags
//https://github.com/ollama/ollama/blob/main/docs/api.md#list-local-models
useEffect(() => {
// Fetch available models from Ollama API
const fetchModels = async () => {
try {
const response = await fetch('http://localhost:11434/api/tags') // Replace with actual API endpoint
const response = await fetch('/api/tags')
const data = await response.json()
setAvailableModels(data.models) // Adjust based on actual API response structure
setAvailableModels(data.models)
} catch (error) {
console.error('Error fetching models:', error)
}
}

fetchModels()

// Set default model based on environment
if (window.location.hostname === 'localhost') {
setSettings((prev) => ({ ...prev, model: 'llama3.2' }))
} else {
setSettings((prev) => ({ ...prev, model: 'gpt-4o-mini' }))
}
}, [])

const handleSaveGeneral = () => {
Expand All @@ -61,100 +47,35 @@ export default function SettingsPage() {
<TabsTrigger value="model">Model Config</TabsTrigger>
</TabsList>
<TabsContent value="general">
{/* Existing general settings content */}
</TabsContent>
<TabsContent value="model">
<Card>
<CardHeader>
<CardTitle>General Settings</CardTitle>
<CardDescription>Manage your account settings and preferences.</CardDescription>
<CardTitle>Model Configuration</CardTitle>
<CardDescription>Select your preferred AI model.</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<label htmlFor="theme">Theme</label>
<Select value={settings.theme} onValueChange={(value) => setSettings({ ...settings, theme: value })}>
<SelectTrigger id="theme">
<SelectValue>{settings.theme}</SelectValue>
<label htmlFor="model">Ollama Model</label>
<Select value={settings.model} onValueChange={handleModelChange}>
<SelectTrigger id="model">
<SelectValue>{settings.model}</SelectValue>
</SelectTrigger>
<SelectContent>
<SelectItem value="light">Light</SelectItem>
<SelectItem value="dark">Dark</SelectItem>
{availableModels.map((model) => (
<SelectItem key={model} value={model}>{model}</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-center space-x-2">
<Switch
id="notifications"
checked={settings.notifications_enabled}
onCheckedChange={(checked: boolean) => setSettings({ ...settings, notifications_enabled: checked })}
/>
<label htmlFor="notifications">Enable notifications</label>
</div>
</CardContent>
<CardFooter>
<Button size="sm" variant="default" onClick={handleSaveGeneral}>Save Changes</Button>
<Button size="sm" variant="default" onClick={() => toast.success("Model settings saved")}>Save Changes</Button>
</CardFooter>
</Card>
</TabsContent>
<TabsContent value="model">
<Card>
<CardHeader>
<CardTitle>Model Configuration</CardTitle>
<CardDescription>Select your preferred AI model.</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Select value={settings.model} onValueChange={handleModelChange}>
<SelectTrigger>
<SelectValue>{settings.model}</SelectValue>
</SelectTrigger>
<SelectContent>
{availableModels.map((model) => (
<SelectItem key={model} value={model}>{model}</SelectItem>
))}
</SelectContent>
</Select>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="security">
<Card>
<CardHeader>
<CardTitle>Security Settings</CardTitle>
<CardDescription>Manage your password and account security.</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<form onSubmit={(e) => { e.preventDefault(); toast.success("Your password has been successfully updated.") }} className="space-y-4">
<div className="space-y-2">
<label htmlFor="current-password">Current Password</label>
<input id="current-password" type="password" required className="input" />
</div>
<div className="space-y-2">
<label htmlFor="new-password">New Password</label>
<input id="new-password" type="password" required className="input" />
</div>
<div className="space-y-2">
<label htmlFor="confirm-password">Confirm New Password</label>
<input id="confirm-password" type="password" required className="input" />
</div>
<Button size="sm" variant="default" type="submit">Change Password</Button>
</form>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="advanced">
<Card>
<CardHeader>
<CardTitle>Advanced Settings</CardTitle>
<CardDescription>Manage advanced settings for your account.</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<h3 className="text-lg font-medium">Delete Account</h3>
<p className="text-sm text-muted-foreground">
Once you delete your account, there is no going back. Please be certain.
</p>
</div>
<Button size="sm" variant="destructive" onClick={() => { toast.error("Your account has been successfully deleted."); }}>Delete Account</Button>
</CardContent>
</Card>
</TabsContent>
{/* Existing security and advanced tabs content */}
</Tabs>
</div>
)
Expand Down
Loading

0 comments on commit 845715d

Please sign in to comment.