nice
This commit is contained in:
parent
a57ab292db
commit
1a9869b0d9
41
src/app/api/ollama/route.ts
Normal file
41
src/app/api/ollama/route.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
export async function POST(req: NextRequest): Promise<NextResponse> {
|
||||
try {
|
||||
const { prompt } = await req.json()
|
||||
|
||||
if (!prompt) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Prompt is required' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const response = await fetch('https://ollama.rxpwnz.xyz/api/generate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: 'llama2',
|
||||
prompt: prompt,
|
||||
stream: false
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Ollama API error: ${response.status}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return NextResponse.json({ response: data.response })
|
||||
} catch (error) {
|
||||
console.error('Ollama generation error:', error)
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : 'Failed to generate text' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
@ -3,7 +3,9 @@
|
||||
import React, { useState } from 'react'
|
||||
import WalletHeader from '../components/WalletHeader'
|
||||
import AIServiceCard from '../components/AIServiceCard'
|
||||
import TextGenerationCard from '../components/TextGenerationCard'
|
||||
import { generateWithFlux, FluxGenerationResult } from '../services/fluxService'
|
||||
import { generateWithOllama, OllamaGenerationResult } from '../services/ollamaService'
|
||||
import { processMTMPayment } from '../services/paymentService'
|
||||
|
||||
interface WalletState {
|
||||
@ -63,6 +65,26 @@ const Page: React.FC = (): React.ReactElement => {
|
||||
return generateWithFlux(prompt)
|
||||
}
|
||||
|
||||
const handleOllamaGeneration = async (prompt: string): Promise<OllamaGenerationResult> => {
|
||||
if (!walletState.connected || !walletState.publicKey || !window.solflare) {
|
||||
return { error: 'Wallet not connected' }
|
||||
}
|
||||
|
||||
// First process payment
|
||||
const paymentResult = await processMTMPayment(
|
||||
walletState.publicKey,
|
||||
2, // 2 MTM tokens for text generation
|
||||
window.solflare
|
||||
)
|
||||
|
||||
if (!paymentResult.success) {
|
||||
return { error: paymentResult.error }
|
||||
}
|
||||
|
||||
// Then generate text
|
||||
return generateWithOllama(prompt)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen w-full flex flex-col items-center bg-gradient-to-b from-gray-900 via-gray-800 to-gray-900">
|
||||
<div className="container max-w-6xl mx-auto px-4 py-8">
|
||||
@ -86,13 +108,26 @@ const Page: React.FC = (): React.ReactElement => {
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<AIServiceCard
|
||||
title="Flux Meme Generator"
|
||||
description="Generate creative memes using Flux AI"
|
||||
description="Generate creative memes and images using Flux AI"
|
||||
tokenCost={1}
|
||||
isWalletConnected={walletState.connected}
|
||||
onGenerate={handleFluxGeneration}
|
||||
/>
|
||||
|
||||
{/* Add more AI service cards here */}
|
||||
<TextGenerationCard
|
||||
title="Ollama AI Chat"
|
||||
description="Get intelligent responses using local LLaMA2 model"
|
||||
tokenCost={2}
|
||||
isWalletConnected={walletState.connected}
|
||||
onGenerate={handleOllamaGeneration}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Info Section */}
|
||||
<div className="mt-12 text-center text-gray-400">
|
||||
<p className="text-sm">
|
||||
Powered by Flux AI and Ollama LLaMA2 • Requires MTM tokens for generation
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
131
src/components/TextGenerationCard.tsx
Normal file
131
src/components/TextGenerationCard.tsx
Normal file
@ -0,0 +1,131 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
|
||||
interface TextGenerationCardProps {
|
||||
title: string
|
||||
description: string
|
||||
tokenCost: number
|
||||
isWalletConnected: boolean
|
||||
onGenerate: (prompt: string) => Promise<{ textResponse?: string, error?: string }>
|
||||
}
|
||||
|
||||
interface GenerationState {
|
||||
loading: boolean
|
||||
processing: boolean
|
||||
textResponse: string | null
|
||||
error: string | null
|
||||
}
|
||||
|
||||
const TextGenerationCard: React.FC<TextGenerationCardProps> = ({
|
||||
title,
|
||||
description,
|
||||
tokenCost,
|
||||
isWalletConnected,
|
||||
onGenerate
|
||||
}) => {
|
||||
const [inputText, setInputText] = useState<string>('')
|
||||
const [generationState, setGenerationState] = useState<GenerationState>({
|
||||
loading: false,
|
||||
processing: false,
|
||||
textResponse: null,
|
||||
error: null,
|
||||
})
|
||||
|
||||
const handleGenerate = async (): Promise<void> => {
|
||||
if (!inputText || !isWalletConnected) return
|
||||
|
||||
setGenerationState({
|
||||
...generationState,
|
||||
loading: true,
|
||||
error: null,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await onGenerate(inputText)
|
||||
|
||||
if (result.error) {
|
||||
setGenerationState({
|
||||
...generationState,
|
||||
loading: false,
|
||||
error: result.error,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (result.textResponse) {
|
||||
setGenerationState({
|
||||
loading: false,
|
||||
processing: false,
|
||||
textResponse: result.textResponse,
|
||||
error: null,
|
||||
})
|
||||
} else {
|
||||
throw new Error('No response received')
|
||||
}
|
||||
} catch (error) {
|
||||
setGenerationState({
|
||||
...generationState,
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : 'Generation failed',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full bg-gray-800/50 backdrop-blur-lg rounded-2xl shadow-xl border border-gray-700/50 mb-8">
|
||||
<div className="p-6">
|
||||
<div className="mb-4">
|
||||
<h2 className="text-2xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-green-400 to-emerald-600">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="text-gray-400 mt-2">{description}</p>
|
||||
<div className="mt-2 inline-block px-3 py-1 bg-green-500/20 rounded-full text-green-300 text-sm">
|
||||
Cost: {tokenCost} MTM
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<textarea
|
||||
value={inputText}
|
||||
onChange={(e) => setInputText(e.target.value)}
|
||||
placeholder="Enter your question here..."
|
||||
disabled={!isWalletConnected}
|
||||
className="w-full bg-gray-900/50 text-gray-100 border border-gray-700 rounded-xl p-4
|
||||
placeholder-gray-500 focus:border-green-500 focus:ring-2 focus:ring-green-500/20
|
||||
focus:outline-none min-h-[120px] transition-all duration-200
|
||||
disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
rows={4}
|
||||
/>
|
||||
<button
|
||||
onClick={handleGenerate}
|
||||
disabled={!isWalletConnected || generationState.loading || !inputText}
|
||||
className="w-full bg-gradient-to-r from-green-500 to-emerald-500 hover:from-green-600
|
||||
hover:to-emerald-600 text-white font-semibold py-4 px-6 rounded-xl
|
||||
transition-all duration-200 shadow-lg hover:shadow-green-500/25
|
||||
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:shadow-none"
|
||||
>
|
||||
{generationState.loading ? 'Processing...' : `Pay ${tokenCost} MTM & Generate`}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{generationState.error && (
|
||||
<div className="mt-4 bg-red-900/20 border border-red-500/20 text-red-400 px-4 py-3 rounded-xl text-center">
|
||||
{generationState.error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{generationState.textResponse && (
|
||||
<div className="mt-6 bg-gray-900/50 rounded-xl p-6 border border-gray-700">
|
||||
<h3 className="text-lg font-semibold text-green-400 mb-3">Response:</h3>
|
||||
<div className="text-gray-300 whitespace-pre-wrap">
|
||||
{generationState.textResponse}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TextGenerationCard
|
35
src/services/ollamaService.ts
Normal file
35
src/services/ollamaService.ts
Normal file
@ -0,0 +1,35 @@
|
||||
export interface OllamaGenerationResult {
|
||||
textResponse?: string
|
||||
error?: string
|
||||
}
|
||||
|
||||
export async function generateWithOllama(prompt: string): Promise<OllamaGenerationResult> {
|
||||
try {
|
||||
const response = await fetch('/api/ollama', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ prompt }),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to generate text')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
console.log('Raw Ollama response:', data)
|
||||
|
||||
if (data.response) {
|
||||
return { textResponse: data.response }
|
||||
} else {
|
||||
console.error('Unexpected response structure:', data)
|
||||
throw new Error('Invalid response format from Ollama API')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Generation error:', error)
|
||||
return {
|
||||
error: error instanceof Error ? error.message : 'Generation failed'
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user