Repository navigation
fix(topic_relevance_llm): label topic config in prompt, support reasoning models - #173
Merged
Merged
Conversation
The v1/v2/v3 templates were appended to the raw `configuration` text with
no header, so the model received a bare list (e.g. "cricket\nglific")
followed by rules about "forbidden topic DESCRIPTIONS" and had to guess
that the list was the forbidden set. Keyword-style configs typically
scored 2 (ambiguous) and passed the default threshold.
Each template now carries a `{{TOPIC_CONFIGURATION}}` placeholder inside a
labelled block (allowed / forbidden / both), matching the convention the
older `topic_relevance` validator already uses. The validator substitutes
the config into the template instead of concatenating, and a template
without the placeholder is rejected at load time.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one): 1
Please check the settings in the CodeRabbit UI or the
|
…ng guide Markdown section headers, XML-tagged config block placed below the instructions, explicit do/don't rules without all-caps, and three few-shot examples per template. Same JSON contract and scoring scale. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Reasoning models such as gpt-5.6-sol (default after #172) can return content=None; strip() on it raised and surfaced as "LLM call failed" instead of an unparseable-response failure. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…=medium Reasoning models count hidden reasoning tokens against max_tokens, so a fixed cap risks truncating the JSON verdict. Remove the cap and pass reasoning_effort=medium when the model supports reasoning. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Ayush8923
approved these changes
Oct 26, 2026
2 tasks done
Prajna1999
added a commit
that referenced
this pull request
Oct 27, 2026
…ing models #173 dropped the cap because a 300-token limit shared with hidden reasoning tokens produced empty replies, leaving no ceiling on cost or latency. Restore it at 1024 (2048 with fallback_message), about 4x the highest gpt-5.6-sol usage seen in a 300-call live eval, so it only stops runaway calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #174
topic_relevance_llmbuilt its system prompt by gluing the tenant'sconfigurationtext straight onto the scoring rules. Nothing told the model what that text was.What
gpt-4o-minisaw before, with a keyword-style config:What it sees now (
v2.md, restructured per OpenAI's prompt engineering guide and GPT-4.1 prompting guide):Guide tactics applied, same across
v1.md(allowed),v2.md(forbidden),v3.md(both):<example>tags1, 2, 3 as an integer, "do not add commentary"reasoningfield beforescope_violation(unchanged)JSON contract, field names, and scoring scale are unchanged, so
_validateparsing andthresholdsemantics are untouched.Why it surfaced now: #166 flipped the default schema to v2 (forbidden), and kaapi-backend #1245 started sending inline specs, so tenants hit this path with bare keyword lists and no stored descriptive config.
Evidence
Before:
main, realgpt-4o-mini, config"Cricket scores\ncricket\ncricket match\nglific\nGlific flows",prompt_schema_version=2,threshold=2After: this branch, same config and queries
Single sample per cell; the LLM judge is non-deterministic.
Before: no test pinned where the config lands in the prompt
After:
test_system_prompt_contains_topic_configasserts the config sits inside<allowed_topics>after# Instructionsand# Examples, and that the placeholder is goneAfter (restructured prompts, second commit): same config and queries, real
gpt-4o-miniSide effect: the Hinglish query's
localized_fallbackcame back transliterated ("Maaf kijiye, main is vishay par...") rather than English or Devanagari, matching the fallback template's intent. Single sample; not a guarantee.gpt-5.6-sol readiness (chore(llm): switch default LLM from gpt-4o/gpt-4o-mini to gpt-5.6-sol #172 makes it the default
llm_callable; this PR does not touch model defaults orllm_utils, chore(llm): switch default LLM from gpt-4o/gpt-4o-mini to gpt-5.6-sol #172 owns those)_validate kwargs = {model, messages} - kwargs["max_tokens"] = 300 (600 with fallback_message) + if supports_reasoning(model): kwargs["reasoning_effort"] = "medium" if supports_response_format(model): kwargs["response_format"] = json_object completion(**kwargs) - content.strip() + (content or "").strip()Before: fixed
max_tokenscap shared with hidden reasoning tokens;Nonecontent raisedAttributeErrorreported asLLM call failedAfter: no cap,
reasoning_effort=mediumon reasoning models only,Nonecontent →LLM returned an unparseable response. Tests:test_no_max_tokens_cap_is_sent,test_reasoning_model_gets_medium_effort,test_non_reasoning_model_omits_reasoning_effort,test_fails_gracefully_when_content_is_noneCricket-config run on
gpt-5.6-solwithreasoning_effort=medium, same five queries: three forbidden score 1, two health score 3, Hinglish fallback transliterated. Same verdicts asgpt-4o-mini.Merge Danger
Door: two-way
Prompt text, one substitution call, and two request kwargs. Revert restores the old concatenation and the 300-token cap with no data or schema impact.
Blast Radius: verdicts
Every
topic_relevance_llmcall on all three schema versions now sends a different system prompt. Expect more blocks on keyword-style configs (the intended fix) and possibly a shift on borderline queries for tenants who already wrote descriptive configs. Storedllm_prompt_configsare unaffected in content, only in how they are framed. No output cap now: a reasoning model that spends heavily on hidden reasoning raises cost and latency per call with no ceiling. Token cost per call rises by roughly 450 input tokens (headers, rules, and three few-shot examples); atgpt-4o-minipricing that is well under a tenth of a cent per call. The few-shot examples use cooking, cricket, Glific, and maternal-health topics, so tenants whose real config overlaps those should sanity-check borderline queries after deploy.🤖 Generated with Claude Code