Skip to content

Add DeepSeek model client + CLINC OOS classification support - #16

Open
purva-8 wants to merge 3 commits into
scaledown-team:mainfrom
purva-8:feat/deepseek-v4-benchmarks
Open

Add DeepSeek model client + CLINC OOS classification support#16
purva-8 wants to merge 3 commits into
scaledown-team:mainfrom
purva-8:feat/deepseek-v4-benchmarks

Conversation

@purva-8

@purva-8 purva-8 commented Aug 11, 2026

Copy link
Copy Markdown

Summary

All four ScaleDown benchmark-page tasks are now working end-to-end for DeepSeek V4 Flash/Pro, verified with real API calls via a funded OpenRouter key.

Task Dataset Status
Summarize QMSum ✅ Pilot-verified (n=8), ready for full n=281 run
Classify CLINC OOS ✅ Pilot-verified (n=8), ready for full n=400 run
Compress FinanceBench ✅ Pilot-verified (n=8), ready for full n=150 run
Extract CUAD (switched from LongExtractBench) ✅ Pilot-verified (n=8), ready for full n=150 run

What's added

  • src/models/deepseek_client.py + src/models/openrouter_client.py: two ways to reach DeepSeek — direct API (deepseek: provider) and via OpenRouter (openrouter: provider, currently the working path since the direct-API key hit 402 Insufficient Balance). OpenRouter routing disables upstream reasoning mode by default — confirmed live that DeepSeek V4 Flash/Pro on OpenRouter default to an extended-thinking mode that burns completion tokens on a hidden reasoning field before/instead of visible content. At max_tokens=10 with reasoning left on, content came back null with finish_reason="length" — this silently breaks any low-max_tokens task like Classify if not caught.
  • Fixed a real, pre-existing repo bug: evaluation/runner.py unconditionally passes model_mode into every task's build_prompt(), but only SummarizationTask popped it before forwarding to GenerationInput() (which has no such field). Confirmed live against RAGTask — this means financebench_baseline_gpt52 and any other rag_qa/retrieval_task experiment would crash the moment it actually ran. Fixed in RAGTask, RetrievalTask, ClassificationTask, ExtractionTask.
  • src/dataset/clinc_oos.py + src/tasks/classification_task.py: net-new Classify support (151 labels incl. out-of-scope), scored via existing compute_exact_match.
  • src/dataset/cuad.py: Extract now uses CUAD, not LongExtractBench — see "Why the switch" below. Reuses the existing rag_qa task + exact_match/f1 metrics, same pattern as FinanceBench.
  • src/dataset/longextractbench.py + src/tasks/extraction_task.py + src/metrics/field_extraction.py: LongExtractBench support is still in the repo (dataset structurally verified against the live HF repo, task/metric logic unit-tested against synthetic fixtures) but not the active Extract path — parked for a future pass. See below.
  • src/dataset/financebench.py: DEFAULT_PDF_DIR was hardcoded to a contributor's local machine path. Now reads FINANCEBENCH_PDF_DIR env var with a documented fallback.
  • config/experiments.yaml: 10 new experiment entries across all four tasks × 2 models.

Why CUAD instead of LongExtractBench for Extract

LongExtractBench documents run ~250K input tokens with schemas needing far more than 12K output tokens to fully extract as valid JSON. Piloted at 8 examples: 0% JSON parse rate even at 12K max_tokens, one single call took 33 minutes and $0.10. CUAD's ~11K-char contexts are ~20x cheaper and ~50x faster per call (8.2s vs 6.9min avg in pilot testing), and — more importantly — CUAD is what actually produced the Extract numbers on the existing Grok/OpenAI/Gemini pages, so this keeps DeepSeek comparable rather than introducing a different benchmark.

Verified (all with real API calls against a funded key)

  • Full pilot sweep (n=8/model) passes cleanly across all four tasks for both models. Sample results: CUAD F1 0.305 (V4 Flash), QMSum ROUGE-L 0.192 (V4 Flash), CLINC accuracy 0.75 (V4 Flash, n=8).
  • DeepSeekClient/OpenRouterClient auth + model routing confirmed live.
  • Real per-call cost pulled from OpenRouter's usage block, not estimated.

Still open

  • Full-scale runs not yet done. Everything above is n=8 pilots; the real n=281/400/150/150 runs per model are the next step.
  • CLINC/CUAD num_examples/seed are placeholders — nothing in this repo records the sample size used for the other models' published Classify/Extract numbers.
  • FinanceBench context source is ambiguous without FINANCEBENCH_PDF_DIR set (falls back to evidence_text_full_page).
  • LongExtractBench's token-budget problem is unsolved, not removed — worth a dedicated follow-up if that benchmark matters later.

Test plan

  • py_compile on all new/changed files
  • Full registry resolution check (models/datasets/tasks/metrics)
  • Live smoke tests: DeepSeek direct API (model listing works, generation blocked on account balance), OpenRouter (generation works, real cost/latency/tokens confirmed)
  • Full 8-task-model pilot sweep (n=8 each) — all passing with real scores
  • Full-n runs pending

🧙 Built with WozCode

- src/models/deepseek_client.py: DeepSeek chat client via the OpenAI SDK
  pointed at api.deepseek.com (OpenAI-compatible API), reusing the
  retry/pricing pattern from OpenAIClient.
- Register "deepseek" in MODEL_REGISTRY with default pricing for
  deepseek-v4-flash and deepseek-v4-pro (cache-miss input rate, deepseek.ai
  pricing as of Aug 2026).
- src/dataset/clinc_oos.py + src/tasks/classification_task.py: net-new
  Classify-task support (CLINC OOS intent classification, 151 labels incl.
  out-of-scope), scored via the existing compute_exact_match metric with a
  label-normalizing Task.normalize().
- config/experiments.yaml: qmsum_baseline_deepseekv4{flash,pro} at the same
  num_examples=281/seed=40 as the existing GPT/Gemini QMSum entries, plus
  classify_clinc_baseline_deepseekv4{flash,pro} (num_examples/seed here are
  placeholders pending confirmation against whatever sample size produced
  the other models' published Classify numbers).

Compress (FinanceBench) intentionally left out of this PR — the dataset
loader's PDF path is hardcoded to a contributor's local machine
(src/dataset/financebench.py DEFAULT_PDF_DIR), so it's unclear whether the
existing Compress numbers were scored against full PDFs or the
evidence-text fallback. Extract (LongExtractBench) is also out of scope
for now — no dataset loader, task, or field-level metric exists in this
repo yet.
Copilot AI lite review requested due to automatic review settings August 11, 2026 18:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…s) support

- src/dataset/longextractbench.py: loader for micro1-inc/longextract-bench-50
  (a raw-file HF dataset repo, one folder per example with document.pdf +
  schema.json + ground_truth.json). Uses huggingface_hub.snapshot_download +
  PyMuPDF, mirroring dataset/financebench.py's PDF handling. Verified against
  the live repo: 50 example folders, matching num_examples=50 in the new
  experiment entries (full set, no sampling needed).
- src/tasks/extraction_task.py: builds a schema-in-system-prompt extraction
  prompt, strips markdown fences models sometimes wrap JSON in.
- src/metrics/field_extraction.py: field-level precision/recall/F1 + parse
  rate, scored by flattening predicted/gold JSON into dotted-path pairs and
  comparing as sets. One function per metric name to match the existing
  one-name-per-key convention (see metrics/retrieval.py) — a single function
  returning multiple keys would silently drop all but one under how
  evaluation/runner.py picks a key out of each metric's return dict.
  This is a first-pass scorer: exact string match after light normalization,
  no fuzzy numeric/date tolerance, no schema validation.
- src/dataset/financebench.py: DEFAULT_PDF_DIR was hardcoded to a
  contributor's local machine path. Now reads FINANCEBENCH_PDF_DIR (or
  defaults to data/financebench-pdfs/ under the repo root) and documents the
  PDF-vs-fallback context-source risk directly in the comment.
- config/experiments.yaml: financebench_baseline_deepseekv4{flash,pro}
  (Compress, n=150/seed=42, matching the existing GPT-5.2 FinanceBench
  entries) and extract_longextractbench_deepseekv4{flash,pro} (Extract,
  n=50 = full set).

Verified: registry resolves all new dataset/task/metric names; extraction
task + field metrics tested against synthetic perfect/partial/unparseable
predictions (1.0 / 0.667 / 0.0 as expected); live HF API call confirms the
LongExtractBench repo has exactly 50 example folders. Not yet run against a
live DeepSeek key — blocked on account balance (see PR discussion).
- src/models/openrouter_client.py: new provider routing DeepSeek (and any
  other OpenRouter-hosted model) through openrouter.ai's OpenAI-compatible
  API. Disables upstream reasoning mode by default — confirmed live that
  DeepSeek V4 Flash/Pro on OpenRouter default to an extended-thinking mode
  that burns completion tokens on a hidden `reasoning` field before (or
  instead of, if max_tokens runs out first) visible `content`. At
  max_tokens=10 with reasoning left on, content came back null with
  finish_reason="length" — silently breaks any low-max_tokens task like
  Classify. Reports OpenRouter's actual per-call cost from the response
  usage block when available, falling back to static PricingInfo otherwise.
- src/models/base.py: pricing entries for the OpenRouter-routed model ids.
- Fixed a real, pre-existing bug affecting every non-Summarization baseline
  task: evaluation/runner.py unconditionally passes model_mode into every
  task's build_prompt(), but only SummarizationTask popped it before
  forwarding **kwargs to GenerationInput() (which has no such field).
  Confirmed live against RAGTask — `financebench_baseline_gpt52` and any
  other rag_qa/retrieval_task/classification/extraction experiment would
  raise "GenerationInput.__init__() got an unexpected keyword argument
  'model_mode'" the moment it actually ran. Fixed by popping model_mode in
  RAGTask, RetrievalTask, ClassificationTask, and ExtractionTask.
- src/dataset/cuad.py: CUAD (Contract Understanding Atticus Dataset) loader,
  replacing LongExtractBench for the Extract task's DeepSeek entries — CUAD
  is the dataset actually used for Extract on the existing Grok/OpenAI/Gemini
  benchmark pages (F1 metric on contract clause spans). Loaded from
  chenghao/cuad_qa, a SQuAD-format Parquet mirror (the official
  theatticusproject/cuad-qa loader script no longer loads under current
  `datasets` versions, which dropped script-based loaders).
- config/experiments.yaml: extract_cuad_baseline_deepseekv4{flash,pro}
  (rag_qa task, exact_match+f1, n=150/seed=42 matching FinanceBench's
  pattern). LongExtractBench entries are left in place but not the active
  path for Extract — see PR discussion for why.

Why the CUAD switch: LongExtractBench documents run ~250K input tokens with
schemas that need far more than 12K output tokens to fully extract as valid
JSON — piloted at 8 examples, hit 0% parse rate even at 12K max_tokens, one
call took 33 minutes and $0.10. CUAD's ~11K-char contexts are 20x cheaper
and 100x+ faster per call (8.2s vs 6.9min avg in pilot testing) and, more
importantly, match what actually produced the other models' Extract numbers.

Verified: full pilot sweep (8 examples/model) now passes cleanly across all
four tasks — Summarize (QMSum), Classify (CLINC OOS), Compress
(FinanceBench), Extract (CUAD) — for both deepseek-v4-flash and
deepseek-v4-pro via OpenRouter, with real costs/latencies/scores recorded
(e.g. CUAD F1 0.305 for V4 Flash, QMSum ROUGE-L 0.192, CLINC accuracy 0.75
on n=8 pilots). LongExtractBench's dataset/task/metric code is left in the
repo (dataset structurally verified, scoring logic unit-tested) in case a
future pass wants to solve the token-budget problem properly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants