Fix HuggingFaceProvider.generate() to apply chat template - #159
Fix HuggingFaceProvider.generate() to apply chat template#159Muhammad-Hashir-Code wants to merge 2 commits into
Conversation
generate() was sending raw, unformatted prompts directly to the model pipeline, bypassing the chat template that instruction-tuned models expect. This caused empty or truncated responses on endpoints like /ask-llm. Fixed by delegating to chat(), matching the pattern already used in BaseProvider.
|
Hi @chimosky and @walterbender, I am checking in on this pull request to see if you have had a chance to review it. Please let me know if there are any adjustments, code formatting updates, or tests you would like me to add before this is ready for approval. Thanks for your time! |
|
Hey @Muhammad-Hashir-Code I tested this locally with SmolLM-135M-Instruct on both main and this branch. |
|
Thanks for testing this thoroughly, @Noaman-Akhtar appreciate the independent verification, and good catch on the fallback concern. You're right that def generate(self, prompt: str, params=None) -> str:
if getattr(self._pipeline.tokenizer, "chat_template", None):
return self.chat([{"role": "user", "content": prompt}], params)
# fall back to the original raw-prompt behavior for models without a chat template
...This way, models with a chat template get the fix, while models without one keep working exactly as before. Worth noting too: all the models currently specified in |
|
@Muhammad-Hashir-Code Yes, a fallback would be useful. We want to keep the Hugging Face provider flexible so users can configure and use different models of their choice . |
generate() now checks for a chat_template on the tokenizer before delegating to chat(). If none is present, it falls back to the original raw-prompt pipeline call, preserving compatibility with base models.
|
@Noaman-Akhtar Just pushed a fix |
|
LGTM @mebinthattil , @chimosky |
Summary
HuggingFaceProvider.generate()sends prompts to the model pipeline without applying the tokenizer's chat template. Since the models used by this provider (including theDEV_MODEdefault,SmolLM-135M-Instruct) are instruction-tuned, they are trained to expect chat-formatted input and do not reliably produce meaningful output from a raw, unformatted string. In practice this surfaces as empty or truncated responses on/ask-llm, and intermittently on/ask(which callsgenerate()internally viaRAGAgent.run()).This PR fixes
generate()to apply the chat template consistently, by delegating to the already-correctchat()implementation.Root Cause
BaseProviderestablishes the correct pattern forgenerate():HuggingFaceProvider.chat()correctly applies the chat template before generation:However,
HuggingFaceProvideroverridesgenerate()with a separate implementation that calls the pipeline directly on the raw prompt, skipping the chat template entirely:For an instruction-tuned model, an unformatted prompt gives the model no clear signal for what to continue — it may emit an end-of-sequence token almost immediately (producing an empty string once the prompt is stripped from the output), or generate an incoherent/truncated continuation.
Call sites affected
Fix
HuggingFaceProvider.generate()now wraps the prompt as a single user message and delegates toself.chat(...), matching the pattern already established inBaseProvider:This removes the duplicated pipeline-calling logic (~15 lines) and ensures
generate()andchat()share a single, consistent code path for prompt formatting and generation.Testing
Reproduced locally against
DEV_MODE=1,DEV_MODEL_NAME=HuggingFaceTB/SmolLM-135M-Instruct, on an 8GB RAM / CPU-only machine.Before fix
After fix — same request
Regression check —
/ask(RAG pipeline)Verified
/askcontinues to return complete, non-empty responses after the change, confirming no regression in the retrieval path that also relies ongenerate().Scope
This change is isolated to
HuggingFaceProvider.generate()inapp/providers/huggingface.py. It does not modifychat(), the RAG pipeline logic inapp/ai.py, route handlers inapp/routes/api.py, or any other provider. No new dependencies, no config changes, no API contract changes.Checklist
/ask-llmempty answers)/ask