DeepSeek rejects Hopper structured output through the OpenAI provider
- Dominant language
- Python
- Stars
- 5.9k
- Forks
- 516
- Avg merge
- 22m
- Merged PRs (30d)
- 5
Description
## Problem
Using ARTEMIS's OpenAI provider with `deepseek-flash` at `https://api.deepseek.com` works for ordinary chat, but a real Flash phone task fails when Hopper requests its typed app-selection result. I reproduced two distinct HTTP 400 responses:
| Request | Result |
| --- | --- |
| Default ChatOpenAI structured output | `This response_format type is unavailable now` |
| Function calling alone | `Thinking mode does not support this tool_choice` |
| Function calling + `thinking.type="disabled"` | HopperOutput parses successfully |
Test environment: Windows, Python 3.12, physical Android 16 device, Accessibility Helper 1.2.0; base `086078819209c7139d6f833cfdc6d5cc80d9f19a`. Tested September 12, 2026 (UTC+08). A repository search for DeepSeek found no existing matching issue or PR before filing.
## Reproduction
Set `DEEPSEEK_API_KEY` in the environment and run with the repository dependencies:
```python
import os
from artemis.agents.hopper.hopper import HopperOutput
from artemis.llm.router import ModelEndpoint, ModelFactory, ModelProvider
model = ModelFactory.create_model(ModelEndpoint(
provider=ModelProvider.OPENAI,
model_name="deepseek-flash",
api_key=os.environ["DEEPSEEK_API_KEY"],
api_base="https://api.deepseek.com",
timeout_seconds=20,
max_tokens=512,
))
prompt = "Find Settings in com.android.settings; return found=true and output=com.android.settings."
model.with_structured_output(HopperOutput).invoke(prompt)
```
Expected: a parsed HopperOutput. Actual: HTTP 400 rejecting the response format. Changing the last line to `model.with_structured_output(HopperOutput, method="function_calling").invoke(prompt)` reveals the second 400. Passing `extra_body={"thinking": {"type": "disabled"}}` to that invocation succeeds.
## Observed phone impact and verified fix
Task: open Android battery settings and read the visible percentage, without changing settings. The original OpenAI-alias configuration failed at Hopper and exhausted retries; the bounded SDK run timed out. A dedicated DeepSeek provider that disables thinking and defaults typed results to function calling completed the task in five Flash turns. The reported 87% matched the battery page inspected locally.
This validation used generic Explorer (`pro` grounding), disabled screenshot step summarization and video analysis, and an eight-turn limit. An earlier post-fix attempt failed at that limit with no focused app while the notification shade was visible; it is not counted as a successful run. After confirming the device was unlocked and returning to the launcher, the same task passed.
The proposed fix uses `DEEPSEEK_API_KEY` and leaves existing OpenAI defaults unchanged. It does not establish full Pro, Gemini-native utilities, video, or long-history compatibility. The accompanying PR includes runnable reproductions, sanitized before/after logs, eight offline regression tests, and the full baseline comparison. No phone screenshots, UI trees, raw traces, or credentials are attached.
Contributor guide
Research direction
Start with artemis/llm/router and the OpenAI provider path, then inspect artemis/agents/hopper/hopper.py for HopperOutput structured results. Reproduce the DeepSeek responses with the issue's model configuration and run the accompanying offline regression tests. Done means DeepSeek typed output succeeds without changing existing OpenAI defaults.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, python
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100