Skip to content

Commit

Permalink
Add OpenAI DALL-E image generation feature
Browse files Browse the repository at this point in the history
- Add image generation component
- Create API route for OpenAI image generation
- Update Next.js config for DALL-E image hosting
- Add toggle between chat and image generation modes
  • Loading branch information
while-basic committed Dec 2, 2024
1 parent e22595d commit 22966c2
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 27 deletions.
27 changes: 27 additions & 0 deletions app/api/generate-image/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { NextResponse } from 'next/server'
import OpenAI from 'openai'

const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})

export async function POST(req: Request) {
try {
const { prompt } = await req.json()

const response = await openai.images.generate({
model: "dall-e-3",
prompt: prompt,
n: 1,
size: "1024x1024",
})

return NextResponse.json({ url: response.data[0].url })
} catch (error) {
console.error('Error generating image:', error)
return NextResponse.json(
{ error: 'Failed to generate image' },
{ status: 500 }
)
}
}
65 changes: 38 additions & 27 deletions components/chat/chat-interface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { useState } from 'react'
import { MessageList } from './message-list'
import { ImageGeneration } from './image-generation'
import { Message, Conversation, TokenUsage } from '@/lib/chat'
import { Button } from "@/components/ui/button"
import { Textarea } from "@/components/ui/textarea"
Expand Down Expand Up @@ -32,6 +33,7 @@ export function ChatInterface({ conversation, onNewMessage }: ChatInterfaceProps
const [topP, setTopP] = useState(1.0)
const [frequencyPenalty, setFrequencyPenalty] = useState(0)
const [presencePenalty, setPresencePenalty] = useState(0)
const [isImageMode, setIsImageMode] = useState(false);

const handleSubmit = async () => {
if (!inputMessage.trim() || isLoading) return
Expand Down Expand Up @@ -108,9 +110,13 @@ export function ChatInterface({ conversation, onNewMessage }: ChatInterfaceProps
<span className="mr-2">💬</span>
Chat
</Button>
<Button variant="ghost" className="w-full justify-start">
<Button
variant="ghost"
className="w-full justify-start"
onClick={() => setIsImageMode(!isImageMode)}
>
<span className="mr-2">⚡</span>
Realtime
{isImageMode ? 'Text Chat' : 'Image Generation'}
</Button>
</div>
</div>
Expand All @@ -120,38 +126,43 @@ export function ChatInterface({ conversation, onNewMessage }: ChatInterfaceProps
<div className="flex-1 flex flex-col">
{/* Top Bar */}
<div className="border-b p-4 flex justify-between items-center">
<h1 className="text-xl font-semibold">Chat</h1>
<h2 className="text-lg font-semibold">
{isImageMode ? 'Image Generation' : 'Chat'}
</h2>
<div className="flex gap-2">
<Button variant="outline" size="sm">Clear</Button>
<Button variant="outline" size="sm">Code</Button>
<Button variant="outline" size="sm">History</Button>
</div>
</div>

{/* Messages Area */}
<ScrollArea className="flex-1 p-4">
<MessageList messages={conversation?.messages || []} isLoading={isLoading} />
</ScrollArea>

{/* Input Area */}
<div className="border-t p-4">
<div className="flex gap-2">
<Textarea
placeholder="Enter user message..."
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyDown={handleKeyPress}
className="min-h-[44px] max-h-[200px]"
/>
<Button
onClick={handleSubmit}
disabled={isLoading || !inputMessage.trim()}
className="px-8"
>
{isLoading ? 'Sending...' : 'Send'}
</Button>
</div>
</div>
{isImageMode ? (
<ImageGeneration />
) : (
<>
<ScrollArea className="flex-1 p-4">
<MessageList messages={conversation?.messages || []} isLoading={isLoading} />
</ScrollArea>
<div className="border-t p-4">
<div className="flex gap-2">
<Textarea
placeholder="Enter user message..."
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyDown={handleKeyPress}
className="min-h-[44px] max-h-[200px]"
/>
<Button
onClick={handleSubmit}
disabled={isLoading || !inputMessage.trim()}
className="px-8"
>
{isLoading ? 'Sending...' : 'Send'}
</Button>
</div>
</div>
</>
)}
</div>

{/* Right Configuration Panel Toggle */}
Expand Down
70 changes: 70 additions & 0 deletions components/chat/image-generation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use client'

import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Card } from '@/components/ui/card'
import Image from 'next/image'

export function ImageGeneration() {
const [prompt, setPrompt] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [generatedImage, setGeneratedImage] = useState<string | null>(null)

const generateImage = async () => {
if (!prompt.trim() || isLoading) return

setIsLoading(true)
try {
const response = await fetch('/api/generate-image', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt }),
})

const data = await response.json()
if (data.url) {
setGeneratedImage(data.url)
}
} catch (error) {
console.error('Error generating image:', error)
} finally {
setIsLoading(false)
}
}

return (
<div className="flex flex-col space-y-4 p-4">
<div className="flex space-x-2">
<Input
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="Describe the image you want to generate..."
className="flex-1"
onKeyPress={(e) => e.key === 'Enter' && generateImage()}
/>
<Button
onClick={generateImage}
disabled={isLoading}
>
{isLoading ? 'Generating...' : 'Generate'}
</Button>
</div>

{generatedImage && (
<Card className="p-4">
<div className="relative w-full aspect-square">
<Image
src={generatedImage}
alt="Generated image"
fill
className="object-contain"
/>
</div>
</Card>
)}
</div>
)
}
6 changes: 6 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ const nextConfig = {
port: '',
pathname: '/**',
},
{
protocol: 'https',
hostname: 'oaidalleapiprodscus.blob.core.windows.net',
port: '',
pathname: '/**',
},
],
},
webpack: (config, { isServer }) => {
Expand Down

0 comments on commit 22966c2

Please sign in to comment.