feat(t208): close candidate decision feedback loop
This commit is contained in:
@@ -0,0 +1,365 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE execution_result_requests RENAME TO execution_result_requests_v5;
|
||||
|
||||
CREATE TABLE execution_result_requests (
|
||||
user_id TEXT NOT NULL
|
||||
REFERENCES users(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
device_id TEXT NOT NULL
|
||||
REFERENCES devices(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
operation TEXT NOT NULL
|
||||
CHECK (
|
||||
operation IN (
|
||||
'EVENTS',
|
||||
'EVIDENCE',
|
||||
'CANDIDATES',
|
||||
'HUMAN_REVIEW',
|
||||
'COMPLETE',
|
||||
'FAIL'
|
||||
)
|
||||
),
|
||||
idempotency_key TEXT NOT NULL
|
||||
CHECK (
|
||||
length(trim(idempotency_key)) > 0
|
||||
AND length(CAST(idempotency_key AS BLOB)) <= 128
|
||||
),
|
||||
request_sha256 TEXT NOT NULL
|
||||
CHECK (
|
||||
length(request_sha256) = 64
|
||||
AND request_sha256 NOT GLOB '*[^0-9a-f]*'
|
||||
),
|
||||
claim_token_sha256 TEXT NOT NULL
|
||||
CHECK (
|
||||
length(claim_token_sha256) = 64
|
||||
AND claim_token_sha256 NOT GLOB '*[^0-9a-f]*'
|
||||
),
|
||||
task_id TEXT NOT NULL
|
||||
REFERENCES purchase_tasks(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
execution_id TEXT NOT NULL
|
||||
REFERENCES task_executions(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
resource_id TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
PRIMARY KEY (user_id, device_id, operation, idempotency_key)
|
||||
);
|
||||
|
||||
INSERT INTO execution_result_requests (
|
||||
user_id,
|
||||
device_id,
|
||||
operation,
|
||||
idempotency_key,
|
||||
request_sha256,
|
||||
claim_token_sha256,
|
||||
task_id,
|
||||
execution_id,
|
||||
resource_id,
|
||||
created_at
|
||||
)
|
||||
SELECT
|
||||
user_id,
|
||||
device_id,
|
||||
operation,
|
||||
idempotency_key,
|
||||
request_sha256,
|
||||
claim_token_sha256,
|
||||
task_id,
|
||||
execution_id,
|
||||
resource_id,
|
||||
created_at
|
||||
FROM execution_result_requests_v5;
|
||||
|
||||
DROP TABLE execution_result_requests_v5;
|
||||
|
||||
CREATE TABLE candidate_search_runs (
|
||||
execution_id TEXT PRIMARY KEY NOT NULL
|
||||
REFERENCES task_executions(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
task_id TEXT NOT NULL
|
||||
REFERENCES purchase_tasks(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
task_content_sha256 TEXT NOT NULL
|
||||
CHECK (
|
||||
length(task_content_sha256) = 64
|
||||
AND task_content_sha256 NOT GLOB '*[^0-9a-f]*'
|
||||
),
|
||||
execution_mode TEXT NOT NULL
|
||||
CHECK (execution_mode IN ('MANUAL_FIRST', 'AI_ASSISTED')),
|
||||
search_query TEXT NOT NULL
|
||||
CHECK (
|
||||
length(trim(search_query)) > 0
|
||||
AND length(CAST(search_query AS BLOB)) <= 512
|
||||
),
|
||||
app_version TEXT,
|
||||
android_version TEXT,
|
||||
pdd_version TEXT,
|
||||
started_at TEXT NOT NULL,
|
||||
received_at TEXT NOT NULL,
|
||||
observation_count INTEGER NOT NULL
|
||||
CHECK (observation_count BETWEEN 0 AND 5),
|
||||
collection_complete INTEGER NOT NULL
|
||||
CHECK (collection_complete = 1),
|
||||
received_after_execution_expiry INTEGER NOT NULL DEFAULT 0
|
||||
CHECK (received_after_execution_expiry IN (0, 1))
|
||||
);
|
||||
|
||||
CREATE TABLE candidate_observations (
|
||||
execution_id TEXT NOT NULL
|
||||
REFERENCES candidate_search_runs(execution_id)
|
||||
ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
task_id TEXT NOT NULL
|
||||
REFERENCES purchase_tasks(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
ordinal INTEGER NOT NULL
|
||||
CHECK (ordinal BETWEEN 1 AND 5),
|
||||
title TEXT NOT NULL
|
||||
CHECK (
|
||||
length(trim(title)) > 0
|
||||
AND length(CAST(title AS BLOB)) <= 512
|
||||
),
|
||||
sku_text TEXT NOT NULL
|
||||
CHECK (length(CAST(sku_text AS BLOB)) <= 512),
|
||||
price_text TEXT NOT NULL
|
||||
CHECK (length(CAST(price_text AS BLOB)) <= 64),
|
||||
product_url TEXT NOT NULL
|
||||
CHECK (length(CAST(product_url AS BLOB)) <= 2048),
|
||||
image_url TEXT NOT NULL
|
||||
CHECK (length(CAST(image_url AS BLOB)) <= 2048),
|
||||
evidence_asset_ids_json TEXT NOT NULL
|
||||
CHECK (length(CAST(evidence_asset_ids_json AS BLOB)) <= 4096),
|
||||
collection_status TEXT NOT NULL
|
||||
CHECK (collection_status IN ('COMPLETE', 'PARTIAL')),
|
||||
observed_at TEXT NOT NULL,
|
||||
PRIMARY KEY (execution_id, ordinal)
|
||||
);
|
||||
|
||||
CREATE TABLE model_runs (
|
||||
execution_id TEXT PRIMARY KEY NOT NULL
|
||||
REFERENCES candidate_search_runs(execution_id)
|
||||
ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
provider_id TEXT NOT NULL
|
||||
CHECK (
|
||||
length(trim(provider_id)) > 0
|
||||
AND length(CAST(provider_id AS BLOB)) <= 128
|
||||
),
|
||||
model TEXT NOT NULL
|
||||
CHECK (
|
||||
length(trim(model)) > 0
|
||||
AND length(CAST(model AS BLOB)) <= 256
|
||||
),
|
||||
prompt_version TEXT NOT NULL
|
||||
CHECK (
|
||||
length(trim(prompt_version)) > 0
|
||||
AND length(CAST(prompt_version AS BLOB)) <= 128
|
||||
),
|
||||
schema_version INTEGER NOT NULL
|
||||
CHECK (schema_version BETWEEN 1 AND 1000),
|
||||
recommendation_threshold REAL NOT NULL
|
||||
CHECK (recommendation_threshold BETWEEN 0 AND 1),
|
||||
request_sha256 TEXT NOT NULL
|
||||
CHECK (
|
||||
length(request_sha256) = 64
|
||||
AND request_sha256 NOT GLOB '*[^0-9a-f]*'
|
||||
),
|
||||
result_sha256 TEXT NOT NULL
|
||||
CHECK (
|
||||
length(result_sha256) = 64
|
||||
AND result_sha256 NOT GLOB '*[^0-9a-f]*'
|
||||
),
|
||||
duration_millis INTEGER,
|
||||
input_tokens INTEGER,
|
||||
output_tokens INTEGER,
|
||||
cost_microunits INTEGER,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE candidate_evaluations (
|
||||
execution_id TEXT NOT NULL,
|
||||
candidate_ordinal INTEGER NOT NULL,
|
||||
decision TEXT NOT NULL
|
||||
CHECK (decision IN ('REVIEW', 'REJECT', 'MANUAL_REQUIRED')),
|
||||
score REAL NOT NULL
|
||||
CHECK (score BETWEEN 0 AND 1),
|
||||
confidence REAL NOT NULL
|
||||
CHECK (confidence BETWEEN 0 AND 1),
|
||||
matched_json TEXT NOT NULL,
|
||||
missing_or_uncertain_json TEXT NOT NULL,
|
||||
rejection_reasons_json TEXT NOT NULL,
|
||||
hard_constraints_json TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
PRIMARY KEY (execution_id, candidate_ordinal),
|
||||
FOREIGN KEY (execution_id)
|
||||
REFERENCES model_runs(execution_id)
|
||||
ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (execution_id, candidate_ordinal)
|
||||
REFERENCES candidate_observations(execution_id, ordinal)
|
||||
ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE candidate_recommendations (
|
||||
execution_id TEXT PRIMARY KEY NOT NULL,
|
||||
candidate_ordinal INTEGER NOT NULL,
|
||||
conclusion TEXT NOT NULL
|
||||
CHECK (conclusion = 'SUGGESTED'),
|
||||
policy_version TEXT NOT NULL
|
||||
CHECK (
|
||||
length(trim(policy_version)) > 0
|
||||
AND length(CAST(policy_version AS BLOB)) <= 128
|
||||
),
|
||||
reasons_json TEXT NOT NULL
|
||||
CHECK (length(CAST(reasons_json AS BLOB)) <= 4096),
|
||||
created_at TEXT NOT NULL,
|
||||
FOREIGN KEY (execution_id, candidate_ordinal)
|
||||
REFERENCES candidate_observations(execution_id, ordinal)
|
||||
ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE candidate_human_reviews (
|
||||
id TEXT PRIMARY KEY NOT NULL
|
||||
CHECK (length(id) = 36),
|
||||
execution_id TEXT NOT NULL
|
||||
REFERENCES candidate_search_runs(execution_id)
|
||||
ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
task_id TEXT NOT NULL
|
||||
REFERENCES purchase_tasks(id) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
version INTEGER NOT NULL
|
||||
CHECK (version > 0),
|
||||
reason_schema_version INTEGER NOT NULL
|
||||
CHECK (reason_schema_version = 1),
|
||||
outcome TEXT NOT NULL
|
||||
CHECK (
|
||||
outcome IN (
|
||||
'CANDIDATE_ACCEPTED',
|
||||
'CANDIDATE_REJECTED',
|
||||
'NO_MATCH',
|
||||
'MANUAL_REQUIRED'
|
||||
)
|
||||
),
|
||||
selected_candidate_ordinal INTEGER,
|
||||
primary_reason_code TEXT NOT NULL,
|
||||
note TEXT NOT NULL
|
||||
CHECK (length(CAST(note AS BLOB)) <= 800),
|
||||
supersedes_review_id TEXT UNIQUE
|
||||
REFERENCES candidate_human_reviews(id)
|
||||
ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
actor_user_id TEXT NOT NULL
|
||||
REFERENCES users(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
actor_device_id TEXT
|
||||
REFERENCES devices(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
created_at TEXT NOT NULL,
|
||||
received_after_execution_expiry INTEGER NOT NULL DEFAULT 0
|
||||
CHECK (received_after_execution_expiry IN (0, 1)),
|
||||
UNIQUE (execution_id, version),
|
||||
FOREIGN KEY (execution_id, selected_candidate_ordinal)
|
||||
REFERENCES candidate_observations(execution_id, ordinal)
|
||||
ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
CREATE INDEX candidate_human_reviews_execution_version_idx
|
||||
ON candidate_human_reviews (execution_id, version DESC);
|
||||
|
||||
CREATE TABLE candidate_human_review_items (
|
||||
review_id TEXT NOT NULL
|
||||
REFERENCES candidate_human_reviews(id)
|
||||
ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
candidate_ordinal INTEGER NOT NULL,
|
||||
label TEXT NOT NULL
|
||||
CHECK (label IN ('ACCEPT', 'REJECT')),
|
||||
primary_reason_code TEXT NOT NULL,
|
||||
note TEXT NOT NULL
|
||||
CHECK (length(CAST(note AS BLOB)) <= 800),
|
||||
PRIMARY KEY (review_id, candidate_ordinal)
|
||||
);
|
||||
|
||||
CREATE TABLE candidate_human_review_reasons (
|
||||
review_id TEXT NOT NULL,
|
||||
candidate_ordinal INTEGER NOT NULL,
|
||||
reason_code TEXT NOT NULL,
|
||||
PRIMARY KEY (review_id, candidate_ordinal, reason_code),
|
||||
FOREIGN KEY (review_id, candidate_ordinal)
|
||||
REFERENCES candidate_human_review_items(review_id, candidate_ordinal)
|
||||
ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
CREATE TEMP TABLE candidate_decisions_v6_down_guard (
|
||||
allowed INTEGER NOT NULL
|
||||
CHECK (allowed = 1)
|
||||
);
|
||||
|
||||
INSERT INTO candidate_decisions_v6_down_guard (allowed)
|
||||
SELECT CASE
|
||||
WHEN EXISTS (SELECT 1 FROM candidate_search_runs)
|
||||
OR EXISTS (SELECT 1 FROM candidate_human_reviews)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM execution_result_requests
|
||||
WHERE operation = 'HUMAN_REVIEW'
|
||||
)
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END;
|
||||
|
||||
DROP TABLE candidate_decisions_v6_down_guard;
|
||||
DROP TABLE candidate_human_review_reasons;
|
||||
DROP TABLE candidate_human_review_items;
|
||||
DROP INDEX candidate_human_reviews_execution_version_idx;
|
||||
DROP TABLE candidate_human_reviews;
|
||||
DROP TABLE candidate_recommendations;
|
||||
DROP TABLE candidate_evaluations;
|
||||
DROP TABLE model_runs;
|
||||
DROP TABLE candidate_observations;
|
||||
DROP TABLE candidate_search_runs;
|
||||
|
||||
ALTER TABLE execution_result_requests RENAME TO execution_result_requests_v6;
|
||||
|
||||
CREATE TABLE execution_result_requests (
|
||||
user_id TEXT NOT NULL
|
||||
REFERENCES users(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
device_id TEXT NOT NULL
|
||||
REFERENCES devices(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
operation TEXT NOT NULL
|
||||
CHECK (operation IN ('EVENTS', 'EVIDENCE', 'CANDIDATES', 'COMPLETE', 'FAIL')),
|
||||
idempotency_key TEXT NOT NULL
|
||||
CHECK (
|
||||
length(trim(idempotency_key)) > 0
|
||||
AND length(CAST(idempotency_key AS BLOB)) <= 128
|
||||
),
|
||||
request_sha256 TEXT NOT NULL
|
||||
CHECK (
|
||||
length(request_sha256) = 64
|
||||
AND request_sha256 NOT GLOB '*[^0-9a-f]*'
|
||||
),
|
||||
claim_token_sha256 TEXT NOT NULL
|
||||
CHECK (
|
||||
length(claim_token_sha256) = 64
|
||||
AND claim_token_sha256 NOT GLOB '*[^0-9a-f]*'
|
||||
),
|
||||
task_id TEXT NOT NULL
|
||||
REFERENCES purchase_tasks(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
execution_id TEXT NOT NULL
|
||||
REFERENCES task_executions(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
|
||||
resource_id TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
PRIMARY KEY (user_id, device_id, operation, idempotency_key)
|
||||
);
|
||||
|
||||
INSERT INTO execution_result_requests (
|
||||
user_id,
|
||||
device_id,
|
||||
operation,
|
||||
idempotency_key,
|
||||
request_sha256,
|
||||
claim_token_sha256,
|
||||
task_id,
|
||||
execution_id,
|
||||
resource_id,
|
||||
created_at
|
||||
)
|
||||
SELECT
|
||||
user_id,
|
||||
device_id,
|
||||
operation,
|
||||
idempotency_key,
|
||||
request_sha256,
|
||||
claim_token_sha256,
|
||||
task_id,
|
||||
execution_id,
|
||||
resource_id,
|
||||
created_at
|
||||
FROM execution_result_requests_v6;
|
||||
|
||||
DROP TABLE execution_result_requests_v6;
|
||||
Reference in New Issue
Block a user