Commit Graph

13 Commits

Author SHA1 Message Date
hkfires
6ff680e90a feat(auth): use home model capabilities for thinking 2026-09-03 13:10:30 +08:00
Luis Pater
272c1cff4e fix(antigravity): bypass quota cooldowns and credit hints when cooling is disabled
- Bypass short cooldown checks and recording in execution flows when cooling is disabled globally or per auth.
- Skip marking credits permanently disabled and refreshing credit hints when cooling is disabled.
- Export quota cooldown status helper functions for auth and configuration evaluations.

Closes: #4793
2026-09-02 22:01:55 +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
Chén Mù
adf052984f fix(antigravity): remove cross-endpoint fallback (#5209) (#5228)
Fixes #5209
2026-08-25 11:51:17 +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
sususu98
62f5a2798c fix(executor): prepend empty user turn for model-first Gemini/Antigravity requests (#4959) (#5048)
* fix(executor): prepend empty user turn for model-first requests targeting Gemini/Antigravity (#4959)

When forwarding sliced conversation histories or tool calls across OpenAI Responses,
OpenAI Chat Completions, Claude Messages, and native Gemini, native Gemini and Antigravity
Gemini endpoints require that conversation contents begin with a user turn.

Normalize leading turns at the executor boundary rather than the translator layer:
- Prepend an empty user turn ({"role":"user","parts":[{"text":""}]}) for Gemini, Gemini Vertex,
  AI Studio, and Antigravity Gemini generation and CountTokens requests if the first turn is 'model'.
- Keep Antigravity Claude requests untouched to avoid adapter 400 errors.
- Ensure normalization runs after payload rules so payload index overrides target the original turns.
- Use no-copy GJSON inspection to keep overhead zero on valid user-first requests.

* fix(executor): inject Antigravity leading user after reasoning replay (#4959)

Replay can insert a model functionCall at contents[0] for sliced
tool-result history. Run the empty-user prepend on the final
requestPayload, after sanitize and prepareAntigravityGeminiReasoningReplayPayload.
2026-08-19 18:56:43 +08:00
sususu
cf8c27fe90 perf(antigravity): translate each upstream request once
Execute, executeClaudeNonStream and ExecuteStream all assign the validated
original payload to the request and then translate both values. Since both
translations saw the same bytes, every Antigravity request paid for a second
full scan of the client payload. On a captured 24MB tool-history request that
second pass cost roughly 0.9s of CPU and 651MB of allocations.

Translate once when both inputs share a backing array and hand the caller an
independent duplicate, because later stages edit the working copy in place.
Payloads that genuinely differ still get two translations.
2026-08-11 16:07:42 +08:00
Luis Pater
42eef103d6 feat(antigravity): obfuscate sensitive words in system instructions
Closes: #4696 #4723 #4732
2026-08-05 00:27:32 +08:00
Supra4E8C
1df21b14bf fix(usage): update token fingerprint after refresh 2026-08-03 21:39:48 +08:00
sususu
0c2ec7da23 fix(thinking): honor normalized summary payloads 2026-07-31 12:45:13 +08:00
sususu
b3046d29b9 feat(thinking): preserve cross-protocol summary visibility 2026-07-30 21:53:30 +08:00
sususu
20784c67ff fix(home): use Home's CAS command instead of EVAL for replay compare-and-swap
Client.KVCompareAndSwap sent Redis EVAL with a Lua script, but the Home RESP
subset does not implement EVAL, so Home replied "ERR unknown command 'eval'".
That broke the Antigravity and Codex reasoning replay caches in Home mode.

Switch the transport to Home's dedicated CAS command:

    CAS <key> <expected-exists 0|1> <expected-value> <new-value> [PX <ttl-ms>]

The semantics match the old script argument for argument, so KVCompareAndSwap's
signature and all its callers are unchanged. Omitting PX when ttl <= 0 mirrors
the script's SET-without-PX branch, which clears the TTL.

Deployments that predate CAS reject the command. Detect that by matching the
unsupported-command error, latch ErrCompareAndSwapUnsupported for the client
lifetime so later calls skip the round trip, and warn exactly once. The latch is
deliberately not carried across NewLifetime, so a Home upgrade takes effect on
the next reconnect rather than requiring a CPA restart.

Also stop replay-state failures from failing the request. A bare replay error has
no HTTP status, so resultErrorFromError does not classify it as request-scoped and
MarkResult marks the credential unavailable for that model, walking every
candidate credential until alias resolution has nothing left and returns 503. A
ledger miss is already a tolerated outcome, so degrade to "no replay this turn"
instead. The pairing failure still returns its 400.

Verified end to end against a real Home over RESP/mTLS on PostgreSQL with
Antigravity OAuth credentials: patched Home recreates the replay row through CAS
with its TTL, while a pre-CAS Home latches once, keeps returning 200 instead of
503, and records no credential error attributable to the replay path.

Refs router-for-me/CLIProxyAPIHome#79
2026-07-29 09:44:05 +08:00
Luis Pater
fe4ae4989c chore(pluginhost): refactor and remove unused interceptors and executor methods
- Removed deprecated interceptor and executor-related methods, including `callRequestInterceptor`, `callResponseInterceptor`, and `callStreamChunkInterceptor`.
- Consolidated unused logic and pruned redundant imports to streamline `adapters.go`.
- No functional changes.
2026-07-26 14:31:45 +08:00