diff --git a/agent/credits_tracker.py b/agent/credits_tracker.py index 93456790e4..eab8cf278e 100644 --- a/agent/credits_tracker.py +++ b/agent/credits_tracker.py @@ -261,8 +261,8 @@ def parse_credits_headers(headers: Mapping[str, str], provider: str = "") -> Opt half-pair (only -micros or only -usd) parses as both-absent (both None).""" global _version_warning_emitted try: - # Cheap probe before the lowercase copy (header names are case-insensitive): - # bail when the version header is absent — the hot path for non-Nous providers. + # Cheap probe before the lowercase copy (header names are case-insensitive): bail when the + # version header is absent — the hot path for non-Nous providers. if not any(k.lower() == "x-nous-credits-version" for k in headers): return None lowered = {k.lower(): v for k, v in headers.items()} @@ -285,21 +285,17 @@ def parse_credits_headers(headers: Mapping[str, str], provider: str = "") -> Opt denominator_kind = lowered.get("x-nous-credits-denominator-kind", "none") if _SENTINEL in fields.values() or denominator_kind not in _VALID_DENOMINATOR_KINDS: return None - return CreditsState( - version=version_val, denominator_kind=denominator_kind, - disabled_reason=lowered.get("x-nous-credits-disabled-reason"), # None if absent (omitted when null) - captured_at=time.time(), from_header=True, **fields, - ) - except Exception: - # Fail-open → miss; the breadcrumb distinguishes a parser regression from a no-headers response. + disabled_reason = lowered.get("x-nous-credits-disabled-reason") # None if absent (omitted when null) + return CreditsState(version=version_val, denominator_kind=denominator_kind, disabled_reason=disabled_reason, + captured_at=time.time(), from_header=True, **fields) + except Exception: # fail-open → miss; the breadcrumb distinguishes a parser regression from a no-headers response logger.debug("credits ▸ parse_credits_headers raised (fail-open miss)", exc_info=True) return None -# ── Dev fixtures (HERMES_DEV_CREDITS_FIXTURE): throwaway scaffolding to trigger -# any notice state without real spend. Value is a state NAME or a FILE PATH whose -# contents are a name (re-read every turn → `echo depleted > /tmp/cf` flips live). -# Drives the per-turn capture/notice path, the cold-start seed, and /usage. +# ── Dev fixtures (HERMES_DEV_CREDITS_FIXTURE): throwaway scaffolding to trigger any notice state +# without real spend. Value is a state NAME or a FILE PATH whose contents are a name (re-read every +# turn → `echo depleted > /tmp/cf` flips live). Drives per-turn notices, the cold-start seed, and /usage. def _fixture(remaining: str, subscription: str, limit: Optional[str] = None, purchased: Optional[str] = None, *, paid: bool = True, reason: Optional[str] = None) -> dict: """Fixture spec from *_usd strings; micros derived exactly (Decimal).""" diff --git a/agent/gemini_native_adapter.py b/agent/gemini_native_adapter.py index cace2cb904..dfdb8784c2 100644 --- a/agent/gemini_native_adapter.py +++ b/agent/gemini_native_adapter.py @@ -344,13 +344,11 @@ _THINKING_KEYS = ( def _normalize_thinking_config(config: Any) -> Optional[Dict[str, Any]]: - if not isinstance(config, dict) or not config: + if not isinstance(config, dict): return None - normalized: Dict[str, Any] = {} - for key, alias, types, norm in _THINKING_KEYS: - value = config.get(key, config.get(alias)) - if isinstance(value, types) and (value.strip() if isinstance(value, str) else True): - normalized[key] = norm(value) + values = {key: config.get(key, config.get(alias)) for key, alias, _, _ in _THINKING_KEYS} + normalized = {key: norm(values[key]) for key, _, types, norm in _THINKING_KEYS + if isinstance(values[key], types) and (values[key].strip() if isinstance(values[key], str) else True)} return normalized or None @@ -669,8 +667,8 @@ class GeminiNativeClient: return translate_gemini_response(payload, model=model) def _stream_completion(self, model: str, url: str, request: Dict[str, Any], timeout: Any) -> Iterator[_GeminiStreamChunk]: - headers = {**self._headers(), "Accept": "text/event-stream"} try: + headers = {**self._headers(), "Accept": "text/event-stream"} with self._http.stream("POST", url, json=request, headers=headers, timeout=timeout) as response: if response.status_code != 200: raise gemini_http_error(response, body_text=read_streaming_error_body(response))