9router/src/app/api
Владимир Акимов 0c832c9c59
feat(gemini): convert OpenAI SSE to Gemini SSE format in /v1beta/models route (#225)
* fix(translator): filter nameless hosted tools when converting Responses API to Chat format

Codex CLI sends "hosted" tools (e.g. `request_user_input`) via the OpenAI
Responses API. These tools have no explicit `name` field. The previous
`body.tools.map()` pass propagated `name: undefined` into the resulting
Chat Completions function declarations, which then became anonymous
`functionDeclarations` after the OpenAI→Gemini translation step.

Gemini strictly requires every function declaration to have a valid name
and rejects the entire request with:

  GenerateContentRequest.tools[0].function_declarations[4].name:
  Invalid function name. Must start with a letter or an underscore.

Fix: filter out any Responses API tool that lacks a non-empty `name`
string before converting to `{ type: "function", function: { name, ... } }`.
Named function tools are unaffected; only unnamed hosted tools are skipped.

Fixes: Gemini 400 error when Codex CLI is routed through 9router.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(gemini): convert OpenAI SSE to Gemini SSE format in /v1beta/models route

The @google/genai SDK always uses :streamGenerateContent?alt=sse for chat
and expects Gemini SSE chunk format. The upstream handleChat returns OpenAI
SSE format, causing the SDK to crash on the [DONE] sentinel.

Changes:
- Add transformOpenAISSEToGeminiSSE() using TransformStream that converts
  each OpenAI SSE chunk (choices[0].delta) to Gemini SSE format
  (candidates[0].content.parts) on the fly
- Drop the OpenAI [DONE] sentinel (Gemini SSE ends by stream close)
- Map finish_reason -> finishReason, attach usageMetadata on final chunk
- Support reasoning_content -> thought: true parts for thinking models
- Refactor finishReasonMap to shared FINISH_REASON_MAP constant
- Fix streaming dispatch: stream=true now calls transformOpenAISSEToGeminiSSE
  instead of passing OpenAI SSE through raw

Fixes: SyntaxError: "[DONE]" is not valid JSON in Gemini CLI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-02 09:36:08 +07:00
..
auth feat(cloud): harden sync/auth flow, SSE fallback, and update changelog 2026-02-08 16:45:31 +07:00
cli-tools Fix MITM on window 2026-02-28 10:04:57 +07:00
cloud feat : 2026-02-21 16:42:46 +07:00
combos feat : 2026-02-21 16:42:46 +07:00
init Initial commit 2026-01-05 09:58:59 +07:00
keys feat : 2026-02-21 16:42:46 +07:00
models Fix MITM on window 2026-02-28 10:04:57 +07:00
oauth chore: Refactor CursorAuthModal to handle manual instructions for Windows users. 2026-02-28 12:12:49 +07:00
pricing feat(usage): implement cost tracking backend and pricing configuration 2026-01-09 17:48:42 +07:00
provider-nodes feat(cli-tools): update CLI tools and add new models 2026-02-06 20:53:20 +07:00
providers Fix MITM on window 2026-02-28 10:04:57 +07:00
settings feat(proxy): add outbound HTTP proxy support for OAuth + provider requests 2026-02-28 10:11:53 +07:00
shutdown Initial commit 2026-01-05 09:58:59 +07:00
tags Initial commit 2026-01-05 09:58:59 +07:00
translator Feat : console log 2026-03-02 09:31:16 +07:00
tunnel feat : 2026-02-21 16:42:46 +07:00
usage feat(usage): implement timeout and error handling for antigravity usage and subscription requests 2026-02-28 12:12:49 +07:00
v1 feat: cherry-pick PR #183 — multi-provider support, PWA, dynamic models, UI improvements 2026-02-25 11:40:50 +07:00
v1beta/models feat(gemini): convert OpenAI SSE to Gemini SSE format in /v1beta/models route (#225) 2026-03-02 09:36:08 +07:00
version Feat : console log 2026-03-02 09:31:16 +07:00