Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
"WebFetch(domain:ui.shadcn.com)",
"mcp__playwright__browser_tabs",
"mcp__playwright__browser_close",
"Bash(psql:*)"
"Bash(psql:*)",
"Bash(jq:*)"
],
"deny": []
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
-- Drop legacy request and response tables and their foreign key constraints
-- ============================================================================
--
-- CONTEXT: These tables were used before the ClickHouse migration (2023-2024).
-- All new request/response data is now stored exclusively in ClickHouse's
-- request_response_rmt table. No new inserts have been happening to these
-- Postgres tables for months.
--
-- TABLES BEING DROPPED:
-- 1. request, response - Legacy request/response storage (replaced by ClickHouse)
-- MAY CONTAIN OLD DATA on production, but it's no longer being read or written
-- 2. asset, cache_hits, feedback - Related legacy tables
-- 3. prompt_input_record - Part of DEPRECATED PromptManager system
-- (marked DEPRECATED at valhalla/jawn/src/managers/prompt/PromptManager.ts:805-806)
-- New system uses prompts_2025_inputs instead
-- Legacy prompt UI (?legacy=true) will break but this is acceptable
--
-- SAFETY:
-- - Tables contain only legacy/deprecated data that is no longer accessed
-- - All FK constraints are explicitly dropped first (no CASCADE operations)
-- - Code has been updated to use ClickHouse or new prompt system
-- - No active reads or writes to these tables
--
-- ROLLBACK PLAN:
-- If issues are found after deployment:
-- 1. Restore tables from backup
-- 2. Revert code changes in VersionedRequestStore.ts, RequestManager.ts, ScoreStore.ts
-- 3. Re-run old migration to recreate tables and constraints
--
-- CODE CHANGES MADE:
-- - valhalla/jawn/src/lib/stores/request/VersionedRequestStore.ts
-- (fixed addPropertyToRequest to use ClickHouse)
-- - valhalla/jawn/src/managers/request/RequestManager.ts
-- (updated waitForRequestAndResponse to use ClickHouse)
-- - valhalla/jawn/src/lib/stores/ScoreStore.ts
-- (fixed array mapping bug, deprecated bumpRequestVersion)
--
-- KNOWN LIMITATIONS:
-- - Legacy prompt UI (?legacy=true) will no longer work
-- - Some experiment-related code may reference these tables but is likely unused

-- First, drop any legacy views that might reference these tables
DROP VIEW IF EXISTS response_and_request;
DROP VIEW IF EXISTS response_rbac;
DROP VIEW IF EXISTS request_rbac;
DROP VIEW IF EXISTS user_metrics_rbac;
DROP VIEW IF EXISTS metrics_rbac;
DROP VIEW IF EXISTS response_and_request_rbac;
DROP VIEW IF EXISTS request_cache_rbac;

-- Drop foreign key constraints from dependent tables (request/response references)
ALTER TABLE experiment_output DROP CONSTRAINT IF EXISTS public_experiment_output_request_id_fkey;
ALTER TABLE experiment_v2_hypothesis_run DROP CONSTRAINT IF EXISTS public_experiment_v2_hypothesis_run_result_request_id_fkey;
ALTER TABLE finetune_dataset_data DROP CONSTRAINT IF EXISTS finetune_dataset_data_request_id_fkey;
ALTER TABLE job_node_request DROP CONSTRAINT IF EXISTS job_node_request_request_id_fkey;
ALTER TABLE properties DROP CONSTRAINT IF EXISTS properties_request_id_fkey;
ALTER TABLE request_job_task DROP CONSTRAINT IF EXISTS request_job_task_request_id_fkey;
ALTER TABLE score_value DROP CONSTRAINT IF EXISTS fk_request_id;

-- Drop foreign key constraints pointing to prompt_input_record (deprecated prompt system)
ALTER TABLE experiment_dataset_v2_row DROP CONSTRAINT IF EXISTS public_experiment_dataset_v2_row_input_record_fkey;
ALTER TABLE experiment_output DROP CONSTRAINT IF EXISTS public_experiment_output_input_record_id_fkey;

-- Drop legacy tables (no longer used, all data in ClickHouse or new prompt system)
DROP TABLE IF EXISTS asset;
DROP TABLE IF EXISTS cache_hits;
DROP TABLE IF EXISTS feedback;
DROP TABLE IF EXISTS prompt_input_record; -- Part of deprecated PromptManager, replaced by prompts_2025_inputs
DROP TABLE IF EXISTS response;
DROP TABLE IF EXISTS request;
41 changes: 14 additions & 27 deletions valhalla/jawn/src/lib/stores/ScoreStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,22 @@ export class ScoreStore extends BaseStore {
uniqueRequestResponseLogs
);

// Create a map of newVersions by request_id and organization_id for correct matching
const newVersionsMap = new Map(
newVersions.map((v) => [`${v.requestId}-${v.organizationId}`, v])
);

const res = await clickhouseDb.dbInsertClickhouse(
"request_response_rmt",
filteredRequestResponseLogs.flatMap((row, index) => {
const newVersion = newVersions[index];
filteredRequestResponseLogs.flatMap((row) => {
const key = `${row.request_id}-${row.organization_id}`;
const newVersion = newVersionsMap.get(key);

// Skip if no matching newVersion found (shouldn't happen but be safe)
if (!newVersion) {
console.warn(`No matching newVersion for request ${row.request_id}`);
return [];
}

// Merge existing scores with new scores
const combinedScores = {
Expand Down Expand Up @@ -194,29 +206,4 @@ export class ScoreStore extends BaseStore {

return "";
}

public async bumpRequestVersion(
requests: { id: string; organizationId: string }[]
): Promise<Result<UpdatedRequestVersion[], string>> {
const placeholders = requests
.map((_, index) => `($${index * 2 + 1}::uuid, $${index * 2 + 2}::uuid)`)
.join(", ");

const values = requests.flatMap((request) => [
request.organizationId,
request.id,
]);

const query = `
UPDATE request AS r
SET version = r.version + 1
FROM (VALUES ${placeholders}) AS v(org_id, req_id)
WHERE r.helicone_org_id = v.org_id AND r.id = v.req_id
RETURNING r.id, r.version, r.provider, r.helicone_org_id
`;

const result = await dbExecute<UpdatedRequestVersion>(query, values);

return result;
}
}
79 changes: 48 additions & 31 deletions valhalla/jawn/src/lib/stores/request/VersionedRequestStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,30 +49,39 @@ export class VersionedRequestStore {
return ok(result.data);
}

// Updates the propeties column (JSONB) of the request table
// to include {property: value}
// and bumps the version column
private async putPropertyAndBumpVersion(
requestId: string,
property: string,
value: string
) {
return await dbExecute<{
id: string;
version: number;
provider: string;
properties: Record<string, string>;
}>(
// DEPRECATED: This method previously updated the legacy Postgres request table.
// Now fetches from ClickHouse instead to get the existing properties and provider.
private async getRequestFromClickhouse(
requestId: string
): Promise<Result<{
id: string;
version: number;
provider: string;
properties: Record<string, string>;
}, string>> {
const result = await clickhouseDb.dbQuery<RequestResponseRMT>(
`
UPDATE request
SET properties = properties || $1,
version = version + 1
WHERE helicone_org_id = $2
AND id = $3
RETURNING version, id, provider, properties
SELECT *
FROM request_response_rmt
WHERE request_id = {val_0: UUID}
AND organization_id = {val_1: String}
ORDER BY updated_at DESC
LIMIT 1
`,
[{ [property]: value }, this.orgId, requestId]
[requestId, this.orgId]
);

if (result.error || !result.data?.[0]) {
return err("Request not found in ClickHouse");
}

const row = result.data[0];
return ok({
id: row.request_id,
version: 1, // Version is not tracked in ClickHouse, defaulting to 1
provider: row.provider,
properties: row.properties || {},
});
}

// Updates the request_response_rmt table in Clickhouse
Expand Down Expand Up @@ -208,24 +217,32 @@ export class VersionedRequestStore {
property: string,
value: string
): Promise<Result<null, string>> {
const request = await this.putPropertyAndBumpVersion(
requestId,
property,
value
);
// Fetch existing request from ClickHouse
const request = await this.getRequestFromClickhouse(requestId);

if (request.error || !request.data) {
return request;
return err(request.error || "Request not found");
}

const requestInClickhouse = await this.putPropertyIntoClickhouse(
request.data[0]
);
// Merge new property with existing properties
const updatedProperties = {
...request.data.properties,
[property]: value,
};

// Update in ClickHouse
const requestInClickhouse = await this.putPropertyIntoClickhouse({
id: request.data.id,
version: request.data.version,
provider: request.data.provider,
properties: updatedProperties,
});

if (requestInClickhouse.error || !requestInClickhouse.data) {
return requestInClickhouse;
return err(requestInClickhouse.error || "Failed to update in ClickHouse");
}

// Also update legacy tables for backward compatibility
await this.addPropertiesToLegacyTables(requestInClickhouse.data, [
{ key: property, value },
]);
Expand Down
12 changes: 5 additions & 7 deletions valhalla/jawn/src/managers/inputs/InputsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,13 +279,11 @@ export class InputsManager extends BaseManager {
prompt_input_record.auto_prompt_inputs as auto_prompt_inputs,
prompt_input_record.source_request as source_request,
prompt_input_record.prompt_version as prompt_version,
prompt_input_record.created_at as created_at,
response.body as response_body
prompt_input_record.created_at as created_at
FROM prompt_input_record
left join request on prompt_input_record.source_request = request.id
left join response on response.request = request.id
left join helicone_dataset_row hdr on hdr.origin_request_id = prompt_input_record.source_request
WHERE request.helicone_org_id = $1 AND
left join prompts_versions pv on pv.id = prompt_input_record.prompt_version
WHERE pv.organization = $1 AND
prompt_input_record.prompt_version = $2 AND
hdr.dataset_id = $3
`,
Expand Down Expand Up @@ -330,8 +328,8 @@ export class InputsManager extends BaseManager {
prompt_input_record.prompt_version as prompt_version,
prompt_input_record.created_at as created_at
FROM prompt_input_record
left join request on prompt_input_record.source_request = request.id
WHERE request.helicone_org_id = $1 AND
left join prompts_versions pv on pv.id = prompt_input_record.prompt_version
WHERE pv.organization = $1 AND
prompt_input_record.prompt_version = $2
${
random
Expand Down
52 changes: 19 additions & 33 deletions valhalla/jawn/src/managers/request/RequestManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ export class RequestManager extends BaseManager {
return ok(null);
}

// DEPRECATED: This method previously waited for request/response in legacy Postgres tables.
// These tables no longer exist. All data is in ClickHouse now.
private async waitForRequestAndResponse(
heliconeId: string,
organizationId: string
Expand All @@ -184,42 +186,26 @@ export class RequestManager extends BaseManager {
string
>
> {
const maxRetries = 3;

let sleepDuration = 30_000; // 30 seconds
for (let i = 0; i < maxRetries; i++) {
const { data: response, error: responseError } = await dbExecute<{
request: string;
response: string;
}>(
`
SELECT
request.id as request,
response.id as response
FROM request inner join response on request.id = response.request
WHERE request.helicone_org_id = $1
AND request.id = $2
`,
[organizationId, heliconeId]
);

if (responseError) {
console.error("Error fetching response:", responseError);
return err(responseError);
}

if (response && response.length > 0) {
return ok({
requestId: response[0].request,
responseId: response[0].response,
});
}
// Check ClickHouse instead of legacy Postgres tables
const requestClickhouse = await this.getRequestsClickhouse({
filter: {
request_response_rmt: {
request_id: {
equals: heliconeId,
},
},
},
limit: 1,
});

await new Promise((resolve) => setTimeout(resolve, sleepDuration));
sleepDuration *= 2.5; // 30s, 75s, 187.5s
if (requestClickhouse.error || !requestClickhouse.data?.[0]) {
return err("Request not found in ClickHouse.");
}

return { error: "Request not found.", data: null };
return ok({
requestId: requestClickhouse.data[0].request_id,
responseId: requestClickhouse.data[0].response_id ?? "",
});
}
async feedbackRequest(
requestId: string,
Expand Down
Loading