ming commited on
Commit
d112a13
Β·
1 Parent(s): 17499f7

Fix buffer parsing and strengthen brevity constraints

Browse files

Buffer parsing fix:
- Parse remaining buffer content after streamer loop ends
- Fixes issue where {"op": "done"} was left unparsed
- Now properly detects completion signal from model

Brevity improvements:
- Reduced title limit: 8-12 words β†’ 6-10 words MAX
- Reduced key points: 10-15 words β†’ 8-12 words MAX
- Added explicit examples of SHORT titles
- Added CRITICAL BREVITY RULES section with caps warnings
- Emphasized 'BE SHORT!' throughout prompt

Expected results:
- Shorter, punchier titles (6-10 words)
- More concise key points
- Proper detection of completion signal

Files changed (1) hide show
  1. app/services/structured_summarizer.py +47 -15
app/services/structured_summarizer.py CHANGED
@@ -196,10 +196,10 @@ Do NOT add markdown code fences, comments, or explanations.
196
 
197
  Your goal is to produce a BRIEF, CONCISE structured summary of an article in the following logical shape:
198
  {
199
- "title": string, // 8-12 words max
200
- "main_summary": string, // 2-3 sentences max
201
- "key_points": string[], // 3-5 items, each 10-15 words
202
- "category": string, // 1-2 words (e.g. "Tech", "Politics")
203
  "sentiment": string, // one of ["positive", "negative", "neutral"]
204
  "read_time_min": number
205
  }
@@ -210,28 +210,30 @@ Patch formats:
210
 
211
  1) Set or overwrite a scalar field (title, main_summary, category, sentiment, read_time_min):
212
  {"op": "set", "field": "<field_name>", "value": <value>}
213
- Examples:
 
214
  {"op": "set", "field": "title", "value": "AI Model Breakthrough"}
215
- {"op": "set", "field": "category", "value": "Tech"}
216
  {"op": "set", "field": "sentiment", "value": "neutral"}
217
  {"op": "set", "field": "read_time_min", "value": 3}
218
 
219
  2) Append a key point to the key_points array:
220
  {"op": "append", "field": "key_points", "value": "<one concise key fact>"}
221
- Example:
222
- {"op": "append", "field": "key_points", "value": "New 0.5B parameter model optimized for efficiency."}
 
223
 
224
  3) At the very end, output exactly one final line to signal completion:
225
  {"op": "done"}
226
 
227
  Rules:
228
  - You MUST always set all scalar fields before finishing:
229
- 1) First patch: {"op": "set", "field": "title", ...} [8-12 words]
230
- 2) Second patch: {"op": "set", "field": "main_summary", ...} [2-3 sentences]
231
- 3) Third patch: {"op": "set", "field": "category", ...} [1-2 words]
232
  4) Fourth patch: {"op": "set", "field": "sentiment", ...}
233
  5) Fifth patch: {"op": "set", "field": "read_time_min", ...}
234
- 6) Then emit {"op": "append", "field": "key_points", ...} patches (3-5 items, each 10-15 words).
235
  7) Only AFTER all fields are set and 3-5 key_points have been appended,
236
  output exactly one final line: {"op": "done"}.
237
  - NEVER output {"op": "done"} if any of title, main_summary, category,
@@ -239,7 +241,12 @@ Rules:
239
  - Output ONLY these JSON patch objects, one per line (NDJSON).
240
  - Never wrap them in an outer array.
241
  - Do NOT output the final combined object; only the patches.
242
- - CRITICAL: Keep ALL text BRIEF and CONCISE. No verbose explanations."""
 
 
 
 
 
243
 
244
  def _build_style_instruction(self, style: str) -> str:
245
  """Build the style-specific instruction."""
@@ -637,9 +644,34 @@ Rules:
637
  # Wait for generation to complete
638
  generation_thread.join()
639
 
640
- # DEBUG: Log what's left in the buffer (partial line)
641
  if buffer.strip():
642
- logger.warning(f"πŸ—‘οΈ Unparsed buffer remaining: {repr(buffer[:200])}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
643
  else:
644
  logger.info("βœ… Buffer was fully consumed (no partial lines)")
645
 
 
196
 
197
  Your goal is to produce a BRIEF, CONCISE structured summary of an article in the following logical shape:
198
  {
199
+ "title": string, // 6-10 words MAX (e.g. "Couple Found Not Guilty in Homicide Case")
200
+ "main_summary": string, // 2 sentences MAX (be extremely brief)
201
+ "key_points": string[], // 3-5 items, each 8-12 words MAX
202
+ "category": string, // 1-2 words ONLY (e.g. "Crime", "Tech", "Politics")
203
  "sentiment": string, // one of ["positive", "negative", "neutral"]
204
  "read_time_min": number
205
  }
 
210
 
211
  1) Set or overwrite a scalar field (title, main_summary, category, sentiment, read_time_min):
212
  {"op": "set", "field": "<field_name>", "value": <value>}
213
+ Examples (NOTE: Keep titles SHORT):
214
+ {"op": "set", "field": "title", "value": "Couple Acquitted in Homicide Case"}
215
  {"op": "set", "field": "title", "value": "AI Model Breakthrough"}
216
+ {"op": "set", "field": "category", "value": "Crime"}
217
  {"op": "set", "field": "sentiment", "value": "neutral"}
218
  {"op": "set", "field": "read_time_min", "value": 3}
219
 
220
  2) Append a key point to the key_points array:
221
  {"op": "append", "field": "key_points", "value": "<one concise key fact>"}
222
+ Examples (NOTE: Keep each point SHORT):
223
+ {"op": "append", "field": "key_points", "value": "Couple found not guilty of murder charges."}
224
+ {"op": "append", "field": "key_points", "value": "New model optimized for efficiency."}
225
 
226
  3) At the very end, output exactly one final line to signal completion:
227
  {"op": "done"}
228
 
229
  Rules:
230
  - You MUST always set all scalar fields before finishing:
231
+ 1) First patch: {"op": "set", "field": "title", ...} [6-10 words MAX - be SHORT!]
232
+ 2) Second patch: {"op": "set", "field": "main_summary", ...} [2 sentences MAX]
233
+ 3) Third patch: {"op": "set", "field": "category", ...} [1-2 words ONLY]
234
  4) Fourth patch: {"op": "set", "field": "sentiment", ...}
235
  5) Fifth patch: {"op": "set", "field": "read_time_min", ...}
236
+ 6) Then emit {"op": "append", "field": "key_points", ...} patches (3-5 items, each 8-12 words MAX).
237
  7) Only AFTER all fields are set and 3-5 key_points have been appended,
238
  output exactly one final line: {"op": "done"}.
239
  - NEVER output {"op": "done"} if any of title, main_summary, category,
 
241
  - Output ONLY these JSON patch objects, one per line (NDJSON).
242
  - Never wrap them in an outer array.
243
  - Do NOT output the final combined object; only the patches.
244
+ - CRITICAL BREVITY RULES:
245
+ * Title MUST be 6-10 words. If longer, shorten it!
246
+ * Main summary MUST be 2 sentences maximum.
247
+ * Each key point MUST be 8-12 words maximum.
248
+ * Category MUST be 1-2 words only.
249
+ * NO verbose explanations. NO long descriptions. BE BRIEF!"""
250
 
251
  def _build_style_instruction(self, style: str) -> str:
252
  """Build the style-specific instruction."""
 
644
  # Wait for generation to complete
645
  generation_thread.join()
646
 
647
+ # Process any remaining buffer content (might contain {"op": "done"})
648
  if buffer.strip():
649
+ logger.info(f"πŸ“¦ Processing remaining buffer: {repr(buffer[:200])}")
650
+ # Try to parse the remaining buffer as a complete JSON object
651
+ buffer_cleaned = buffer.strip()
652
+ if buffer_cleaned.startswith("{") and "op" in buffer_cleaned:
653
+ try:
654
+ patch = json.loads(buffer_cleaned)
655
+ is_done = self._apply_patch(state, patch)
656
+ if is_done:
657
+ done_received = True
658
+ yield {
659
+ "delta": patch,
660
+ "state": dict(state),
661
+ "done": True,
662
+ "tokens_used": token_count,
663
+ }
664
+ else:
665
+ yield {
666
+ "delta": patch,
667
+ "state": dict(state),
668
+ "done": False,
669
+ "tokens_used": token_count,
670
+ }
671
+ except json.JSONDecodeError:
672
+ logger.warning(f"⚠️ Could not parse remaining buffer as JSON: {buffer_cleaned[:100]}")
673
+ else:
674
+ logger.warning(f"πŸ—‘οΈ Unparsed buffer remaining (not JSON): {repr(buffer[:200])}")
675
  else:
676
  logger.info("βœ… Buffer was fully consumed (no partial lines)")
677