import { NextResponse } from "next/server"; import { encode } from "gpt-tokenizer/model/gpt-4"; import { Pool, PoolClient } from "pg"; import { createClient } from "@vercel/postgres"; import { query, getClient } from "@/lib/db/client"; import { getModelInletCost } from "@/lib/utils/inlet-cost"; const isVercel = process.env.VERCEL === "1"; interface Message { role: string; content: string; } interface ModelPrice { id: string; name: string; input_price: number; output_price: number; per_msg_price: number; } type DbClient = ReturnType | Pool | PoolClient; async function getModelPrice(modelId: string): Promise { const result = await query( `SELECT id, name, input_price, output_price, per_msg_price FROM model_prices WHERE id = $1`, [modelId] ); if (result.rows[0]) { return result.rows[0]; } const defaultInputPrice = parseFloat( process.env.DEFAULT_MODEL_INPUT_PRICE || "60" ); const defaultOutputPrice = parseFloat( process.env.DEFAULT_MODEL_OUTPUT_PRICE || "60" ); if ( isNaN(defaultInputPrice) || defaultInputPrice < 0 || isNaN(defaultOutputPrice) || defaultOutputPrice < 0 ) { return null; } return { id: modelId, name: modelId, input_price: defaultInputPrice, output_price: defaultOutputPrice, per_msg_price: -1, }; } export async function POST(req: Request) { const client = (await getClient()) as DbClient; let pgClient: DbClient | null = null; try { if (isVercel) { pgClient = client; } else { pgClient = await (client as Pool).connect(); } const data = await req.json(); console.log("Request data:", JSON.stringify(data, null, 2)); const modelId = data.body.model; const userId = data.user.id; const userName = data.user.name || "Unknown User"; await query("BEGIN"); const modelPrice = await getModelPrice(modelId); if (!modelPrice) { throw new Error(`Fail to fetch price info of model ${modelId}`); } const lastMessage = data.body.messages[data.body.messages.length - 1]; let inputTokens: number; let outputTokens: number; if ( lastMessage.usage && lastMessage.usage.prompt_tokens && lastMessage.usage.completion_tokens ) { inputTokens = lastMessage.usage.prompt_tokens; outputTokens = lastMessage.usage.completion_tokens; } else { outputTokens = encode(lastMessage.content).length; const totalTokens = data.body.messages.reduce( (sum: number, msg: Message) => sum + encode(msg.content).length, 0 ); inputTokens = totalTokens - outputTokens; } let totalCost: number; if (outputTokens === 0) { totalCost = 0; console.log("No charge for zero output tokens"); } else if (modelPrice.per_msg_price >= 0) { totalCost = Number(modelPrice.per_msg_price); console.log( `Using fixed pricing: ${totalCost} (${modelPrice.per_msg_price} per message)` ); } else { const inputCost = (inputTokens / 1_000_000) * modelPrice.input_price; const outputCost = (outputTokens / 1_000_000) * modelPrice.output_price; totalCost = inputCost + outputCost; } const inletCost = getModelInletCost(modelId); const actualCost = totalCost - inletCost; const userResult = await query( `UPDATE users SET balance = LEAST( balance - CAST($1 AS DECIMAL(16,4)), 999999.9999 ) WHERE id = $2 RETURNING balance`, [actualCost, userId] ); if (userResult.rows.length === 0) { throw new Error("User does not exist"); } const newBalance = Number(userResult.rows[0].balance); if (newBalance > 999999.9999) { throw new Error("Balance exceeds maximum allowed value"); } await query( `INSERT INTO user_usage_records ( user_id, nickname, model_name, input_tokens, output_tokens, cost, balance_after ) VALUES ($1, $2, $3, $4, $5, $6, $7)`, [ userId, userName, modelId, inputTokens, outputTokens, totalCost, newBalance, ] ); await query("COMMIT"); console.log( JSON.stringify({ success: true, inputTokens, outputTokens, totalCost, newBalance, message: "Request successful", }) ); return NextResponse.json({ success: true, inputTokens, outputTokens, totalCost, newBalance, message: "Request successful", }); } catch (error) { await query("ROLLBACK"); console.error("Outlet error:", error); return NextResponse.json( { success: false, error: error instanceof Error ? error.message : "Error processing request", error_type: error instanceof Error ? error.name : "UNKNOWN_ERROR", }, { status: 500 } ); } finally { if (!isVercel && pgClient && "release" in pgClient) { pgClient.release(); } } }