Download mindxtrain/data/synth.py from PYTHAI/mindXtrain: direct link, hf CLI and curl.
- Browser
- Download file 3.66 kB
-
https://huggingface.co/PYTHAI/mindXtrain/resolve/main/mindxtrain/data/synth.py
- Command line
-
hf download hf://PYTHAI/mindXtrain/mindxtrain/data/synth.py
-
curl -L -o synth.py https://huggingface.co/PYTHAI/mindXtrain/resolve/main/mindxtrain/data/synth.py
3.66 kB
| """Synthetic data generation via teacher models. | |
| Drives bulk-prompt rollouts through an OpenAI-compatible teacher endpoint | |
| (default vLLM-ROCm at `MINDXTRAIN_TEACHER_BASE_URL`). Useful for filling | |
| gaps in the curated corpus (style transfer, persona conditioning, tool-call | |
| trajectories). | |
| """ | |
| from __future__ import annotations | |
| import os | |
| from collections.abc import Iterable, Iterator | |
| from typing import Literal | |
| import httpx | |
| from pydantic import BaseModel, ConfigDict, Field | |
| class SynthRecipe(BaseModel): | |
| model_config = ConfigDict(extra="forbid") | |
| teacher: str = Field(default="Qwen/Qwen3.5-8B", description="HF id of the teacher model") | |
| seeds: list[str] = Field(default_factory=list) | |
| n_per_seed: int = Field(default=8, ge=1) | |
| style: Literal["sft", "dpo", "tool_use"] = "sft" | |
| temperature: float = Field(default=0.9, ge=0.0, le=2.0) | |
| max_tokens: int = Field(default=512, ge=1) | |
| def _post_completion( | |
| base_url: str, | |
| teacher: str, | |
| prompt: str, | |
| *, | |
| temperature: float, | |
| max_tokens: int, | |
| timeout_s: float, | |
| ) -> str: | |
| body = { | |
| "model": teacher, | |
| "messages": [{"role": "user", "content": prompt}], | |
| "temperature": temperature, | |
| "max_tokens": max_tokens, | |
| } | |
| with httpx.Client(timeout=timeout_s) as client: | |
| resp = client.post(f"{base_url}/chat/completions", json=body) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| return ((data.get("choices") or [{}])[0].get("message") or {}).get("content", "") or "" | |
| def synthesize(recipe: SynthRecipe, *, base_url: str | None = None, timeout_s: float = 120.0) -> Iterator[dict[str, str]]: | |
| """Yield synthetic samples per `recipe`. POSTs to a vLLM-compatible endpoint.""" | |
| base_url = ( | |
| base_url or os.environ.get("MINDXTRAIN_TEACHER_BASE_URL", "http://localhost:8000/v1") | |
| ).rstrip("/") | |
| teacher = os.environ.get("MINDXTRAIN_TEACHER_MODEL", recipe.teacher) | |
| for seed in recipe.seeds: | |
| for i in range(recipe.n_per_seed): | |
| response = _post_completion( | |
| base_url, | |
| teacher, | |
| seed, | |
| temperature=recipe.temperature, | |
| max_tokens=recipe.max_tokens, | |
| timeout_s=timeout_s, | |
| ) | |
| yield { | |
| "seed": seed, | |
| "rollout_index": str(i), | |
| "response": response, | |
| "style": recipe.style, | |
| } | |
| def merge_synth_with_real( | |
| synth: Iterable[dict[str, str]], | |
| real: Iterable[dict[str, str]], | |
| ratio: float = 0.3, | |
| ) -> Iterator[dict[str, str]]: | |
| """Interleave synth and real samples at the given synth-share ratio. | |
| Deterministic round-robin: every k-th sample is synth where k=1/ratio. | |
| """ | |
| if not (0.0 <= ratio <= 1.0): | |
| msg = f"ratio must be in [0,1]; got {ratio}" | |
| raise ValueError(msg) | |
| if ratio == 0.0: | |
| yield from real | |
| return | |
| if ratio == 1.0: | |
| yield from synth | |
| return | |
| real_iter = iter(real) | |
| synth_iter = iter(synth) | |
| counter = 0.0 | |
| for _ in range(10**9): # effectively infinite; consumer breaks | |
| counter += ratio | |
| if counter >= 1.0: | |
| counter -= 1.0 | |
| try: | |
| yield next(synth_iter) | |
| except StopIteration: | |
| # synth exhausted; fall through to real-only | |
| yield from real_iter | |
| return | |
| else: | |
| try: | |
| yield next(real_iter) | |
| except StopIteration: | |
| yield from synth_iter | |
| return | |
| __all__ = ["SynthRecipe", "merge_synth_with_real", "synthesize"] | |