6767monitor_start = time .time ()
6868last_log_offset = 0 # позиция в файле для инкрементального чтения
6969
70+ # Run ID — отличает текущий запуск индексации от предыдущих
71+ current_run_id = None # устанавливается при "Auto-index: starting"
72+
7073
7174def read_log_incremental ():
7275 """Читает только новые строки из лога (быстро, без перечитывания всего файла)."""
@@ -85,6 +88,28 @@ def read_log_incremental():
8588 return new_lines
8689
8790
91+ def reset_run_state ():
92+ """Сбрасывает ВСЁ состояние при новом запуске индексации."""
93+ global total_chunks , done_chunks , phase , phase_start_time
94+ global db_files_written , db_chunks_written , ivf_started
95+ global recent_ema , ema_initialized , not_found_count
96+ global last_done , last_time , lock_status
97+ total_chunks = 0
98+ done_chunks = 0
99+ phase = PHASE_LOADING
100+ phase_start_time = time .time ()
101+ db_files_written = 0
102+ db_chunks_written = 0
103+ ivf_started = False
104+ recent_ema = 0.0
105+ ema_initialized = False
106+ not_found_count = 0
107+ last_done = 0
108+ last_time = time .time ()
109+ lock_status = '🔒 Ожидание...'
110+ phase_metrics .clear ()
111+
112+
88113def read_log_tail (n = 50 ):
89114 """Читает последние N строк лога (для начальной загрузки)."""
90115 if not LOG_FILE .exists ():
@@ -206,6 +231,8 @@ def parse_embed_complete(line):
206231def update_ema (new_speed ):
207232 """Обновляет EMA скорость. Чем больше α, тем быстрее реагирует."""
208233 global recent_ema , ema_initialized
234+ if new_speed <= 0 :
235+ return # не портим EMA нулевыми/отрицательными
209236 if not ema_initialized :
210237 recent_ema = new_speed
211238 ema_initialized = True
@@ -252,7 +279,7 @@ def render():
252279 now = time .time ()
253280 total_elapsed = now - monitor_start
254281
255- print ("📊 МОНИТОР ИНДЕКСАЦИИ v2 " )
282+ print ("📊 МОНИТОР ИНДЕКСАЦИИ v3 " )
256283 print ("─" * 60 )
257284 print (f" Фаза: { phase } " )
258285 print (f" Время: { fmt_time (total_elapsed )} " )
@@ -275,7 +302,7 @@ def render():
275302 print ()
276303
277304 # Эмбеддинг
278- if total_chunks > 0 and phase in (PHASE_EMBED , PHASE_WRITING , PHASE_IVF , PHASE_DONE ):
305+ if total_chunks > 0 and phase in (PHASE_EMBED , PHASE_WRITING , PHASE_IVF ):
279306 pct = done_chunks / total_chunks * 100
280307 print (f" 🧠 Эмбеддинг: { bar (done_chunks , total_chunks )} { pct :.0f} %" )
281308 print (f" { done_chunks } /{ total_chunks } ({ total_chunks - done_chunks } осталось)" )
@@ -333,17 +360,42 @@ def render():
333360# ─── Main loop ────────────────────────────────────────────
334361
335362os .system ('cls' if os .name == 'nt' else 'clear' )
336- print ("📊 МОНИТОР ИНДЕКСАЦИИ v2 " )
363+ print ("📊 МОНИТОР ИНДЕКСАЦИИ v3 " )
337364print ("─" * 60 )
338365print (" Инициализация... чтение лога" )
339366print ("─" * 60 )
340367
341- # Начальная загрузка — последние 100 строк
342- initial_lines = read_log_tail (100 )
343- # Обрабатываем начальные данные
368+ # Начальная загрузка — последние 200 строк
369+ initial_lines = read_log_tail (200 )
370+
371+ # Шаг 1: Находим последний Auto-index: starting — всё ДО него = старый запуск
372+ _last_auto_start_idx = - 1
373+ for _i , line in enumerate (initial_lines ):
374+ if parse_auto_index (line ) == 'start' :
375+ _last_auto_start_idx = _i
376+
377+ if _last_auto_start_idx >= 0 :
378+ initial_lines = initial_lines [_last_auto_start_idx + 1 :]
379+ _found_complete = any (parse_indexing_complete (l ) or parse_ivf_done (l ) for l in initial_lines )
380+ if _found_complete :
381+ # Последний запуск уже завершился — начнём как будто ничего не идёт
382+ phase = PHASE_LOADING
383+
384+ # Шаг 2: Обрабатываем строки текущего запуска
344385for line in initial_lines :
345- if parse_found_files (line ):
346- phase_metrics ['parse_files' ] = parse_found_files (line )
386+ # ★ Сброс при новом auto-index start
387+ ai_init = parse_auto_index (line )
388+ if ai_init == 'start' :
389+ reset_run_state ()
390+ auto_index_status = '🚀 running'
391+ elif ai_init == 'done' :
392+ auto_index_status = '✅ done'
393+ elif ai_init == 'failed' :
394+ auto_index_status = '❌ failed'
395+
396+ ff = parse_found_files (line )
397+ if ff :
398+ phase_metrics ['parse_files' ] = ff
347399
348400 tc = parse_total_chunks (line )
349401 if tc :
@@ -359,25 +411,28 @@ def render():
359411 phase_metrics ['embed' ]['elapsed' ] = embed ['elapsed' ]
360412 update_ema (embed ['inst' ])
361413
362- if parse_db_write (line ):
414+ cw = parse_db_write (line )
415+ if cw :
363416 db_files_written += 1
364- db_chunks_written += parse_db_write ( line )
417+ db_chunks_written += cw
365418
366- if parse_creating_index (line ):
367- phase_metrics ['ivf_chunks' ] = parse_creating_index (line )
419+ ic = parse_creating_index (line )
420+ if ic :
421+ phase_metrics ['ivf_chunks' ] = ic
368422 ivf_started = True
369423
370424 if parse_ivf_done (line ):
371425 phase = PHASE_DONE
372426
373427 ec = parse_embed_complete (line )
374428 if ec :
429+ phase_metrics .setdefault ('embed' , {})
375430 phase_metrics ['embed' ]['final_speed' ] = ec ['speed' ]
376431 phase_metrics ['embed' ]['elapsed' ] = ec ['elapsed' ]
377432
378- ic = parse_indexing_complete (line )
379- if ic :
380- total_chunks = ic
433+ ic2 = parse_indexing_complete (line )
434+ if ic2 :
435+ total_chunks = ic2
381436 phase = PHASE_DONE
382437
383438# Определяем начальную фазу
@@ -399,6 +454,17 @@ def render():
399454
400455 # Обрабатываем новые строки
401456 for line in new_lines :
457+ # ★ ПЕРВЫМ ДЕЛОМ: ловим Auto-index start → сброс всего
458+ ai = parse_auto_index (line )
459+ if ai == 'start' :
460+ reset_run_state ()
461+ current_run_id = time .time () # noqa: F841 — глобальная
462+ auto_index_status = '🚀 running'
463+ elif ai == 'done' :
464+ auto_index_status = '✅ done'
465+ elif ai == 'failed' :
466+ auto_index_status = '❌ failed'
467+
402468 # Парсинг файлов
403469 ff = parse_found_files (line )
404470 if ff :
@@ -423,10 +489,10 @@ def render():
423489 phase_metrics ['embed' ]['last_avg_log' ] = embed ['avg_log' ]
424490 phase_metrics ['embed' ]['elapsed' ] = embed ['elapsed' ]
425491
426- # EMA: обновляем только если прошло достаточно времени
492+ # EMA: только при росте done_chunks (защита от reset)
427493 now = time .time ()
428494 dt = now - last_time
429- if dt > 0.5 and done_chunks != last_done :
495+ if dt > 0.5 and done_chunks > last_done :
430496 instant_speed = (done_chunks - last_done ) / dt
431497 update_ema (instant_speed )
432498 last_done = done_chunks
@@ -438,6 +504,7 @@ def render():
438504 # Embed complete
439505 ec = parse_embed_complete (line )
440506 if ec :
507+ phase_metrics .setdefault ('embed' , {})
441508 phase_metrics ['embed' ]['final_speed' ] = ec ['speed' ]
442509 phase_metrics ['embed' ]['elapsed' ] = ec ['elapsed' ]
443510 if phase == PHASE_EMBED :
@@ -477,22 +544,13 @@ def render():
477544 elif nf == 'self_healed' :
478545 phase_metrics ['self_healed' ] = phase_metrics .get ('self_healed' , 0 ) + 1
479546
480- # Auto-index lifecycle
481- ai = parse_auto_index (line )
482- if ai == 'start' :
483- auto_index_status = '🚀 running'
484- elif ai == 'done' :
485- auto_index_status = '✅ done'
486- elif ai == 'failed' :
487- auto_index_status = '❌ failed'
488-
489547 # IVF done
490548 if parse_ivf_done (line ):
491549 phase = PHASE_DONE
492550
493- # Indexing complete
551+ # Indexing complete — только если мы в активной фазе
494552 ic2 = parse_indexing_complete (line )
495- if ic2 :
553+ if ic2 and phase != PHASE_LOADING :
496554 total_chunks = ic2
497555 phase = PHASE_DONE
498556
0 commit comments