e93507a09c
Lockfile supply-chain audit / lockfile supply-chain audit (push) Has been cancelled
Windows Studio GGUF CI / GPU prebuilt resolves without Visual Studio (push) Has been cancelled
Windows Studio GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2022) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Has been cancelled
Windows Studio Update CI / Studio Updating Tests (push) Has been cancelled
Wheel CI / Wheel build + content sanity + import smoke (push) Has been cancelled
Lint CI / Source lint (Python + shell + YAML + JSON + safety nets) (push) Has been cancelled
MLX CI on Mac M1 / dispatch (push) Has been cancelled
Security audit / advisory audit (pip + npm + cargo) (push) Has been cancelled
Security audit / pip scan-packages :: extras (push) Has been cancelled
Security audit / pip scan-packages :: studio (push) Has been cancelled
Security audit / pip scan-packages :: hf-stack (push) Has been cancelled
Security audit / npm scan-packages (Studio frontend tarballs) (push) Has been cancelled
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Has been cancelled
Security audit / pytest tests/security (push) Has been cancelled
Security audit / npm provenance + new install-script diff (push) Has been cancelled
Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Backend CI / (Python 3.10) (push) Has been cancelled
Backend CI / (Python 3.11) (push) Has been cancelled
Backend CI / (Python 3.12) (push) Has been cancelled
Backend CI / (Python 3.13) (push) Has been cancelled
Backend CI / Repo tests (CPU) (push) Has been cancelled
Frontend CI / Frontend build + bundle sanity (push) Has been cancelled
Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Mac Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Mac Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Has been cancelled
Mac Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Has been cancelled
Mac Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Has been cancelled
Mac Studio Update CI / Studio Updating Tests (push) Has been cancelled
Studio UI CI / Chat UI Tests (push) Has been cancelled
Windows Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Windows Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Update CI / Studio Updating Tests (push) Has been cancelled
Core / Core (HF=default + TRL=default) (push) Has been cancelled
Core / Core (HF=4.57.6 + TRL<1) (push) Has been cancelled
Core / Core (HF=latest + TRL=latest) (push) Has been cancelled
Core / llama.cpp build + smoke (push) Has been cancelled
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Windows Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Windows Studio GGUF CI / JSON, images (push) Has been cancelled
Windows Studio GGUF CI / Studio install + inference without Visual Studio (push) Has been cancelled
Studio export capability / capability (macos-latest) (push) Has been cancelled
Studio export capability / capability (ubuntu-latest) (push) Has been cancelled
Studio export capability / capability (windows-latest) (push) Has been cancelled
Cross-platform parity / parity (macos-latest) (push) Has been cancelled
Cross-platform parity / parity (windows-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Studio load-orchestrator CI / test (push) Has been cancelled
115 lines
4.2 KiB
Python
115 lines
4.2 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
|
|
"""Startup reconciliation must not strip chunks from already-completed docs.
|
|
|
|
A crash can leave an ingestion_jobs row non-terminal after the worker already
|
|
committed the document as ``completed`` with all its chunks. Reconciliation flips
|
|
the orphaned job to ``failed`` but must touch the document (and its chunks) only
|
|
when it actually transitions the document to ``failed`` -- otherwise a completed
|
|
source loses every chunk yet still reports ``completed``, so retrieval finds
|
|
nothing and dedup (``status != 'failed'``) blocks re-ingest.
|
|
"""
|
|
|
|
import math
|
|
|
|
from core.rag import store
|
|
from core.rag.chunking import Chunk
|
|
from storage import rag_db
|
|
|
|
VOCAB = ["alpha", "bravo", "charlie", "delta"]
|
|
|
|
|
|
def _embed(text):
|
|
v = [float(text.lower().count(w)) for w in VOCAB]
|
|
n = math.sqrt(sum(x * x for x in v)) or 1.0
|
|
return [x / n for x in v]
|
|
|
|
|
|
def _chunk(text, index = 0):
|
|
return Chunk(
|
|
text = text,
|
|
token_count = len(text.split()),
|
|
page_number = None,
|
|
source_page_index = 0,
|
|
chunk_index = index,
|
|
page_char_start = 0,
|
|
page_char_end = len(text),
|
|
)
|
|
|
|
|
|
def _add_doc(conn, scope, doc_id, status, texts):
|
|
store.create_document(
|
|
conn, scope = scope, filename = f"{doc_id}.txt", sha256 = doc_id, document_id = doc_id
|
|
)
|
|
store.add_chunks(
|
|
conn, scope, doc_id, [_chunk(t, i) for i, t in enumerate(texts)], [_embed(t) for t in texts]
|
|
)
|
|
store.set_document_status(conn, doc_id, status, num_chunks = len(texts))
|
|
|
|
|
|
def _orphan_job(
|
|
conn,
|
|
doc_id,
|
|
scope,
|
|
status = "running",
|
|
):
|
|
conn.execute(
|
|
"INSERT INTO ingestion_jobs(id, document_id, scope, status, stage, progress, created_at) "
|
|
"VALUES(?,?,?,?,?,?,datetime('now'))",
|
|
(f"job-{doc_id}", doc_id, scope, status, "embedding", 0.5),
|
|
)
|
|
conn.commit()
|
|
|
|
|
|
def _chunk_count(conn, doc_id):
|
|
return conn.execute("SELECT COUNT(*) FROM chunks WHERE document_id=?", (doc_id,)).fetchone()[0]
|
|
|
|
|
|
def _job_status(conn, doc_id):
|
|
return conn.execute(
|
|
"SELECT status FROM ingestion_jobs WHERE id=?", (f"job-{doc_id}",)
|
|
).fetchone()["status"]
|
|
|
|
|
|
def test_completed_doc_keeps_chunks_when_its_job_is_orphaned(rag_conn):
|
|
# Worker finished the document but crashed before retiring the job row.
|
|
_add_doc(rag_conn, "kb_a", "done", "completed", ["alpha bravo", "charlie delta"])
|
|
_orphan_job(rag_conn, "done", "kb_a")
|
|
|
|
assert rag_db.reconcile_orphaned_ingestion_jobs() == 1
|
|
|
|
# Document stays completed with all chunks; dedup still finds it.
|
|
assert store.get_document(rag_conn, "done")["status"] == "completed"
|
|
assert _chunk_count(rag_conn, "done") == 2
|
|
assert store.document_by_hash(rag_conn, "kb_a", "done") == "done"
|
|
# The orphaned job is reconciled to completed (not failed), so the UI's getJob
|
|
# fallback doesn't flag a searchable document as a failed ingestion.
|
|
assert _job_status(rag_conn, "done") == "completed"
|
|
|
|
|
|
def test_in_flight_doc_is_failed_and_its_chunks_dropped(rag_conn):
|
|
# Partial chunks committed, document never marked terminal -> genuine orphan.
|
|
_add_doc(rag_conn, "kb_a", "partial", "processing", ["alpha bravo"])
|
|
_orphan_job(rag_conn, "partial", "kb_a")
|
|
|
|
assert rag_db.reconcile_orphaned_ingestion_jobs() == 1
|
|
|
|
assert store.get_document(rag_conn, "partial")["status"] == "failed"
|
|
assert _chunk_count(rag_conn, "partial") == 0
|
|
# Failed doc is re-ingestible (not deduped).
|
|
assert store.document_by_hash(rag_conn, "kb_a", "partial") is None
|
|
|
|
|
|
def test_already_failed_doc_has_its_chunks_dropped(rag_conn):
|
|
# Worker committed chunks then marked the doc 'failed', but crashed before
|
|
# retiring the job row. Reconcile won't re-flip the doc (already failed), but
|
|
# its chunks must still be purged so they aren't retrievable/citable.
|
|
_add_doc(rag_conn, "kb_a", "failed_doc", "failed", ["alpha bravo"])
|
|
_orphan_job(rag_conn, "failed_doc", "kb_a")
|
|
|
|
assert rag_db.reconcile_orphaned_ingestion_jobs() == 1
|
|
|
|
assert store.get_document(rag_conn, "failed_doc")["status"] == "failed"
|
|
assert _chunk_count(rag_conn, "failed_doc") == 0
|