code-yeongyu / code-yeongyu/senpi
OpenAI-completions tools lose root type object when parameters use oneOf/anyOf (Console Go 400)
- Dominant language
- TypeScript
- Stars
- 429
- Forks
- 98
- Avg merge
- 5h 3m
- Merged PRs (30d)
- 526
Description
### Summary
Function tools whose `parameters` schema uses a combiner (`anyOf` / `oneOf` / `allOf`) are sent to OpenAI-compatible backends without the root `type: "object"` marker. `normalizeToolParametersForOpenAICompat` moves the root `type` into combiner branches and then deletes it (`moveTypeIntoCombinerBranches` in `packages/ai/src/utils/tool-schema-compat.ts`).
Gateways backed by Gemini-style validation (e.g. OpenCode Go / Zen proxying to Console) reject the first request:
```text
Error: 400: {"param":null,"type":"invalid_request_error","code":"invalid_request_error","message":"Error from provider (Console Go): Upstream request failed: [invalid_request_error] Invalid schema for function 'mcp__ast_grep_scan': schema must be a JSON Schema of 'type: \"object\"', got 'type: null'."}
```
### Reproduction
1. Use the ast-grep MCP `scan` tool, whose parameters schema is `type: "object"` plus a `oneOf` (ruleFile XOR inlineRules) contract.
2. Start a chat with any `opencode-go` model (for example `deepseek-v4-flash`).
3. The first request fails with the 400 above.
Verified locally: `normalizeToolParametersForOpenAICompat(scanSchema)` drops the top-level `type`, while `search` / `rewrite` (no combiner) keep it.
### Root cause
`moveTypeIntoCombinerBranches` deletes the parent `type` for every combiner schema. That behavior exists for Moonshot-flavored backends (Apitopia → Kimi / Moonshot), but OpenAI Chat Completions requires every function's `parameters` root to be a JSON Schema of `type: "object"`, and the Gemini-backed Console gateways enforce the same.
### Suggested fix
Restore the root `type: "object"` marker at the OpenAI-completions wire boundary (`convertTools` and `normalizeRequestToolSchemas`) instead of changing the Moonshot-flavored normalizer behavior. A PR with tests for both conversion paths is attached.
Contributor guide
Research direction
Start in packages/ai/src/utils/tool-schema-compat.ts, especially moveTypeIntoCombinerBranches, then trace the OpenAI-completions wire paths through convertTools and normalizeRequestToolSchemas. Add tests covering both conversion paths, confirming combiner schemas retain the root type: "object" while the existing Moonshot-flavored normalization behavior remains unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100