51 lines
4.4 KiB
SQL
51 lines
4.4 KiB
SQL
ALTER TABLE raw_posts
|
|
ADD COLUMN IF NOT EXISTS qualification_status TEXT,
|
|
ADD COLUMN IF NOT EXISTS qualification_score INTEGER,
|
|
ADD COLUMN IF NOT EXISTS qualification_decision TEXT,
|
|
ADD COLUMN IF NOT EXISTS qualification_reason TEXT,
|
|
ADD COLUMN IF NOT EXISTS qualification_model TEXT,
|
|
ADD COLUMN IF NOT EXISTS qualification_prompt_hash TEXT,
|
|
ADD COLUMN IF NOT EXISTS qualification_batch_id BIGINT,
|
|
ADD COLUMN IF NOT EXISTS qualified_at TIMESTAMPTZ;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_raw_posts_qualification_claim
|
|
ON raw_posts(status, qualification_status, created_at)
|
|
WHERE status='storage_ready';
|
|
|
|
CREATE TABLE IF NOT EXISTS ai_qualification_batches (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
status TEXT NOT NULL DEFAULT 'processing',
|
|
provider TEXT NOT NULL,
|
|
model TEXT NOT NULL,
|
|
prompt_hash TEXT NOT NULL,
|
|
posts_count INTEGER NOT NULL DEFAULT 0,
|
|
accepted_count INTEGER NOT NULL DEFAULT 0,
|
|
rejected_count INTEGER NOT NULL DEFAULT 0,
|
|
maybe_count INTEGER NOT NULL DEFAULT 0,
|
|
request_json JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
response_json JSONB,
|
|
error TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
completed_at TIMESTAMPTZ
|
|
);
|
|
|
|
INSERT INTO worker_controls(name, enabled, settings_json)
|
|
VALUES ('ai-qualifier', FALSE, '{}'::jsonb)
|
|
ON CONFLICT (name) DO NOTHING;
|
|
|
|
INSERT INTO app_settings(key, value_json, value_type, title, description, category)
|
|
VALUES
|
|
('ai_qualifier_enabled', 'false'::jsonb, 'bool', 'AI qualifier enabled', 'Enable qualification requests to AI API.', 'AI Qualifier'),
|
|
('ai_qualifier_provider', '"openrouter"'::jsonb, 'str', 'Provider', 'LiteLLM provider: openrouter, openai, anthropic, gemini, openai_compatible.', 'AI Qualifier'),
|
|
('ai_qualifier_model', '"openrouter/openai/gpt-4.1-mini"'::jsonb, 'str', 'Model', 'Model name in LiteLLM format.', 'AI Qualifier'),
|
|
('ai_qualifier_api_key', '""'::jsonb, 'secret', 'API key', 'AI provider API key.', 'AI Qualifier'),
|
|
('ai_qualifier_api_base', '""'::jsonb, 'str', 'API base', 'Optional base URL for OpenAI-compatible providers.', 'AI Qualifier'),
|
|
('ai_qualifier_batch_size', '30'::jsonb, 'int', 'Batch size', 'How many raw posts to classify in one request.', 'AI Qualifier'),
|
|
('ai_qualifier_min_score', '7'::jsonb, 'int', 'Minimum accepted score', 'Posts with score >= this value are accepted.', 'AI Qualifier'),
|
|
('ai_qualifier_interval_sec', '60'::jsonb, 'int', 'Idle interval, sec', 'Pause between qualifier checks.', 'AI Qualifier'),
|
|
('ai_qualifier_max_text_chars', '2000'::jsonb, 'int', 'Max text chars per post', 'Trim raw text before sending it to AI.', 'AI Qualifier'),
|
|
('ai_qualifier_temperature', '0'::jsonb, 'float', 'Temperature', 'AI sampling temperature for stable classification.', 'AI Qualifier'),
|
|
('ai_qualifier_timeout_sec', '120'::jsonb, 'int', 'Timeout, sec', 'AI request timeout.', 'AI Qualifier'),
|
|
('ai_qualifier_prompt', '"Ты квалификатор постов для проекта, который собирает посты производителей и магазинов милитари, тактической и airsoft-снаряги.\\n\\nОцени каждый raw-пост по пригодности для дальнейшего рерайта и публикации.\\n\\nПодходит: товарные посты, новинки, обзоры, производство, экипировка, тактическая одежда, броня, подсумки, рюкзаки, разгрузки, ремни, патчи, аксессуары, airsoft/страйкбольная снаряга, полезные анонсы производителя.\\n\\nМусор: мемы, поздравления, вакансии, политика, бытовуха, пустые посты, посты без конкретного товара/снаряжения, розыгрыши без предметного контента, чистая реклама скидки без понятного товара, нерелевантные новости.\\n\\nВерни только JSON-объект по схеме: {\\\"results\\\":[{\\\"id\\\":123,\\\"score\\\":8,\\\"decision\\\":\\\"accepted\\\",\\\"reason\\\":\\\"короткая причина\\\"}]}. score — целое число от 1 до 10. decision: accepted, rejected или maybe. Не добавляй markdown и пояснения вне JSON."'::jsonb, 'text', 'Prompt', 'Main qualification prompt.', 'AI Qualifier')
|
|
ON CONFLICT (key) DO NOTHING;
|