feat(recipe-studio): normalize and slugify run_name, update job naming logic
This commit is contained in:
parent
e30fc87187
commit
a66b1678e8
6 changed files with 56 additions and 6 deletions
|
|
@ -1,9 +1,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
import shutil
|
||||
import time
|
||||
import traceback
|
||||
import unicodedata
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
|
@ -34,6 +36,27 @@ class _QueueLogHandler(logging.Handler):
|
|||
pass
|
||||
|
||||
|
||||
def _slugify_run_name(value: str) -> str:
|
||||
normalized = unicodedata.normalize("NFKD", value)
|
||||
ascii_only = normalized.encode("ascii", "ignore").decode("ascii")
|
||||
slug = re.sub(r"[^a-zA-Z0-9]+", "-", ascii_only).strip("-").lower()
|
||||
if not slug:
|
||||
return ""
|
||||
return slug[:80].strip("-")
|
||||
|
||||
|
||||
def _build_dataset_name(*, run_name: str | None, job_id: str, artifact_root: Path) -> str:
|
||||
fallback = f"recipe_{job_id}"
|
||||
slug = _slugify_run_name(run_name or "")
|
||||
base_name = f"recipe_{slug}" if slug else fallback
|
||||
candidate = base_name
|
||||
suffix = 2
|
||||
while (artifact_root / candidate).exists():
|
||||
candidate = f"{base_name}_{suffix}"
|
||||
suffix += 1
|
||||
return candidate
|
||||
|
||||
|
||||
def run_job_process(
|
||||
*,
|
||||
event_queue,
|
||||
|
|
@ -53,7 +76,13 @@ def run_job_process(
|
|||
job_id = str(run.get("_job_id") or "").strip()
|
||||
if not job_id:
|
||||
job_id = f"{int(time.time())}"
|
||||
dataset_name = f"recipe_{job_id}"
|
||||
run_name_raw = run.get("run_name")
|
||||
run_name = run_name_raw if isinstance(run_name_raw, str) else None
|
||||
dataset_name = _build_dataset_name(
|
||||
run_name=run_name,
|
||||
job_id=job_id,
|
||||
artifact_root=_ARTIFACT_ROOT,
|
||||
)
|
||||
merge_batches = bool(run.get("merge_batches"))
|
||||
_ARTIFACT_ROOT.mkdir(parents=True, exist_ok=True)
|
||||
run_config_raw = run.get("run_config") or {}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,17 @@ from models.data_recipe import JobCreateResponse, RecipePayload
|
|||
router = APIRouter()
|
||||
|
||||
|
||||
def _normalize_run_name(value: Any) -> str | None:
|
||||
if value is None:
|
||||
return None
|
||||
if not isinstance(value, str):
|
||||
raise HTTPException(status_code=400, detail="invalid run_name: must be a string")
|
||||
trimmed = value.strip()
|
||||
if not trimmed:
|
||||
return None
|
||||
return trimmed[:120]
|
||||
|
||||
|
||||
@router.post("/jobs", response_class=JSONResponse, response_model=JobCreateResponse)
|
||||
def create_job(payload: RecipePayload):
|
||||
recipe = payload.recipe
|
||||
|
|
@ -27,6 +38,7 @@ def create_job(payload: RecipePayload):
|
|||
if execution_type not in {"preview", "full"}:
|
||||
raise HTTPException(status_code=400, detail="invalid execution_type: must be 'preview' or 'full'")
|
||||
run["execution_type"] = execution_type
|
||||
run["run_name"] = _normalize_run_name(run.get("run_name"))
|
||||
run_config_raw = run.get("run_config")
|
||||
if run_config_raw is not None:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -153,13 +153,13 @@ export function buildExecutionPayload(input: {
|
|||
kind: RecipeExecutionKind;
|
||||
rows: number;
|
||||
settings: RecipeRunSettings;
|
||||
runName?: string | null;
|
||||
}): RecipePayload {
|
||||
const normalizedSettings = normalizeRunSettings(input.settings);
|
||||
const payloadWithParallelism = applyGlobalParallelismOverride(
|
||||
input.payload,
|
||||
normalizedSettings.llmParallelRequests,
|
||||
);
|
||||
|
||||
return {
|
||||
...payloadWithParallelism,
|
||||
run: {
|
||||
|
|
@ -174,6 +174,8 @@ export function buildExecutionPayload(input: {
|
|||
input.kind === "full" &&
|
||||
normalizedSettings.batchEnabled &&
|
||||
normalizedSettings.mergeBatches,
|
||||
// biome-ignore lint/style/useNamingConvention: backend schema
|
||||
run_name: input.kind === "full" ? (input.runName ?? null) : null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -250,7 +250,6 @@ export function useRecipeExecutions({
|
|||
const { kind, payload, rows, settings, runName } = input;
|
||||
const setLoading = kind === "preview" ? setPreviewLoading : setFullLoading;
|
||||
const label = executionLabel(kind);
|
||||
const normalizedRunName = kind === "full" ? normalizeRunName(runName) : null;
|
||||
|
||||
setLoading(true);
|
||||
const baseExecution = createBaseExecutionRecord({
|
||||
|
|
@ -258,7 +257,7 @@ export function useRecipeExecutions({
|
|||
kind,
|
||||
rows,
|
||||
currentSignature,
|
||||
runName: normalizedRunName,
|
||||
runName,
|
||||
});
|
||||
|
||||
upsertAndPersist(baseExecution);
|
||||
|
|
@ -271,6 +270,7 @@ export function useRecipeExecutions({
|
|||
kind,
|
||||
rows,
|
||||
settings,
|
||||
runName,
|
||||
});
|
||||
const createdJob = await createRecipeJob(jobPayload);
|
||||
const executionWithJob = {
|
||||
|
|
@ -330,11 +330,13 @@ export function useRecipeExecutions({
|
|||
}
|
||||
|
||||
const normalizedRows = sanitizeExecutionRows(rows, kind);
|
||||
const normalizedRunName = kind === "full" ? normalizeRunName(runName) : null;
|
||||
const executionPayload = buildExecutionPayload({
|
||||
payload,
|
||||
kind,
|
||||
rows: normalizedRows,
|
||||
settings: runSettings,
|
||||
runName: normalizedRunName,
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
@ -359,7 +361,7 @@ export function useRecipeExecutions({
|
|||
payload,
|
||||
rows: normalizedRows,
|
||||
settings: runSettings,
|
||||
runName,
|
||||
runName: normalizedRunName,
|
||||
});
|
||||
},
|
||||
[readExecutablePayload, runExecution, runSettings, setRunErrors],
|
||||
|
|
@ -403,6 +405,7 @@ export function useRecipeExecutions({
|
|||
kind: runDialogKind,
|
||||
rows: normalizedRows,
|
||||
settings: runSettings,
|
||||
runName: runDialogKind === "full" ? normalizeRunName(fullRunName) : null,
|
||||
});
|
||||
|
||||
setValidateLoading(true);
|
||||
|
|
@ -427,6 +430,7 @@ export function useRecipeExecutions({
|
|||
setValidateLoading(false);
|
||||
}
|
||||
}, [
|
||||
fullRunName,
|
||||
fullRows,
|
||||
payloadErrorMessage,
|
||||
payloadResult.errors,
|
||||
|
|
@ -434,6 +438,7 @@ export function useRecipeExecutions({
|
|||
readPayload,
|
||||
runDialogKind,
|
||||
runSettings,
|
||||
setRunErrors,
|
||||
]);
|
||||
|
||||
const openRunDialog = useCallback(
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ export type RecipePayload = {
|
|||
artifact_path?: string;
|
||||
// biome-ignore lint/style/useNamingConvention: backend schema
|
||||
merge_batches?: boolean;
|
||||
// biome-ignore lint/style/useNamingConvention: backend schema
|
||||
run_name?: string | null;
|
||||
};
|
||||
ui: {
|
||||
nodes: Array<{
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@ export function DatasetSection() {
|
|||
}}
|
||||
className="w-full"
|
||||
>
|
||||
<TabsList className="mb-2 w-full">
|
||||
<TabsList className=" w-full">
|
||||
<TabsTrigger value="huggingface">Hugging Face</TabsTrigger>
|
||||
<TabsTrigger value="local">Local</TabsTrigger>
|
||||
</TabsList>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue