Skip to content

fix(workflow-operator): send provider-specific model id on HF text-gen chat fallback routes - #6972

Open
PG1204 wants to merge 6 commits into
apache:mainfrom
PG1204:fix/hf-textgen-provider-model-id
Open

fix(workflow-operator): send provider-specific model id on HF text-gen chat fallback routes#6972
PG1204 wants to merge 6 commits into
apache:mainfrom
PG1204:fix/hf-textgen-provider-model-id

Conversation

@PG1204

@PG1204 PG1204 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

Fixes two related bugs in the HuggingFace inference operator's provider-fallback loop (_post_with_fallback in the generated Python, emitted by PythonCodegenBase.scala) that break the text-generation / image-text-to-text path, which is the operator's default task.

  1. Wrong model id on provider-scoped chat routes. The chat branch posted the same payload, carrying "model": self.MODEL_ID (the HF Hub ID), to every provider's route (router.huggingface.co/{provider}/...). Provider-scoped routes require the provider's own model name (providerId, e.g. the Hub's Qwen/Qwen2.5-72B-Instruct is a different string on Groq or Fireworks), which _resolve_providers already fetches and _call_provider already uses for the other task families. Providers whose internal name differs from the Hub ID rejected every request with 400/404, so fallback only succeeded when the names happened to match. The chat branch now posts a per-attempt copy with the model overridden:

    chat_payload = {**pipeline_payload, "model": provider_id}

    (a copy, not in-place mutation, pipeline_payload is reused for the next provider
    attempt).

  2. Malformed hf-inference chat URL. The chat URL for hf-inference was built as hf-inference/v1/chat/completions, but hf-inference expects the model in the URL path: hf-inference/models/{model-id}/v1/chat/completions (the form this file already uses for pipeline tasks). Since hf-inference sorts first in PROVIDER_COST_PRIORITY, the cheapest provider failed on every row, wasting a doomed request (up to the 120 s timeout) per row before falling through.

Net effect: text-gen rows now succeed on the cheapest live provider instead of failing with "All inference providers failed" or silently drifting to pricier providers. No behavior change for other task families - _call_provider already handled them correctly.

Any related issues, documentation, discussions?

Closes #6965

How was this PR tested?

Added a generated-code test to HuggingFaceInferenceOpDescSpec ("send the provider-specific model id on provider-scoped chat routes") pinning both fixes: the corrected hf-inference chat URL, and the chat branch posting the per-provider payload copy (asserted as an anchored two-line block so pipeline routes that legitimately post pipeline_payload directly stay unaffected).

Run:

sbt "WorkflowOperator/testOnly org.apache.texera.amber.operator.huggingFace.*"

Full HF package passes (123 tests, 12 suites), including PythonCodeRawInvalidTextSpec, which py_compiles the generated Python of all 117 Python operators, verifying the edited template still emits syntactically valid Python. scalafmt clean.

Was this PR authored or co-authored using generative AI tooling?

Co-authored with Claude Fable 5 in compliance with ASF.

@github-actions

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @anishshiva7
    You can notify them by mentioning @anishshiva7 in a comment.

@codecov-commenter

codecov-commenter commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.52%. Comparing base (06f8e42) to head (1dbfc30).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #6972      +/-   ##
============================================
- Coverage     78.98%   78.52%   -0.46%     
- Complexity     3785     3786       +1     
============================================
  Files          1160     1160              
  Lines         46105    46031      -74     
  Branches       5115     5110       -5     
============================================
- Hits          36414    36148     -266     
- Misses         8069     8257     +188     
- Partials       1622     1626       +4     
Flag Coverage Δ *Carryforward flag
access-control-service 70.00% <ø> (ø)
agent-service 76.76% <ø> (ø) Carriedforward from ebc78fd
amber 72.18% <ø> (+<0.01%) ⬆️
computing-unit-managing-service 20.49% <ø> (ø)
config-service 66.66% <ø> (ø)
file-service 67.21% <ø> (ø)
frontend 82.64% <ø> (-0.36%) ⬇️ Carriedforward from ebc78fd
notebook-migration-service 78.94% <ø> (ø)
pyamber 92.45% <ø> (-2.94%) ⬇️ Carriedforward from ebc78fd
workflow-compiling-service 26.31% <ø> (ø)

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 2 better · 🔴 5 worse · ⚪ 8 noise (<±5%) · 0 without baseline

Compared against main 06f8e42 benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 397 0.242 25,799/33,711/33,711 us 🟢 -39.6% / 🔴 +113.3%
🔴 bs=100 sw=10 sl=64 928 0.566 105,438/160,379/160,379 us 🔴 +15.9% / 🔴 +49.4%
🔴 bs=1000 sw=10 sl=64 1,072 0.654 924,898/1,100,901/1,100,901 us 🔴 +15.8% / 🔴 +7.1%
Baseline details

Latest main 06f8e42 from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 397 tuples/sec 382 tuples/sec 787.55 tuples/sec +3.9% -49.6%
bs=10 sw=10 sl=64 MB/s 0.242 MB/s 0.233 MB/s 0.481 MB/s +3.9% -49.7%
bs=10 sw=10 sl=64 p50 25,799 us 24,514 us 12,255 us +5.2% +110.5%
bs=10 sw=10 sl=64 p95 33,711 us 55,830 us 15,802 us -39.6% +113.3%
bs=10 sw=10 sl=64 p99 33,711 us 55,830 us 19,008 us -39.6% +77.4%
bs=100 sw=10 sl=64 throughput 928 tuples/sec 946 tuples/sec 997.81 tuples/sec -1.9% -7.0%
bs=100 sw=10 sl=64 MB/s 0.566 MB/s 0.577 MB/s 0.609 MB/s -1.9% -7.1%
bs=100 sw=10 sl=64 p50 105,438 us 103,071 us 100,690 us +2.3% +4.7%
bs=100 sw=10 sl=64 p95 160,379 us 138,428 us 107,316 us +15.9% +49.4%
bs=100 sw=10 sl=64 p99 160,379 us 138,428 us 113,823 us +15.9% +40.9%
bs=1000 sw=10 sl=64 throughput 1,072 tuples/sec 1,100 tuples/sec 1,030 tuples/sec -2.5% +4.0%
bs=1000 sw=10 sl=64 MB/s 0.654 MB/s 0.671 MB/s 0.629 MB/s -2.5% +4.0%
bs=1000 sw=10 sl=64 p50 924,898 us 913,947 us 981,213 us +1.2% -5.7%
bs=1000 sw=10 sl=64 p95 1,100,901 us 950,611 us 1,027,605 us +15.8% +7.1%
bs=1000 sw=10 sl=64 p99 1,100,901 us 950,611 us 1,055,466 us +15.8% +4.3%
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,503.56,200,128000,397,0.242,25798.67,33711.04,33711.04
1,100,10,64,20,2155.39,2000,1280000,928,0.566,105437.88,160378.97,160378.97
2,1000,10,64,20,18664.22,20000,12800000,1072,0.654,924897.74,1100900.71,1100900.71

@Yicong-Huang

Copy link
Copy Markdown
Contributor

@carloea2 I think the need to programmatically test operators becomes higher... There are more and more efforts on editing operators logic. It will be good to have the tests in CI soon.

@carloea2

Copy link
Copy Markdown
Contributor

I am working on it, I will raise a discussion about it soon.

@PG1204

PG1204 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

/request-review @xuang7

@github-actions
github-actions Bot requested a review from xuang7 July 28, 2026 19:48

@xuang7 xuang7 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@xuang7
xuang7 added this pull request to the merge queue Jul 28, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 28, 2026
@PG1204

PG1204 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

@xuang7 github merge bot removed the pr from merge queue, requesting to add this pr to merge queue again. Thanks!

@PG1204
PG1204 requested a review from xuang7 July 28, 2026 22:56
@Yicong-Huang Yicong-Huang added the release/v1.2 back porting to release/v1.2 label Jul 28, 2026
Comment on lines +204 to +215
| if provider_name == "hf-inference":
| # hf-inference expects the model in the URL path, like the
| # pipeline route below.
| url = f"https://router.huggingface.co/hf-inference/models/{self.MODEL_ID}/v1/chat/completions"
| else:
| route = self.CHAT_ROUTES.get(provider_name, "v1/chat/completions")
| url = f"https://router.huggingface.co/{provider_name}/{route}"
| # Provider-scoped routes need the provider's own model name
| # (providerId), not the HF Hub ID. Copy instead of mutating:
| # pipeline_payload is reused for the next provider attempt.
| chat_payload = {**pipeline_payload, "model": provider_id}
| resp = requests.post(url, headers=json_headers, json=chat_payload, timeout=120)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

might be a todo for future PR: this is python code gen base class, should this hugging face specific logic moved to somewhere else?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point, so to clarify the current state: despite the Base in the name, this isn't a shared/cross-operator base class. It lives in ...operator.huggingFace.codegen and is referenced only within that package. The Base refers to it being the shared base of the HF per-task codegens, as it holds the common template and provider-fallback scaffolding that all of the TaskCodegen implementations (TextGenCodegen, ImageTaskCodegen, AudioTaskCodegen, MediaGenCodegen, QaRankingCodegen) plug their per-task payload/parse snippets into. So the Hugging Face-specific logic is already scoped to the HF package.

You're right that the name reads generic, though. I'd rather keep this PR focused on the model-id fix, but i can do a naming-clarity follow-up: e.g. renaming to HuggingFaceCodegenBase (or HuggingFacePythonTemplate) so its scope is obvious at a glance. This can be tracked as a separate follow up task.

@xuang7 xuang7 removed the release/v1.2 back porting to release/v1.2 label Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HuggingFace inference operator: text-gen provider fallback sends Hub model ID instead of providerId to provider-scoped chat routes

5 participants