diff --git a/db/migrations/021_writer_contract_no_empty_response.sql b/db/migrations/021_writer_contract_no_empty_response.sql new file mode 100644 index 0000000..3f49cc0 --- /dev/null +++ b/db/migrations/021_writer_contract_no_empty_response.sql @@ -0,0 +1,19 @@ +UPDATE app_settings +SET value_json = to_jsonb($contract$ +OUTPUT SCHEMA (return array matching input order): +{"rewrites":[{"id":123,"category_id":2,"text":"готовый текст без хэштегов","notes":"короткая заметка для редактора или null"}]} + +Rules: +- Input is a JSON object with key "posts" containing accepted posts. +- Input has key "categories" with objects: id, name, tag. +- Each post includes producer_name and producer_tag. producer_name can be a manufacturer, shop, or publishing source. +- Pick exactly one category from the categories list in input and return its numeric id as category_id. +- Do not add links or hashtags to rewritten text. +- Return JSON only. No markdown. No text outside JSON. +- Never return an empty object. Never omit the "rewrites" key. +- Return one rewrite for every input post id. +- The "text" field must be a ready-to-publish Russian post with normal paragraph line breaks encoded as JSON string newlines. +- The "notes" field must be a short editor note in Russian or null. +$contract$::text), +updated_at = NOW() +WHERE key='ai_writer_contract'; diff --git a/src/vk_parser_app/workers/ai_writer.py b/src/vk_parser_app/workers/ai_writer.py index 7614b2b..264185c 100644 --- a/src/vk_parser_app/workers/ai_writer.py +++ b/src/vk_parser_app/workers/ai_writer.py @@ -41,6 +41,7 @@ Rules: - Pick exactly one category from the categories list in input and return its numeric id as category_id. - Do not add links or hashtags to rewritten text. - Return JSON only. No markdown. No text outside JSON. +- Never return an empty object. Never omit the "rewrites" key. - Return one rewrite for every input post id. - The "text" field must be a ready-to-publish Russian post with normal paragraph line breaks encoded as JSON string newlines. - The "notes" field must be a short editor note in Russian or null. @@ -434,6 +435,8 @@ class AIWriterWorker: for p in posts } batch_id = await self.create_batch(provider, model, prompt, payload) + response = None + usage = None try: response, usage = await self.call_ai(provider, model, api_key, api_base, prompt, payload, temperature, timeout) rewrites = validate_rewrites(response, set(post_ids), categories, source_tags_by_id) @@ -443,7 +446,7 @@ class AIWriterWorker: return True except Exception as exc: await self.mark_posts_failed(post_ids, str(exc)) - await self.finish_batch(batch_id, "failed", error=str(exc)) + await self.finish_batch(batch_id, "failed", response=response, error=str(exc), usage=usage) logger.exception("AI writer batch failed: id={} error={}", batch_id, exc) return True