From 31152ae108e84325db1d75a2c7f0f49037c866ee Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Sat, 11 Jul 2026 04:00:15 -0700 Subject: [PATCH] fix(providers): align Fireworks integration with project policy --- .env.example | 5 +-- apps/desktop/public/fireworks-white.svg | 5 --- apps/desktop/public/fireworks.svg | 5 --- .../src/components/onboarding/index.test.tsx | 22 ++--------- .../src/components/onboarding/index.tsx | 23 +++++------ .../src/components/onboarding/providers.tsx | 28 ------------- apps/desktop/src/i18n/en.ts | 5 +-- apps/desktop/src/i18n/ja.ts | 5 +-- apps/desktop/src/i18n/types.ts | 1 - apps/desktop/src/i18n/zh-hant.ts | 3 +- apps/desktop/src/i18n/zh.ts | 3 +- hermes_cli/config.py | 8 ---- hermes_cli/models.py | 4 +- hermes_cli/providers.py | 1 - plugins/model-providers/fireworks/__init__.py | 38 ++---------------- plugins/model-providers/fireworks/plugin.yaml | 2 +- scripts/release.py | 1 + tests/hermes_cli/test_fireworks_provider.py | 39 +++++-------------- .../model_providers/test_fireworks_profile.py | 18 ++++----- tests/run_agent/test_fireworks_live.py | 4 +- website/docs/getting-started/quickstart.md | 1 + website/docs/integrations/providers.md | 7 ++++ .../docs/reference/environment-variables.md | 1 + website/docs/user-guide/configuration.md | 4 +- 24 files changed, 55 insertions(+), 178 deletions(-) delete mode 100644 apps/desktop/public/fireworks-white.svg delete mode 100644 apps/desktop/public/fireworks.svg diff --git a/.env.example b/.env.example index 0e78d2023f9..828878c68c0 100644 --- a/.env.example +++ b/.env.example @@ -2,15 +2,12 @@ # Copy this file to .env and fill in your API keys # ============================================================================= -# LLM PROVIDER (Fireworks AI) — preferred BYOK provider +# LLM PROVIDER (Fireworks AI) # ============================================================================= -# Fireworks AI — fastest inference for production AI, 300+ models, one API key. # Get your key at: https://app.fireworks.ai/settings/users/api-keys # Address models directly by catalog ID, e.g. # accounts/fireworks/models/kimi-k2p6, accounts/fireworks/models/glm-5p2 # FIREWORKS_API_KEY= -# FIREWORKS_BASE_URL=https://api.fireworks.ai/inference/v1 # Override default base URL - # ============================================================================= # LLM PROVIDER (OpenRouter) # ============================================================================= diff --git a/apps/desktop/public/fireworks-white.svg b/apps/desktop/public/fireworks-white.svg deleted file mode 100644 index cfab71d67ff..00000000000 --- a/apps/desktop/public/fireworks-white.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/apps/desktop/public/fireworks.svg b/apps/desktop/public/fireworks.svg deleted file mode 100644 index f392879f60c..00000000000 --- a/apps/desktop/public/fireworks.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/apps/desktop/src/components/onboarding/index.test.tsx b/apps/desktop/src/components/onboarding/index.test.tsx index dd9028943fd..35cac9c4d17 100644 --- a/apps/desktop/src/components/onboarding/index.test.tsx +++ b/apps/desktop/src/components/onboarding/index.test.tsx @@ -56,15 +56,12 @@ afterEach(() => { }) describe('onboarding Picker', () => { - it('features Nous Portal and Fireworks, and hides other providers behind a disclosure', () => { + it('features Nous Portal and hides other providers behind a disclosure', () => { setProviders([provider('anthropic', 'Anthropic Claude'), provider('nous', 'Nous Portal')]) render() expect(screen.getByText('Nous Portal')).toBeTruthy() - // Fireworks is the preferred BYOK provider — a second hero card after Nous. - expect(screen.getByText('Fireworks AI')).toBeTruthy() - // Both hero cards carry a Recommended badge; the plain rows below do not. - expect(screen.getAllByText('Recommended')).toHaveLength(2) + expect(screen.getByText('Recommended')).toBeTruthy() expect(screen.queryByText('Anthropic API Key')).toBeNull() fireEvent.click(screen.getByRole('button', { name: 'Other providers' })) @@ -73,25 +70,14 @@ describe('onboarding Picker', () => { expect(screen.getByRole('button', { name: 'Collapse' })).toBeTruthy() }) - it('still features Fireworks when Nous Portal is absent', () => { + it('shows every provider directly when Nous Portal is absent', () => { setProviders([provider('anthropic', 'Anthropic Claude'), provider('openai-codex', 'OpenAI Codex / ChatGPT')]) render() expect(screen.getByText('Anthropic API Key')).toBeTruthy() expect(screen.getByText('OpenAI OAuth (ChatGPT)')).toBeTruthy() expect(screen.queryByText('Other sign-in options')).toBeNull() - // Fireworks stays featured even without Nous — exactly one Recommended badge. - expect(screen.getByText('Fireworks AI')).toBeTruthy() - expect(screen.getAllByText('Recommended')).toHaveLength(1) - }) - - it('routes the Fireworks hero card into the API-key form', () => { - setProviders([provider('nous', 'Nous Portal')]) - render() - - fireEvent.click(screen.getByText('Fireworks AI')) - - expect($desktopOnboarding.get().mode).toBe('apikey') + expect(screen.queryByText('Recommended')).toBeNull() }) it('offers "choose later" on first run and persists the skip', () => { diff --git a/apps/desktop/src/components/onboarding/index.tsx b/apps/desktop/src/components/onboarding/index.tsx index 8c889ed20f6..df547b91a65 100644 --- a/apps/desktop/src/components/onboarding/index.tsx +++ b/apps/desktop/src/components/onboarding/index.tsx @@ -28,10 +28,9 @@ import { import type { ModelOptionProvider, OAuthProvider } from '@/types/hermes' import { DocsLink, FlowPanel, Status } from './flow' -import { FeaturedFireworksRow, FeaturedProviderRow, KeyProviderRow, ProviderRow, sortProviders } from './providers' +import { FeaturedProviderRow, KeyProviderRow, ProviderRow, sortProviders } from './providers' export { - FeaturedFireworksRow, FeaturedProviderRow, KeyProviderRow, ProviderRow, @@ -56,18 +55,18 @@ export interface ApiKeyOption { } const API_KEY_OPTIONS: ApiKeyOption[] = [ - { - id: 'fireworks', - name: 'Fireworks AI', - envKey: 'FIREWORKS_API_KEY', - docsUrl: 'https://app.fireworks.ai/settings/users/api-keys' - }, { id: 'openrouter', name: 'OpenRouter', envKey: 'OPENROUTER_API_KEY', docsUrl: 'https://openrouter.ai/keys' }, + { + id: 'fireworks', + name: 'Fireworks AI', + envKey: 'FIREWORKS_API_KEY', + docsUrl: 'https://app.fireworks.ai/settings/users/api-keys' + }, { id: 'openai', name: 'OpenAI', @@ -408,9 +407,8 @@ export function Picker({ ctx }: { ctx: OnboardingContext }) { const { t } = useI18n() const { localEndpoint, manual, mode, providers } = useStore($desktopOnboarding) const [showAll, setShowAll] = useState(readShowAll) - // Which key-form option to preselect when we flip to 'apikey' mode. Set by the - // Fireworks hero card (its BYOK key) and the OpenRouter row; the generic - // "I have an API key" link leaves it undefined and lands on the first option. + // Which key-form option to preselect when we flip to 'apikey' mode. The + // OpenRouter row selects its key; the generic link lands on the first option. const [apiKeyInitialEnv, setApiKeyInitialEnv] = useState(undefined) const openKeyForm = (envKey?: string) => { setApiKeyInitialEnv(envKey) @@ -459,9 +457,6 @@ export function Picker({ ctx }: { ctx: OnboardingContext }) {
{featured ? : null} - {/* Fireworks — preferred BYOK provider, second hero card after Nous. - Routes into the API-key form preselecting the Fireworks key. */} - openKeyForm('FIREWORKS_API_KEY')} /> {showRest ? ( <> {rest.map(p => ( diff --git a/apps/desktop/src/components/onboarding/providers.tsx b/apps/desktop/src/components/onboarding/providers.tsx index 3bac195cfbb..f7dafdae95d 100644 --- a/apps/desktop/src/components/onboarding/providers.tsx +++ b/apps/desktop/src/components/onboarding/providers.tsx @@ -62,34 +62,6 @@ export function FeaturedProviderRow({ ) } -// Fireworks is the preferred BYOK provider — a second hero card directly below -// Nous Portal. It isn't an OAuth provider (no sign-in flow), so it takes a bare -// onSelect that routes into the API-key form rather than an OAuthProvider. -export function FeaturedFireworksRow({ onSelect }: { onSelect: () => void }) { - const { t } = useI18n() - - return ( - - ) -} - function ConnectedTag() { const { t } = useI18n() diff --git a/apps/desktop/src/i18n/en.ts b/apps/desktop/src/i18n/en.ts index 246beec9bcf..b056bbca5f5 100644 --- a/apps/desktop/src/i18n/en.ts +++ b/apps/desktop/src/i18n/en.ts @@ -1928,12 +1928,11 @@ export const en: Translations = { recommended: 'Recommended', connected: 'Connected', featuredPitch: 'One subscription, 300+ frontier models — the recommended way to run Hermes', - fireworksPitch: 'Fastest inference for production AI — 300+ models, one API, deploy in a click', openRouterPitch: 'One key, hundreds of models — a solid default', apiKeyOptions: { fireworks: { - short: 'fastest inference', - description: 'Direct access to 300+ open & proprietary models on Fireworks AI — one API key, production speed.' + short: 'direct model API', + description: 'Direct access to models hosted by Fireworks AI.' }, openrouter: { short: 'one key, many models', diff --git a/apps/desktop/src/i18n/ja.ts b/apps/desktop/src/i18n/ja.ts index 0efe672db9e..20ff811423b 100644 --- a/apps/desktop/src/i18n/ja.ts +++ b/apps/desktop/src/i18n/ja.ts @@ -1873,12 +1873,11 @@ export const ja = defineLocale({ recommended: '推奨', connected: '接続済み', featuredPitch: '1 つのサブスクリプションで 300 以上の最先端モデル — Hermes を実行するための推奨方法', - fireworksPitch: 'プロダクション AI 向けの最速推論 — 300 以上のモデル、1 つの API、ワンクリックでデプロイ', openRouterPitch: '1 つのキーで数百のモデル — 堅実なデフォルト', apiKeyOptions: { fireworks: { - short: '最速の推論', - description: 'Fireworks AI で 300 以上のオープン/プロプライエタリモデルに直接アクセス — 1 つの API キー、プロダクション速度。' + short: 'モデル API に直接接続', + description: 'Fireworks AI がホストするモデルに直接アクセスします。' }, openrouter: { short: '1 つのキーで多くのモデル', diff --git a/apps/desktop/src/i18n/types.ts b/apps/desktop/src/i18n/types.ts index ff19f13191b..031f5c78f6d 100644 --- a/apps/desktop/src/i18n/types.ts +++ b/apps/desktop/src/i18n/types.ts @@ -1582,7 +1582,6 @@ export interface Translations { recommended: string connected: string featuredPitch: string - fireworksPitch: string openRouterPitch: string apiKeyOptions: Record backToSignIn: string diff --git a/apps/desktop/src/i18n/zh-hant.ts b/apps/desktop/src/i18n/zh-hant.ts index 008bf7def57..f58fe80edd5 100644 --- a/apps/desktop/src/i18n/zh-hant.ts +++ b/apps/desktop/src/i18n/zh-hant.ts @@ -1817,10 +1817,9 @@ export const zhHant = defineLocale({ recommended: '建議', connected: '已連線', featuredPitch: '一個訂閱,300+ 前沿模型 — 執行 Hermes 的建議方式', - fireworksPitch: '面向生產 AI 的最快推理 — 300+ 模型,一個 API,一鍵部署', openRouterPitch: '一個金鑰,數百個模型 — 穩定的預設選擇', apiKeyOptions: { - fireworks: { short: '最快推理', description: '透過 Fireworks AI 直接存取 300+ 開源與專有模型 — 一個 API 金鑰,生產級速度。' }, + fireworks: { short: '直接模型 API', description: '直接存取 Fireworks AI 託管的模型。' }, openrouter: { short: '一個金鑰,多個模型', description: '用一個金鑰存取數百個模型。適合新安裝的預設選擇。' }, openai: { short: 'GPT 等級模型', description: '直接存取 OpenAI 模型。' }, gemini: { short: 'Gemini 模型', description: '直接存取 Google Gemini 模型。' }, diff --git a/apps/desktop/src/i18n/zh.ts b/apps/desktop/src/i18n/zh.ts index 4a6a9ca9241..babc9b778d2 100644 --- a/apps/desktop/src/i18n/zh.ts +++ b/apps/desktop/src/i18n/zh.ts @@ -2100,10 +2100,9 @@ export const zh: Translations = { recommended: '推荐', connected: '已连接', featuredPitch: '一个订阅,300+ 前沿模型 — 运行 Hermes 的推荐方式', - fireworksPitch: '面向生产 AI 的最快推理 — 300+ 模型,一个 API,一键部署', openRouterPitch: '一个密钥,数百个模型 — 稳妥的默认选择', apiKeyOptions: { - fireworks: { short: '最快推理', description: '通过 Fireworks AI 直接访问 300+ 开源与专有模型 — 一个 API 密钥,生产级速度。' }, + fireworks: { short: '直接模型 API', description: '直接访问 Fireworks AI 托管的模型。' }, openrouter: { short: '一个密钥,多个模型', description: '用一个密钥访问数百个模型。适合新安装的默认选择。' }, openai: { short: 'GPT 级模型', description: '直接访问 OpenAI 模型。' }, gemini: { short: 'Gemini 模型', description: '直接访问 Google Gemini 模型。' }, diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 31fd9dbc764..9b9359888fa 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -3543,14 +3543,6 @@ OPTIONAL_ENV_VARS = { "category": "provider", "advanced": True, }, - "FIREWORKS_BASE_URL": { - "description": "Fireworks AI base URL override", - "prompt": "Fireworks AI base URL (leave empty for default)", - "url": None, - "password": False, - "category": "provider", - "advanced": True, - }, "MINIMAX_API_KEY": { "description": "MiniMax API key (international)", "prompt": "MiniMax API key", diff --git a/hermes_cli/models.py b/hermes_cli/models.py index 54df537b8ed..944e52d1d0a 100644 --- a/hermes_cli/models.py +++ b/hermes_cli/models.py @@ -1051,9 +1051,6 @@ class ProviderEntry(NamedTuple): CANONICAL_PROVIDERS: list[ProviderEntry] = [ ProviderEntry("nous", "Nous Portal", "Nous Portal (Everything your agent needs, 300+ models with bundled tool use)"), - # Fireworks AI — preferred BYOK inference partner. Sits directly below Nous - # Portal (Nous's own provider stays first); everything else follows. - ProviderEntry("fireworks", "Fireworks AI", "Fireworks AI (Fastest inference for production AI, 300+ models, one API key)"), ProviderEntry("openrouter", "OpenRouter", "OpenRouter (Pay-per-use API aggregator)"), ProviderEntry("moa", "Mixture of Agents", "Mixture of Agents (named presets; aggregator acts after reference models)"), ProviderEntry("novita", "NovitaAI", "NovitaAI (Cloud: Model API, Agent Sandbox, GPU Cloud)"), @@ -1083,6 +1080,7 @@ CANONICAL_PROVIDERS: list[ProviderEntry] = [ ProviderEntry("ollama-cloud", "Ollama Cloud", "Ollama Cloud (Cloud-hosted open models, ollama.com)"), ProviderEntry("arcee", "Arcee AI", "Arcee AI (Trinity models, direct API)"), ProviderEntry("gmi", "GMI Cloud", "GMI Cloud (Multi-model direct API)"), + ProviderEntry("fireworks", "Fireworks AI", "Fireworks AI (OpenAI-compatible direct model API)"), ProviderEntry("kilocode", "Kilo Code", "Kilo Code (Kilo Gateway API)"), ProviderEntry("opencode-zen", "OpenCode Zen", "OpenCode Zen (Curated models, pay-as-you-go)"), ProviderEntry("opencode-go", "OpenCode Go", "OpenCode Go (Open models subscription)"), diff --git a/hermes_cli/providers.py b/hermes_cli/providers.py index 6d73683717a..53d0e23e06b 100644 --- a/hermes_cli/providers.py +++ b/hermes_cli/providers.py @@ -200,7 +200,6 @@ HERMES_OVERLAYS: Dict[str, HermesOverlay] = { transport="openai_chat", extra_env_vars=("FIREWORKS_API_KEY",), base_url_override="https://api.fireworks.ai/inference/v1", - base_url_env_var="FIREWORKS_BASE_URL", ), "ollama-cloud": HermesOverlay( transport="openai_chat", diff --git a/plugins/model-providers/fireworks/__init__.py b/plugins/model-providers/fireworks/__init__.py index 55b9f37da19..54f9758df9b 100644 --- a/plugins/model-providers/fireworks/__init__.py +++ b/plugins/model-providers/fireworks/__init__.py @@ -1,8 +1,7 @@ """Fireworks AI provider profile. Fireworks AI serves fast, production-grade inference for open and proprietary -models through an OpenAI-compatible chat-completions endpoint. It is a bundled, -preferred BYOK provider — users paste a Fireworks API key and go. +models through an OpenAI-compatible chat-completions endpoint. Address models directly by their catalog ID, e.g. ``accounts/fireworks/models/kimi-k2p6`` or ``accounts/fireworks/models/glm-5p2``. @@ -18,19 +17,11 @@ fireworks = ProviderProfile( name="fireworks", aliases=("fireworks-ai", "fw"), display_name="Fireworks AI", - description="Fireworks AI — fastest inference for production AI, 300+ models, one API key", + description="Fireworks AI — OpenAI-compatible direct model API", signup_url="https://app.fireworks.ai/settings/users/api-keys", - env_vars=("FIREWORKS_API_KEY", "FIREWORKS_BASE_URL"), + env_vars=("FIREWORKS_API_KEY",), base_url="https://api.fireworks.ai/inference/v1", auth_type="api_key", - # Attribution: lets Fireworks identify traffic originating from Hermes Agent - # (partner/revenue attribution). Matches the canonical Hermes attribution - # values in agent/auxiliary_client.py. Applied at client construction via the - # generic profile.default_headers path in run_agent.py. - default_headers={ - "HTTP-Referer": "https://hermes-agent.nousresearch.com", - "X-Title": "Hermes Agent", - }, # Auxiliary model for cheap tasks (compaction, title generation, vision). # A standard pay-as-you-go catalog ``/models/`` ID. default_aux_model="accounts/fireworks/models/glm-5p2", @@ -43,26 +34,3 @@ fireworks = ProviderProfile( ) register_provider(fireworks) - - -# --------------------------------------------------------------------------- -# Future work (intentionally disabled for now — do not ship as customer-facing). -# -# A second Fireworks key tier authenticates against the same endpoint but can -# only address managed router IDs (accounts/fireworks/routers/...), not raw -# /models/ IDs, and cannot list the account catalog. Enabling it means detecting -# the key prefix and swapping the picker catalog + aux model to routers. Kept -# here (commented out) so the wiring is ready when we turn it on. Router IDs and -# behavior track fw-ai/fireconnect setup-cli. -# -# _MANAGED_KEY_PREFIX = "fpk_" -# _MANAGED_ROUTERS = ( -# "accounts/fireworks/routers/glm-latest", # default -# "accounts/fireworks/routers/glm-fast-latest", -# "accounts/fireworks/routers/kimi-latest", -# "accounts/fireworks/routers/kimi-k2p6-turbo", # turbo tier — not a default -# ) -# -# def _is_managed_key(api_key: str) -> bool: -# return bool(api_key) and api_key.startswith(_MANAGED_KEY_PREFIX) -# --------------------------------------------------------------------------- diff --git a/plugins/model-providers/fireworks/plugin.yaml b/plugins/model-providers/fireworks/plugin.yaml index 1523792ce80..dc9b25602ac 100644 --- a/plugins/model-providers/fireworks/plugin.yaml +++ b/plugins/model-providers/fireworks/plugin.yaml @@ -2,4 +2,4 @@ name: fireworks-provider kind: model-provider version: 1.0.0 description: Fireworks AI — fast inference for open and proprietary models -author: Nous Research +author: Alex Jestin Taylor (@alex-fireworks) + Hermes Agent diff --git a/scripts/release.py b/scripts/release.py index bacca2ef2b6..beaa0380269 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -77,6 +77,7 @@ AUTHOR_MAP = { "lord-dubious@users.noreply.github.com": "lord-dubious", # PR #58453 salvage (preserve static custom provider models declared as dict rows) "williamumu@users.noreply.github.com": "williamumu", # PR #31041 salvage (pairing: merge split legacy/new pairing store dirs at PairingStore init so approved users aren't re-prompted to pair) "jonathan@mintrx.com": "JAlmanzarMint", # PR #52688 salvage (vision: rasterize SVG / re-encode unsupported raster formats to PNG before embedding), folded into #57890 + "alex@fireworks.ai": "alex-fireworks", # PR #61182 salvage (Fireworks AI model-provider integration) "al3060388206@gmail.com": "ooiuuii", # PR #58466/#58377 salvage (redact: fireworks fw-/fpk_ prefixes; telegram: redact bot tokens out of transport error strings). Also PR #58433 salvage (codex: accept recorded final_text when app-server omits turn/completed) and PR #58472 salvage (gateway: cap proxy SSE line buffer at 16MiB). "Jigoooo@users.noreply.github.com": "Jigoooo", # PR #58474 salvage (auxiliary: fall back to token resolver when anthropic pool has no usable entry) "root@vmi3351581.contaboserver.net": "ostravajih", # PR #58374 salvage (poolside: coerce integer finish_reason and tool_call id to strings) diff --git a/tests/hermes_cli/test_fireworks_provider.py b/tests/hermes_cli/test_fireworks_provider.py index 7ea528dd6c7..6ebb589ee9c 100644 --- a/tests/hermes_cli/test_fireworks_provider.py +++ b/tests/hermes_cli/test_fireworks_provider.py @@ -1,9 +1,8 @@ """Focused tests for Fireworks AI first-class provider wiring. -Fireworks is the bundled, preferred BYOK provider (sits right after Nous -Portal). These tests pin the wiring that makes it a real provider — alias -resolution through BOTH CLI resolvers, ordering, config/doctor/overlay -registration, attribution headers, and credential/base-URL resolution — without +These tests pin the wiring that makes Fireworks a real provider — alias +resolution through both CLI resolvers, config/doctor/overlay registration, +and credential/base-URL resolution — without any live network calls. """ @@ -48,21 +47,12 @@ class TestFireworksAliases: class TestFireworksOrdering: - """Product contract: Fireworks is the preferred provider and sits directly - below Nous Portal (Nous's own provider stays first).""" + """Fireworks participates in the canonical provider catalog.""" def test_present_in_canonical_providers(self): slugs = [p.slug for p in CANONICAL_PROVIDERS] assert "fireworks" in slugs - def test_sits_immediately_after_nous(self): - slugs = [p.slug for p in CANONICAL_PROVIDERS] - assert slugs.index("fireworks") == slugs.index("nous") + 1, slugs[:4] - - def test_ranks_ahead_of_the_other_byok_providers(self): - slugs = [p.slug for p in CANONICAL_PROVIDERS] - # Ahead of the generic aggregators / other direct providers. - assert slugs.index("fireworks") < slugs.index("openrouter") def test_has_a_label(self): assert _PROVIDER_LABELS.get("fireworks") == "Fireworks AI" @@ -76,8 +66,7 @@ class TestFireworksConfigRegistry: assert OPTIONAL_ENV_VARS["FIREWORKS_API_KEY"]["category"] == "provider" assert OPTIONAL_ENV_VARS["FIREWORKS_API_KEY"]["password"] is True - assert "FIREWORKS_BASE_URL" in OPTIONAL_ENV_VARS - assert OPTIONAL_ENV_VARS["FIREWORKS_BASE_URL"]["password"] is False + assert "FIREWORKS_BASE_URL" not in OPTIONAL_ENV_VARS class TestFireworksOverlay: @@ -88,7 +77,7 @@ class TestFireworksOverlay: overlay = HERMES_OVERLAYS["fireworks"] assert overlay.transport == "openai_chat" assert overlay.base_url_override == "https://api.fireworks.ai/inference/v1" - assert overlay.base_url_env_var == "FIREWORKS_BASE_URL" + assert not overlay.base_url_env_var assert not overlay.is_aggregator @@ -153,16 +142,8 @@ class TestFireworksCredentials: assert creds["api_key"] == "fw_test_key" assert creds["base_url"] == "https://api.fireworks.ai/inference/v1" - def test_base_url_override_is_honored(self, monkeypatch): - monkeypatch.setenv("FIREWORKS_API_KEY", "fw_test_key") - monkeypatch.setenv("FIREWORKS_BASE_URL", "https://gateway.example.com/v1") - creds = resolve_api_key_provider_credentials("fireworks") - assert creds["base_url"] == "https://gateway.example.com/v1" - - class TestFireworksAuxiliary: - """resolve_provider_client wires the BYOK key, PAYG-safe aux model, and the - partner-attribution headers through to the OpenAI client.""" + """resolve_provider_client wires the BYOK key and PAYG-safe aux model.""" def _resolve(self, name): from unittest.mock import patch @@ -174,13 +155,13 @@ class TestFireworksAuxiliary: client, model = resolve_provider_client(name) return client, model, mock_openai.call_args.kwargs - def test_client_carries_attribution_headers(self, monkeypatch): + def test_client_has_no_partner_attribution_headers(self, monkeypatch): monkeypatch.setenv("FIREWORKS_API_KEY", "fw_test_key") client, model, kwargs = self._resolve("fireworks") assert client is not None headers = kwargs.get("default_headers", {}) - assert headers.get("HTTP-Referer") == "https://hermes-agent.nousresearch.com" - assert headers.get("X-Title") == "Hermes Agent" + assert "HTTP-Referer" not in headers + assert "X-Title" not in headers assert kwargs["base_url"] == "https://api.fireworks.ai/inference/v1" def test_aux_model_is_payg_safe(self, monkeypatch): diff --git a/tests/plugins/model_providers/test_fireworks_profile.py b/tests/plugins/model_providers/test_fireworks_profile.py index c44ecbb79b1..0a04c7d97f9 100644 --- a/tests/plugins/model_providers/test_fireworks_profile.py +++ b/tests/plugins/model_providers/test_fireworks_profile.py @@ -1,8 +1,7 @@ """Unit tests for the Fireworks AI provider profile. -Pins the profile's contract without going live: identity, attribution headers -(so Fireworks can attribute Hermes traffic for the partnership), alias -registration, and the pay-as-you-go model defaults (direct catalog ``/models/`` +Pins the profile's contract without going live: identity, alias registration, +and the pay-as-you-go model defaults (direct catalog ``/models/`` IDs, not the router-only tier). """ @@ -30,7 +29,7 @@ class TestFireworksIdentity: assert p.auth_type == "api_key" assert p.base_url == "https://api.fireworks.ai/inference/v1" assert "FIREWORKS_API_KEY" in p.env_vars - assert "FIREWORKS_BASE_URL" in p.env_vars + assert "FIREWORKS_BASE_URL" not in p.env_vars def test_display_metadata_present(self, fireworks_profile): # Prominence copy is surfaced in the picker; keep it non-empty rather @@ -40,13 +39,10 @@ class TestFireworksIdentity: assert fireworks_profile.signup_url.startswith("https://") -class TestFireworksAttributionHeaders: - """The profile carries the partner-attribution headers Fireworks captures.""" - - def test_referer_and_title_headers(self, fireworks_profile): - headers = fireworks_profile.default_headers - assert headers.get("HTTP-Referer") == "https://hermes-agent.nousresearch.com" - assert headers.get("X-Title") == "Hermes Agent" +class TestFireworksHeaders: + def test_no_partner_attribution_headers(self, fireworks_profile): + assert "HTTP-Referer" not in fireworks_profile.default_headers + assert "X-Title" not in fireworks_profile.default_headers class TestFireworksAliases: diff --git a/tests/run_agent/test_fireworks_live.py b/tests/run_agent/test_fireworks_live.py index 7f2abfd1de9..9e2944ef98c 100644 --- a/tests/run_agent/test_fireworks_live.py +++ b/tests/run_agent/test_fireworks_live.py @@ -6,7 +6,7 @@ Opt-in only: Unlike a bare OpenAI() client pointed at the endpoint, this drives Hermes' own provider resolution — ``resolve_provider_client('fireworks')`` — so it -verifies the auth/config/base-URL/attribution-header/aux-model wiring that the +verifies the auth/config/base-URL/aux-model wiring that the bundled provider actually ships, then makes a real call through that client. """ @@ -51,8 +51,6 @@ def test_fireworks_basic_chat_through_runtime(): response = client.chat.completions.create( model=model, messages=[{"role": "user", "content": "Say exactly the word 'pong' and nothing else."}], - max_tokens=32, - temperature=0.0, timeout=60, ) diff --git a/website/docs/getting-started/quickstart.md b/website/docs/getting-started/quickstart.md index 907af9c2402..2b390b3edef 100644 --- a/website/docs/getting-started/quickstart.md +++ b/website/docs/getting-started/quickstart.md @@ -113,6 +113,7 @@ Good defaults: | **OpenAI Codex** | ChatGPT OAuth, uses Codex models | Device code auth via `hermes model` | | **Anthropic** | Claude models directly — Max plan + extra usage credits (OAuth), or API key for pay-per-token | `hermes model` → OAuth login (requires Max + extra credits), or an Anthropic API key | | **OpenRouter** | Multi-provider routing across many models | Enter your API key | +| **Fireworks AI** | Direct OpenAI-compatible model API | Set `FIREWORKS_API_KEY` | | **Z.AI** | GLM / Zhipu-hosted models | Set `GLM_API_KEY` / `ZAI_API_KEY` (also accepts `Z_AI_API_KEY`) | | **Kimi / Moonshot** | Moonshot-hosted coding and chat models | Set `KIMI_API_KEY` (or the Kimi-Coding-specific `KIMI_CODING_API_KEY`) | | **Kimi / Moonshot China** | China-region Moonshot endpoint | Set `KIMI_CN_API_KEY` | diff --git a/website/docs/integrations/providers.md b/website/docs/integrations/providers.md index a49b15d04de..d07d27e22b3 100644 --- a/website/docs/integrations/providers.md +++ b/website/docs/integrations/providers.md @@ -20,6 +20,7 @@ You need at least one way to connect to an LLM. Use `hermes model` to switch pro | **GitHub Copilot ACP** | `hermes model` (spawns local `copilot --acp --stdio`) | | **Anthropic** | `hermes model` (Claude Max + extra usage credits via OAuth; also supports Anthropic API key or manual setup-token — see note below) | | **OpenRouter** | `OPENROUTER_API_KEY` in `~/.hermes/.env` | +| **Fireworks AI** | `FIREWORKS_API_KEY` in `~/.hermes/.env` (provider: `fireworks`; aliases: `fireworks-ai`, `fw`) | | **NovitaAI** | `NOVITA_API_KEY` in `~/.hermes/.env` (provider: `novita`, 200+ models, Model API, Agent Sandbox, GPU Cloud) | | **z.ai / GLM** | `GLM_API_KEY` in `~/.hermes/.env` (provider: `zai`) | | **Kimi / Moonshot** | `KIMI_API_KEY` in `~/.hermes/.env` (provider: `kimi-coding`) | @@ -214,6 +215,10 @@ model: These providers have built-in support with dedicated provider IDs. Set the API key and use `--provider` to select: ```bash +# Fireworks AI +hermes chat --provider fireworks --model accounts/fireworks/models/kimi-k2p6 +# Requires: FIREWORKS_API_KEY in ~/.hermes/.env + # NovitaAI Model API hermes chat --provider novita --model moonshotai/kimi-k2.5 # Requires: NOVITA_API_KEY in ~/.hermes/.env @@ -260,6 +265,8 @@ hermes chat --provider gmi --model zai-org/GLM-5.1-FP8 # Requires: GMI_API_KEY in ~/.hermes/.env ``` +Fireworks uses its native slash-form catalog IDs, such as `accounts/fireworks/models/kimi-k2p6`. Run `hermes model`, choose **Fireworks AI**, and select from the live catalog or enter another Fireworks model ID. The default endpoint is `https://api.fireworks.ai/inference/v1`; configure a different endpoint through `model.base_url` in `config.yaml`, not `.env`. + Or set the provider permanently in `config.yaml`: ```yaml model: diff --git a/website/docs/reference/environment-variables.md b/website/docs/reference/environment-variables.md index b7df6823837..53f7a18a0be 100644 --- a/website/docs/reference/environment-variables.md +++ b/website/docs/reference/environment-variables.md @@ -14,6 +14,7 @@ Hermes reads environment variables from the process environment and, for user-ma |----------|-------------| | `OPENROUTER_API_KEY` | OpenRouter API key (recommended for flexibility) | | `OPENROUTER_BASE_URL` | Override the OpenRouter-compatible base URL | +| `FIREWORKS_API_KEY` | Fireworks AI API key ([app.fireworks.ai](https://app.fireworks.ai/settings/users/api-keys)). Configure endpoint overrides with `model.base_url` in `config.yaml`. | | `HERMES_OPENROUTER_CACHE` | Enable OpenRouter response caching (`1`/`true`/`yes`/`on`). Overrides `openrouter.response_cache` in config.yaml. See [Response Caching](https://openrouter.ai/docs/guides/features/response-caching). | | `HERMES_OPENROUTER_CACHE_TTL` | Cache TTL in seconds (1-86400). Overrides `openrouter.response_cache_ttl` in config.yaml. | | `NOUS_BASE_URL` | Override Nous Portal base URL (rarely needed; development/testing only) | diff --git a/website/docs/user-guide/configuration.md b/website/docs/user-guide/configuration.md index 46afc11bcfd..269b7cff44c 100644 --- a/website/docs/user-guide/configuration.md +++ b/website/docs/user-guide/configuration.md @@ -1170,7 +1170,7 @@ These options apply to **auxiliary task configs** (`auxiliary:`, `compression:`) | `"xai-oauth"` | Force xAI Grok OAuth (browser login for SuperGrok or X Premium+ subscribers, no API key). Same OAuth token covers chat, TTS, image, video, and transcription. | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) | | `"main"` | Use your active custom/main endpoint. This can come from `OPENAI_BASE_URL` + `OPENAI_API_KEY` or from a custom endpoint saved via `hermes model` / `config.yaml`. Works with OpenAI, local models, or any OpenAI-compatible API. **Auxiliary tasks only — not valid for `model.provider`.** | Custom endpoint credentials + base URL | -Direct API-key providers from the main provider catalog also work here when you want side tasks to bypass your default router. `gmi` is valid once `GMI_API_KEY` is configured: +Direct API-key providers from the main provider catalog also work here when you want side tasks to bypass your default router. For example, `gmi` is valid once `GMI_API_KEY` is configured, and `fireworks` is valid once `FIREWORKS_API_KEY` is configured: ```yaml auxiliary: @@ -1179,7 +1179,7 @@ auxiliary: model: "anthropic/claude-opus-4.6" ``` -For GMI auxiliary routing, use the exact model ID returned by GMI's `/v1/models` endpoint. +For GMI auxiliary routing, use the exact model ID returned by GMI's `/v1/models` endpoint. Fireworks model IDs use the provider's native slash form, for example `accounts/fireworks/models/glm-5p2`. ### Common Setups