Commit Graph

3548 Commits

Author SHA1 Message Date
Luis Pater
b7f6c15f83 fix(pluginhost): detach context and log rpc failures in usage handling
- Detach cancellation from context before dispatching usage records to plugins.
- Log debug messages when RPC `usage.handle` calls fail.

Closes: #5244
2026-08-27 04:35:51 +08:00
Luis Pater
6f6856e784 fix(gemini): map cached content tokens to claude cache read usage
- Deduct `cachedContentTokenCount` from `promptTokenCount` for `usage.input_tokens`.
- Set `usage.cache_read_input_tokens` when cached tokens are present in streaming and non-streaming responses.

Closes: #5238
2026-08-27 04:19:22 +08:00
Luis Pater
4fa1de2f9b feat(claude): support server-side web search translation for openai responses
- Map Claude `server_tool_use` and `web_search_tool_result` content blocks to OpenAI Responses `web_search_call` items in streaming and non-streaming modes.
- Support replaying `web_search_call` items and text search annotations back to Claude server tool blocks and citations.

Closes: #5236
2026-08-27 04:04:11 +08:00
Luis Pater
cb8746fb63 fix(claude): use zero-based sequential index for streamed tool calls
- Track tool call indices independently using a sequential counter instead of reusing Claude content block indices.
- Set the sequential tool call index when emitting streaming delta chunks.

Closes: #5229
2026-08-27 02:39:02 +08:00
Luis Pater
adac1e5816 fix(gemini): improve schema normalization for unions and unsupported constraints
- Avoid overwriting parent properties when resolving `anyOf`/`oneOf` unions on object schemas, merging branch properties instead.
- Add `contains` to unsupported constraints and preserve object/array constraint hints in descriptions.
- Strip `required` arrays when the schema does not define a `properties` object.

Closes: #5219
2026-08-27 02:14:40 +08:00
Luis Pater
9b88808fc7 fix(xai): fold namespace tools and restore dispatcher tool calls
- Introduce `xaiNamespaceRestorer` to track and restore folded dispatcher tool calls across SSE and WebSocket response events.
- Support unwrapping dispatcher tool calls and arguments in `output_item.added` and `function_call_arguments.done` events.
- Normalize historical input namespace tool calls to dispatcher format when namespace folding is active.

Closes: #5214
2026-08-27 01:08:35 +08:00
Luis Pater
2555cde2f1 fix(xai): inline local refs and broaden codex app tool normalization
- Export `InlineLocalRefs` utility to resolve local JSON Pointer references.
- Inline local definitions and remove `$defs`/`definitions` in tool function parameters.
- Support `mcp__` prefixes and `codex_apps` namespace variations when identifying Codex app automation update tools.
- Handle `$ref` entries when inspecting and normalizing root union schema branches.
2026-08-26 23:41:44 +08:00
sususu
1502ac826d fix(home): allow home config to override default port 2026-08-26 15:17:16 +08:00
Supra4E8C
ba200aefa0 fix: update Infistar registration links in README files 2026-08-26 13:54:53 +08:00
sususu
1f53b2eb03 fix(auth): keep credential rotation fair when candidates are filtered
Both built-in rotation strategies lost their position whenever the candidate
set shrank, which happens on every retry that excludes an already tried
credential and on every cooldown transition.

Smooth weighted round-robin reset every accumulated credit as soon as the
weight vector differed from the previous call. A transient subset is not a
configuration change, so the reset fired constantly. With all credits back at
zero and equal weights, the strict `>` comparison always resolves ties to the
first entry in slice order, and candidates are sorted by auth ID, so every
retry restarted the cascade at the alphabetically first credential. Credits are
now reset only when a credential's configured weight actually changes, and the
accumulator is bounded so permanently removed credentials cannot leak entries.

Plain round-robin indexed a monotonic counter into the filtered slice via
`available[index%len(available)]`. Once the slice shrank, the modulo re-seated
the rotation instead of resuming it. Rotation now continues from the identity
of the previous pick, resolved with a binary search over the sorted ring.

Measured over 9 equally weighted credentials with a realistic mix of long
sessions, new sessions and retries, the busiest-to-quietest ratio drops from
11331x to 1.1x. Distribution is exact for equal weights, matches configured
ratios for unequal weights, and tracks the theoretical optimum within 3% under
random credential unavailability.
v7.2.142
2026-08-25 18:55:00 +08:00
sususu98
998dcfeba2 fix(antigravity): safely synthesize terminal finish reasons (#5230)
The Antigravity backend sometimes ends a 200 stream without ever emitting
finishReason. Evidence from local request logs: 25 of 18,346 captured
cloudcode-pa streams have no finishReason at all (gemini-3.7-flash x23,
gemini-3.6-flash x2). Gemini and OpenAI chat clients then never see a
terminal event and wait forever.

Only synthesize on a clean end of stream
- The [DONE] tail is now translated only when scanner.Err() is nil. A
  truncated upstream stream previously still produced a terminal event:
  replaying a cut stream to a Claude client emitted the full
  content_block_stop / message_delta / message_stop sequence, so the
  truncation was reported as a completed message.
- Only Antigravity translators synthesize on [DONE], so the other
  executors that emit the tail before checking scanner.Err() cannot leak a
  fake terminal event and are left unchanged.

Never finalize a stream that produced nothing
- Synthesis requires at least one chunk carrying candidates or token
  accounting. Both translators share the same check, and presence alone is
  not enough: `{}`, `{"response":{}}` and `{"response":{"candidates":[]}}`
  leave the stream unstarted.
- Without that guard the synthetic chunk defeats the existing empty_stream
  detection in sdk/cliproxy/auth/conductor_stream.go, which only fires when
  the executor produced no chunk at all. An empty 200 would be reported as
  a successful empty completion instead of a failure.

Synthetic chunks mirror the observed upstream shape
- All 18,321 real terminal chunks carry candidates/usageMetadata/
  modelVersion/responseId with a model-role candidate whose parts are
  [{"text":""}]. The Gemini synthetic chunk now reproduces that shape and
  key order instead of a bare finishReason candidate.
- The last known usage snapshot is carried into the synthetic chunk.
  Without it the final chunk a client sees reports no tokens, because
  FilterSSEUsageMetadata renames non-terminal usage to cpaUsageMetadata
  and the Gemini path restores it per chunk.
- The OpenAI chat path keeps the latest cpaUsageMetadata as pending usage
  and emits it on [DONE] for the same reason.

Do not mistake an intermediate chunk for the terminal one
- A chunk carrying usage but no finishReason stays non-terminal.
  FilterSSEUsageMetadata forwards real usageMetadata on such a chunk only
  after an earlier chunk already carried finishReason, which the existing
  condition covers; finalizing on usage alone would cut the stream short.
- finish_reason and native_finish_reason are resolved by one shared
  helper, so the upstream terminal chunk and the synthesized [DONE] chunk
  cannot drift apart.
- The non-stream Gemini conversion defaults a missing finishReason for
  every candidate rather than only the first one.

Also fixes the unreachable alt != "" branch, which parsed an always-nil
buffer, and replaces an unchecked param type assertion.

Verified by replaying byte-exact upstream bodies extracted from request
logs through a mock backend, comparing this change against the unmodified
branch point: clean streams keep exactly one terminal event, streams
without finishReason gain one carrying the last usage snapshot, a stream
cut mid-chunk surfaces the read error with no terminal event, and an empty
200 now fails with empty_stream instead of reporting a successful empty
completion.
2026-08-25 15:13:04 +08:00
Luis Pater
f2b1996b3f fix(gemini,antigravity): align parallel tool results with preceding tool calls
- Add `AlignClaudeToolResults` to order `tool_result` blocks to match the preceding `tool_use` IDs while preserving other content parts.
- Apply tool result alignment in Claude-to-Gemini and Claude-to-Antigravity request translators.
- Preserve mixed non-response parts when normalizing and reordering parallel function responses in Antigravity executor.

Closes: #5199
2026-08-25 14:57:46 +08:00
Luis Pater
80de901550 fix: preserve multi-reference video durations 2026-08-25 12:44:00 +08:00
Chén Mù
adf052984f fix(antigravity): remove cross-endpoint fallback (#5209) (#5228)
Fixes #5209
2026-08-25 11:51:17 +08:00
Luis Pater
e1bf893956 feat(github): resolve GitHub token for release checks and asset updates
- Add `util.ResolveGitHubToken` to resolve GitHub API tokens with priority order (`GITHUB_TOKEN`, `github_token`, and `GITSTORE_GIT_TOKEN` for GitHub repositories).
- Set GitHub `Authorization` headers in management version check and asset updater requests when a token is resolved.

Closes: #5189
2026-08-25 04:22:46 +08:00
Luis Pater
ba510f85a2 fix(pluginhost): add plugin quiesce handling with safe rollback during hot reload
- Add new `plugin.quiesce` ABI method and propagate RPC error codes from plugin call failures.
- Invoke quiesce on the replaced plugin before loading a new version, then only activate replacement after quiesce succeeds.
- Improve hot-reload safety by serializing lifecycle transitions, cleaning up failed/canceled loads, and rolling back to the previous plugin state when replacement fails or is canceled.

Closes: #5134
2026-08-25 01:32:57 +08:00
sususu98
ca601db05d feat: observe upstream provider quota signals (#5211)
Codex and Claude already emit credential-level quota watermarks on ordinary
responses. CPA used to drop them. Keep the latest watermark in memory and
return it from the management auth-file API.

Hard rule: this is observation only. It must not change scheduling, cooldown
selection, or auth-file persistence.

Snapshot, not accumulation
- QuotaState now has ObservedAt and a bounded Signals map. MarkResult fills
  them from the response headers already recorded on the request.
- Signals is the current response, not a union of earlier ones. Retry-After
  and "limit reached" only appear on the response that produced them; merging
  across responses would keep an expired value forever.
- A response with no quota header (transport failure, 5xx, unrelated endpoint)
  leaves the previous snapshot in place.
- ObservedAt is the time of the current snapshot. It advances even when the
  values did not change, so a consumer can tell a fresh reading from a stale
  one.
- When two model states merge, keep the newer snapshot. Do not union keys
  captured at different times.

What is observed, and what is not
- One predicate, ProviderSupportsQuotaObservation, decides the provider set.
- Keep Codex and Claude. Drop Kimi, xAI/Grok, Antigravity, and the Gemini
  family (gemini/vertex/aistudio): their ordinary headers are not a reliable
  credential-level remaining quota.
- Count-tokens reuses the credential but is not generation traffic.
  ExecuteCount sets SkipQuotaObservation so those headers cannot replace the
  last generation snapshot. Cooldown and success/failure accounting still run.

Cooldown must not overwrite the last snapshot
- Observation writes only ObservedAt and Signals.
- Cooldown writes only Exceeded, Reason, NextRecoverAt, and BackoffLevel,
  through applyCooldownFields. Never assign a fresh QuotaState{...} over a
  live value: that would zero the snapshot on 429, Cloudflare, credential-
  scope sibling updates, and cooldown clears.
- If a credential-quota cooldown is still active, MarkResult still observes
  an already-present model state. It does not create scheduler state just to
  record a watermark.
- .cds files persist cooldownFieldsOf(Quota) only. Restore keeps the newer
  ObservedAt, so reloading cooldown cannot clobber a newer in-memory snapshot.
- cooldownQuotaEqual still ignores observation fields, so a watermark change
  cannot by itself persist cooldown or move the scheduler.
- The management payload omits every cooldown field, so it cannot be mistaken
  for scheduler state or wired back into scheduling.
- Manual ResetQuota still clears the full QuotaState.

Codex websocket events
- Codex WS reports quota as codex.rate_limits frames, not HTTP headers.
  ParseCodexQuotaEventHeaders turns one event into the same bounded header
  shape, and MergeResponseHeaders folds it into the request-scoped holder.
  additional_rate_limits is accepted as an object (websocket) or an array
  (/wham/usage).
- Parse only through AppendCodexAPIWebsocketResponse. The shared
  AppendAPIWebsocketResponse is also used by xAI, and xAI error frames really
  do carry x-ratelimit-* headers. Parsing every frame as Codex quota would
  forge Codex headers into another provider's request log.
- Also capture code_review_rate_limits.
- A malformed active-limit name drops only that one header, not the window
  watermarks parsed from the same event.
- The type discriminator scans a bounded frame prefix, not every byte of
  every frame.
- HTTP namespaces an extra limit by short name (x-codex-bengalfox-*); WS
  namespaces it by limit name (GPT-5.3-Codex-Spark). The two paths cannot
  emit the same header names. The X-Codex-Additional- prefix marks the WS
  origin, and snapshot replacement keeps the two spellings from piling up.

Hardening
- Reject observed values with control characters. These strings reach the
  plain-text request log, and Limit-Name is upstream-controlled, so CR/LF
  could forge a header line.
- When the header cap is hit, keep plan/credits/primary ahead of
  additional-limit namespaces, then sort names so truncation is deterministic.
- QuotaState.Clone deep-copies Signals and is used by Auth.Clone and
  ModelState.Clone.
- Token stores still serialize credential metadata only, so observation adds
  no auth-file writes.
2026-08-24 17:15:37 +08:00
lzt404
dc3c3b1ec3 Merge pull request #5212 from router-for-me/apimart-sponsor
Apimart sponsor
v7.2.141
2026-08-24 16:35:18 +08:00
lzt404
cd445f2be8 Merge remote-tracking branch 'origin/dev' into apimart-sponsor 2026-08-24 16:31:23 +08:00
lzt404
85749db9bc docs(readme): add APIMart sponsor to Japanese README
- rename Apimart-en.png to apimart-en.png
- rename Apimart-zh.png to apimart-zh.png
- update image paths in localized READMEs
2026-08-24 16:23:21 +08:00
Luis Pater
5fead38f0d docs: remove VisionCoder sponsor section from all READMEs 2026-08-24 16:17:59 +08:00
lzt404
b232394018 Merge pull request #5210 from router-for-me/apimart-sponsor
Apimart sponsor
2026-08-24 16:11:39 +08:00
lzt404
cef351a464 docs(readme): add APIMart sponsor 2026-08-24 16:03:05 +08:00
Luis Pater
9d0a60bfc3 fix(gemini,antigravity): preserve function/tool results as raw strings
- Keep `functionResponse.response.result` as a string in Gemini responses translation instead of JSON-parsing tool output.
- Apply the same string-preserving behavior for antigravity tool responses to avoid upstream 400 errors from parsed payloads.
2026-08-23 15:09:34 +08:00
Luis Pater
3faf70957f Merge pull request #5184 from HuiCheng/fix/xai-image-generation-tool-choice-required
fix(xai): map image_generation tool_choice to required
2026-08-23 13:34:19 +08:00
程辉
6f4b6dc5f5 fix(xai): keep forced image_generation from other tools
Rewriting {type: image_generation} to string required would let later
x_search injection or leftover web_search satisfy the choice. Reduce
the tools list to image_generation for that forced case, and skip
x_search injection while the remaining tools are image-only.

Closes: #5183
2026-08-23 12:22:14 +08:00
程辉
fba1ff24ac fix(xai): preserve auto when rewriting image-only allowed_tools
An allowed_tools list that only names image_generation cannot be sent
to chat-proxy. Map that empty remainder to the original mode so
mode=auto stays optional instead of becoming required.

Closes: #5183
2026-08-23 12:03:46 +08:00
程辉
d2742c5f37 fix(xai): map image_generation tool_choice to required
chat-proxy rejects allowed_tools lists that name image_generation.
Rewrite a forced image_generation choice, and an allowed_tools list
that only names that hosted tool, to the string "required". Mixed
allowed_tools lists drop the image_generation entry. Prune orphans
before this rewrite so older models still lose the leftover choice.

Closes: #5183
2026-08-23 10:12:52 +08:00
Luis Pater
a7e3596b7e fix(gemini): normalize malformed schema nodes before cleanup
- Added a preprocessing pass to repair malformed MCP-style JSON schemas.
- Wraps bare property maps as object schemas, promotes `required: true` flags to parent `required` arrays, and removes boolean `required` from properties.
- Recurses through nested schema containers and skips known API request envelopes to avoid rewriting non-schema docs.
- Switched schema decoding/serialization path to preserve large numeric values and avoid HTML-escaping side effects.

Closes: #5178
v7.2.140
2026-08-22 22:39:03 +08:00
Luis Pater
1be84170e6 Merge pull request #5177 from YogaSakti/fix/antigravity-fallback-version
fix(antigravity): raise the fallback client version to 2.9.1 (#5175)
2026-08-22 21:32:28 +08:00
Luis Pater
7d5b23f177 Merge pull request #5174 from HuiCheng/fix/xai-keep-image-generation-grok-46
fix(xai): keep image_generation on grok-4.6+ conversation requests
2026-08-22 21:20:00 +08:00
Luis Pater
4d68ca8a63 fix(codex): convert Grok client keepalive SSE frames to comments
- Add Grok client detection via User-Agent (including Gin context fallback) in a new `grokbuild` helper.
- Transform keepalive SSE `event`/`data` frames into `: keepalive` comments when streaming to Grok clients.
- Keep keepalive frames untouched for non-Grok clients while preserving existing stream translation behavior.

Closes: #5171
2026-08-22 20:58:35 +08:00
Luis Pater
d5b57a2d8a fix(openai): validate and filter thought signatures in responses conversion
Closes: #5166
2026-08-22 17:30:26 +08:00
Luis Pater
eeaa2e1830 Merge pull request #5176 from router-for-me/auth
Normalize credential metadata and enhance registry interactions
2026-08-22 17:13:39 +08:00
hkfires
ebda750911 feat(auth): enhance key deletion and patching with base URL validation 2026-08-22 16:53:30 +08:00
Luis Pater
ab8f00dbd9 fix(claude): derive stable request-scoped metadata.user_id for converters
Closes: #5153
2026-08-22 16:01:25 +08:00
Luis Pater
b3f72cef65 fix(gemini): normalize Claude thinking signatures in Gemini request and response conversion
Closes: #5151
2026-08-22 15:35:54 +08:00
Yoga Sakti
a834917e87 fix(antigravity): raise the fallback client version to 2.9.1 (#5175)
Cloud Code resolves newer Antigravity models only for clients reporting
at least 2.9.0; below that it answers 404 Requested entity was not found.
The offline fallback still reported 2.9.0's predecessor 2.2.1, so every
request sent before the hub manifest is first fetched — or from a
deployment that cannot reach the manifest at all — asked for models such
as gemini-3.7-flash-high with a version the backend rejects.

The hub manifest the updater already polls currently publishes 2.9.1, so
this only aligns the offline floor with what the online path resolves.
The test now asserts the floor rather than a literal, so a future
downgrade below 2.9.0 fails instead of silently reintroducing the 404.
2026-08-22 13:19:24 +07:00
Luis Pater
65071f7c47 test(claude): expand OpenAI conversion tests for reasoning_content and stream/non-stream parity
Closes: #5148
2026-08-22 13:20:00 +08:00
Luis Pater
d9869ed908 test(openai): expand reasoning fallback coverage in OpenAI responses non-stream conversion tests
Closes: #5147
2026-08-22 13:08:11 +08:00
程辉
87fb01b237 fix(xai): drop orphaned tool_choice after compact strips tools
Compact deletes tools after prepareResponsesRequestTo. On grok-4.6+
image_generation is now kept and rewritten to allowed_tools, so the
leftover choice would be sent without tools. Reuse the existing
normalizer to drop that orphaned selection.

Closes: #5173
2026-08-22 12:47:57 +08:00
程辉
dfdf183fcf fix(xai): keep image_generation on grok-4.6+ conversation requests
normalizeXAITool still strips Codex hosted image tools on older Grok
conversation models. grok-4.6 and later accept xAI native Imagine
tool, so keep client-supplied image_generation there and rewrite a
forced choice into allowed_tools. grok-4.20-* stays on the old strip
because that product line is not comparable to grok-4.6.

Closes: #5173
2026-08-22 12:38:30 +08:00
hkfires
71c3c144a0 fix(registry): detect gemini interactions changes 2026-08-22 12:20:53 +08:00
hkfires
e04d620cc1 feat(auth): normalize credential metadata keys
Canonicalize legacy config-style credential keys across stores,
management handlers, plugin auth, and file synthesis while preserving
explicit canonical values. Expose per-auth request_retry in auth file
management and add max-retry-credentials management routes.
2026-08-22 12:01:06 +08:00
hkfires
0a14eb70ce feat(auth): add retry round credential filtering v7.2.139 2026-08-22 01:20:02 +08:00
hkfires
601ca43090 feat(auth): add credential retry round contract
Redefine request-retry as additional credential retry rounds and
enforce max-retry-credentials per round. Home dispatch now carries
excluded and pinned auth constraints, supports remote retry limits, and
propagates cooldown retry-after metadata across exhausted rounds.

Move Antigravity upstream retries under conductor ownership to avoid
double-consuming retry attempts. Update configuration comments and add
coverage for Home retry rounds, cooldown handling, pinned credentials,
and legacy dispatcher compatibility.
2026-08-22 01:19:58 +08:00
Luis Pater
85e7add6ad feat(models): add Gemini 3.7 Flash model registrations to model registry
Closes: #5137 #5046
2026-08-21 22:51:45 +08:00
Luis Pater
1d5b7612c6 fix(cliproxy): add protocol-aware plugin executor usage parsing for response and streaming payloads
Closes: #5122
v7.2.138
2026-08-21 13:05:54 +08:00
W ARELIK
4053c026e7 fix(executor): sanitize thought signatures in Gemini and Gemini Vertex executors (#5110)
Ensure all Gemini and Gemini Vertex execution and token count pathways invoke SanitizeGeminiRequestThoughtSignatures before dispatching upstream requests. This prevents raw non-Gemini (e.g. Claude CAIS) thought signatures from leaking to upstream Gemini endpoints while preserving valid native protobuf signatures and setting appropriate validator bypass sentinels for function calls.

Co-authored-by: W ARELIK <warelik@WARELIK-MB.local>
2026-08-21 10:57:03 +08:00
Luis Pater
b1c000590b fix(gemini): include empty annotations/logprobs in response.output_item.done message content
Closes: #5116
2026-08-21 04:16:08 +08:00