feat(tools-pve/asr-pipeline): SQLite model config, task delete/rerefine, multi-recording review, lexicon CRUD
Build image / build (push) Canceled after 0s
Build ASR Pipeline / image (push) Canceled after 0s

This commit is contained in:
Codex
2026-11-01 23:14:31 -08:00
parent 2367c5eb4e
commit be589ff5e0
7 changed files with 2064 additions and 340 deletions
@@ -4,6 +4,7 @@ import fcntl
import hashlib
import json
import os
import re
import shutil
import sqlite3
import time
@@ -11,9 +12,10 @@ import uuid
from pathlib import Path
from asrpipe import graph, lexicon, providers, refine, review, revise, vault
from asrpipe.config import CFG
from . import db
from .config import (
DATA, DEFAULT_PROVIDER, DRAFTS, FAILED, INBOX, LOGS, MAX_ATTEMPTS,
DATA, DRAFTS, FAILED, INBOX, LOGS, MAX_ATTEMPTS,
POLL_SECONDS, PROCESSED, PROCESSING, PUBLIC_URL, REVISION_QUEUE,
STABLE_SECONDS, STATE, SUPPORTED, TRANSCRIPTS, ensure_dirs,
)
@@ -50,7 +52,7 @@ def transcribe(
) -> tuple[Path, providers.TranscribeResult]:
res = providers.transcribe_audio(
audio,
provider=provider or DEFAULT_PROVIDER,
provider=provider or CFG.asr_provider,
model=model,
title=title,
user_context=user_context,
@@ -71,7 +73,7 @@ def transcribe(
def process_audio(source: Path, job: dict) -> None:
job_id = job["id"]
attempts = int(job.get("attempts") or 0) + 1
prov = providers.normalize_provider_name(job.get("asr_provider") or DEFAULT_PROVIDER)
prov = providers.normalize_provider_name(job.get("asr_provider") or CFG.asr_provider)
model = str(job.get("asr_model") or "")
digest = sha256(source)
prov_label = providers.PROVIDERS.get(prov, {}).get("label", prov)
@@ -120,6 +122,47 @@ def process_audio(source: Path, job: dict) -> None:
f"{PUBLIC_URL}/jobs/{job_id}/review/")
def rerefine_job(job: dict) -> None:
"""Re-run LLM refinement on an existing raw transcript without re-running ASR."""
job_id = job["id"]
transcript = Path(str(job.get("transcript") or ""))
if not transcript.is_file():
raise FileNotFoundError(f"transcript file missing for job {job_id}: {transcript}")
raw = transcript.read_text(encoding="utf-8", errors="ignore")
intake = {
"job_id": job_id,
"title": job.get("title", ""),
"context": job.get("context", ""),
"source": job.get("source", ""),
"asr_provider": job.get("asr_provider") or CFG.asr_provider,
"asr_model": job.get("asr_model") or "",
}
intake_line = f"<!-- intake: {json.dumps(intake, ensure_ascii=False)} -->"
if re.match(r"^<!--\s*intake:\s*\{.*?\}\s*-->", raw):
raw = re.sub(r"^<!--\s*intake:\s*\{.*?\}\s*-->", intake_line, raw, count=1)
else:
raw = intake_line + "\n" + raw
transcript.write_text(raw, encoding="utf-8")
db.update_job(job_id, "refining", "正在基于现有转写重新生成精修草稿")
review.delete_for_job(
transcript_path=str(transcript),
draft_path=str(job.get("draft") or ""),
job_id=job_id,
)
result = refine.refine(
raw, date=time.strftime("%Y-%m-%d"),
transcript_path=str(transcript), queue_review=True,
)
draft = DRAFTS / f"{job_id}.md"
vault.write_draft_file(draft, result.draft)
questions = review.enqueue_draft(str(transcript), draft)
db.update_job(
job_id, "review", f"整理稿已重新生成,{questions} 个问题待确认",
transcript=str(transcript), draft=str(draft),
)
def claim(path: Path) -> tuple[Path, dict]:
job = db.find_by_stored(path.name)
if not job:
@@ -127,7 +170,7 @@ def claim(path: Path) -> tuple[Path, dict]:
job = db.create_job(
job_id, path.name, path.name, title=path.stem,
size=path.stat().st_size, source="ssh",
asr_provider=DEFAULT_PROVIDER,
asr_provider=CFG.asr_provider,
)
target = PROCESSING / path.name
if target.exists():
@@ -164,8 +207,15 @@ def process_revisions() -> None:
for marker in sorted(REVISION_QUEUE.glob("*.json")):
try:
payload = json.loads(marker.read_text(encoding="utf-8"))
draft = Path(payload["draft"])
action = str(payload.get("action") or "revise")
job_id = str(payload.get("job_id") or "")
if action == "rerefine":
job = db.get_job(job_id)
if job:
rerefine_job(job)
marker.unlink(missing_ok=True)
continue
draft = Path(payload["draft"])
db.update_job(job_id, "publishing", "正在应用人工确认")
revise.revise(draft)
current = db.get_job(job_id) or {}
@@ -181,6 +231,14 @@ def process_revisions() -> None:
marker.unlink(missing_ok=True)
except Exception as exc:
log(f"revision marker={marker.name} failed type={type(exc).__name__} err={exc}")
try:
payload = json.loads(marker.read_text(encoding="utf-8"))
jid = str(payload.get("job_id") or "")
if jid:
db.update_job(jid, "failed", f"精修/修订失败:{exc}", error_code=type(exc).__name__)
except Exception:
pass
marker.unlink(missing_ok=True)
def recover() -> None: