FIX: Preserve Responses API reasoning state - #162
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR hardens the OpenAI “Responses API” integration for the chatbot by preserving provider-native response state across tool loops, validating response statuses, and introducing clearer/typed termination paths for token budgets and chain limits. It also refines URL integrity validation so it only activates once trusted provenance is established, and adds new admin settings for reasoning summaries and loop/token constraints.
Changes:
- Preserve Responses API output items for continuation while exposing only reasoning summaries in the inner-thoughts trace, and treat reasoning-only turns as intermediate continuation state.
- Replace heuristic loop termination with bounded iteration/tool/URL-repair limits plus aggregate token-budget enforcement, using typed non-retryable errors for deterministic stop conditions.
- Improve URL integrity checking by collecting/normalizing trusted URL provenance from tool outputs, and validating/repairing URLs under controlled limits.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/lib/bot/open_ai_bot_basic_spec.rb | Adds coverage for Responses status handling, refusals, incomplete outputs, and provider-default token budgets. |
| spec/lib/bot/open_ai_agent_spec.rb | Expands RAG coverage for reasoning summaries, continuation behavior, tool error handling, URL provenance/validation, and loop limits. |
| spec/lib/bot/bot_spec.rb | Ensures quota consumption includes accumulated tokens even when responses fail. |
| spec/jobs/regular/chatbot_reply_spec.rb | Verifies non-retryable failures return immediate localized replies (and include inner thoughts when enabled). |
| plugin.rb | Bumps plugin version to 1.8.0. |
| lib/discourse_chatbot/post/post_reply_creator.rb | Uses responses_text for reasoning-model title extraction with validation. |
| lib/discourse_chatbot/functions/escalate_to_staff_function.rb | Uses responses_text for reasoning-model escalation title extraction with validation. |
| lib/discourse_chatbot/bots/open_ai_bot_rag.rb | Implements preserved Responses context, reasoning-summary extraction, bounded loops/limits, improved URL provenance + validation, and tool error continuation. |
| lib/discourse_chatbot/bots/open_ai_bot_basic.rb | Switches to shared completion-token limit helper and validated responses_text; normalizes token accounting. |
| lib/discourse_chatbot/bots/open_ai_bot_base.rb | Adds typed non-retryable errors, Responses parameter/budget handling, Responses validation, and a shared completion-token limit helper. |
| lib/discourse_chatbot/bot.rb | Moves quota consumption to an ensure block to charge usage even on failures. |
| config/settings.yml | Adds new settings for reasoning summaries and chain/token/tool/url-repair limits; allows provider defaults via 0. |
| config/locales/server.en.yml | Adds admin-facing descriptions for new settings and new localized terminal error messages. |
| app/jobs/regular/chatbot_reply.rb | Treats token-budget/chain-limit errors as terminal (non-retryable) and posts localized replies. |
| app/controllers/discourse_chatbot/chatbot_controller.rb | Uses validated responses_text and shared completion-token limit helper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
Previously, Responses API tool loops discarded ordered provider state and could treat incomplete or refused responses as successful blank replies.
This change preserves native response items for model continuation while exposing only readable reasoning summaries in the inner-thoughts trace. Reasoning-only turns are treated as intermediate state and continued until the model returns a message or tool call. It also validates Responses statuses across every call site, retains rejected URL responses during repair attempts, and replaces the transcript-length heuristic with bounded model, tool, and URL-repair iterations.
Compatible endpoints can disable reasoning summaries with the new
chatbot_open_ai_include_reasoning_summariessetting. Chat Completions retain their existing output budget, Responses use a dedicated 25,000-token reasoning/output budget, and RAG loops have a 100,000-token aggregate threshold. Setting any per-call budget to0uses the provider default, while setting the aggregate threshold to0disables it. Usable partial text is returned, but truncated tool calls and reasoning runs with no visible output raise a specific token-budget error.Token-budget and deterministic chain-limit failures are terminal job outcomes instead of retryable provider failures. Iteration, tool-count, URL-repair, and disabled-tool limits use typed non-retryable errors, while provider and malformed-response failures remain retryable. Reported usage is charged even when response validation fails, and users receive a localized explanation when a configured limit is reached.
Administrators can configure iteration, tool-call, and URL-repair limits through consistently named
chatbot_chain_of_thought_max_*settings. Their defaults preserve the previous fixed limits, and URL repairs can be disabled by setting their limit to0.Reasoning summaries returned with an incomplete Responses result are preserved before terminal token-limit handling and carried into the job's inner-thought trace.
URL integrity validation now starts when a trusted tool establishes URL provenance, rather than after any second model iteration. Declared provenance and URLs present in any tool result are collected through one contract, while unrelated tools and reasoning-only continuations do not activate validation. Forum links must use the local hostname and a recognized topic/post path; external links are normalized before comparison so host casing, fragments, root paths, trailing slashes, and Markdown punctuation do not cause false mismatches. Length-limited partial responses are validated before being returned.
Malformed tool arguments are returned to the model as tool errors instead of escaping the chain and restarting the job. A successful image upload is returned directly only for a single paint/edit call, while mixed tool batches always continue with every result regardless of call order. Completed Responses messages with no visible text or refusal are rejected instead of creating blank replies.
Tests:
bin/rspec plugins/discourse-chatbot/spec— 148 examples, 0 failuresgit diff --checkand YAML parsing — cleanThe plugin's standalone
bin/lint --fixwrapper remains blocked by its existing lint bundle:syntax_treeis absent and its RuboCop Discourse version does not recognize the current core cops.