Skip to content

Commit be32a5a

Browse files
page update & styles
1 parent eac8442 commit be32a5a

File tree

13 files changed

+885
-589
lines changed

13 files changed

+885
-589
lines changed

.firebaserc

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

app/api/ai/enhance-text/route.ts

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
import { NextRequest, NextResponse } from "next/server"
2+
import { createServerClient, type CookieOptions } from "@supabase/ssr"
3+
import { cookies } from "next/headers"
4+
import { streamObject, type LanguageModel } from "ai"
5+
import { getModelClient } from "@/lib/models"
6+
import modelsList from "@/lib/models.json"
7+
import ratelimit from "@/lib/ratelimit"
8+
import { logError, generateRequestId } from "@/lib/debug"
9+
import { z } from "zod"
10+
import type { Duration } from "@/lib/duration"
11+
12+
export const maxDuration = 30
13+
14+
const rateLimitMaxRequests = process.env.RATE_LIMIT_MAX_REQUESTS
15+
? Number.parseInt(process.env.RATE_LIMIT_MAX_REQUESTS)
16+
: 20
17+
const ratelimitWindow = process.env.RATE_LIMIT_WINDOW ? (process.env.RATE_LIMIT_WINDOW as Duration) : "1h"
18+
19+
const enhanceRequestSchema = z.object({
20+
textToEnhance: z.string().min(1).max(2000)
21+
})
22+
23+
const enhanceResponseSchema = z.object({
24+
enhancedText: z.string(),
25+
reasoning: z.string().optional()
26+
})
27+
28+
export async function POST(req: NextRequest) {
29+
const requestId = generateRequestId()
30+
31+
try {
32+
console.log(`[Enhance Text API ${requestId}] Processing request`)
33+
34+
// Parse and validate request body
35+
let body: any
36+
try {
37+
body = await req.json()
38+
const validatedBody = enhanceRequestSchema.parse(body)
39+
console.log(`[Enhance Text API ${requestId}] Request validated successfully`)
40+
} catch (error: any) {
41+
logError("Request validation failed", error, { requestId })
42+
return NextResponse.json(
43+
{
44+
error: "Invalid request. Text to enhance is required and must be between 1-2000 characters.",
45+
code: "VALIDATION_ERROR",
46+
requestId,
47+
},
48+
{ status: 400 }
49+
)
50+
}
51+
52+
const { textToEnhance } = body
53+
54+
// Get user authentication
55+
const cookieStore = cookies()
56+
const supabase = createServerClient(
57+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
58+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
59+
{
60+
cookies: {
61+
async get(name: string) {
62+
return (await cookieStore).get(name)?.value
63+
},
64+
async set(name: string, value: string, options: CookieOptions) {
65+
try {
66+
(await cookieStore).set(name, value, options)
67+
} catch (error) {
68+
console.warn(`Failed to set cookie '${name}':`, error)
69+
}
70+
},
71+
async remove(name: string) {
72+
try {
73+
(await cookieStore).delete(name)
74+
} catch (error) {
75+
console.warn(`Failed to delete cookie '${name}':`, error)
76+
}
77+
},
78+
},
79+
}
80+
)
81+
82+
const { data: { user } } = await supabase.auth.getUser()
83+
84+
if (!user) {
85+
return NextResponse.json(
86+
{ error: "Authentication required", code: "AUTH_REQUIRED", requestId },
87+
{ status: 401 }
88+
)
89+
}
90+
91+
// Apply rate limiting
92+
try {
93+
const limit = rateLimitMaxRequests
94+
? await ratelimit(req.headers.get("x-forwarded-for"), rateLimitMaxRequests, ratelimitWindow)
95+
: false
96+
97+
if (limit) {
98+
console.log(`[Enhance Text API ${requestId}] Rate limit hit`)
99+
return NextResponse.json(
100+
{
101+
error: "Rate limit exceeded. Please try again later.",
102+
code: "RATE_LIMITED",
103+
requestId,
104+
retryAfter: limit.reset,
105+
},
106+
{
107+
status: 429,
108+
headers: {
109+
"X-RateLimit-Limit": limit.amount.toString(),
110+
"X-RateLimit-Remaining": limit.remaining.toString(),
111+
"X-RateLimit-Reset": limit.reset.toString(),
112+
},
113+
}
114+
)
115+
}
116+
} catch (error) {
117+
logError("Rate limiting check failed", error, { requestId })
118+
// Continue without rate limiting if it fails
119+
}
120+
121+
// Get default model for enhancement
122+
const defaultModel = modelsList.models.find(m => m.providerId === "openai" && m.id === "gpt-4o") ||
123+
modelsList.models.find(m => m.providerId === "anthropic" && m.id === "claude-3-5-sonnet-20241022") ||
124+
modelsList.models[0]
125+
126+
if (!defaultModel) {
127+
return NextResponse.json(
128+
{ error: "No available model for text enhancement", code: "NO_MODEL", requestId },
129+
{ status: 500 }
130+
)
131+
}
132+
133+
// Create model client
134+
let modelClient: LanguageModel
135+
try {
136+
console.log(`[Enhance Text API ${requestId}] Creating model client: ${defaultModel.providerId}/${defaultModel.id}`)
137+
modelClient = getModelClient(defaultModel, {}) as LanguageModel
138+
} catch (error: any) {
139+
logError("Model client creation failed", error, { requestId, provider: defaultModel.providerId, modelId: defaultModel.id })
140+
return NextResponse.json(
141+
{
142+
error: "Failed to initialize AI model for text enhancement",
143+
code: "MODEL_INIT_ERROR",
144+
provider: defaultModel.providerId,
145+
requestId,
146+
},
147+
{ status: 500 }
148+
)
149+
}
150+
151+
// Generate enhanced text using streamObject for compatibility
152+
try {
153+
console.log(`[Enhance Text API ${requestId}] Generating enhanced text`)
154+
155+
const result = await streamObject({
156+
model: modelClient,
157+
schema: enhanceResponseSchema,
158+
system: `You are an expert writing assistant that enhances text to be more clear, engaging, and effective while preserving the original intent and tone.
159+
160+
Your task is to improve the given text by:
161+
- Making it more clear and concise
162+
- Improving grammar and sentence structure
163+
- Enhancing readability and flow
164+
- Maintaining the original meaning and intent
165+
- Keeping the same approximate length and tone
166+
- Making it more engaging without being overly formal
167+
168+
Always preserve the core message while making it sound more professional and polished.`,
169+
messages: [
170+
{
171+
role: "user",
172+
content: `Please enhance this text while preserving its original intent and tone:
173+
174+
"${textToEnhance}"
175+
176+
Make it more clear, engaging, and well-structured while keeping the same general meaning and length.`
177+
}
178+
],
179+
maxRetries: 2,
180+
temperature: 0.7,
181+
})
182+
183+
// Wait for the complete object
184+
const finalResult = await result.object
185+
186+
console.log(`[Enhance Text API ${requestId}] Enhancement completed successfully`)
187+
188+
return NextResponse.json({
189+
enhancedText: finalResult.enhancedText,
190+
requestId,
191+
})
192+
193+
} catch (error: any) {
194+
logError("Text enhancement failed", error, { requestId, provider: defaultModel.providerId })
195+
196+
const errorMessage = error.message || "Unknown error"
197+
198+
if (errorMessage.includes("API key") || errorMessage.includes("authentication") || error.status === 401) {
199+
return NextResponse.json(
200+
{
201+
error: "Authentication failed. Please check AI model configuration.",
202+
code: "AUTH_ERROR",
203+
provider: defaultModel.providerId,
204+
requestId,
205+
},
206+
{ status: 401 }
207+
)
208+
}
209+
210+
if (errorMessage.includes("rate limit") || errorMessage.includes("quota") || error.status === 429) {
211+
return NextResponse.json(
212+
{
213+
error: "AI service rate limit exceeded. Please try again later.",
214+
code: "AI_RATE_LIMITED",
215+
provider: defaultModel.providerId,
216+
requestId,
217+
},
218+
{ status: 429 }
219+
)
220+
}
221+
222+
return NextResponse.json(
223+
{
224+
error: "Failed to enhance text. Please try again.",
225+
code: "ENHANCEMENT_ERROR",
226+
provider: defaultModel.providerId,
227+
requestId,
228+
},
229+
{ status: 500 }
230+
)
231+
}
232+
233+
} catch (error: any) {
234+
logError("Request processing failed", error, { requestId })
235+
236+
return NextResponse.json(
237+
{
238+
error: "Internal server error occurred during text enhancement",
239+
code: "INTERNAL_ERROR",
240+
requestId,
241+
},
242+
{ status: 500 }
243+
)
244+
}
245+
}

0 commit comments

Comments
 (0)