Skip to content

Conversation

@ksylvan
Copy link
Collaborator

@ksylvan ksylvan commented Jul 16, 2025

Feature: Optional Hiding of Model Thinking Process with Configurable Tags

Summary

This pull request implements a new feature to suppress "thinking" output from Claude models. The feature allows users to hide internal reasoning text that appears between configurable start and end tags (defaulting to <think> and </think>), while still displaying the final response to the user.

image

Related Issues

Closes #1458

Files Changed

  • internal/cli/chat.go: Modified output logic to handle suppressed thinking mode
  • internal/cli/example.yaml: Added configuration examples for the new thinking suppression feature
  • internal/cli/flags.go: Added new command-line flags and YAML configuration options for thinking suppression
  • internal/cli/flags_test.go: Added test coverage for the new thinking suppression flags
  • internal/core/chatter.go: Implemented core logic to suppress streaming output and filter thinking blocks
  • internal/core/chatter_test.go: Added comprehensive tests for the thinking suppression functionality
  • internal/domain/domain.go: Extended ChatOptions struct with thinking suppression configuration
  • internal/domain/think.go: New utility module for stripping thinking blocks from text
  • internal/domain/think_test.go: Test coverage for the thinking block stripping functionality

Code Changes

New Command-Line Flags

SuppressThink     bool   `long:"suppress-think" yaml:"suppressThink" description:"Suppress text enclosed in thinking tags"`
ThinkStartTag     string `long:"think-start-tag" yaml:"thinkStartTag" description:"Start tag for thinking sections" default:"<think>"`
ThinkEndTag       string `long:"think-end-tag" yaml:"thinkEndTag" description:"End tag for thinking sections" default:"</think>"`

Core Filtering Logic

if opts.SuppressThink {
    message = domain.StripThinkBlocks(message, opts.ThinkStartTag, opts.ThinkEndTag)
}

Streaming Output Control

if !opts.SuppressThink {
    fmt.Print(response)
}

Text Processing Function

func StripThinkBlocks(input, startTag, endTag string) string {
    if startTag == "" || endTag == "" {
        return input
    }
    pattern := "(?s)" + regexp.QuoteMeta(startTag) + ".*?" + regexp.QuoteMeta(endTag) + "\\s*"
    re := regexp.MustCompile(pattern)
    return re.ReplaceAllString(input, "")
}

Reason for Changes

This feature addresses the need to hide Claude's internal reasoning process from end users while still allowing the model to perform its thinking steps. When Claude models use thinking tags to show their reasoning process, users may want to see only the final answer without the intermediate steps. This is particularly useful for:

  1. Cleaner output in production environments
  2. Focusing on results rather than process
  3. Reducing cognitive load for end users
  4. Maintaining the benefits of model reasoning without exposing internal details

Impact of Changes

Positive Impacts:

  • User Experience: Provides cleaner, more focused output when thinking suppression is enabled
  • Flexibility: Configurable tags allow adaptation to different model behaviors
  • Backward Compatibility: Feature is opt-in and doesn't affect existing functionality
  • Performance: Minimal overhead when feature is disabled

Potential Concerns:

  • Regex Performance: The regex-based stripping could impact performance with very large responses
  • Tag Matching: Malformed or nested tags might cause unexpected behavior
  • Streaming Behavior: When thinking is suppressed, streaming effectively becomes disabled for the thinking portions

Test Plan

The changes include comprehensive test coverage:

  1. Unit Tests: New tests verify thinking block stripping with default and custom tags
  2. Integration Tests: Tests ensure the feature works end-to-end with the chatter system
  3. Flag Tests: Verification that command-line flags and YAML configuration work correctly
  4. Edge Cases: Tests handle empty tags and malformed input

Testing should verify:

  • Thinking blocks are properly removed from output
  • Custom tags work as expected
  • Streaming behavior is correct when suppression is enabled
  • Configuration via both CLI flags and YAML works
  • Performance with large responses containing thinking blocks

Additional Notes

  • The feature uses regex with the (?s) flag to handle multi-line thinking blocks
  • Default tags are <think> and </think> but can be customized
  • When thinking suppression is enabled, the final output logic treats it similarly to non-streaming mode
  • The regex pattern includes \s* to clean up whitespace after thinking blocks
  • All existing functionality remains unchanged when the feature is disabled

## CHANGES

- Add suppress-think flag to hide thinking blocks
- Configure customizable start and end thinking tags
- Strip thinking content from final response output
- Update streaming logic to respect suppress-think setting
- Add YAML configuration support for thinking options
- Implement StripThinkBlocks utility function for content filtering
- Add comprehensive tests for thinking suppression functionality
@ksylvan ksylvan requested a review from Copilot July 16, 2025 04:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Implements optional hiding of the model’s internal “thinking” output by allowing users to suppress text between configurable start/end tags.

  • Adds new CLI flags and YAML options for suppressing thinking output and customizing tags
  • Integrates core logic to filter out thinking blocks during streaming and finalize response
  • Provides tests and example configuration for the new suppress-think feature

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
internal/domain/think.go Utility function StripThinkBlocks to remove tagged thinking sections
internal/domain/think_test.go Unit tests for default and custom tag stripping
internal/domain/domain.go Extended ChatOptions with SuppressThink, ThinkStartTag, and ThinkEndTag
internal/core/chatter.go Conditional streaming and post-processing based on SuppressThink
internal/core/chatter_test.go Added TestChatter_Send_SuppressThink to verify suppress-think handling in Chatter.Send
internal/cli/flags.go Introduced suppress-think, think-start-tag, and think-end-tag flags and defaults
internal/cli/flags_test.go Tests for BuildChatOptions with the new thinking suppression flags
internal/cli/example.yaml Example YAML entries for suppress-think configuration
internal/cli/chat.go Adjusted CLI printing logic to ensure final output is shown when thinking is suppressed
Comments suppressed due to low confidence (3)

internal/cli/chat.go:45

  • [nitpick] Clarify this comment to accurately describe the logic, for example: "print the final result when streaming is disabled or when suppress-think is enabled."
		// print the result if it was not streamed already or suppress-think disabled streaming output

internal/cli/chat.go:44

  • Add a unit test for handleChatProcessing to verify that when SuppressThink is enabled (even with Stream=true), the final result is still printed.
	if !currentFlags.Stream || currentFlags.SuppressThink {

internal/domain/think.go:9

  • Add a unit test covering the branch where startTag or endTag is empty to ensure the input is returned unchanged.
	if startTag == "" || endTag == "" {

@ksylvan ksylvan merged commit d44bc19 into danielmiessler:main Jul 16, 2025
1 check passed
@ksylvan ksylvan deleted the 0715-suppress-think branch July 16, 2025 05:05
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.

[Feature request]: Suppress thinking block from thinking models

1 participant