AnswerDotAI / AnswerDotAI/fastllm
Thinking content not captured from Openrouter streaming
- Dominant language
- Jupyter Notebook
- Stars
- 15
- Forks
- 4
- Avg merge
- 1h 23m
- Merged PRs (30d)
- 12
Description
`norm_sse_event` in `openai_chat.py` (line 62) extracts thinking via `dlt.get('reasoning_content')`, but Openrouter returns reasoning content under the field name `reasoning`:
```json
{"delta": {"reasoning": "We", "reasoning_details": [...]}}
```
As a result, when calling DeepSeek (and other models) through OpenRouter, `reasoning_tokens` are counted but `Completion.message.content` never contains `PartType.thinking` — thinking is always `None`.
Reproduction, using the `stream` function defined in the readme.
```python
r = await stream([user('What is 2+2?')], model='deepseek/deepseek-v4-flash', vendor_name='openrouter', reasoning_effort='high')
```
No brains will be output. `r.usage` shows reasoning tokens have been used, and `r.raw['deltas'][0]` will show the reasoning content being stored under the key `'reasoning'`.
```py
Delta(text='', thinking=None, refusal=None, tool_calls=[], citations=[], server_tool_result=None, finish_reason=None, usage=None, raw={'id': 'gen-1783478615-YqwCVIhusHs2HE5dOmrs', 'object': 'chat.completion.chunk', 'created': 1783478615, 'model': 'deepseek/deepseek-v4-flash-20260423', 'provider': 'AtlasCloud', 'choices': [{'index': 0, 'delta': {'content': '', 'role': 'assistant', 'reasoning': 'We', 'reasoning_details': [{'type': 'reasoning.text', 'text': 'We', 'format': 'unknown', 'index': 0}]}, 'finish_reason': None, 'native_finish_reason': None}]})
```
I've also tested this with Hunyuan 3 on Openrouter, and it also returns.
```py
Delta(text='', thinking=None, refusal=None, tool_calls=[], citations=[], server_tool_result=None, finish_reason=None, usage=None, raw={'id': 'gen-1783478615-YqwCVIhusHs2HE5dOmrs', 'object': 'chat.completion.chunk', 'created': 1783478615, 'model': 'deepseek/deepseek-v4-flash-20260423', 'provider': 'AtlasCloud', 'choices': [{'index': 0, 'delta': {'content': '', 'role': 'assistant', 'reasoning': 'We', 'reasoning_details': [{'type': 'reasoning.text', 'text': 'We', 'format': 'unknown', 'index': 0}]}, 'finish_reason': None, 'native_finish_reason': None}]})
```
And with Gemma 4 31B.
```py
Delta(text='', thinking=None, refusal=None, tool_calls=[], citations=[], server_tool_result=None, finish_reason=None, usage=None, raw={'id': 'gen-1783479613-fRKBw5Vto4vcuUwNVjUZ', 'object': 'chat.completion.chunk', 'created': 1783479613, 'model': 'google/gemma-4-31b-it-20260402', 'provider': 'WandB', 'choices': [{'index': 0, 'delta': {'content': '', 'role': 'assistant', 'reasoning': 'The', 'reasoning_details': [{'type': 'reasoning.text', 'text': 'The', 'format': 'unknown', 'index': 0}]}, 'finish_reason': None, 'native_finish_reason': None}]})
```
Due to my region, I'm unable to try Sonnet/Opus/GPT through Openrouter.
This is in contrast to using, say, the Deepseek API directly, which will return `'reasoning_content'` when looking at `r.raw['deltas'][0]`.
```py
Delta(text=None, thinking='', refusal=None, tool_calls=[], citations=[], server_tool_result=None, finish_reason=None, usage=None, raw={'id': 'c7211552-934d-47be-9bdb-9addb3a0d7b9', 'object': 'chat.completion.chunk', 'created': 1783478377, 'model': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'choices': [{'index': 0, 'delta': {'role': 'assistant', 'content': None, 'reasoning_content': ''}, 'logprobs': None, 'finish_reason': None}], 'usage': None})
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in openai_chat.py at norm_sse_event around line 62, then run the stream reproduction from the README and inspect r.raw['deltas'][0]. Done means OpenRouter reasoning under 'reasoning' is captured so Completion.message.content contains PartType.thinking, while the existing direct DeepSeek 'reasoning_content' behavior remains intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- ai, api
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100