aline-ai 0.6.0__py3-none-any.whl → 0.6.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
realign/db/sqlite_db.py CHANGED
@@ -442,6 +442,21 @@ class SQLiteDatabase(DatabaseInterface):
442
442
  )
443
443
  conn.commit()
444
444
 
445
+ def update_session_metadata_flag(self, session_id: str, key: str, value: Any) -> None:
446
+ """Update a single key in session metadata JSON."""
447
+ conn = self._get_connection()
448
+ row = conn.execute(
449
+ "SELECT metadata FROM sessions WHERE id = ?", (session_id,)
450
+ ).fetchone()
451
+ if row:
452
+ meta = json.loads(row[0] or "{}")
453
+ meta[key] = value
454
+ conn.execute(
455
+ "UPDATE sessions SET metadata = ?, updated_at = datetime('now') WHERE id = ?",
456
+ (json.dumps(meta), session_id),
457
+ )
458
+ conn.commit()
459
+
445
460
  def backfill_session_total_turns(self) -> int:
446
461
  """Backfill total_turns for all sessions from turns table (V10 migration).
447
462
 
@@ -957,6 +972,7 @@ class SQLiteDatabase(DatabaseInterface):
957
972
  skip_session_summary: bool = False,
958
973
  expected_turns: Optional[int] = None,
959
974
  skip_dedup: bool = False,
975
+ no_track: bool = False,
960
976
  ) -> str:
961
977
  session_id = session_file_path.stem
962
978
  dedupe_key = f"turn:{session_id}:{int(turn_number)}"
@@ -973,6 +989,8 @@ class SQLiteDatabase(DatabaseInterface):
973
989
  payload["expected_turns"] = int(expected_turns)
974
990
  if skip_dedup:
975
991
  payload["skip_dedup"] = True
992
+ if no_track:
993
+ payload["no_track"] = True
976
994
 
977
995
  # For append-only session formats (Claude/Codex/Gemini), a turn is immutable once completed.
978
996
  # Avoid re-running already-done turn jobs on repeated enqueue attempts.
@@ -97,9 +97,24 @@ def update_session_summary_now(db: SQLiteDatabase, session_id: str) -> bool:
97
97
  pass
98
98
  return True
99
99
 
100
+ # Check session metadata for no_track mode
101
+ is_no_track = False
100
102
  try:
101
- # Generate title and summary using LLM
102
- title, summary = _generate_session_summary_llm(turns)
103
+ session = db.get_session_by_id(session_id)
104
+ if session:
105
+ session_meta = getattr(session, "metadata", None) or {}
106
+ is_no_track = bool(session_meta.get("no_track", False))
107
+ except Exception:
108
+ pass
109
+
110
+ try:
111
+ # Skip LLM call for no-track mode
112
+ if is_no_track:
113
+ title, summary = "No Track", "No Track"
114
+ logger.info(f"No-track mode: skipping LLM for session summary {session_id}")
115
+ else:
116
+ # Generate title and summary using LLM
117
+ title, summary = _generate_session_summary_llm(turns)
103
118
 
104
119
  # Update database
105
120
  db.update_session_summary(session_id, title, summary)
realign/watcher_core.py CHANGED
@@ -368,6 +368,7 @@ class DialogueWatcher:
368
368
  session_id = signal_data.get("session_id", "")
369
369
  project_dir = signal_data.get("project_dir", "")
370
370
  transcript_path = signal_data.get("transcript_path", "")
371
+ no_track = bool(signal_data.get("no_track", False))
371
372
 
372
373
  logger.info(f"Stop signal received for session {session_id}")
373
374
  print(f"[Watcher] Stop signal received for {session_id}", file=sys.stderr)
@@ -402,6 +403,7 @@ class DialogueWatcher:
402
403
  workspace_path=project_path,
403
404
  turn_number=target_turn,
404
405
  session_type=self._detect_session_type(session_file),
406
+ no_track=no_track,
405
407
  )
406
408
  except Exception as e:
407
409
  logger.warning(
@@ -462,6 +464,7 @@ class DialogueWatcher:
462
464
  prompt = str(signal_data.get("prompt") or "")
463
465
  transcript_path = str(signal_data.get("transcript_path") or "")
464
466
  project_dir = str(signal_data.get("project_dir") or "")
467
+ no_track = bool(signal_data.get("no_track", False))
465
468
 
466
469
  session_file = None
467
470
  if transcript_path and Path(transcript_path).exists():
@@ -488,6 +491,7 @@ class DialogueWatcher:
488
491
  session_id,
489
492
  prompt,
490
493
  project_dir,
494
+ no_track,
491
495
  )
492
496
 
493
497
  signal_file.unlink(missing_ok=True)
@@ -1318,6 +1322,7 @@ class DialogueWatcher:
1318
1322
  debug_callback: Optional[Callable[[Dict[str, Any]], None]] = None,
1319
1323
  skip_dedup: bool = False,
1320
1324
  skip_session_summary: bool = False,
1325
+ no_track: bool = False,
1321
1326
  ) -> bool:
1322
1327
  """
1323
1328
  Execute commit with DB-backed lease locking to prevent cross-process races.
@@ -1365,6 +1370,7 @@ class DialogueWatcher:
1365
1370
  debug_callback=debug_callback,
1366
1371
  skip_dedup=skip_dedup,
1367
1372
  skip_session_summary=skip_session_summary,
1373
+ no_track=no_track,
1368
1374
  )
1369
1375
  except Exception as e:
1370
1376
  print(f"[Watcher] Commit error: {e}", file=sys.stderr)
@@ -1381,6 +1387,7 @@ class DialogueWatcher:
1381
1387
  debug_callback: Optional[Callable[[Dict[str, Any]], None]] = None,
1382
1388
  skip_dedup: bool = False,
1383
1389
  skip_session_summary: bool = False,
1390
+ no_track: bool = False,
1384
1391
  ) -> bool:
1385
1392
  """
1386
1393
  Perform the actual commit operation to SQLite database.
@@ -1443,7 +1450,7 @@ class DialogueWatcher:
1443
1450
  file_created = datetime.fromtimestamp(
1444
1451
  getattr(file_stat, "st_birthtime", file_stat.st_ctime)
1445
1452
  )
1446
- db.get_or_create_session(
1453
+ session = db.get_or_create_session(
1447
1454
  session_id=session_id,
1448
1455
  session_file_path=session_file,
1449
1456
  session_type=self._detect_session_type(session_file),
@@ -1451,6 +1458,19 @@ class DialogueWatcher:
1451
1458
  workspace_path=str(project_path) if project_path else None,
1452
1459
  )
1453
1460
 
1461
+ # Check no_track from parameter or existing session metadata (polling path)
1462
+ is_no_track = no_track
1463
+ if not is_no_track and session:
1464
+ session_meta = getattr(session, "metadata", None) or {}
1465
+ is_no_track = bool(session_meta.get("no_track", False))
1466
+
1467
+ # Store no_track flag in session metadata if applicable
1468
+ if is_no_track:
1469
+ try:
1470
+ db.update_session_metadata_flag(session_id, "no_track", True)
1471
+ except Exception:
1472
+ pass
1473
+
1454
1474
  takeover_attempt = False
1455
1475
  existing_turn = db.get_turn_by_number(session_id, turn_number)
1456
1476
  if existing_turn and not skip_dedup:
@@ -1517,14 +1537,19 @@ class DialogueWatcher:
1517
1537
  logger.debug(f"Failed to write processing placeholder: {e}")
1518
1538
 
1519
1539
  try:
1520
- # Generate LLM summary with fallback for errors
1521
- llm_result = self._generate_llm_summary(
1522
- session_file,
1523
- turn_number=turn_number,
1524
- turn_content=turn_content,
1525
- user_message=user_message,
1526
- debug_callback=debug_callback,
1527
- )
1540
+ # Skip LLM call for no-track mode
1541
+ if is_no_track:
1542
+ llm_result = ("No Track", None, "No Track", "no", "fine")
1543
+ logger.info(f"No-track mode: skipping LLM for {session_id} turn {turn_number}")
1544
+ else:
1545
+ # Generate LLM summary with fallback for errors
1546
+ llm_result = self._generate_llm_summary(
1547
+ session_file,
1548
+ turn_number=turn_number,
1549
+ turn_content=turn_content,
1550
+ user_message=user_message,
1551
+ debug_callback=debug_callback,
1552
+ )
1528
1553
 
1529
1554
  if not llm_result:
1530
1555
  # LLM summary failed, use error marker to continue commit
@@ -1774,6 +1799,7 @@ class DialogueWatcher:
1774
1799
  session_id: str,
1775
1800
  prompt: str,
1776
1801
  project_dir: str,
1802
+ no_track: bool = False,
1777
1803
  ) -> None:
1778
1804
  """Generate and store a temporary turn title for a newly submitted user prompt."""
1779
1805
  try:
@@ -1802,20 +1828,28 @@ class DialogueWatcher:
1802
1828
  if not user_message:
1803
1829
  user_message = str(group.get("user_message") or "")
1804
1830
 
1805
- turn_content = self._extract_turn_content_by_number(session_file, turn_number)
1806
- result = self._generate_llm_summary(
1807
- session_file,
1808
- turn_number=turn_number,
1809
- turn_content=turn_content,
1810
- user_message=user_message or None,
1811
- session_id=session_id or session_file.stem,
1812
- )
1813
- if not result:
1814
- return
1831
+ # Skip LLM call for no-track mode
1832
+ if no_track:
1833
+ title = "No Track"
1834
+ model_name = None
1835
+ description = "No Track"
1836
+ if_last_task = "no"
1837
+ satisfaction = "fine"
1838
+ else:
1839
+ turn_content = self._extract_turn_content_by_number(session_file, turn_number)
1840
+ result = self._generate_llm_summary(
1841
+ session_file,
1842
+ turn_number=turn_number,
1843
+ turn_content=turn_content,
1844
+ user_message=user_message or None,
1845
+ session_id=session_id or session_file.stem,
1846
+ )
1847
+ if not result:
1848
+ return
1815
1849
 
1816
- title, model_name, description, if_last_task, satisfaction = result
1817
- if not title:
1818
- return
1850
+ title, model_name, description, if_last_task, satisfaction = result
1851
+ if not title:
1852
+ return
1819
1853
 
1820
1854
  from .db import get_database
1821
1855
  from .db.base import TurnRecord
realign/worker_core.py CHANGED
@@ -186,6 +186,7 @@ class AlineWorker:
186
186
  expected_turns_raw = payload.get("expected_turns")
187
187
  expected_turns = int(expected_turns_raw) if expected_turns_raw is not None else None
188
188
  skip_dedup = bool(payload.get("skip_dedup") or False)
189
+ no_track = bool(payload.get("no_track") or False)
189
190
 
190
191
  if not session_id or turn_number <= 0 or not session_file_path:
191
192
  raise ValueError(f"Invalid turn_summary payload: {payload}")
@@ -212,6 +213,7 @@ class AlineWorker:
212
213
  quiet=True,
213
214
  skip_session_summary=skip_session_summary,
214
215
  skip_dedup=skip_dedup,
216
+ no_track=no_track,
215
217
  )
216
218
 
217
219
  if created: