mirror of https://github.com/razor-ai/soup.git
127 lines
4.3 KiB
Python
127 lines
4.3 KiB
Python
"""soup data forge — Synthetic Data Forge CLI (v0.47.0 Part A)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import typer
|
|
from rich.console import Console
|
|
from rich.markup import escape
|
|
from rich.panel import Panel
|
|
|
|
console = Console()
|
|
|
|
|
|
def _default_judge(prompt: str) -> dict:
|
|
"""Deterministic offline judge used when no provider is configured.
|
|
|
|
Live judge integration (Ollama / Anthropic / vLLM via v0.20.0
|
|
providers) is wired through a ``--judge-provider`` flag in v0.47.1.
|
|
"""
|
|
# Echo a short stand-in answer; the active-pruning step will still
|
|
# score these against the source chunk so we don't keep duplicates.
|
|
head = prompt.splitlines()[0] if prompt else ""
|
|
return {"text": f"Synthesised answer (offline): {head[:80]}"}
|
|
|
|
|
|
def forge(
|
|
docs: str = typer.Option(
|
|
..., "--docs", "-d",
|
|
help="Directory of documents (txt/md/json/jsonl, under cwd).",
|
|
),
|
|
task: str = typer.Option(
|
|
"sft", "--task", "-t",
|
|
help="Forge task: sft | preference | tool.",
|
|
),
|
|
target_rows: int = typer.Option(
|
|
100, "--target-rows", "-r",
|
|
min=1, max=1_000_000,
|
|
help="Maximum number of synthesised rows.",
|
|
),
|
|
teacher: str = typer.Option(
|
|
"local-judge", "--teacher",
|
|
help="Label recorded in provenance for the judge backend.",
|
|
),
|
|
output: str = typer.Option(
|
|
"forge_dataset.jsonl", "--output", "-o",
|
|
help="JSONL output path (under cwd).",
|
|
),
|
|
provenance: str = typer.Option(
|
|
"forge_provenance.json", "--provenance",
|
|
help="Provenance manifest output path (under cwd).",
|
|
),
|
|
uncertainty_threshold: float = typer.Option(
|
|
0.0, "--uncertainty-threshold",
|
|
min=0.0, max=1.0,
|
|
help="Minimum Jaccard-distance score required to keep a synthesised row.",
|
|
),
|
|
max_chunk_chars: int = typer.Option(
|
|
1000, "--max-chunk-chars",
|
|
min=1, max=64_000,
|
|
help="Maximum chars per document chunk before judge call.",
|
|
),
|
|
):
|
|
"""Run the multi-stage synthetic data pipeline with provenance."""
|
|
from soup_cli.utils.data_forge import (
|
|
build_forge_plan,
|
|
discover_documents,
|
|
synthesise_forge_rows,
|
|
write_forge_dataset,
|
|
write_provenance,
|
|
)
|
|
|
|
try:
|
|
plan = build_forge_plan(
|
|
docs_dir=docs,
|
|
task=task,
|
|
target_rows=target_rows,
|
|
teacher=teacher,
|
|
uncertainty_threshold=uncertainty_threshold,
|
|
)
|
|
# Discover once; plan.num_docs already attests the directory is
|
|
# non-empty and contained, so this scan is the cached enumeration.
|
|
doc_paths = discover_documents(docs)
|
|
except (TypeError, ValueError, FileNotFoundError) as exc:
|
|
console.print(f"[red]Plan failed:[/] {escape(str(exc))}")
|
|
raise typer.Exit(1) from exc
|
|
|
|
rows = synthesise_forge_rows(
|
|
doc_paths,
|
|
task=plan.task,
|
|
target_rows=plan.target_rows,
|
|
judge=_default_judge,
|
|
teacher=plan.teacher,
|
|
uncertainty_threshold=plan.uncertainty_threshold,
|
|
max_chunk_chars=max_chunk_chars,
|
|
)
|
|
|
|
if not rows:
|
|
console.print(
|
|
"[yellow]No rows produced.[/] Try a lower --uncertainty-threshold "
|
|
"or check your --docs directory."
|
|
)
|
|
raise typer.Exit(1)
|
|
|
|
try:
|
|
dataset_path = write_forge_dataset(rows, output)
|
|
manifest_path = write_provenance(rows, provenance)
|
|
except (TypeError, ValueError) as exc:
|
|
console.print(f"[red]Write failed:[/] {escape(str(exc))}")
|
|
raise typer.Exit(1) from exc
|
|
|
|
console.print(
|
|
Panel(
|
|
f"Task: [bold]{escape(plan.task)}[/]\n"
|
|
f"Docs scanned:[bold] {plan.num_docs}[/]\n"
|
|
f"Rows kept: [bold]{len(rows)}[/] / target {plan.target_rows}\n"
|
|
f"Teacher: [bold]{escape(plan.teacher)}[/]\n"
|
|
f"Threshold: [bold]{plan.uncertainty_threshold:.2f}[/]\n"
|
|
f"Dataset: [bold]{escape(dataset_path)}[/]\n"
|
|
f"Provenance: [bold]{escape(manifest_path)}[/]",
|
|
title="[bold green]Data Forge — synth complete[/]",
|
|
)
|
|
)
|
|
console.print(
|
|
"[dim]The built-in judge is the deterministic offline stub. "
|
|
"Live Ollama / Anthropic / vLLM judge providers wire through "
|
|
"--judge-provider in v0.47.1.[/]"
|
|
)
|