Ir para o conteúdo

API reference

Generated from the apogee-ai-training source with mkdocstrings. Every symbol below is exported from apogee_ai_training, so it is part of the supported public surface.

Application · DTOs

BenchDTO dataclass

Python
BenchDTO(examples: int = 1000)

examples class-attribute instance-attribute

Python
examples: int = 1000

ConvertDTO dataclass

Python
ConvertDTO(source_path: str, target_path: str, fmt_in: str = 'chatml', fmt_out: str = 'openai-sft')

source_path instance-attribute

Python
source_path: str

target_path instance-attribute

Python
target_path: str

fmt_in class-attribute instance-attribute

Python
fmt_in: str = 'chatml'

fmt_out class-attribute instance-attribute

Python
fmt_out: str = 'openai-sft'

PrepareDTO dataclass

Python
PrepareDTO(source_path: str, fmt: str = 'chatml', dataset_name: str = '')

source_path instance-attribute

Python
source_path: str

fmt class-attribute instance-attribute

Python
fmt: str = 'chatml'

dataset_name class-attribute instance-attribute

Python
dataset_name: str = ''

StartDTO dataclass

Python
StartDTO(base_model: str, dataset_name: str, trainer: str = 'dry_run')

base_model instance-attribute

Python
base_model: str

dataset_name instance-attribute

Python
dataset_name: str

trainer class-attribute instance-attribute

Python
trainer: str = 'dry_run'

Application · Use cases

BenchPrepUseCase

Synthetic SFT prep: N examples + 10% duplicates, measure throughput.

execute async

Python
execute(examples: int) -> dict[str, float]
Source code in apogee_ai_training/application/use_cases/bench_prep_use_case.py
Python
async def execute(self, examples: int) -> dict[str, float]:
    if examples <= 0:
        raise ValueError("examples must be positive")
    items: list[TrainingExample] = []
    for i in range(examples):
        base = i // 2 if i % 10 == 0 else i  # forces ~10% duplication
        items.append(TrainingExample(messages=(
            {"role": "user", "content": f"pergunta {base}"},
            {"role": "assistant", "content": f"resposta {base}"},
        )))
    start = time.perf_counter()
    dataset = await PrepareDatasetUseCase().execute(items, name="bench")
    elapsed = (time.perf_counter() - start) * 1000.0
    return {
        "examples": float(examples),
        "deduped": float(len(dataset)),
        "elapsed_ms": elapsed,
        "examples_per_second": (examples / elapsed * 1000.0) if elapsed > 0 else 0.0,
    }

ConvertFormatUseCase

Round-trips a dataset between supported formats via in-memory lines.

execute async

Python
execute(dataset: TrainingDataset, target_format: str) -> list[str]
Source code in apogee_ai_training/application/use_cases/convert_format_use_case.py
Python
async def execute(
    self, dataset: TrainingDataset, target_format: str
) -> list[str]:
    if target_format not in _BY_NAME:
        raise DatasetValidationError(f"unknown target format: {target_format!r}")
    converter = _BY_NAME[target_format]()
    return converter.to_lines(dataset)

GetJobUseCase

Python
GetJobUseCase(trainer)
Source code in apogee_ai_training/application/use_cases/get_job_use_case.py
Python
def __init__(self, trainer) -> None:
    self._trainer = trainer

execute async

Python
execute(job_id: str) -> TrainingJob
Source code in apogee_ai_training/application/use_cases/get_job_use_case.py
Python
async def execute(self, job_id: str) -> TrainingJob:
    return await self._trainer.status(job_id)

PrepareDatasetUseCase

Wraps a list of TrainingExample into a TrainingDataset.

Drops obvious duplicates (same JSON serialisation of messages or prompt/chosen/rejected).

execute async

Python
execute(examples: Iterable[TrainingExample], name: str = 'dataset') -> TrainingDataset
Source code in apogee_ai_training/application/use_cases/prepare_dataset_use_case.py
Python
async def execute(
    self,
    examples: Iterable[TrainingExample],
    name: str = "dataset",
) -> TrainingDataset:
    seen: set[str] = set()
    out: list[TrainingExample] = []
    for ex in examples:
        key = (
            f"M::{tuple(tuple(sorted(m.items())) for m in ex.messages)}"
            if ex.messages
            else f"D::{ex.prompt}::{ex.chosen}::{ex.rejected}"
        )
        if key in seen:
            continue
        seen.add(key)
        out.append(ex)
    return TrainingDataset(name=name, examples=tuple(out))

StartJobUseCase

Python
StartJobUseCase(trainer, repository=None)
Source code in apogee_ai_training/application/use_cases/start_job_use_case.py
Python
def __init__(self, trainer, repository=None) -> None:
    self._trainer = trainer
    self._repo = repository

execute async

Python
execute(config: TrainingConfig, dataset: TrainingDataset) -> TrainingJob
Source code in apogee_ai_training/application/use_cases/start_job_use_case.py
Python
async def execute(
    self, config: TrainingConfig, dataset: TrainingDataset
) -> TrainingJob:
    job = await self._trainer.start(config, dataset)
    if self._repo is not None:
        await self._repo.save(job)
    return job

ValidateDatasetUseCase

Walk over examples to surface common shape mistakes.

execute async

Python
execute(dataset: TrainingDataset) -> dict[str, int]
Source code in apogee_ai_training/application/use_cases/validate_dataset_use_case.py
Python
async def execute(self, dataset: TrainingDataset) -> dict[str, int]:
    if len(dataset) == 0:
        raise DatasetValidationError("dataset is empty")
    sft_count = 0
    dpo_count = 0
    bad = 0
    for i, ex in enumerate(dataset.examples, 1):
        if ex.is_dpo:
            dpo_count += 1
            if not ex.prompt or not (ex.chosen and ex.rejected):
                bad += 1
        else:
            sft_count += 1
            if not ex.messages:
                raise DatasetValidationError(
                    "SFT example without messages", line=i,
                )
            roles = {m.get("role") for m in ex.messages}
            if "user" not in roles and "assistant" not in roles:
                raise DatasetValidationError(
                    "SFT example missing user/assistant roles", line=i,
                )
    return {
        "examples": len(dataset),
        "sft_count": sft_count,
        "dpo_count": dpo_count,
        "warnings": bad,
    }

Domain

DatasetFormat

Bases: str, Enum

CHATML class-attribute instance-attribute

Python
CHATML = 'chatml'

OPENAI_SFT class-attribute instance-attribute

Python
OPENAI_SFT = 'openai-sft'

HF_DPO class-attribute instance-attribute

Python
HF_DPO = 'hf-dpo'

JSONL class-attribute instance-attribute

Python
JSONL = 'jsonl'

ALPACA class-attribute instance-attribute

Python
ALPACA = 'alpaca'

JobStatus

Bases: str, Enum

PENDING class-attribute instance-attribute

Python
PENDING = 'pending'

VALIDATING class-attribute instance-attribute

Python
VALIDATING = 'validating'

QUEUED class-attribute instance-attribute

Python
QUEUED = 'queued'

RUNNING class-attribute instance-attribute

Python
RUNNING = 'running'

SUCCEEDED class-attribute instance-attribute

Python
SUCCEEDED = 'succeeded'

FAILED class-attribute instance-attribute

Python
FAILED = 'failed'

CANCELLED class-attribute instance-attribute

Python
CANCELLED = 'cancelled'

TrainingConfig dataclass

Python
TrainingConfig(base_model: str, kind: TrainingKind = SFT, epochs: int = 3, batch_size: int = 8, learning_rate: float = 5e-05, seed: int = 42, suffix: str = '', hyperparameters: dict[str, str] = dict())

base_model instance-attribute

Python
base_model: str

kind class-attribute instance-attribute

Python
kind: TrainingKind = SFT

epochs class-attribute instance-attribute

Python
epochs: int = 3

batch_size class-attribute instance-attribute

Python
batch_size: int = 8

learning_rate class-attribute instance-attribute

Python
learning_rate: float = 5e-05

seed class-attribute instance-attribute

Python
seed: int = 42

suffix class-attribute instance-attribute

Python
suffix: str = ''

hyperparameters class-attribute instance-attribute

Python
hyperparameters: dict[str, str] = field(default_factory=dict)

TrainingDataset dataclass

Python
TrainingDataset(name: str, examples: tuple[TrainingExample, ...] = (), metadata: dict[str, str] = dict())

name instance-attribute

Python
name: str

examples class-attribute instance-attribute

Python
examples: tuple[TrainingExample, ...] = ()

metadata class-attribute instance-attribute

Python
metadata: dict[str, str] = field(default_factory=dict)

is_dpo property

Python
is_dpo: bool

TrainingExample dataclass

Python
TrainingExample(messages: tuple[dict[str, str], ...] = (), prompt: str = '', chosen: str = '', rejected: str = '', metadata: dict[str, str] = dict())

A single SFT/DPO example.

For SFT: messages only. For DPO: prompt plus chosen and rejected responses.

messages class-attribute instance-attribute

Python
messages: tuple[dict[str, str], ...] = ()

prompt class-attribute instance-attribute

Python
prompt: str = ''

chosen class-attribute instance-attribute

Python
chosen: str = ''

rejected class-attribute instance-attribute

Python
rejected: str = ''

metadata class-attribute instance-attribute

Python
metadata: dict[str, str] = field(default_factory=dict)

is_dpo property

Python
is_dpo: bool

TrainingJob dataclass

Python
TrainingJob(base_model: str, dataset_name: str, status: JobStatus = PENDING, fine_tuned_model: str = '', error_message: str = '', progress: float = 0.0, id: str = (lambda: f'job-{hex[:10]}')(), created_at_s: float = time(), metadata: dict[str, str] = dict())

base_model instance-attribute

Python
base_model: str

dataset_name instance-attribute

Python
dataset_name: str

status class-attribute instance-attribute

Python
status: JobStatus = PENDING

fine_tuned_model class-attribute instance-attribute

Python
fine_tuned_model: str = ''

error_message class-attribute instance-attribute

Python
error_message: str = ''

progress class-attribute instance-attribute

Python
progress: float = 0.0

id class-attribute instance-attribute

Python
id: str = field(default_factory=lambda: f'job-{hex[:10]}')

created_at_s class-attribute instance-attribute

Python
created_at_s: float = field(default_factory=time)

metadata class-attribute instance-attribute

Python
metadata: dict[str, str] = field(default_factory=dict)

Domain · Enums

TrainerKind

Bases: str, Enum

DRY_RUN class-attribute instance-attribute

Python
DRY_RUN = 'dry_run'

OPENAI class-attribute instance-attribute

Python
OPENAI = 'openai'

HUGGINGFACE class-attribute instance-attribute

Python
HUGGINGFACE = 'huggingface'

TrainingKind

Bases: str, Enum

SFT class-attribute instance-attribute

Python
SFT = 'sft'

DPO class-attribute instance-attribute

Python
DPO = 'dpo'

REWARD class-attribute instance-attribute

Python
REWARD = 'reward'

EMBEDDING class-attribute instance-attribute

Python
EMBEDDING = 'embedding'

Domain · Exceptions

DatasetValidationError

Python
DatasetValidationError(reason: str, line: int | None = None)

Bases: TrainingError

Source code in apogee_ai_training/domain/exceptions/training_exceptions.py
Python
def __init__(self, reason: str, line: int | None = None) -> None:
    loc = f" (line {line})" if line is not None else ""
    super().__init__(f"Dataset invalid{loc}: {reason}")
    self.line = line

line instance-attribute

Python
line = line

JobNotFoundException

Python
JobNotFoundException(job_id: str)

Bases: TrainingError

Source code in apogee_ai_training/domain/exceptions/training_exceptions.py
Python
def __init__(self, job_id: str) -> None:
    super().__init__(f"Job not found: {job_id!r}")
    self.job_id = job_id

job_id instance-attribute

Python
job_id = job_id

TrainerNotAvailableException

Bases: TrainingError

TrainingError

Bases: Exception

Base for apogee-ai-training errors.

Domain · Protocols (ports)

IFormatConverter

Bases: Protocol

name instance-attribute

Python
name: str

to_lines

Python
to_lines(dataset: TrainingDataset) -> list[str]
Source code in apogee_ai_training/domain/services/i_format_converter.py
Python
def to_lines(self, dataset: TrainingDataset) -> list[str]: ...

from_lines

Python
from_lines(lines: list[str], dataset_name: str) -> TrainingDataset
Source code in apogee_ai_training/domain/services/i_format_converter.py
Python
def from_lines(self, lines: list[str], dataset_name: str) -> TrainingDataset: ...

IJobRepository

Bases: Protocol

save async

Python
save(job: TrainingJob) -> None
Source code in apogee_ai_training/domain/services/i_job_repository.py
Python
async def save(self, job: TrainingJob) -> None: ...

get async

Python
get(job_id: str) -> TrainingJob
Source code in apogee_ai_training/domain/services/i_job_repository.py
Python
async def get(self, job_id: str) -> TrainingJob: ...

list async

Python
list() -> Iterable[TrainingJob]
Source code in apogee_ai_training/domain/services/i_job_repository.py
Python
async def list(self) -> Iterable[TrainingJob]: ...

ITrainer

Bases: Protocol

name instance-attribute

Python
name: str

start async

Python
start(config: TrainingConfig, dataset: TrainingDataset) -> TrainingJob
Source code in apogee_ai_training/domain/services/i_trainer.py
Python
async def start(
    self, config: TrainingConfig, dataset: TrainingDataset
) -> TrainingJob: ...

status async

Python
status(job_id: str) -> TrainingJob
Source code in apogee_ai_training/domain/services/i_trainer.py
Python
async def status(self, job_id: str) -> TrainingJob: ...

cancel async

Python
cancel(job_id: str) -> None
Source code in apogee_ai_training/domain/services/i_trainer.py
Python
async def cancel(self, job_id: str) -> None: ...

Infrastructure

AlpacaConverter

Alpaca JSONL: {"instruction", "input", "output"} per line.

name class-attribute instance-attribute

Python
name = 'alpaca'

to_lines

Python
to_lines(dataset: TrainingDataset) -> list[str]
Source code in apogee_ai_training/infrastructure/formats/alpaca_converter.py
Python
def to_lines(self, dataset: TrainingDataset) -> list[str]:
    out: list[str] = []
    for ex in dataset.examples:
        if not ex.messages or len(ex.messages) < 2:
            raise DatasetValidationError(
                "Alpaca export requires user → assistant pair",
            )
        user = next((m for m in ex.messages if m.get("role") == "user"), None)
        assistant = next(
            (m for m in ex.messages if m.get("role") == "assistant"), None
        )
        if user is None or assistant is None:
            raise DatasetValidationError(
                "missing user or assistant message",
            )
        out.append(json.dumps({
            "instruction": user.get("content", ""),
            "input": "",
            "output": assistant.get("content", ""),
        }))
    return out

from_lines

Python
from_lines(lines: list[str], dataset_name: str) -> TrainingDataset
Source code in apogee_ai_training/infrastructure/formats/alpaca_converter.py
Python
def from_lines(self, lines: list[str], dataset_name: str) -> TrainingDataset:
    examples: list[TrainingExample] = []
    for i, line in enumerate(lines, 1):
        line = line.strip()
        if not line:
            continue
        try:
            payload = json.loads(line)
        except json.JSONDecodeError as exc:
            raise DatasetValidationError(f"invalid json: {exc.msg}", line=i) from exc
        instruction = payload.get("instruction", "")
        inp = payload.get("input", "")
        output = payload.get("output", "")
        if not instruction or not output:
            raise DatasetValidationError(
                "instruction and output are required", line=i,
            )
        user_content = f"{instruction}\n\n{inp}".strip()
        examples.append(TrainingExample(messages=(
            {"role": "user", "content": user_content},
            {"role": "assistant", "content": output},
        )))
    return TrainingDataset(name=dataset_name, examples=tuple(examples))

ChatMLConverter

ChatML JSONL: each line {"messages": [{"role": ..., "content": ...}]}.

name class-attribute instance-attribute

Python
name = 'chatml'

to_lines

Python
to_lines(dataset: TrainingDataset) -> list[str]
Source code in apogee_ai_training/infrastructure/formats/chatml_converter.py
Python
def to_lines(self, dataset: TrainingDataset) -> list[str]:
    out: list[str] = []
    for ex in dataset.examples:
        out.append(json.dumps({"messages": list(ex.messages)}))
    return out

from_lines

Python
from_lines(lines: list[str], dataset_name: str) -> TrainingDataset
Source code in apogee_ai_training/infrastructure/formats/chatml_converter.py
Python
def from_lines(self, lines: list[str], dataset_name: str) -> TrainingDataset:
    examples: list[TrainingExample] = []
    for i, line in enumerate(lines, 1):
        line = line.strip()
        if not line:
            continue
        try:
            payload = json.loads(line)
        except json.JSONDecodeError as exc:
            raise DatasetValidationError(f"invalid json: {exc.msg}", line=i) from exc
        messages = payload.get("messages")
        if not isinstance(messages, list) or not messages:
            raise DatasetValidationError("missing or empty messages", line=i)
        examples.append(TrainingExample(messages=tuple(messages)))
    return TrainingDataset(name=dataset_name, examples=tuple(examples))

DryRunTrainer

Python
DryRunTrainer()

In-memory trainer for CI/tests. Marks job SUCCEEDED immediately.

Source code in apogee_ai_training/infrastructure/trainers/dry_run_trainer.py
Python
def __init__(self) -> None:
    self._jobs: dict[str, TrainingJob] = {}

name class-attribute instance-attribute

Python
name = 'dry_run'

start async

Python
start(config: TrainingConfig, dataset: TrainingDataset) -> TrainingJob
Source code in apogee_ai_training/infrastructure/trainers/dry_run_trainer.py
Python
async def start(
    self, config: TrainingConfig, dataset: TrainingDataset
) -> TrainingJob:
    suffix = f"-{config.suffix}" if config.suffix else ""
    fine_tuned_model = f"{config.base_model}-ft{suffix}"
    job = TrainingJob(
        base_model=config.base_model,
        dataset_name=dataset.name,
        status=JobStatus.SUCCEEDED,
        fine_tuned_model=fine_tuned_model,
        progress=1.0,
        metadata={
            "kind": config.kind.value,
            "epochs": str(config.epochs),
            "batch_size": str(config.batch_size),
            "examples": str(len(dataset)),
        },
    )
    self._jobs[job.id] = job
    return job

status async

Python
status(job_id: str) -> TrainingJob
Source code in apogee_ai_training/infrastructure/trainers/dry_run_trainer.py
Python
async def status(self, job_id: str) -> TrainingJob:
    if job_id not in self._jobs:
        raise JobNotFoundException(job_id)
    return self._jobs[job_id]

cancel async

Python
cancel(job_id: str) -> None
Source code in apogee_ai_training/infrastructure/trainers/dry_run_trainer.py
Python
async def cancel(self, job_id: str) -> None:
    if job_id not in self._jobs:
        raise JobNotFoundException(job_id)
    self._jobs[job_id] = replace(self._jobs[job_id], status=JobStatus.CANCELLED)

HfDpoConverter

HuggingFace DPO JSONL: {"prompt", "chosen", "rejected"} per line.

name class-attribute instance-attribute

Python
name = 'hf-dpo'

to_lines

Python
to_lines(dataset: TrainingDataset) -> list[str]
Source code in apogee_ai_training/infrastructure/formats/dpo_converter.py
Python
def to_lines(self, dataset: TrainingDataset) -> list[str]:
    out: list[str] = []
    for ex in dataset.examples:
        if not ex.is_dpo:
            raise DatasetValidationError(
                "example missing prompt/chosen/rejected for HF DPO export",
            )
        out.append(json.dumps({
            "prompt": ex.prompt,
            "chosen": ex.chosen,
            "rejected": ex.rejected,
        }))
    return out

from_lines

Python
from_lines(lines: list[str], dataset_name: str) -> TrainingDataset
Source code in apogee_ai_training/infrastructure/formats/dpo_converter.py
Python
def from_lines(self, lines: list[str], dataset_name: str) -> TrainingDataset:
    examples: list[TrainingExample] = []
    for i, line in enumerate(lines, 1):
        line = line.strip()
        if not line:
            continue
        try:
            payload = json.loads(line)
        except json.JSONDecodeError as exc:
            raise DatasetValidationError(f"invalid json: {exc.msg}", line=i) from exc
        for required in ("prompt", "chosen", "rejected"):
            if required not in payload:
                raise DatasetValidationError(
                    f"missing required field {required!r}", line=i,
                )
        examples.append(TrainingExample(
            prompt=str(payload["prompt"]),
            chosen=str(payload["chosen"]),
            rejected=str(payload["rejected"]),
        ))
    return TrainingDataset(name=dataset_name, examples=tuple(examples))

InMemoryJobRepository

Python
InMemoryJobRepository()
Source code in apogee_ai_training/infrastructure/registries/in_memory_job_repository.py
Python
def __init__(self) -> None:
    self._jobs: dict[str, TrainingJob] = {}

name class-attribute instance-attribute

Python
name = 'in_memory_job'

save async

Python
save(job: TrainingJob) -> None
Source code in apogee_ai_training/infrastructure/registries/in_memory_job_repository.py
Python
async def save(self, job: TrainingJob) -> None:
    self._jobs[job.id] = job

get async

Python
get(job_id: str) -> TrainingJob
Source code in apogee_ai_training/infrastructure/registries/in_memory_job_repository.py
Python
async def get(self, job_id: str) -> TrainingJob:
    if job_id not in self._jobs:
        raise JobNotFoundException(job_id)
    return self._jobs[job_id]

list async

Python
list() -> Iterable[TrainingJob]
Source code in apogee_ai_training/infrastructure/registries/in_memory_job_repository.py
Python
async def list(self) -> Iterable[TrainingJob]:
    return list(self._jobs.values())

JsonlDatasetLoader

Loads a JSONL file into a TrainingDataset using the chosen format.

name class-attribute instance-attribute

Python
name = 'jsonl'

load

Python
load(path: str | Path, fmt: str = 'chatml', dataset_name: str = '') -> TrainingDataset
Source code in apogee_ai_training/infrastructure/datasets/jsonl_loader.py
Python
def load(self, path: str | Path, fmt: str = "chatml", dataset_name: str = "") -> TrainingDataset:
    if fmt not in _CONVERTERS:
        raise DatasetValidationError(f"unknown format: {fmt!r}")
    path = Path(path)
    if not path.is_file():
        raise DatasetValidationError(f"file not found: {path}")
    lines = path.read_text(encoding="utf-8").splitlines()
    converter = _CONVERTERS[fmt]()
    return converter.from_lines(lines, dataset_name or path.stem)

OpenAISftConverter

OpenAI fine-tune SFT: same shape as ChatML.

name class-attribute instance-attribute

Python
name = 'openai-sft'

to_lines

Python
to_lines(dataset: TrainingDataset) -> list[str]
Source code in apogee_ai_training/infrastructure/formats/openai_sft_converter.py
Python
def to_lines(self, dataset: TrainingDataset) -> list[str]:
    return [json.dumps({"messages": list(ex.messages)}) for ex in dataset.examples]

from_lines

Python
from_lines(lines: list[str], dataset_name: str) -> TrainingDataset
Source code in apogee_ai_training/infrastructure/formats/openai_sft_converter.py
Python
def from_lines(self, lines: list[str], dataset_name: str) -> TrainingDataset:
    examples: list[TrainingExample] = []
    for i, line in enumerate(lines, 1):
        line = line.strip()
        if not line:
            continue
        try:
            payload = json.loads(line)
        except json.JSONDecodeError as exc:
            raise DatasetValidationError(f"invalid json: {exc.msg}", line=i) from exc
        messages = payload.get("messages")
        if not isinstance(messages, list) or not messages:
            raise DatasetValidationError("missing or empty messages", line=i)
        for j, m in enumerate(messages):
            if not isinstance(m, dict) or "role" not in m or "content" not in m:
                raise DatasetValidationError(
                    f"message[{j}] requires role + content", line=i,
                )
        examples.append(TrainingExample(messages=tuple(messages)))
    return TrainingDataset(name=dataset_name, examples=tuple(examples))

OpenAITrainer

Python
OpenAITrainer(api_key: str)

Lazy OpenAI fine-tunes adapter — install via extras=openai.

Source code in apogee_ai_training/infrastructure/trainers/openai_trainer.py
Python
def __init__(self, api_key: str) -> None:
    if not api_key:
        raise ValueError("api_key cannot be empty")
    self._api_key = api_key
    self._client = None

name class-attribute instance-attribute

Python
name = 'openai'

start async

Python
start(config: TrainingConfig, dataset: TrainingDataset) -> TrainingJob
Source code in apogee_ai_training/infrastructure/trainers/openai_trainer.py
Python
async def start(
    self, config: TrainingConfig, dataset: TrainingDataset
) -> TrainingJob:  # pragma: no cover
    self._ensure_client()
    # Real implementation would upload the JSONL file then call
    # client.fine_tuning.jobs.create(...). Network-dependent, skipped in CI.
    raise TrainerNotAvailableException(
        "OpenAI training requires network access; not exercised in CI"
    )

status async

Python
status(job_id: str) -> TrainingJob
Source code in apogee_ai_training/infrastructure/trainers/openai_trainer.py
Python
async def status(self, job_id: str) -> TrainingJob:  # pragma: no cover
    self._ensure_client()
    try:
        payload = await self._client.fine_tuning.jobs.retrieve(job_id)  # type: ignore[union-attr]
    except Exception as exc:
        raise JobNotFoundException(job_id) from exc
    return TrainingJob(
        base_model=payload.model,
        dataset_name=str(payload.training_file or ""),
        status=_OPENAI_STATUS_MAP.get(payload.status, JobStatus.PENDING),
        fine_tuned_model=payload.fine_tuned_model or "",
        id=payload.id,
    )

cancel async

Python
cancel(job_id: str) -> None
Source code in apogee_ai_training/infrastructure/trainers/openai_trainer.py
Python
async def cancel(self, job_id: str) -> None:  # pragma: no cover
    self._ensure_client()
    await self._client.fine_tuning.jobs.cancel(job_id)  # type: ignore[union-attr]

write_jsonl

Python
write_jsonl(dataset: TrainingDataset, path: str | Path, fmt: str = 'chatml') -> Path
Source code in apogee_ai_training/infrastructure/datasets/jsonl_loader.py
Python
def write_jsonl(dataset: TrainingDataset, path: str | Path, fmt: str = "chatml") -> Path:
    if fmt not in _CONVERTERS:
        raise DatasetValidationError(f"unknown format: {fmt!r}")
    path = Path(path)
    path.parent.mkdir(parents=True, exist_ok=True)
    converter = _CONVERTERS[fmt]()
    lines = converter.to_lines(dataset)
    path.write_text("\n".join(lines) + "\n", encoding="utf-8")
    return path