Files
english/supabase/functions/writing-check-dbiz/index.ts
2026-04-13 13:45:18 +07:00

93 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Supabase Edge Function: writing-check-dbiz
// Uses DBIZ LLM API (qwen-35b, OpenAI-compatible) to analyze English writing.
// Deploy: supabase functions deploy writing-check-dbiz --no-verify-jwt
// Secrets: supabase secrets set DBIZ_API_KEY=<your_key>
import OpenAI from "npm:openai@^4"
const dbiz = new OpenAI({
apiKey: Deno.env.get("DBIZ_API_KEY") ?? "",
baseURL: "https://ai-api.dbiz.com/v1",
})
const CORS_HEADERS = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
}
// Structure enforced via system prompt — qwen-35b via LiteLLM drops response_format params.
const SYSTEM_PROMPT = `You are an expert English writing teacher specialising in TOEIC and IELTS assessment.
Analyse the student's writing and respond ONLY with valid JSON — no markdown, no extra text:
{
"score": "<estimated band score, e.g. 6.5>",
"grammar": ["<issue 1 with correction, mix English example + Vietnamese explanation>"],
"vocabulary": ["<vocabulary observation in Vietnamese>"],
"structure": "<23 sentence structure assessment in Vietnamese>",
"improved_version": "<the full improved text in English>",
"summary": "<23 sentence overall assessment in Vietnamese>"
}
All string values must use double quotes. Do not use single quotes or unquoted keys.`
Deno.serve(async (req: Request) => {
if (req.method === "OPTIONS") {
return new Response("ok", { headers: CORS_HEADERS })
}
try {
const { content } = await req.json() as { content: string }
if (!content || content.trim().length < 10) {
return new Response(
JSON.stringify({ error: "Bài viết quá ngắn. Vui lòng nhập ít nhất 10 ký tự." }),
{ status: 400, headers: { ...CORS_HEADERS, "Content-Type": "application/json" } },
)
}
const stream = await dbiz.chat.completions.create({
model: "qwen-35b",
messages: [
{ role: "system", content: SYSTEM_PROMPT },
{ role: "user", content: `Analyse this writing:\n\n${content.slice(0, 2000)}` },
],
temperature: 0.3,
max_tokens: 10000,
stream: true,
})
const encoder = new TextEncoder()
const body = new ReadableStream({
async start(controller) {
try {
for await (const chunk of stream) {
const text = chunk.choices[0]?.delta?.content ?? ""
if (text) {
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ text })}\n\n`))
}
}
} catch (err) {
console.error("writing-check-dbiz stream error:", err)
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({ error: "Đã có lỗi khi chấm bài. Vui lòng thử lại." })}\n\n`),
)
}
controller.enqueue(encoder.encode("data: [DONE]\n\n"))
controller.close()
},
})
return new Response(body, {
headers: {
...CORS_HEADERS,
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
},
})
} catch (err) {
console.error("writing-check-dbiz error:", err)
return new Response(
JSON.stringify({ error: "Đã có lỗi khi chấm bài. Vui lòng thử lại." }),
{ status: 500, headers: { ...CORS_HEADERS, "Content-Type": "application/json" } },
)
}
})