Commit Graph

66 Commits

Author SHA1 Message Date
teknium1
13fe9c7171 feat(providers): external-process provider support for standalone model-provider plugins (from #105863)
The provider-agnostic half of PR #105863, so a CLI-driven subscription provider can ship as a
standalone `kind: model-provider` plugin instead of a bundled one:

- ProviderProfile: `native_reasoning_details_type`, `model_aliases`, `get_model_context_length`,
  `get_usage_cost`, `setup_status`, `discover_models` hooks (all default None / no-op).
- Chat Completions transport: provider-native `reasoning_details` carriers follow only their
  declaring profile; standard records still replay on OpenRouter-style routes, strict routes
  drop the field wholesale (#70233). Relay/stream accumulate `delta.reasoning_details` verbatim.
- `hermes model`: the generic plugin flow gates an external-process row on the CLI's own login
  status (inline `login_command` on a TTY), offers `discover_models()` rows with per-row notes,
  and never writes config when the executable is missing.
- `/model` and the pickers: process providers list their live catalog merged with the pinned
  one, declared aliases/ids resolve inside the provider, and validation accepts a listed id
  without probing `process://`.
- Delegation keeps the selected external-process provider and protocol for the child.
- Model metadata / usage pricing consult the profile's bound and cost hooks first.
- Desktop: `[1m]` renders as a "1M" tag and hyphenated Anthropic versions read "Haiku 4.5".

The bespoke `_model_flow_external_process` and hard-coded `hermes_cli/main.py` paths from the
PR were dropped in favour of main's `_model_flow_plugin_provider`.

Co-authored-by: unsupportedpastels <unsupportedpastels@users.noreply.github.com>
2026-09-20 14:29:39 -07:00
Teknium
aeecb110f8 fix(deepseek): deepseek-flash is the canonical Flash id; retired names fold onto it
DeepSeek retired deepseek-v4-flash on 2026-09-10 (V4.1-Flash release); the API's
model name is now `deepseek-flash` and /v1/models lists only it. Hermes still
folded every non-V-series name onto deepseek-v4-flash, so `/model deepseek-flash`
on the DeepSeek provider was rewritten, then the validator "auto-corrected" it
back against the live listing: "Auto-corrected deepseek-v4-flash -> deepseek-flash"
on every switch.

Retired aliases (deepseek-chat / -reasoner and other fuzzy names) now fold onto
deepseek-flash; the curated catalog, profile fallback list, aux default, goal-judge
hint and pricing snapshot (2026-09-10 off-peak USD) follow the docs. Dated
deepseek-v4-* ids still pass through untouched.

Builds on YipTszkwan's #107126 (earliest fix in the cluster).
2026-09-10 02:44:26 -07:00
Eva
2c315ff59b feat(openai): add GPT-6 Astra baseline support
(cherry picked from commit a8c53d20c6b16cc35745e364e16bb7259166a1d3)
2026-09-07 21:43:54 +05:30
tylman
b0adce1cbf feat(models): add gemini-3.7-flash and gemini-3.8-flash support
- Generalize Gemini 3 thinking config model prefix match to gemini-3*
- Add pricing snapshot entries for gemini-3.7-flash and gemini-3.8-flash
- Add model identifiers to Google, OpenRouter, and Vertex CLI lists
- Add test coverage for gemini-3.8-flash thinkingLevel configuration
2026-09-06 05:42:22 -07:00
Teknium
2776813df3 compat(plugins): temporary import-path shims for external plugins — ONE commit, revert on schedule
The Sep 2026 decomposition (PR #102117) makes internal import paths a non-API: names now live in
the focused modules that define them. This commit is the ONLY thing keeping the old paths alive,
so external plugins have time to update. It is deliberately a single, unsquashed commit:

    git revert <this sha>

removes every shim, stub and manifest at once on the announced date. Nothing in-tree may depend on
these pointers: scripts/check_compat_pointers.py (wired into lint.yml) fails CI if it does.

What it adds (see COMPAT_MANIFEST.md, compat_manifest.json):
- 332 facade modules get one delimited `PLUGIN-COMPAT` block appended at the end of the file
- 1,172 moved names resolved lazily via a module `__getattr__` (PEP 562) — never a top-level import,
  so no import cycles; facades that already had `__getattr__` get a chained one
- 592 third-party/stdlib names the old modules used to expose, with their original import statements
- 266 public definitions that had been deleted as unused, restored byte-for-byte from the pre-decomposition
  tree (+40 private helpers and 16 imports pulled in only because a restored definition needs them)
- 3 deleted modules recreated as re-export stubs (gateway/startup_watchdog, hermes_cli/observability/
  relay_runtime, tools/environments/modal_utils)
- private names (`_x`) get no pointer: they were never API (3,792 skipped)

Verified: all 335 touched modules import under a fresh HERMES_HOME and every manifest name resolves;
the lint reports zero in-tree uses; ruff clean; targeted suites unchanged.
2026-09-03 17:13:22 -07:00
Teknium
e83816a4d1 review-fix(comments): restore lost #NNNN rationale comments across non-test source (mechanical sweep, condensed, code unchanged)
For each issue anchor present in BASE 63279301bc non-test .py and absent on HEAD, the BASE comment/docstring block was re-attached at the HEAD location of the code it explained (matched by the distinctive code line / enclosing def). Sentences already covered by an existing HEAD comment were deduped; the issue number always survives. Insert-only: no code lines changed.
2026-09-03 09:44:26 -07:00
Teknium
0071ba9965 Merge origin/main (561b053f79) into simp/forwardport: forward-port 220 main commits into the simplified tree 2026-09-03 03:31:03 -07:00
Teknium
9f652b5677 refactor(agent/usage_pricing,web_search_registry): single-pass rate accumulation; slice-rotate the keyless ring 2026-09-02 20:04:22 -07:00
Teknium
53718c5f5c refactor(agent/misc-g6): compact module/function docstrings; CanonicalUsage.__add__ via dataclass fields 2026-09-02 19:14:31 -07:00
Teknium
9ce851867d refactor(agent/usage_pricing): fold usage getter/count helpers into _usage_field; drop redundant included-route pre-check 2026-09-02 19:11:19 -07:00
Teknium
cc7b9ef8b2 refactor(agent/usage_pricing): table-drive normalize_usage field paths per API shape 2026-09-02 19:07:15 -07:00
Teknium
99beb63ce2 refactor(agent/usage_pricing): express the official-docs snapshot as per-provider rate rows; table-drive snapshot-provider routing 2026-09-02 18:35:48 -07:00
Teknium
fcb19ea1f1 refactor(agent/misc-g6): dedupe qwen prep, image-part checks, pricing normalizers; tighten provider ABC/registry modules 2026-09-02 18:29:10 -07:00
Teknium
c94ced6225 refactor(agent/turn): AST-neutral bracket/signature packing across r3-08 slice 2026-09-02 18:01:27 -07:00
fangliquanflq
6064668c8f perf(usage): prefer bundled pricing before metadata fetch 2026-09-03 04:00:51 +05:30
Teknium
77743eac8a refactor(agent/models): compact pricing snapshot, billing/subscription views, reasoning helpers
- usage_pricing: _snap() builder for official-docs pricing entries (table values identical, verified by dump), shared source/version dicts, drop dead DEFAULT_PRICING
- models_dev: _registry_models/_iter_model_entries/_extract_limit helpers replace repeated registry walking; drop dead ModelInfo.format_cost
- billing_view/subscription_view: OrgRoleCapability mixin replaces duplicated is_admin/can_change_plan; shared fetch_portal_state/parse_org_fields
- reasoning_effort/timeouts/summaries, thinking_timeout_guidance, portal_tags: dispatch tables and compacted comment essays; drop dead CODEX_RESPONSES_EFFORTS alias and _match_any
2026-09-02 13:52:51 -07:00
Teknium
c9e2a46df6 fix(pricing): support Gemini context-tiered rates in pricing snapshot (#93469)
The pricing snapshot could only express flat per-million rates, so
gemini-3.1-pro sessions with prompts over 200k tokens under-counted
input 2x ($2 vs $4/M) and output 1.5x ($12 vs $18/M).

- Add optional tier fields to PricingEntry: tier_threshold_tokens,
  input/output/cache_read_cost_per_million_above (None = flat, falls
  back to base rate per-field).
- estimate_usage_cost selects the above-threshold rates for the WHOLE
  request once usage.prompt_tokens (input + cache read + cache write)
  exceeds the threshold, matching Google's billing semantics.
- Populate gemini-3.1-pro (4.00/18.00/0.40 above 200k; alias
  gemini-3.1-pro-preview inherits) and gemini-2.5-pro (2.50/15.00
  above 200k).
- Flat entries are untouched: no threshold means no behavior change.

Reported and tier-field shape designed by @tornike14 (#93469).

Tests: below/at threshold unchanged, above-threshold tiered whole-request
pricing, cache-read tier rate and base-rate fallback, preview alias,
flat entries unaffected.
2026-08-24 03:23:07 -07:00
Teknium
63a9c26fbe feat(model): Codex GPT slugs default back to 272K; explicit -900k picker variants opt into the verified large window
The Aug 16 change that auto-raised gpt-5.4/5.6 Codex OAuth context to the
live-verified 900K burned through subscription usage for users who never
asked for the larger window (bigger window = more input tokens per request).

- Base Codex slugs (gpt-5.6-sol/terra/luna, gpt-5.4) now resolve to the
  advertised 272K again — the cheaper limit is the default.
- The model picker synthesizes explicit <slug>-900k variants (e.g.
  gpt-5.6-sol-900k) for every live-verified slug; selecting one opts into
  the 900K window. Slugs that genuinely enforce 272K (gpt-5.5,
  gpt-5.4-mini) get no variant.
- The -900k suffix is Hermes-side only: stripped before the model id hits
  the wire (main transport + auxiliary Responses adapter), and pricing
  aliases the variants onto the base entries.
- Docs: new opt-in section in context-compression-and-caching.md.
2026-08-23 02:14:35 -07:00
Teknium
4b7b2b0049 fix: widen base-URL hostname identity class to remaining substring sites
Follow-up to #85737, which migrated five provider-identity sites onto
utils.base_url_host_matches()/base_url_hostname(). This completes the class
sweep (never-patch-predicates: one owner, every site) and folds in the two
open contributor PRs attacking individual sites:

- agent/auxiliary_client.py ZAI/Kimi OpenAI-wire rewrite (PR #85715,
  pierrenode): 'bigmodel'/'api.z.ai'/'api.kimi.com' substring checks
  rewrote proxy paths containing those markers.
- hermes_cli/runtime_provider.py Azure endpoint detection (PR #74721,
  RelaxJonh, issue #74312): 'azure.com' substring picked the Azure key
  for non-Azure hosts whose path contained the text.
- run_agent.py: _is_azure_openai_url, _is_copilot_url, Anthropic
  credential-refresh azure guard, _anthropic_preserve_dots host
  allowlist, OpenRouter/mistral reasoning gates.
- agent/chat_completion_helpers.py: nousresearch / nvidia detection.
- agent/conversation_loop.py: GitHub Models 413 hint.
- agent/usage_pricing.py: localhost billing-route detection.
- hermes_cli/model_switch.py: api.openai.com catalog fallback and
  localhost custom-provider detection.
- cli.py: local-model autodetect and Ollama/LM Studio context-length
  hints (port-anchored instead of '11434' in URL).
- tools/mcp_oauth.py: Figma remote-MCP detection.
- tools/skills_hub.py: raw.githubusercontent.com source-URL check.

Regression tests extend tests/hermes_cli/test_base_url_host_identity.py
(azure/copilot/dotted-model/figma proxy-path + lookalike cases) and
tests/agent/test_minimax_auxiliary_url.py (ZAI/Kimi path false positives).

Closes #74312. Salvages #85715 and #74721 with authorship preserved.
2026-08-14 22:04:16 -07:00
joaomarcos
e331b834fe fix(usage): preserve OpenAI-wire cache writes at canonical accounting boundary (#85706)
Salvaged from PR #85702 by @JoaoMarcos44, composed onto the mapping-safe
_usage_get reads (PR #74591 by @RelaxJonh) and the flat cached_tokens /
Anthropic-name fallbacks (PRs #66105, #52571):

- cache-write precedence in the chat_completions branch:
  details.cache_write_tokens > details.cache_creation_input_tokens >
  usage.cache_creation_input_tokens > usage.cache_write_tokens
- codex_responses branch reads details.cache_write_tokens (GPT-5.6+
  documented name) with cache_creation_tokens fallback (from PR #70522)
- _usage_count(): clamp malformed negative counters to 0
- all reads in every branch are mapping-safe via _usage_get
2026-08-13 22:09:37 -07:00
RelaxJonh
11de734331 fix(usage): support dict-shaped usage objects in normalize_usage (#74314)
When the Responses API returns usage as a plain dict (e.g. from a
middleware or proxy that deserialises JSON to dict instead of a typed
SDK object), normalize_usage() used getattr() exclusively, which
silently returned 0 for every field on a dict.

Add _usage_get() helper that reads via .get() for dicts and getattr()
for attribute-style objects. All accessor sites in normalize_usage()
now use this helper, so token counts and cost are correct regardless
of the usage object's type.

Regression tests: two new tests feed the same payload as both a dict
and a SimpleNamespace through the codex_responses and
chat_completions branches, asserting identical output and non-zero
values.
2026-08-13 22:09:37 -07:00
mehmetkr-31
5b82e69693 fix(agent): read Kimi/Moonshot top-level cached_tokens in normalize_usage (#65722)
Kimi/Moonshot's native API (api.moonshot.cn / .ai) reports context-cache hits
as a top-level ``usage.cached_tokens``. The chat-completions branch of
normalize_usage() walks a fallback chain of
prompt_tokens_details.cached_tokens -> cache_read_input_tokens ->
prompt_cache_hit_tokens; none of those names match, so direct Kimi sessions
normalized to cache_read_tokens=0. The hits were invisible in accounting and
the cached prefix was billed at the full input rate.

Appended as the last link in that chain, so it only fills a genuine zero and
cannot override a provider that reports the nested OpenAI shape or DeepSeek's
prompt_cache_hit_tokens.

Rebuilt on current main rather than rebased — the branch was ~3400 commits
behind. The DeepSeek half of the original branch is dropped: 03c0b00f4
(#65678) landed prompt_cache_hit_tokens on main, so this is Kimi-only as the
review asked. The scripts/release.py addition to the frozen LEGACY_AUTHOR_MAP
is dropped too; contributors/emails/mehmet.kar@std.yildiz.edu.tr already
exists on main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 22:09:37 -07:00
Chengxi Zhou
69bc3159e8 fix(agent): fallback to Anthropic-style token fields in normalize_usage
Local OpenAI-compatible servers like mlx_vlm.server emit
input_tokens/output_tokens in chat completion responses instead of
prompt_tokens/completion_tokens. The OpenAI Python client preserves
these as extra attributes, but normalize_usage() only looked at
OpenAI-style field names, causing input_tokens to always be 0.

This made the context progress bar stay at 0% forever and prevented
auto-compression from triggering.

Fix: add Anthropic-style fallback (input_tokens/output_tokens) in the
default else-branch of normalize_usage, with OpenAI-style names taking
priority.

Fixes #14686
2026-08-13 22:09:37 -07:00
kshitij
f80f453ae0 refactor(usage): simplify-pass follow-ups
Some checks are pending
Install & Update E2E / Pick release tags (push) Waiting to run
Install & Update E2E / update (push) Blocked by required conditions
Install & Update E2E / installer (push) Blocked by required conditions
- Single-source the included note as _INCLUDED_NOTE and attach it at
  BOTH status='included' sites (the zero-amount pricing-entry branch
  previously returned the same status with no note).
- Docstring/comment precision on format_cost_label: the fallback
  triggers on 4dp ROUNDING to 0.0000 (banker's rounding includes the
  exact $0.00005 boundary), not truncation; note why the rendered-label
  guard beats a naive Decimal threshold.
- Tests: replaced a dead assertion with the exact-boundary case
  ($0.00005), fixed an overclaiming comment, aligned the terminal
  cost column.
2026-08-14 02:04:24 +05:30
kshitij
2c068d7680 fix(usage): close sub-cent gaps found in review
- Insights formatters now route aggregate estimated cost through the
  shared format_cost_label() instead of hardcoded 2dp — a sub-cent
  aggregate (one cheap DeepSeek session, ~$0.0046) no longer renders
  'Estimated: ~$0.00', the exact bug class this PR fixes (#79220).
- format_cost_label: positive amounts below $0.00005 render '~$<0.0001'
  instead of the zero-looking '~$0.0000' 4dp truncation artifact.
- Renamed _format_cost_label -> format_cost_label (now a cross-module
  shared helper).
- Tests: renamed test_gateway_format_hides_cost ->
  test_gateway_format_hides_cache_details and
  test_no_cost_section_when_all_zero ->
  test_unknown_bucket_shown_for_costless_session (names contradicted
  behavior); restored a real assertion in the custom-models test that
  had been weakened to a comment; added sub-cent-aggregate and 4dp-floor
  contract tests (mutation-checked).
2026-08-14 02:04:24 +05:30
kshitij
ccaaca7e66 fix(usage): cost display honesty — sub-cent labels, cost buckets, included notes
Three cost-display honesty fixes:

1. Sub-cent cost label rendering (#79220) — _format_cost_label() scales
   precision to magnitude: zero renders as '$0.00', sub-cent (< $0.01)
   renders at 4 decimal places (e.g. '~$0.0046'), normal costs keep 2dp.
   This fixes the bug where DeepSeek per-turn costs of $0.004640 rendered
   as '~$0.00' despite amount_usd carrying full Decimal precision.

2. Cost bucket surfacing (#77223) — insights format_terminal and
   format_gateway now display three cost buckets: estimated (with dollar
   figure), included (session count, labeled 'subscription — no provider
   invoice'), and unknown (session count, labeled 'no pricing data').
   Previously, included and unknown sessions silently collapsed to $0 in
   the aggregate view, hiding 315 of 473 sessions in the reporter's DB.

3. Subscription-included cost notes — estimate_usage_cost now attaches a
   'subscription-included; no provider invoice for usage' note to
   CostResult for subscription-included routes (openai-codex), so
   consumers can distinguish 'free because subscription' from 'free
   because $0 pricing'.

Fixes #79220
Fixes #77223
2026-08-14 02:04:24 +05:30
kshitij
223f703012 fix: close provider-anthropic MiniMax proxy bypass + rework cache observability
Follow-up fixes on top of the salvaged #83678 commit:

1. Hoist the MiniMax-M3 marker exclusion ABOVE the native-Anthropic
   early return. provider="anthropic" pointed at a MiniMax /anthropic
   proxy is a supported override (_anthropic_base_url_override_ok), and
   the is_native_anthropic branch matched on provider alone — returning
   (True, True) before the M3 exclusion was reached. Two regression
   tests pin the proxy route (M3 off, M2.7 still on).

2. Reuse the existing _model_name_suggests_minimax_m3() helper from
   agent/model_metadata.py instead of a second inline substring copy.

3. Drop the debug kwarg on normalize_usage() — it had zero production
   callers and duplicated standard logging level gating. The
   cache-observability line is now a plain logger.debug scoped to
   MiniMax providers on the Anthropic wire only, so the "+128 floor"
   note can no longer appear for native Anthropic where it is false.
   Tests updated accordingly (MiniMax logs, native Anthropic does not).
2026-08-12 13:58:26 +05:30
Hermes Agent
c1e2529ae2 fix(cache): opt M3 out of cache_control markers on Anthropic wire
MiniMax-M3 ships server-side automatic prefix caching on the
Anthropic-compatible endpoint (content-keyed, no marker needed —
see platform.minimax.io/docs/api-reference/text-prompt-caching).
cache_control markers are NOT on its explicit-cache support list
(which covers only M2.7/M2.5/M2.1/M2).

Emitting markers on M3:
  - wasted serialization overhead
  - risked perturbing the server-side prefix hash
  - gave users a false sense of explicit-cache savings (the
    cache_read_input_tokens field carries a +128 constant floor
    and cache_creation_input_tokens is always 0 for M3)

Also add an opt-in debug=True parameter to normalize_usage() that
emits a debug-level log line carrying the observable cache fields.
This is the only reliable cache signal for M3 — off by default,
debug-level, scoped to the anthropic_messages wire, so production
callers see no impact.

Pin both changes with 8 new tests:
  - 4 M3 tests covering provider, host, and custom-provider paths
  - 1 regression guard ensuring M2.x caching is unaffected
  - 3 observability tests (off-by-default, on-with-M3, on-with-Claude)

Verified end-to-end against api.minimaxi.com/anthropic/v1/messages
with MiniMax-M3[1m]: identical system prompt hit-rate with and
without markers; cache_read field is unreliable (128 floor),
input_tokens drop (8467 -> 1) is the real hit signal.
2026-08-12 13:58:26 +05:30
Teknium
ad12df6ba4 Revert "remove Vercel AI Gateway and Vercel Sandbox (#33067)"
This reverts commit febc4cfec0.
2026-07-29 19:48:37 -07:00
Teknium
d259b24ede feat(pricing): add gemini-3.6-flash and gemini-3.5-flash-lite official snapshot entries
Follow-up to the #60063 salvage: the curated gemini list now carries
gemini-3.6-flash (aux default, #70416) and the vertex list carries
gemini-3.5-flash-lite (#68767) — both need snapshot pricing so direct
Gemini/Vertex sessions don't report cost=unknown.

Rates verified against https://ai.google.dev/gemini-api/docs/pricing
(2026-07-28): 3.6-flash $1.50/$7.50, cache read $0.15;
3.5-flash-lite $0.30/$2.50, cache read $0.03.
2026-07-28 11:54:46 -07:00
Ivan Miao
26dd976fbe fix: align Gemini billing pricing with provider catalog 2026-07-28 11:54:46 -07:00
Ivan Miao
ecebff82d2 fix: support Gemini billing route mapping and pricing update 2026-07-28 11:54:46 -07:00
Drexuxux
487574c5b6 fix(pricing): strip apac./au. Bedrock region prefixes so cost isn't unknown
_normalize_bedrock_model_name stripped ("us.", "global.", "eu.", "ap.",
"jp.") before the pricing lookup, but AWS Bedrock's Asia-Pacific
cross-region inference profiles are prefixed "apac." (and Australia
"au."), not "ap.". A bare "ap." never matches an "apac.*" id
(str.startswith stops at the 'a' where "ap." expects '.'), so
"apac.anthropic.claude-*" and "au.anthropic.claude-*" fell through with
the prefix intact, missed the bare "anthropic.claude-*" pricing key, and
every Asia-Pacific / Australia Bedrock session priced as "unknown" — no
cost estimate or tracking for two whole geographies, while us./eu./global.
worked.

Add "apac." and "au." to the strip list (mirrors the same fix landing in
bedrock_adapter.is_anthropic_bedrock_model via #46297, which covers the
prompt-caching capability gate but not this duplicated cost-lookup copy).

Extends the existing cross-region pricing test to cover apac./au.; without
the fix it fails with scoped == None for "apac.".
2026-07-20 05:38:45 -07:00
Teknium
3441b80f4f feat(pricing): add Bedrock rows for Opus 4.8/4.7, correct Opus 4.6 to $5/$25
Adds current-gen Claude Opus pricing rows on Bedrock keyed to Anthropic's
published list price, which commercial Bedrock on-demand mirrors. Also
corrects the existing Opus 4.6 row: it carried Claude-3-era Opus pricing
($15/$75); Opus 4.5+ list at $5/$25 with cache write 1.25x / read 0.1x.

The AWS Price List API had not published these SKUs machine-readably as
of 2026-07, so these are commercial-list snapshots pending an
authoritative machine source.

Reapplied from PR #62327 (commit authored under a placeholder identity,
so cherry-pick was not usable; sonnet-5 row from that PR already landed
via #67932).

Co-authored-by: pgregg88 <4943027+pgregg88@users.noreply.github.com>
2026-07-20 03:27:16 -07:00
Osraka
e101bbaebb fix(pricing): restrict Bedrock profile normalization 2026-07-20 03:27:16 -07:00
Osraka
54418a888e fix(pricing): resolve versioned Bedrock profile IDs 2026-07-20 03:27:16 -07:00
Teknium
f6abfc05be fix: keep Sonnet 5 intro pricing over duplicate standard-rate entry
PR #55848 and #60410 both added an (anthropic, claude-sonnet-5) pricing
key; the later duplicate (/$15 standard rate) would silently win in
the dict literal. Keep the intro pricing entry ($2/$10 through
2026-08-31 per Anthropic docs) which carries the reversion note.
2026-07-20 02:25:44 -07:00
Daniel Brunsdon
f44074df7f feat(pricing): add Claude Sonnet 5 intro pricing entry
Sonnet 5 launched 2026-06-30 with introductory pricing ($2/$10 per
MTok input/output) through 2026-08-31, after which it reverts to
$3/$15. The model had no entry in the official-docs pricing snapshot,
so any session on claude-sonnet-5 was tracked as cost_status=unknown
with $0 estimated cost -- silently hiding real spend from
hermes insights and any downstream cost sync.

Source: https://platform.claude.com/docs/en/about-claude/pricing
2026-07-20 02:25:44 -07:00
Ariel Bravy
e2561466c7 feat(models): add Claude Sonnet 5 support 2026-07-20 02:25:44 -07:00
Teknium
03c0b00f45 fix(usage): read DeepSeek's native prompt_cache_hit_tokens cache field (#65678)
DeepSeek's own API (api.deepseek.com) reports context-cache hits as
top-level usage.prompt_cache_hit_tokens / prompt_cache_miss_tokens
(prompt_tokens = hit + miss), not the OpenAI nested
prompt_tokens_details.cached_tokens shape. Neither normalize_usage()
nor the chat_completions transport's extract_cache_stats() read those
fields, so direct DeepSeek sessions always showed 0 cache-hit tokens:
invisible in accounting, mis-billed at the full input rate, and 0%
cache display.

Both layers now fall back to prompt_cache_hit_tokens when the nested
shape is absent; the nested value wins when both are present (proxies).

Fixes #61871.
2026-07-16 07:29:53 -07:00
Teknium
a61e29e879 fix(pricing): refresh full DeepSeek snapshot to 2026-07 rates
Widens the deepseek-v4-flash addition to the whole stale-snapshot class:
- deepseek-v4-pro: $1.74/$3.48 → $0.435/$0.87, cache-read $0.003625
  (DeepSeek's 2026-07 price cut; every pro session was over-reporting 4x)
- deepseek-chat / deepseek-reasoner: deprecated 2026-07-24, now alias
  v4-flash non-thinking/thinking modes — repriced to match flash
  (reasoner was $0.55/$2.19 with no cache rate)
- cache_read added to every row; pricing_version unified at
  deepseek-pricing-2026-07
- invariant tests: aliases price identically to flash; every deepseek
  row carries cache_read < input
2026-07-16 05:56:22 -07:00
shuangxinniao
97397a1ccc feat(pricing): add deepseek-v4-flash to official-docs pricing snapshot
DeepSeek's /models endpoint returns no pricing, so direct-provider routes fall back to the _OFFICIAL_DOCS_PRICING snapshot. The table included deepseek-v4-pro but not the newer deepseek-v4-flash, so flash sessions reported $0.00 with cost_source "none". Add the flash entry (values from DeepSeek's official pricing page, mirroring the v4-pro entry; DeepSeek bills no separate cache-write cost) plus two regression tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 05:56:22 -07:00
Teknium
fe5c0cb6c3 feat(pricing): refresh Fireworks snapshot to 2026-07, cover full serverless catalog + cached picker pricing
- Refresh _OFFICIAL_DOCS_PRICING fireworks entries against current
  docs.fireworks.ai/serverless/pricing: qwen3p6-plus is gone (replaced
  by qwen3p7-plus); add glm-5p2/5p1, kimi-k2p7-code, deepseek-v4-flash,
  minimax-m3/m2p7, gpt-oss-120b/20b, and the routers/*-fast tiers with
  their distinct higher rates.
- Picker pricing via get_pricing_for_provider('fireworks'): pure dict
  transform over the shared models.dev in-memory/disk cache (1h TTL) +
  _pricing_cache memoization — no new network call on the picker path.
- Wire pricing display into the generic api-key-provider setup flow so
  Fireworks model pickers show $/M columns like OpenRouter/Nous do.
- Invariant tests: plugin fallback_models all priced, fast tiers price
  higher than standard, every row carries cache_read < input.
2026-07-16 04:24:14 -07:00
Brendan DeBeasi
365620ab28 feat(agent): add Fireworks pricing entries + routing branch
Fireworks-hosted sessions previously showed estimated_cost_usd = 0
because (a) _OFFICIAL_DOCS_PRICING had no Fireworks entries and (b)
resolve_billing_route() had no branch for provider="fireworks",
falling through to billing_mode="unknown".

Adds entries for the three Fireworks models hermes operators are
most likely to route through (Kimi K2.6, DeepSeek V4 Pro, Qwen3.6-Plus)
and a routing branch that triggers on either explicit
provider="fireworks" or api.fireworks.ai base_url match. Mirrors the
recently-merged MiniMax addition pattern; pricing snapshot sourced
from https://docs.fireworks.ai/serverless/pricing and the per-model
pages on fireworks.ai.

Tests cover: (a) full Fireworks model id resolves to the snapshot
entry, (b) base_url alone is sufficient to route, (c) end-to-end
estimate returns "estimated" status with the expected dollar amount.

A follow-up upstream issue is open proposing a dynamic pricing
source (e.g. litellm's pricing JSON) as a permanent fix to the
PR-per-model treadmill that this snapshot keeps adding to.
2026-07-16 04:24:14 -07:00
Kshitij Kapoor
a3828a94d0 feat(openai): cover gpt-5.6 -pro variants (PR #61587 complement)
PR #61587 adds sol-pro/terra-pro/luna-pro to the aggregator lists.
Complete those on the native surfaces the same way this PR completes
the base tiers:

- hermes_cli/models.py: -pro variants in _PROVIDER_MODELS[openai-api].
- agent/usage_pricing.py: alias ("openai", "gpt-5.6-*-pro") onto the
  base-tier PricingEntry rows — the -pro high-effort modes bill at the
  SAME per-token rates (verified against OpenRouter live pricing
  2026-07-09: identical prompt/completion prices for base and -pro);
  they cost more per task by consuming more tokens, not a higher rate.
- Context lengths need no new entries: "gpt-5.6-sol" et al. are
  substrings of their -pro variants and both lookup tables match
  longest-key-first (verified: sol-pro -> 1.05M direct / 272K codex).
- model_switch sort: -pro variants parse as suffix "sol-pro" (rank 1),
  so /model gpt still defaults to base sol — pinned by test.
- Not added to DEFAULT_CODEX_MODELS: only confirmed routable via API/
  OpenRouter so far; codex live discovery will surface them if ChatGPT
  exposes them, same policy as other unconfirmed codex slugs.

Tests: invariant tests extended (pro aliases share base entries, base
sol outranks sol-pro); 191 targeted tests pass.
2026-07-10 00:47:51 +05:30
Kshitij Kapoor
db117af478 review fixes: openai-api pricing route normalization, GA pricing_version, invariant tests
Phase-2 review findings addressed:
- resolve_billing_route: normalize the "openai-api" picker slug to the
  "openai" billing provider — without this the ("openai", <model>)
  _OFFICIAL_DOCS_PRICING keys (incl. every pre-existing gpt-4o/gpt-4.1
  entry, not just 5.6) were unreachable when the provider is openai-api.
- pricing_version: drop the "preview" tag (GA 2026-07-09 at same rates).
- model_metadata comment: dict order is cosmetic — lookups length-sort
  keys at match time; the old comment implied a positional invariant.
- model_switch comment: note "sol" is a series codename, not a generic
  quality word.
- tests/hermes_cli/test_gpt56_registration.py: behavior contracts (no
  list snapshots) — sol > terra/luna > 5.5 sort invariant, pricing
  reachability from both openai and openai-api routes, cache-write
  1.25x / cache-read 0.10x input relation.
2026-07-10 00:47:51 +05:30
Kshitij Kapoor
bd767b574b feat(openai): complete gpt-5.6 registration — context, codex catalog, native picker, pricing
PR #61578 added the GPT-5.6 series (sol/terra/luna) to the two aggregator
surfaces (OPENROUTER_MODELS, _PROVIDER_MODELS[nous]). This completes the
registration on the remaining surfaces per the standard add-model checklist:

- agent/model_metadata.py: DEFAULT_CONTEXT_LENGTHS 1.05M (direct API, same
  as gpt-5.5; more-specific keys precede gpt-5.5 for longest-substring
  matching) + _CODEX_OAUTH_CONTEXT_FALLBACK 272K for all three slugs.
  Without these the direct-API fallback matched generic "gpt-5" = 400K.
- hermes_cli/codex_models.py: DEFAULT_CODEX_MODELS + forward-compat
  templates so ChatGPT-OAuth (openai-codex) pickers surface the series.
- hermes_cli/models.py: _PROVIDER_MODELS[openai-api] (native API picker).
- agent/usage_pricing.py: _OFFICIAL_DOCS_PRICING snapshot — sol 5/30,
  terra 2.50/15, luna 1/6 per 1M in/out; cache read 0.10x input, cache
  write 1.25x input (OpenAI billing change starting with the 5.6 series).
  GA 2026-07-09 at preview rates. Sol Fast mode (Cerebras tier) excluded.
- hermes_cli/model_switch.py: rank "sol" as a flagship suffix so
  /model gpt resolves to gpt-5.6-sol, not alphabetical-first luna.

Verified: registry E2E via real imports (both context tables, codex
forward-compat from a gpt-5.5 template, billing-route lookup for
openai/gpt-5.6-sol -> 5.00/M), alias resolution on openai-codex and
openai-api resolves to gpt-5.6-sol; 183 targeted tests pass
(model_metadata, usage_pricing, codex_models, model_catalog).
2026-07-10 00:47:51 +05:30
Teknium
3a122ba4ac fix(usage): capture reasoning_tokens from completion_tokens_details on chat_completions (#57340)
normalize_usage only read output_tokens_details.reasoning_tokens (the
Responses API shape). Chat Completions providers — OpenAI, OpenRouter,
DeepSeek, and every OpenAI-compatible proxy — report it under
completion_tokens_details.reasoning_tokens, so reasoning_tokens was 0 for
every chat_completions reasoning model: hidden thinking was invisible in
session accounting, MoA traces, and the eval's per-task token columns.

Measured impact (HermesBench MoA run on deepseek-v4-flash, 4,828 advisor
calls): reasoning_tokens showed 0 everywhere while individual calls burned
up to 21.5K hidden thinking tokens to emit ~500 visible tokens. Verified
live against OpenRouter: deepseek-v4-flash returns
completion_tokens_details.reasoning_tokens=61 for a 74-completion-token
call; the field was simply never read.

Responses-shape reads are unchanged; the new read only fires when the
Responses shape yielded nothing.
2026-07-02 13:52:42 -07:00
Steve Lawton
c73e74386b feat(vertex): add Google Vertex AI provider for Gemini (OAuth2)
Adds Vertex AI as a first-class provider for Gemini models via Vertex's
OpenAI-compatible endpoint. Vertex authenticates with short-lived OAuth2
access tokens (service-account JSON or ADC), not a static API key — the
missing piece behind the recurring requests (#13484, #12639, #56259).

- agent/vertex_adapter.py: OAuth2 token minting + refresh-on-expiry
  (5-min margin), ADC->service-account fallback, global vs regional
  endpoint URLs. Config precedence: env var > config.yaml > default.
- plugins/model-providers/vertex/: provider profile (auth_type=vertex),
  reuses Gemini's extra_body.google.thinking_config translation.
- runtime_provider: vertex short-circuit BEFORE the credential pool so a
  credentials-file path is never mistaken for a static API key; mints a
  fresh token + computes base_url per resolve.
- run_agent + conversation_loop: _try_refresh_vertex_client_credentials()
  re-mints the token and rebuilds the client on a mid-session 401, so a
  long-lived gateway agent survives token expiry (~1h).
- auxiliary_client: vertex auth_type branch for side-LLM tasks.
- config.yaml: vertex.project_id / vertex.region (non-secret, bridged to
  env); credential path stays in .env (VERTEX_CREDENTIALS_PATH).
- setup wizard + model picker: dedicated _model_flow_vertex; curated
  google/gemini-* model list; --provider choices.
- pricing/metadata: Vertex prices off the gemini docs snapshot; endpoint
  host auto-maps to the vertex provider (no probe spam).
- lazy_deps + pyproject [vertex] extra: google-auth, opt-in only.
- docs: guides/google-vertex.md + providers page; tests for adapter +
  runtime resolution.

Salvages and modernizes #8427 by @slawt onto current main: rewired from
the legacy PROVIDER_REGISTRY path to the provider-profile architecture,
moved non-secret config out of .env into config.yaml, and added the
per-turn 401 token-refresh the original lacked.
2026-07-01 05:25:33 -07:00
Teknium
3bdb23de10 fix(moa): count reference (advisor) fan-out token usage + cost (#56087)
MoA ran the reference models before the aggregator but returned only the
aggregator's usage to the loop — _run_reference discarded each advisor
response's .usage entirely. Session accounting (state.db, /insights, cost)
therefore undercounted every MoA turn by the whole reference fan-out, which
is usually the bulk of the spend and scales with advisor count.

- _run_reference normalizes each advisor's usage with ITS OWN resolved
  provider/api_mode and prices it at ITS OWN model rate (correct cache-read/
  cache-write split), returning a _RefAccounting(usage, cost).
- create() sums advisor usage + cost once per turn (cache MISS only, so a
  repeat tool-iteration reusing cached advice does not double-charge) and
  exposes it via MoAClient.consume_reference_usage().
- conversation_loop folds advisor tokens into the reported/persisted token
  counts and adds advisor cost (priced per-advisor) on top of the
  aggregator cost, in both the in-memory session totals and the state.db
  per-call delta. Aggregator cost is still priced on aggregator-only usage
  so advisor tokens are never repriced at the aggregator rate.
- CanonicalUsage gains __add__ for per-bucket summing.

Tests: advisor usage/cost capture, per-turn sum + consume-clears +
cache-hit no-double-charge, CanonicalUsage.__add__.
2026-06-30 23:08:37 -07:00