add stable request ids for replays
This commit is contained in:
+22
-2
@@ -2,6 +2,7 @@
|
||||
|
||||
import http from "node:http";
|
||||
import { spawn } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import { chmod, copyFile, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
@@ -456,6 +457,7 @@ function openRequestsDatabase(dbPath) {
|
||||
PRAGMA journal_mode = WAL;
|
||||
CREATE TABLE IF NOT EXISTS requests (
|
||||
seq INTEGER PRIMARY KEY,
|
||||
request_id TEXT,
|
||||
started_at TEXT,
|
||||
finished_at TEXT,
|
||||
duration_ms INTEGER,
|
||||
@@ -489,6 +491,12 @@ function openRequestsDatabase(dbPath) {
|
||||
CREATE INDEX IF NOT EXISTS idx_requests_matched ON requests(matched);
|
||||
CREATE INDEX IF NOT EXISTS idx_requests_response_stream ON requests(response_stream);
|
||||
`);
|
||||
const requestColumns = db.prepare("PRAGMA table_info(requests)").all();
|
||||
const requestColumnNames = new Set(requestColumns.map((column) => column.name));
|
||||
if (!requestColumnNames.has("request_id")) {
|
||||
db.exec("ALTER TABLE requests ADD COLUMN request_id TEXT");
|
||||
}
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_requests_request_id ON requests(request_id)");
|
||||
return db;
|
||||
}
|
||||
|
||||
@@ -507,10 +515,19 @@ function buildPersistedRequestPayload(entry) {
|
||||
);
|
||||
}
|
||||
|
||||
function computeRequestId(pathname, rawBody) {
|
||||
const hash = createHash("sha256");
|
||||
hash.update(normalizePath(pathname));
|
||||
hash.update("\n");
|
||||
hash.update(Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(rawBody || ""));
|
||||
return `req_${hash.digest("hex").slice(0, 16)}`;
|
||||
}
|
||||
|
||||
function requestRowFromEntry(entry) {
|
||||
const payload = buildPersistedRequestPayload(entry);
|
||||
return {
|
||||
seq: entry.seq,
|
||||
request_id: entry.request_id || null,
|
||||
started_at: entry.started_at || null,
|
||||
finished_at: entry.finished_at || null,
|
||||
duration_ms: Number.isInteger(entry.duration_ms) ? entry.duration_ms : null,
|
||||
@@ -543,13 +560,13 @@ function requestRowFromEntry(entry) {
|
||||
function insertRequestRow(db, row) {
|
||||
db.prepare(`
|
||||
INSERT OR REPLACE INTO requests (
|
||||
seq, started_at, finished_at, duration_ms, profile_name, method, path, model,
|
||||
seq, request_id, started_at, finished_at, duration_ms, profile_name, method, path, model,
|
||||
requested_model, forwarded_model, request_stream, response_stream, inspected, matched,
|
||||
status_code, upstream_status_code, reasoning_tokens, input_tokens, output_tokens,
|
||||
total_tokens, cached_tokens, error, upstream_origin, upstream_path,
|
||||
upstream_auth_mode, upstream_auth_source, payload_json
|
||||
) VALUES (
|
||||
@seq, @started_at, @finished_at, @duration_ms, @profile_name, @method, @path, @model,
|
||||
@seq, @request_id, @started_at, @finished_at, @duration_ms, @profile_name, @method, @path, @model,
|
||||
@requested_model, @forwarded_model, @request_stream, @response_stream, @inspected, @matched,
|
||||
@status_code, @upstream_status_code, @reasoning_tokens, @input_tokens, @output_tokens,
|
||||
@total_tokens, @cached_tokens, @error, @upstream_origin, @upstream_path,
|
||||
@@ -609,6 +626,7 @@ function buildRequestQueryFilters({ query, filter }) {
|
||||
lower(coalesce(profile_name, '')) LIKE @query OR
|
||||
lower(coalesce(method, '')) LIKE @query OR
|
||||
lower(coalesce(path, '')) LIKE @query OR
|
||||
lower(coalesce(request_id, '')) LIKE @query OR
|
||||
lower(coalesce(model, '')) LIKE @query OR
|
||||
lower(coalesce(requested_model, '')) LIKE @query OR
|
||||
lower(coalesce(forwarded_model, '')) LIKE @query OR
|
||||
@@ -1203,6 +1221,7 @@ async function buildPersistentRequestsSnapshot(runtime, { limit = 50, offset = 0
|
||||
function buildRequestEntry({ seq, startedAt, startedMs, req, pathname, requestJson, profileName }) {
|
||||
return {
|
||||
seq,
|
||||
request_id: null,
|
||||
lifecycle_state: "sent",
|
||||
started_at: startedAt.toISOString(),
|
||||
first_response_at: null,
|
||||
@@ -3060,6 +3079,7 @@ async function proxyRequest(runtime, req, res) {
|
||||
const requestIsStream = Boolean(requestJson?.stream);
|
||||
let totalUpstreamAttempts = 0;
|
||||
requestEntry.request_body_bytes = rawRequestBody.length;
|
||||
requestEntry.request_id = computeRequestId(pathname, rawRequestBody);
|
||||
requestEntry.model = requestJson?.model || null;
|
||||
requestEntry.requested_model = parsedRequestJson?.model || null;
|
||||
requestEntry.forwarded_model = forwardedModel || parsedRequestJson?.model || null;
|
||||
|
||||
Reference in New Issue
Block a user