Support evaluation-local configuration and names overrides

This commit is contained in:
sebastien committed 2026-11-03 18:44:56 +01:00
1 parent 0bb6f7d97c
commit d4404e4c27
36 files changed
+409 -184

No files matched your search

+16 -14
View File
@@ -41,9 +41,6 @@ from copienator.utils import enonce_total, read_all_labels
NB_THREADS = 12
# PROXY_URL = "http://192.168.241.1:3128"
MODEL_ID_pro = config.MODEL_PRO_ID
MODEL_ID_flash = config.MODEL_FLASH_ID
api_key = config.API_KEY
# Runtime globals retained while the processing helpers are migrated incrementally.
INPUT_DIR = Path()
@@ -270,13 +267,18 @@ def reset_workspace(workspace: EvaluationWorkspace) -> None:
"_new PDFs from 'Par label'."
)
_DEFAULT_FALLBACK = object()
def call_gemini_with_retries(model_id, contents, config,
fallback_model_id=MODEL_ID_flash):
fallback_model_id=_DEFAULT_FALLBACK):
"""Handles requests to Gemini with a 1min and 5min retry mechanism, and quota fallback."""
global pro_quota_exhausted
delays = [60, 300]
request_seed = None
# Own the retries here so the SDK cannot silently resend a fixed seed.
if fallback_model_id is _DEFAULT_FALLBACK:
fallback_model_id = settings.MODEL_FLASH_ID
http_options = config.http_options or types.HttpOptions()
config = config.model_copy(update={"http_options": http_options.model_copy(
update={"retry_options": types.HttpRetryOptions(attempts=1)}
@@ -289,7 +291,7 @@ def call_gemini_with_retries(model_id, contents, config,
config = config.model_copy(update={"seed": request_seed})
tprint(f"\tGemini attempt {attempt + 1}/3: seed={request_seed}")
# Switch to fallback immediately if quota was exhausted by another thread
if model_id == MODEL_ID_pro and pro_quota_exhausted and fallback_model_id:
if model_id == settings.MODEL_PRO_ID and pro_quota_exhausted and fallback_model_id:
model_id = fallback_model_id
config = config.model_copy(update={"thinking_config": thinking_config(
settings.THINKING_LEVEL_FLASH
@@ -324,7 +326,7 @@ def call_gemini_with_retries(model_id, contents, config,
continue # Retry same model
# Immediately fallback to Flash without waiting if it's a Pro quota error
if is_quota_error and model_id == MODEL_ID_pro and fallback_model_id:
if is_quota_error and model_id == settings.MODEL_PRO_ID and fallback_model_id:
tprint(f"\tGemini Pro quota hit ({e}). \n\n\tFalling back to Flash permanently...")
model_id = fallback_model_id
config = config.model_copy(update={"thinking_config": thinking_config(
@@ -347,7 +349,7 @@ def correct_boxes_with_gemini(pid, label, pdf_path, original_feedbacks,
# pdf_path = COPIES_DIR / f"Copie{pid}" / f"{label}.pdf"
contents, config = prompting.request_for_box_correction(pdf_path, original_feedbacks)
response_text = call_gemini_with_retries(MODEL_ID_flash, contents, config)
response_text = call_gemini_with_retries(settings.MODEL_FLASH_ID, contents, config)
corrected_feedbacks = parse_correction_response(response_text)
global_feedbacks = [f for f in original_feedbacks if not f["box_2d"]]
@@ -404,7 +406,7 @@ def handle_label_errors(pid, label, res, pdf_path, *, group_geometry=None):
if error_type == "wrong-label":
tprint(f"\tHandling wrong-label for {pid} {label}")
contents, config = prompting.request_for_wrong_label(pdf_path, label, enonce, labels_txt)
new_label = call_gemini_with_retries(MODEL_ID_flash, contents, config).strip().strip('"\'')
new_label = call_gemini_with_retries(settings.MODEL_FLASH_ID, contents, config).strip().strip('"\'')
if new_label not in all_labels:
tprint(f"\t\tCopie{pid} returned an incorrect label {new_label} from an initial wrong label {label}. Ignoring")
res["error"] = "wrg-lbl:cldtfix"
@@ -413,7 +415,7 @@ def handle_label_errors(pid, label, res, pdf_path, *, group_geometry=None):
tprint(f"\t\tLabel confirmed for Copie{pid}: regrading {label}.")
contents, config = prompting.request_for_confirmed_label(INPUT_DIR, pdf_path, label, pid)
response = parse_correction_response(
call_gemini_with_retries(MODEL_ID_flash, contents, config)
call_gemini_with_retries(settings.MODEL_FLASH_ID, contents, config)
)
if not isinstance(response, list) or len(response) != 1 or response[0].get("id") != pid:
raise ValueError(f"Confirmed-label regrading must return exactly Copie{pid}")
@@ -469,7 +471,7 @@ def handle_label_errors(pid, label, res, pdf_path, *, group_geometry=None):
contents, config = prompting.request_for_additional_answer(pdf_path, label, enonce, labels_txt)
tprint(f"\tHandling additional-answer for {pid} {label}")
try:
add_labels = json.loads(call_gemini_with_retries(MODEL_ID_flash, contents, config))
add_labels = json.loads(call_gemini_with_retries(settings.MODEL_FLASH_ID, contents, config))
except CorrectionStopRequested:
raise
except Exception: # noqa: BLE001 - invalid auxiliary model response
@@ -555,7 +557,7 @@ def process_single_task(task_tuple, precomputed_response=None):
thinking_level=(settings.THINKING_LEVEL_FLASH if use_flash
else settings.THINKING_LEVEL_PRO),
)
model_to_use = MODEL_ID_flash if use_flash else MODEL_ID_pro
model_to_use = settings.MODEL_FLASH_ID if use_flash else settings.MODEL_PRO_ID
if precomputed_response:
tprint(f"Using saved response for: {label} {group_name}")
@@ -810,7 +812,7 @@ def run_configured(args: argparse.Namespace) -> ExitCode:
global client, tasks_to_process
workers = request_workers(args)
if client is None:
client = genai.Client(api_key=api_key)
client = genai.Client(api_key=settings.API_KEY)
if args.refaire:
refaire_path = INPUT_DIR / "refaire.json"
overwritten_path = INPUT_DIR / "overwritten_correction.json"
@@ -979,8 +981,8 @@ def run_configured(args: argparse.Namespace) -> ExitCode:
)
print("Batch generation complete.")
print(f" - {count_flash} requests saved to {batch_flash_file} (for {MODEL_ID_flash})")
print(f" - {count_pro} requests saved to {batch_pro_file} (for {MODEL_ID_pro})")
print(f" - {count_flash} requests saved to {batch_flash_file} (for {settings.MODEL_FLASH_ID})")
print(f" - {count_pro} requests saved to {batch_pro_file} (for {settings.MODEL_PRO_ID})")
print("Upload these files via the File API and create two separate batch jobs.")
# If there's no live tasks to do, and we aren't doing a batched ingestion, exit right away