Skip to content

[Reference Only] Upstream PR #290 - feat: respect Accept header for response format negotiation - #10

Draft
nsalvacao wants to merge 6 commits into
masterfrom
upstream-prs/pr-290
Draft

nsalvacao wants to merge 6 commits into
masterfrom
upstream-prs/pr-290

Conversation

@nsalvacao

@nsalvacao nsalvacao commented Mar 16, 2026 •

Copy link
Copy Markdown

Context

This draft PR mirrors upstream branch 'upstream-prs/pr-290' into 'master' for review, provenance, and later decision-making only.
It must not be merged wholesale. If we adopt anything from it, the intended path is commit-level intake only.

Upstream Source

Current Intake Recommendation

  • Decision: Reference only / absorbed-noop
  • Status: Planned review only, with no integration performed yet
  • Credit policy: preserve original author metadata, use git cherry-pick -x for any later intake, and keep any conflict adaptation in separate follow-up commits with explicit upstream attribution

Selected Commits For Any Future Intake

  • No direct intake currently selected. Relevant behavior appears already represented in current master.

Skipped / Excluded Scope

  • Direct intake skipped: Accept-header negotiation and related response handling appear already covered in current master.

Acceptance Gates

  • Do not merge this mirrored branch as-is.
  • If anything is adopted later, intake only the explicitly selected commits or a newly re-validated subset.
  • Preserve upstream authorship and provenance in Git history.
  • Keep conflict-resolution/adaptation commits separate from upstream-authored commits.
  • Reconfirm branch contents against current master at intake time before changing the selected scope.

Verification / Validation Checklist

  • Current master already handles Accept-header response negotiation sufficiently.
  • Any future extracted commit is justified by a proven missing behavior, not by branch presence alone.

Review Focus

  • Confirm current master already negotiates response format correctly
  • Confirm no residual commit in this branch still warrants extraction

Notes

  • This PR is intentionally draft and serves as a reviewable reference mirror inside the fork.
  • Current recommendation is to keep it unmerged unless a missing residual commit is proven.

…ders

Providers like Antigravity maintain separate quota buckets per model family
(e.g. Claude vs Gemini). A 429 on claude-opus previously locked the entire
account, preventing gemini-pro requests even though its quota was full.

This adds in-memory per-model locking so that only the specific model is
skipped during account selection while other models remain accessible.

Changes:
- Add model-aware lock tracking in auth.js (Map<connectionId:model, expiry>)
- Pass model context from chat handler to auth service
- Multi-bucket behavior gated to known providers (MULTI_BUCKET_PROVIDERS set)
- No database schema changes — locks are in-memory and clear on restart

Closes decolua#110
When models (like kimi) return JSON wrapped in markdown code blocks
(\`\`\`json...\`\`\`), strip the markers before sending to client.

Changes:
- Add markdown stripping to streaming text_delta responses
- Add markdown stripping to non-streaming Claude format responses
- Translate response_format to system prompts for JSON schema/object modes
- Add jsonExtractor utility for consistent markdown removal

This fixes AI_APICallError: Invalid JSON response when using
AI SDK generateObject with kimi models through 9router.
Makes 9router check the client's Accept header when determining whether
to return streaming (SSE) or non-streaming (JSON) responses.

Changes:
- Check clientRawRequest.headers.accept in handleChatCore()
- Return non-streaming JSON when Accept: application/json
- Return SSE when Accept: text/event-stream or not specified
- Only applies when body.stream is not explicitly set to true

This fixes AI_APICallError: Invalid JSON response when using AI SDK
with 9router, as AI SDK sends Accept: application/json but previously
received SSE format responses.

Fixes decolua#289
Documents why markdown stripping is needed even when using
response_format with kimi models.

Key finding: kimi/kimi-k2.5-thinking adds markdown code blocks
despite response_format settings, but kimi/kimi-k2.5 works
without markdown.

Markdown stripping remains as defensive backstop.
- Move KIMI_JSON_MODE_INVESTIGATION.md to docs/investigations/
- Update .gitignore to allow docs/investigations/ tracking
- Better project organization for documentation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants