From 0a69232257a20e187edc76696ef53d3a8e431f27 Mon Sep 17 00:00:00 2001 From: Praveen Thirumurugan Date: Sat, 18 Oct 2025 02:22:52 +0530 Subject: [PATCH] feat: add UUID generation for request tracking and update PostgreSQL schema --- README.md | 33 +++++++++++---------------------- package-lock.json | 24 +++++++++++++++++++++++- package.json | 4 +++- proxy.ts | 15 ++++++++++++++- 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 51ed25e..f01e448 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,7 @@ export const MODEL_COSTS: Record = { You can add more models to the `MODEL_COSTS` object to support your specific LLM providers. -## 📊 Database Table Schema - -Before running the proxy, you need to create the table in the database. +## 📊 PostgreSQL Table Schema ```sql CREATE TABLE IF NOT EXISTS ( @@ -46,10 +44,19 @@ CREATE TABLE IF NOT EXISTS ( request_body JSONB, response_body JSONB, response_status INTEGER, - provider_url VARCHAR(500) + provider_url VARCHAR(500), + client_ip INET, + user_agent TEXT, + request_size INTEGER, + response_size INTEGER, + stream BOOLEAN, + temperature REAL, + max_tokens INTEGER, + request_id UUID ); CREATE INDEX IF NOT EXISTS idx__timestamp ON (timestamp); +CREATE INDEX IF NOT EXISTS idx__request_id ON (request_id); ``` ## 🚀 Quick Start @@ -124,24 +131,6 @@ The corresponding database entry will include: - Response time metrics - Complete request/response bodies for audit purposes -## 🔧 Advanced Features - -### Custom Cost Models - -Extend the `cost.ts` file to support your specific pricing models: - -```typescript -export const MODEL_COSTS: Record = { - "gpt-4": { input: 0.03, cached: 0.015, output: 0.06 }, - "claude-3": { input: 0.025, cached: 0.0125, output: 0.125 }, - "custom-model": { input: 0.01, cached: 0.005, output: 0.02 }, -}; -``` - -### Database Integration - -The proxy automatically logs all requests to your PostgreSQL database with comprehensive metadata for analysis and reporting. - ## 🛡️ Security - Bearer token authentication required diff --git a/package-lock.json b/package-lock.json index 1b2a264..5cf8797 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,9 +7,12 @@ "": { "name": "llm-proxy-server", "version": "1.0.0", + "license": "MIT", "dependencies": { + "@types/uuid": "^10.0.0", "dotenv": "^17.2.3", - "pg": "^8.16.3" + "pg": "^8.16.3", + "uuid": "^13.0.0" }, "devDependencies": { "@types/node": "^24.8.1", @@ -124,6 +127,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "license": "MIT" + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -935,6 +944,19 @@ "dev": true, "license": "MIT" }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", diff --git a/package.json b/package.json index 57d5ef8..6cf6241 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,9 @@ "typescript": "^5.9.3" }, "dependencies": { + "@types/uuid": "^10.0.0", "dotenv": "^17.2.3", - "pg": "^8.16.3" + "pg": "^8.16.3", + "uuid": "^13.0.0" } } diff --git a/proxy.ts b/proxy.ts index aa78931..69c7c7d 100644 --- a/proxy.ts +++ b/proxy.ts @@ -6,6 +6,7 @@ import "dotenv/config"; import http, { IncomingMessage, ServerResponse } from "http"; import { TextDecoder } from "util"; import { Pool } from "pg"; +import { v4 as uuidv4 } from "uuid"; import { calculateCost } from "./cost"; @@ -24,6 +25,10 @@ const pool = new Pool({ }); // --- Helper functions --- +function generateRequestId(): string { + return uuidv4(); +} + function setCors(res: ServerResponse) { res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Requested-With"); @@ -189,7 +194,15 @@ const server = http.createServer(async (req, res) => { request_body: requestJson, response_body: responseBody, response_status: upstreamRes.status, - provider_url: UPSTREAM_URL + provider_url: UPSTREAM_URL, + client_ip: req.socket?.remoteAddress || null, + user_agent: req.headers["user-agent"] || null, + request_size: bodyBuf.length, + response_size: Buffer.from(JSON.stringify(responseBody)).length, + stream: contentType.includes("text/event-stream"), + temperature: requestJson?.temperature || null, + max_tokens: requestJson?.max_tokens || null, + request_id: generateRequestId(), }; logToPG(logData).catch(err => console.error("PG log error:", err));