#14323: feat: add forcePrependThinkTag option for reasoning models
agents
stale
size: M
Cluster:
Agent Thinking Defaults Enhancement
## Summary
Adds a user-configurable option to force-prepend an opening `<think>` tag to model responses. This is useful for reasoning models that do not reliably emit think tags (e.g., GLM-4.7-Flash, MiniMax-M2.1, step-3.5-flash).
## Changes
- Added `forcePrependThinkTag` option to `AgentModelEntryConfig` (per-model) and `AgentDefaultsConfig` (global)
- Added Zod schema validation for the new config option
- Implemented force prepend logic in `handleMessageUpdate` that runs before reasoning parser
- Added state flag `thinkTagPrepended` to track when tag has been prepended
- Includes unit tests
## Configuration
```yaml
# Global setting (applies to all models)
forcePrependThinkTag: true
# Per-model override
models:
glm-4.7-flash:
forcePrependThinkTag: true
```
## Behavior
- Default: `false` (OFF)
- When enabled, prepends `<think>` tag to the first chunk of the assistant message
- Only prepends once per assistant message
- Runs before reasoning parser processes the content
- Works with streaming responses
Closes #14151
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
Adds `forcePrependThinkTag` config option and subscribe-level implementation for prepending `<think>` tags to model responses for reasoning models that don't reliably emit them. Config types, Zod schema validation, state tracking, and unit tests are all present.
- **Missing wiring**: The option is defined in config types and the subscribe function accepts it, but the runner layer (`pi-embedded-runner/run/attempt.ts`, `run/types.ts`, `run/params.ts`) doesn't thread it through. The `subscribeEmbeddedPiSession()` call never receives `forcePrependThinkTag`, so the feature won't activate from user configuration — only tests (which call subscribe directly) exercise this code path.
- **Double-tag risk**: No guard against models that intermittently emit `<think>` tags. If the model does emit one, the prepend produces `<think><think>...` which causes the tag parser's state machine to leak a raw `<think>` tag into visible output.
<h3>Confidence Score: 2/5</h3>
- Feature is non-functional in production due to missing parameter threading through the runner layer.
- The subscribe-level implementation and tests are sound, but the config-to-subscribe wiring is completely absent. The feature will silently do nothing when users enable it via configuration. Additionally, there's no guard against double-prepending when a model already emits a think tag.
- `src/agents/pi-embedded-runner/run/attempt.ts` (missing `forcePrependThinkTag` passthrough), `src/agents/pi-embedded-runner/run/types.ts` and `run/params.ts` (missing type definitions), and `src/agents/pi-embedded-subscribe.handlers.messages.ts` (double-tag guard needed).
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#17304: feat(gemini): robust handling for non-XML reasoning headers (`Think...
by YoshiaKefasu · 2026-02-15
75.5%
#21558: config: support agents.list[].thinkingDefault
by Uarmagan · 2026-02-20
75.4%
#6678: fix(agents): support thinking tags with attributes
by GHesericsu · 2026-02-01
73.6%
#6685: fix: suppress thinking leak for Synthetic reasoning models
by AkiLetschne · 2026-02-01
73.2%
#16298: feat(xai): switch grok-4-1-fast variants by thinking level
by avirweb · 2026-02-14
72.8%
#16899: feat(config): per-agent and per-model thinking defaults
by jh280722 · 2026-02-15
72.4%
#22797: Feat/auto thinking mode
by jrthib · 2026-02-21
72.3%
#6559: Fix LiteLLM reasoning-tag handling + fallback to <think> content
by Najia-afk · 2026-02-01
72.3%
#8455: feat: add thinking/model config to skills.entries
by tsukhani · 2026-02-04
72.1%
#11297: fix: add thinkingFormat 'qwen' compat for bailian/dashscope providers
by AdJIa · 2026-02-07
72.0%