feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry - #638

Merged
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry
Jun 15, 2026
Merged

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry#638
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Why

Give a new user the shortest path to a working agent: bring an OpenRouter or Venice key and go. The headline command is the requested verb —

obol buy inference venice
obol buy inference openrouter --free

— which opens the provider's API-key page in the browser (hermes-agent-style), takes the key, and wires the agent's LiteLLM gateway to it. obol buy inference <seller-url> (or no arg) is unchanged — still the x402 crypto-paid seller flow. Dispatch keys on whether the positional argument matches a known provider id, so the two doors coexist behind one verb.

Two front doors, one engine

  • obol buy inference <provider> — friendly onboarding. Resolves the key (--api-key → provider env var → prompt), opens SignupURL via openBrowser (skipped when a key is already in hand or non-TTY), then wires LiteLLM and syncs agents.
  • obol model setup <provider> --api-key <key> — the scriptable, no-browser equivalent (CI/automation).

Both call the same setupCloudProvider engine, so behavior can't drift.

The registry refactor (the DB smell)

Provider knowledge was smeared across five sites — adding one provider meant editing all of them: knownProviders, the setup dispatch switch, a default-model switch, detectCredentials, and buildModelEntries. This PR collapses them into one source of truth: a richer ProviderInfo row (id, EnvVar/AltEnvVars, Mode, BaseURL, Default, SignupURL, Free) + ProviderByID. Every layer — buy/setup dispatch, default-model selection, credential auto-detection, LiteLLM entry shaping, and model status labelling — reads the registry. Adding a provider is one row, no switch edits.

What's added

providerbase_urlenv var
venicehttps://api.venice.ai/api/v1VENICE_API_KEY
openrouterhttps://openrouter.ai/api/v1OPENROUTER_API_KEY
nvidiahttps://integrate.api.nvidia.com/v1NVIDIA_API_KEY
gmihttps://api.gmi-serving.com/v1GMI_API_KEY
novitahttps://api.novita.ai/openai/v1NOVITA_API_KEY
huggingfacehttps://router.huggingface.co/v1HF_TOKEN

All Mode=openai-compatible: entries are openai/<id> + explicit api_base + key from env, no wildcard. litellm-secrets is envFrom-mounted and record.go persists model_list verbatim, so new providers authenticate and survive obol stack up with zero changes to the secret/record layers.

Model resolution (no guessed ids)

Aggregator catalogs rotate, so hardcoding model ids would ship 404s. With --model omitted: the registry Default is used if set (openrouter/auto; native flagships for anthropic/openai), otherwise setup lists the live GET <base>/v1/models — a picker in a TTY, or a non-TTY error naming real ids. --free seeds OpenRouter's curated free snapshot (mapped from our hermes-agent free list), documented as rotating.

Tests

buildModelEntries openai-compatible shape + legacy fallback; ProviderByID; detectProvider labelling aggregators by api_base; registry invariants; the model setup and buy inference flag surfaces (incl. --free/--api-key) and the provider-vs-ollama dispatch gate. Existing anthropic/openai/ollama entry tests pass unchanged. Full go build + vet + test ./... green.

Follow-ups (not here)

  • OAuth PKCE for OpenRouter (hermes uses api-key today, so this matches; PKCE is a nice-to-have).
  • Cache /v1/models; live free-tier discovery (filter pricing==0) instead of a static snapshot.

🤖 Generated with Claude Code

…penRouter & co.
Collapses provider knowledge (previously smeared across knownProviders, the
setup dispatch switch, a default-model switch, detectCredentials, and
buildModelEntries) into a single source-of-truth registry: one ProviderInfo
row per provider carries id/env-var/Mode/BaseURL/Default/SignupURL/Free, and
every layer reads from it. Adding a provider is now one row, no switch edits.
New BYOK getting-started path (distinct from `obol buy inference`, which is
x402 crypto-paid sellers):
obol model setup venice --api-key $VENICE_API_KEY
obol model setup openrouter --api-key $OPENROUTER_API_KEY --free
- Mode=openai-compatible providers emit model_list entries openai/<id> +
explicit api_base + key from env var (no wildcard). litellm-secrets is
envFrom-mounted and record.go stores entries verbatim, so new providers
persist across stack up with zero changes to those layers.
- --model omitted → registry Default, else live GET <base>/v1/models
(TTY picker / non-TTY error naming real ids) so we never ship guessed,
rotating model ids as a hardcoded default.
- --free seeds OpenRouter's curated free-tier snapshot (mapped from
hermes-agent's free list).
- detectCredentials + detectProvider are now registry-driven (BYOK env keys
auto-detected; aggregator models labelled by api_base, not 'openai').
Providers seeded: venice, openrouter, nvidia, gmi, novita, huggingface
(plus existing anthropic/openai/ollama). All OpenAI-compatible, so they are
pure data — no new wiring.
…URL onboarding
Adds the requested getting-started verb: `obol buy inference venice`,
`obol buy inference openrouter --free`, etc. Dispatch keys on whether the
positional arg matches a registry provider id — provider → BYOK onboarding,
URL/none → the existing x402 crypto-paid seller flow (unchanged).
Onboarding mirrors hermes-agent's api-key UX: resolve the key (--api-key →
provider env var → prompt), open the provider's SignupURL in the browser
(reuses openBrowser; skipped when a key is already in hand or non-TTY), then
delegate to the shared setupCloudProvider engine (model resolution, --free
seeding, LiteLLM patch, agent sync). No wallet, no x402 for this path.
Also adds --api-key/--free to buy inference and corrects the CLAUDE.md
framing (buy inference is now BOTH the BYOK door and the x402 door).
@bussyjdbussyjd changed the title feat(model): BYOK provider registry — one-command setup for Venice, OpenRouter & co.feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registryJun 12, 2026
@OisinKyne

Copy link
Copy Markdown
Contributor

Brave to add this many providers in one go, you're telling me all 6 of them work? Have you tested them or are you hoping?

@OisinKyne

OisinKyne commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

How does obol buy inference <provider> differ from obol model setup provider? it feels like we're just configuring them, is there a way to actually pay for them from the agents balance?

Seems to me that maybe we mostly change obol model setup and save obol buy inference for if we're paying from the agent? or maybe we just loop our DIY inference as a service into an obol model setup custom or something and use obol buy differently?

I'm thinking if we do have obol buy inference ` it should probably be more of an 'add credit/top up' type of experience rather than an api key setup?

@OisinKyne

Copy link
Copy Markdown
Contributor

added to my pr based on above thoughts. happy to merge that in here, and both into main. #644

@OisinKyne
OisinKyne merged commit f3bc655 into mainJun 15, 2026
8 checks passed
@OisinKyne
OisinKyne deleted the feat/byok-provider-registry branch June 15, 2026 21:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry - #638

Merged
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry
Jun 15, 2026
Merged

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry#638
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Why

Give a new user the shortest path to a working agent: bring an OpenRouter or Venice key and go. The headline command is the requested verb —

obol buy inference venice
obol buy inference openrouter --free

— which opens the provider's API-key page in the browser (hermes-agent-style), takes the key, and wires the agent's LiteLLM gateway to it. obol buy inference <seller-url> (or no arg) is unchanged — still the x402 crypto-paid seller flow. Dispatch keys on whether the positional argument matches a known provider id, so the two doors coexist behind one verb.

Two front doors, one engine

  • obol buy inference <provider> — friendly onboarding. Resolves the key (--api-key → provider env var → prompt), opens SignupURL via openBrowser (skipped when a key is already in hand or non-TTY), then wires LiteLLM and syncs agents.
  • obol model setup <provider> --api-key <key> — the scriptable, no-browser equivalent (CI/automation).

Both call the same setupCloudProvider engine, so behavior can't drift.

The registry refactor (the DB smell)

Provider knowledge was smeared across five sites — adding one provider meant editing all of them: knownProviders, the setup dispatch switch, a default-model switch, detectCredentials, and buildModelEntries. This PR collapses them into one source of truth: a richer ProviderInfo row (id, EnvVar/AltEnvVars, Mode, BaseURL, Default, SignupURL, Free) + ProviderByID. Every layer — buy/setup dispatch, default-model selection, credential auto-detection, LiteLLM entry shaping, and model status labelling — reads the registry. Adding a provider is one row, no switch edits.

What's added

providerbase_urlenv var
venicehttps://api.venice.ai/api/v1VENICE_API_KEY
openrouterhttps://openrouter.ai/api/v1OPENROUTER_API_KEY
nvidiahttps://integrate.api.nvidia.com/v1NVIDIA_API_KEY
gmihttps://api.gmi-serving.com/v1GMI_API_KEY
novitahttps://api.novita.ai/openai/v1NOVITA_API_KEY
huggingfacehttps://router.huggingface.co/v1HF_TOKEN

All Mode=openai-compatible: entries are openai/<id> + explicit api_base + key from env, no wildcard. litellm-secrets is envFrom-mounted and record.go persists model_list verbatim, so new providers authenticate and survive obol stack up with zero changes to the secret/record layers.

Model resolution (no guessed ids)

Aggregator catalogs rotate, so hardcoding model ids would ship 404s. With --model omitted: the registry Default is used if set (openrouter/auto; native flagships for anthropic/openai), otherwise setup lists the live GET <base>/v1/models — a picker in a TTY, or a non-TTY error naming real ids. --free seeds OpenRouter's curated free snapshot (mapped from our hermes-agent free list), documented as rotating.

Tests

buildModelEntries openai-compatible shape + legacy fallback; ProviderByID; detectProvider labelling aggregators by api_base; registry invariants; the model setup and buy inference flag surfaces (incl. --free/--api-key) and the provider-vs-ollama dispatch gate. Existing anthropic/openai/ollama entry tests pass unchanged. Full go build + vet + test ./... green.

Follow-ups (not here)

  • OAuth PKCE for OpenRouter (hermes uses api-key today, so this matches; PKCE is a nice-to-have).
  • Cache /v1/models; live free-tier discovery (filter pricing==0) instead of a static snapshot.

🤖 Generated with Claude Code

…penRouter & co.
Collapses provider knowledge (previously smeared across knownProviders, the
setup dispatch switch, a default-model switch, detectCredentials, and
buildModelEntries) into a single source-of-truth registry: one ProviderInfo
row per provider carries id/env-var/Mode/BaseURL/Default/SignupURL/Free, and
every layer reads from it. Adding a provider is now one row, no switch edits.
New BYOK getting-started path (distinct from `obol buy inference`, which is
x402 crypto-paid sellers):
obol model setup venice --api-key $VENICE_API_KEY
obol model setup openrouter --api-key $OPENROUTER_API_KEY --free
- Mode=openai-compatible providers emit model_list entries openai/<id> +
explicit api_base + key from env var (no wildcard). litellm-secrets is
envFrom-mounted and record.go stores entries verbatim, so new providers
persist across stack up with zero changes to those layers.
- --model omitted → registry Default, else live GET <base>/v1/models
(TTY picker / non-TTY error naming real ids) so we never ship guessed,
rotating model ids as a hardcoded default.
- --free seeds OpenRouter's curated free-tier snapshot (mapped from
hermes-agent's free list).
- detectCredentials + detectProvider are now registry-driven (BYOK env keys
auto-detected; aggregator models labelled by api_base, not 'openai').
Providers seeded: venice, openrouter, nvidia, gmi, novita, huggingface
(plus existing anthropic/openai/ollama). All OpenAI-compatible, so they are
pure data — no new wiring.
…URL onboarding
Adds the requested getting-started verb: `obol buy inference venice`,
`obol buy inference openrouter --free`, etc. Dispatch keys on whether the
positional arg matches a registry provider id — provider → BYOK onboarding,
URL/none → the existing x402 crypto-paid seller flow (unchanged).
Onboarding mirrors hermes-agent's api-key UX: resolve the key (--api-key →
provider env var → prompt), open the provider's SignupURL in the browser
(reuses openBrowser; skipped when a key is already in hand or non-TTY), then
delegate to the shared setupCloudProvider engine (model resolution, --free
seeding, LiteLLM patch, agent sync). No wallet, no x402 for this path.
Also adds --api-key/--free to buy inference and corrects the CLAUDE.md
framing (buy inference is now BOTH the BYOK door and the x402 door).
@bussyjdbussyjd changed the title feat(model): BYOK provider registry — one-command setup for Venice, OpenRouter & co.feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registryJun 12, 2026
@OisinKyne

Copy link
Copy Markdown
Contributor

Brave to add this many providers in one go, you're telling me all 6 of them work? Have you tested them or are you hoping?

@OisinKyne

OisinKyne commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

How does obol buy inference <provider> differ from obol model setup provider? it feels like we're just configuring them, is there a way to actually pay for them from the agents balance?

Seems to me that maybe we mostly change obol model setup and save obol buy inference for if we're paying from the agent? or maybe we just loop our DIY inference as a service into an obol model setup custom or something and use obol buy differently?

I'm thinking if we do have obol buy inference ` it should probably be more of an 'add credit/top up' type of experience rather than an api key setup?

@OisinKyne

Copy link
Copy Markdown
Contributor

added to my pr based on above thoughts. happy to merge that in here, and both into main. #644

@OisinKyne
OisinKyne merged commit f3bc655 into mainJun 15, 2026
8 checks passed
@OisinKyne
OisinKyne deleted the feat/byok-provider-registry branch June 15, 2026 21:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry - #638

Merged
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry
Jun 15, 2026
Merged

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry#638
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Why

Give a new user the shortest path to a working agent: bring an OpenRouter or Venice key and go. The headline command is the requested verb —

obol buy inference venice
obol buy inference openrouter --free

— which opens the provider's API-key page in the browser (hermes-agent-style), takes the key, and wires the agent's LiteLLM gateway to it. obol buy inference <seller-url> (or no arg) is unchanged — still the x402 crypto-paid seller flow. Dispatch keys on whether the positional argument matches a known provider id, so the two doors coexist behind one verb.

Two front doors, one engine

  • obol buy inference <provider> — friendly onboarding. Resolves the key (--api-key → provider env var → prompt), opens SignupURL via openBrowser (skipped when a key is already in hand or non-TTY), then wires LiteLLM and syncs agents.
  • obol model setup <provider> --api-key <key> — the scriptable, no-browser equivalent (CI/automation).

Both call the same setupCloudProvider engine, so behavior can't drift.

The registry refactor (the DB smell)

Provider knowledge was smeared across five sites — adding one provider meant editing all of them: knownProviders, the setup dispatch switch, a default-model switch, detectCredentials, and buildModelEntries. This PR collapses them into one source of truth: a richer ProviderInfo row (id, EnvVar/AltEnvVars, Mode, BaseURL, Default, SignupURL, Free) + ProviderByID. Every layer — buy/setup dispatch, default-model selection, credential auto-detection, LiteLLM entry shaping, and model status labelling — reads the registry. Adding a provider is one row, no switch edits.

What's added

providerbase_urlenv var
venicehttps://api.venice.ai/api/v1VENICE_API_KEY
openrouterhttps://openrouter.ai/api/v1OPENROUTER_API_KEY
nvidiahttps://integrate.api.nvidia.com/v1NVIDIA_API_KEY
gmihttps://api.gmi-serving.com/v1GMI_API_KEY
novitahttps://api.novita.ai/openai/v1NOVITA_API_KEY
huggingfacehttps://router.huggingface.co/v1HF_TOKEN

All Mode=openai-compatible: entries are openai/<id> + explicit api_base + key from env, no wildcard. litellm-secrets is envFrom-mounted and record.go persists model_list verbatim, so new providers authenticate and survive obol stack up with zero changes to the secret/record layers.

Model resolution (no guessed ids)

Aggregator catalogs rotate, so hardcoding model ids would ship 404s. With --model omitted: the registry Default is used if set (openrouter/auto; native flagships for anthropic/openai), otherwise setup lists the live GET <base>/v1/models — a picker in a TTY, or a non-TTY error naming real ids. --free seeds OpenRouter's curated free snapshot (mapped from our hermes-agent free list), documented as rotating.

Tests

buildModelEntries openai-compatible shape + legacy fallback; ProviderByID; detectProvider labelling aggregators by api_base; registry invariants; the model setup and buy inference flag surfaces (incl. --free/--api-key) and the provider-vs-ollama dispatch gate. Existing anthropic/openai/ollama entry tests pass unchanged. Full go build + vet + test ./... green.

Follow-ups (not here)

  • OAuth PKCE for OpenRouter (hermes uses api-key today, so this matches; PKCE is a nice-to-have).
  • Cache /v1/models; live free-tier discovery (filter pricing==0) instead of a static snapshot.

🤖 Generated with Claude Code

…penRouter & co.
Collapses provider knowledge (previously smeared across knownProviders, the
setup dispatch switch, a default-model switch, detectCredentials, and
buildModelEntries) into a single source-of-truth registry: one ProviderInfo
row per provider carries id/env-var/Mode/BaseURL/Default/SignupURL/Free, and
every layer reads from it. Adding a provider is now one row, no switch edits.
New BYOK getting-started path (distinct from `obol buy inference`, which is
x402 crypto-paid sellers):
obol model setup venice --api-key $VENICE_API_KEY
obol model setup openrouter --api-key $OPENROUTER_API_KEY --free
- Mode=openai-compatible providers emit model_list entries openai/<id> +
explicit api_base + key from env var (no wildcard). litellm-secrets is
envFrom-mounted and record.go stores entries verbatim, so new providers
persist across stack up with zero changes to those layers.
- --model omitted → registry Default, else live GET <base>/v1/models
(TTY picker / non-TTY error naming real ids) so we never ship guessed,
rotating model ids as a hardcoded default.
- --free seeds OpenRouter's curated free-tier snapshot (mapped from
hermes-agent's free list).
- detectCredentials + detectProvider are now registry-driven (BYOK env keys
auto-detected; aggregator models labelled by api_base, not 'openai').
Providers seeded: venice, openrouter, nvidia, gmi, novita, huggingface
(plus existing anthropic/openai/ollama). All OpenAI-compatible, so they are
pure data — no new wiring.
…URL onboarding
Adds the requested getting-started verb: `obol buy inference venice`,
`obol buy inference openrouter --free`, etc. Dispatch keys on whether the
positional arg matches a registry provider id — provider → BYOK onboarding,
URL/none → the existing x402 crypto-paid seller flow (unchanged).
Onboarding mirrors hermes-agent's api-key UX: resolve the key (--api-key →
provider env var → prompt), open the provider's SignupURL in the browser
(reuses openBrowser; skipped when a key is already in hand or non-TTY), then
delegate to the shared setupCloudProvider engine (model resolution, --free
seeding, LiteLLM patch, agent sync). No wallet, no x402 for this path.
Also adds --api-key/--free to buy inference and corrects the CLAUDE.md
framing (buy inference is now BOTH the BYOK door and the x402 door).
@bussyjdbussyjd changed the title feat(model): BYOK provider registry — one-command setup for Venice, OpenRouter & co.feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registryJun 12, 2026
@OisinKyne

Copy link
Copy Markdown
Contributor

Brave to add this many providers in one go, you're telling me all 6 of them work? Have you tested them or are you hoping?

@OisinKyne

OisinKyne commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

How does obol buy inference <provider> differ from obol model setup provider? it feels like we're just configuring them, is there a way to actually pay for them from the agents balance?

Seems to me that maybe we mostly change obol model setup and save obol buy inference for if we're paying from the agent? or maybe we just loop our DIY inference as a service into an obol model setup custom or something and use obol buy differently?

I'm thinking if we do have obol buy inference ` it should probably be more of an 'add credit/top up' type of experience rather than an api key setup?

@OisinKyne

Copy link
Copy Markdown
Contributor

added to my pr based on above thoughts. happy to merge that in here, and both into main. #644

@OisinKyne
OisinKyne merged commit f3bc655 into mainJun 15, 2026
8 checks passed
@OisinKyne
OisinKyne deleted the feat/byok-provider-registry branch June 15, 2026 21:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry - #638

Merged
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry
Jun 15, 2026
Merged

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry#638
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Why

Give a new user the shortest path to a working agent: bring an OpenRouter or Venice key and go. The headline command is the requested verb —

obol buy inference venice
obol buy inference openrouter --free

— which opens the provider's API-key page in the browser (hermes-agent-style), takes the key, and wires the agent's LiteLLM gateway to it. obol buy inference <seller-url> (or no arg) is unchanged — still the x402 crypto-paid seller flow. Dispatch keys on whether the positional argument matches a known provider id, so the two doors coexist behind one verb.

Two front doors, one engine

  • obol buy inference <provider> — friendly onboarding. Resolves the key (--api-key → provider env var → prompt), opens SignupURL via openBrowser (skipped when a key is already in hand or non-TTY), then wires LiteLLM and syncs agents.
  • obol model setup <provider> --api-key <key> — the scriptable, no-browser equivalent (CI/automation).

Both call the same setupCloudProvider engine, so behavior can't drift.

The registry refactor (the DB smell)

Provider knowledge was smeared across five sites — adding one provider meant editing all of them: knownProviders, the setup dispatch switch, a default-model switch, detectCredentials, and buildModelEntries. This PR collapses them into one source of truth: a richer ProviderInfo row (id, EnvVar/AltEnvVars, Mode, BaseURL, Default, SignupURL, Free) + ProviderByID. Every layer — buy/setup dispatch, default-model selection, credential auto-detection, LiteLLM entry shaping, and model status labelling — reads the registry. Adding a provider is one row, no switch edits.

What's added

providerbase_urlenv var
venicehttps://api.venice.ai/api/v1VENICE_API_KEY
openrouterhttps://openrouter.ai/api/v1OPENROUTER_API_KEY
nvidiahttps://integrate.api.nvidia.com/v1NVIDIA_API_KEY
gmihttps://api.gmi-serving.com/v1GMI_API_KEY
novitahttps://api.novita.ai/openai/v1NOVITA_API_KEY
huggingfacehttps://router.huggingface.co/v1HF_TOKEN

All Mode=openai-compatible: entries are openai/<id> + explicit api_base + key from env, no wildcard. litellm-secrets is envFrom-mounted and record.go persists model_list verbatim, so new providers authenticate and survive obol stack up with zero changes to the secret/record layers.

Model resolution (no guessed ids)

Aggregator catalogs rotate, so hardcoding model ids would ship 404s. With --model omitted: the registry Default is used if set (openrouter/auto; native flagships for anthropic/openai), otherwise setup lists the live GET <base>/v1/models — a picker in a TTY, or a non-TTY error naming real ids. --free seeds OpenRouter's curated free snapshot (mapped from our hermes-agent free list), documented as rotating.

Tests

buildModelEntries openai-compatible shape + legacy fallback; ProviderByID; detectProvider labelling aggregators by api_base; registry invariants; the model setup and buy inference flag surfaces (incl. --free/--api-key) and the provider-vs-ollama dispatch gate. Existing anthropic/openai/ollama entry tests pass unchanged. Full go build + vet + test ./... green.

Follow-ups (not here)

  • OAuth PKCE for OpenRouter (hermes uses api-key today, so this matches; PKCE is a nice-to-have).
  • Cache /v1/models; live free-tier discovery (filter pricing==0) instead of a static snapshot.

🤖 Generated with Claude Code

…penRouter & co.
Collapses provider knowledge (previously smeared across knownProviders, the
setup dispatch switch, a default-model switch, detectCredentials, and
buildModelEntries) into a single source-of-truth registry: one ProviderInfo
row per provider carries id/env-var/Mode/BaseURL/Default/SignupURL/Free, and
every layer reads from it. Adding a provider is now one row, no switch edits.
New BYOK getting-started path (distinct from `obol buy inference`, which is
x402 crypto-paid sellers):
obol model setup venice --api-key $VENICE_API_KEY
obol model setup openrouter --api-key $OPENROUTER_API_KEY --free
- Mode=openai-compatible providers emit model_list entries openai/<id> +
explicit api_base + key from env var (no wildcard). litellm-secrets is
envFrom-mounted and record.go stores entries verbatim, so new providers
persist across stack up with zero changes to those layers.
- --model omitted → registry Default, else live GET <base>/v1/models
(TTY picker / non-TTY error naming real ids) so we never ship guessed,
rotating model ids as a hardcoded default.
- --free seeds OpenRouter's curated free-tier snapshot (mapped from
hermes-agent's free list).
- detectCredentials + detectProvider are now registry-driven (BYOK env keys
auto-detected; aggregator models labelled by api_base, not 'openai').
Providers seeded: venice, openrouter, nvidia, gmi, novita, huggingface
(plus existing anthropic/openai/ollama). All OpenAI-compatible, so they are
pure data — no new wiring.
…URL onboarding
Adds the requested getting-started verb: `obol buy inference venice`,
`obol buy inference openrouter --free`, etc. Dispatch keys on whether the
positional arg matches a registry provider id — provider → BYOK onboarding,
URL/none → the existing x402 crypto-paid seller flow (unchanged).
Onboarding mirrors hermes-agent's api-key UX: resolve the key (--api-key →
provider env var → prompt), open the provider's SignupURL in the browser
(reuses openBrowser; skipped when a key is already in hand or non-TTY), then
delegate to the shared setupCloudProvider engine (model resolution, --free
seeding, LiteLLM patch, agent sync). No wallet, no x402 for this path.
Also adds --api-key/--free to buy inference and corrects the CLAUDE.md
framing (buy inference is now BOTH the BYOK door and the x402 door).
@bussyjdbussyjd changed the title feat(model): BYOK provider registry — one-command setup for Venice, OpenRouter & co.feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registryJun 12, 2026
@OisinKyne

Copy link
Copy Markdown
Contributor

Brave to add this many providers in one go, you're telling me all 6 of them work? Have you tested them or are you hoping?

@OisinKyne

OisinKyne commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

How does obol buy inference <provider> differ from obol model setup provider? it feels like we're just configuring them, is there a way to actually pay for them from the agents balance?

Seems to me that maybe we mostly change obol model setup and save obol buy inference for if we're paying from the agent? or maybe we just loop our DIY inference as a service into an obol model setup custom or something and use obol buy differently?

I'm thinking if we do have obol buy inference ` it should probably be more of an 'add credit/top up' type of experience rather than an api key setup?

@OisinKyne

Copy link
Copy Markdown
Contributor

added to my pr based on above thoughts. happy to merge that in here, and both into main. #644

@OisinKyne
OisinKyne merged commit f3bc655 into mainJun 15, 2026
8 checks passed
@OisinKyne
OisinKyne deleted the feat/byok-provider-registry branch June 15, 2026 21:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry - #638

Merged
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry
Jun 15, 2026
Merged

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry#638
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Why

Give a new user the shortest path to a working agent: bring an OpenRouter or Venice key and go. The headline command is the requested verb —

obol buy inference venice
obol buy inference openrouter --free

— which opens the provider's API-key page in the browser (hermes-agent-style), takes the key, and wires the agent's LiteLLM gateway to it. obol buy inference <seller-url> (or no arg) is unchanged — still the x402 crypto-paid seller flow. Dispatch keys on whether the positional argument matches a known provider id, so the two doors coexist behind one verb.

Two front doors, one engine

  • obol buy inference <provider> — friendly onboarding. Resolves the key (--api-key → provider env var → prompt), opens SignupURL via openBrowser (skipped when a key is already in hand or non-TTY), then wires LiteLLM and syncs agents.
  • obol model setup <provider> --api-key <key> — the scriptable, no-browser equivalent (CI/automation).

Both call the same setupCloudProvider engine, so behavior can't drift.

The registry refactor (the DB smell)

Provider knowledge was smeared across five sites — adding one provider meant editing all of them: knownProviders, the setup dispatch switch, a default-model switch, detectCredentials, and buildModelEntries. This PR collapses them into one source of truth: a richer ProviderInfo row (id, EnvVar/AltEnvVars, Mode, BaseURL, Default, SignupURL, Free) + ProviderByID. Every layer — buy/setup dispatch, default-model selection, credential auto-detection, LiteLLM entry shaping, and model status labelling — reads the registry. Adding a provider is one row, no switch edits.

What's added

providerbase_urlenv var
venicehttps://api.venice.ai/api/v1VENICE_API_KEY
openrouterhttps://openrouter.ai/api/v1OPENROUTER_API_KEY
nvidiahttps://integrate.api.nvidia.com/v1NVIDIA_API_KEY
gmihttps://api.gmi-serving.com/v1GMI_API_KEY
novitahttps://api.novita.ai/openai/v1NOVITA_API_KEY
huggingfacehttps://router.huggingface.co/v1HF_TOKEN

All Mode=openai-compatible: entries are openai/<id> + explicit api_base + key from env, no wildcard. litellm-secrets is envFrom-mounted and record.go persists model_list verbatim, so new providers authenticate and survive obol stack up with zero changes to the secret/record layers.

Model resolution (no guessed ids)

Aggregator catalogs rotate, so hardcoding model ids would ship 404s. With --model omitted: the registry Default is used if set (openrouter/auto; native flagships for anthropic/openai), otherwise setup lists the live GET <base>/v1/models — a picker in a TTY, or a non-TTY error naming real ids. --free seeds OpenRouter's curated free snapshot (mapped from our hermes-agent free list), documented as rotating.

Tests

buildModelEntries openai-compatible shape + legacy fallback; ProviderByID; detectProvider labelling aggregators by api_base; registry invariants; the model setup and buy inference flag surfaces (incl. --free/--api-key) and the provider-vs-ollama dispatch gate. Existing anthropic/openai/ollama entry tests pass unchanged. Full go build + vet + test ./... green.

Follow-ups (not here)

  • OAuth PKCE for OpenRouter (hermes uses api-key today, so this matches; PKCE is a nice-to-have).
  • Cache /v1/models; live free-tier discovery (filter pricing==0) instead of a static snapshot.

🤖 Generated with Claude Code

…penRouter & co.
Collapses provider knowledge (previously smeared across knownProviders, the
setup dispatch switch, a default-model switch, detectCredentials, and
buildModelEntries) into a single source-of-truth registry: one ProviderInfo
row per provider carries id/env-var/Mode/BaseURL/Default/SignupURL/Free, and
every layer reads from it. Adding a provider is now one row, no switch edits.
New BYOK getting-started path (distinct from `obol buy inference`, which is
x402 crypto-paid sellers):
obol model setup venice --api-key $VENICE_API_KEY
obol model setup openrouter --api-key $OPENROUTER_API_KEY --free
- Mode=openai-compatible providers emit model_list entries openai/<id> +
explicit api_base + key from env var (no wildcard). litellm-secrets is
envFrom-mounted and record.go stores entries verbatim, so new providers
persist across stack up with zero changes to those layers.
- --model omitted → registry Default, else live GET <base>/v1/models
(TTY picker / non-TTY error naming real ids) so we never ship guessed,
rotating model ids as a hardcoded default.
- --free seeds OpenRouter's curated free-tier snapshot (mapped from
hermes-agent's free list).
- detectCredentials + detectProvider are now registry-driven (BYOK env keys
auto-detected; aggregator models labelled by api_base, not 'openai').
Providers seeded: venice, openrouter, nvidia, gmi, novita, huggingface
(plus existing anthropic/openai/ollama). All OpenAI-compatible, so they are
pure data — no new wiring.
…URL onboarding
Adds the requested getting-started verb: `obol buy inference venice`,
`obol buy inference openrouter --free`, etc. Dispatch keys on whether the
positional arg matches a registry provider id — provider → BYOK onboarding,
URL/none → the existing x402 crypto-paid seller flow (unchanged).
Onboarding mirrors hermes-agent's api-key UX: resolve the key (--api-key →
provider env var → prompt), open the provider's SignupURL in the browser
(reuses openBrowser; skipped when a key is already in hand or non-TTY), then
delegate to the shared setupCloudProvider engine (model resolution, --free
seeding, LiteLLM patch, agent sync). No wallet, no x402 for this path.
Also adds --api-key/--free to buy inference and corrects the CLAUDE.md
framing (buy inference is now BOTH the BYOK door and the x402 door).
@bussyjdbussyjd changed the title feat(model): BYOK provider registry — one-command setup for Venice, OpenRouter & co.feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registryJun 12, 2026
@OisinKyne

Copy link
Copy Markdown
Contributor

Brave to add this many providers in one go, you're telling me all 6 of them work? Have you tested them or are you hoping?

@OisinKyne

OisinKyne commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

How does obol buy inference <provider> differ from obol model setup provider? it feels like we're just configuring them, is there a way to actually pay for them from the agents balance?

Seems to me that maybe we mostly change obol model setup and save obol buy inference for if we're paying from the agent? or maybe we just loop our DIY inference as a service into an obol model setup custom or something and use obol buy differently?

I'm thinking if we do have obol buy inference ` it should probably be more of an 'add credit/top up' type of experience rather than an api key setup?

@OisinKyne

Copy link
Copy Markdown
Contributor

added to my pr based on above thoughts. happy to merge that in here, and both into main. #644

@OisinKyne
OisinKyne merged commit f3bc655 into mainJun 15, 2026
8 checks passed
@OisinKyne
OisinKyne deleted the feat/byok-provider-registry branch June 15, 2026 21:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry - #638

Merged
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry
Jun 15, 2026
Merged

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry#638
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Why

Give a new user the shortest path to a working agent: bring an OpenRouter or Venice key and go. The headline command is the requested verb —

obol buy inference venice
obol buy inference openrouter --free

— which opens the provider's API-key page in the browser (hermes-agent-style), takes the key, and wires the agent's LiteLLM gateway to it. obol buy inference <seller-url> (or no arg) is unchanged — still the x402 crypto-paid seller flow. Dispatch keys on whether the positional argument matches a known provider id, so the two doors coexist behind one verb.

Two front doors, one engine

  • obol buy inference <provider> — friendly onboarding. Resolves the key (--api-key → provider env var → prompt), opens SignupURL via openBrowser (skipped when a key is already in hand or non-TTY), then wires LiteLLM and syncs agents.
  • obol model setup <provider> --api-key <key> — the scriptable, no-browser equivalent (CI/automation).

Both call the same setupCloudProvider engine, so behavior can't drift.

The registry refactor (the DB smell)

Provider knowledge was smeared across five sites — adding one provider meant editing all of them: knownProviders, the setup dispatch switch, a default-model switch, detectCredentials, and buildModelEntries. This PR collapses them into one source of truth: a richer ProviderInfo row (id, EnvVar/AltEnvVars, Mode, BaseURL, Default, SignupURL, Free) + ProviderByID. Every layer — buy/setup dispatch, default-model selection, credential auto-detection, LiteLLM entry shaping, and model status labelling — reads the registry. Adding a provider is one row, no switch edits.

What's added

providerbase_urlenv var
venicehttps://api.venice.ai/api/v1VENICE_API_KEY
openrouterhttps://openrouter.ai/api/v1OPENROUTER_API_KEY
nvidiahttps://integrate.api.nvidia.com/v1NVIDIA_API_KEY
gmihttps://api.gmi-serving.com/v1GMI_API_KEY
novitahttps://api.novita.ai/openai/v1NOVITA_API_KEY
huggingfacehttps://router.huggingface.co/v1HF_TOKEN

All Mode=openai-compatible: entries are openai/<id> + explicit api_base + key from env, no wildcard. litellm-secrets is envFrom-mounted and record.go persists model_list verbatim, so new providers authenticate and survive obol stack up with zero changes to the secret/record layers.

Model resolution (no guessed ids)

Aggregator catalogs rotate, so hardcoding model ids would ship 404s. With --model omitted: the registry Default is used if set (openrouter/auto; native flagships for anthropic/openai), otherwise setup lists the live GET <base>/v1/models — a picker in a TTY, or a non-TTY error naming real ids. --free seeds OpenRouter's curated free snapshot (mapped from our hermes-agent free list), documented as rotating.

Tests

buildModelEntries openai-compatible shape + legacy fallback; ProviderByID; detectProvider labelling aggregators by api_base; registry invariants; the model setup and buy inference flag surfaces (incl. --free/--api-key) and the provider-vs-ollama dispatch gate. Existing anthropic/openai/ollama entry tests pass unchanged. Full go build + vet + test ./... green.

Follow-ups (not here)

  • OAuth PKCE for OpenRouter (hermes uses api-key today, so this matches; PKCE is a nice-to-have).
  • Cache /v1/models; live free-tier discovery (filter pricing==0) instead of a static snapshot.

🤖 Generated with Claude Code

…penRouter & co.
Collapses provider knowledge (previously smeared across knownProviders, the
setup dispatch switch, a default-model switch, detectCredentials, and
buildModelEntries) into a single source-of-truth registry: one ProviderInfo
row per provider carries id/env-var/Mode/BaseURL/Default/SignupURL/Free, and
every layer reads from it. Adding a provider is now one row, no switch edits.
New BYOK getting-started path (distinct from `obol buy inference`, which is
x402 crypto-paid sellers):
obol model setup venice --api-key $VENICE_API_KEY
obol model setup openrouter --api-key $OPENROUTER_API_KEY --free
- Mode=openai-compatible providers emit model_list entries openai/<id> +
explicit api_base + key from env var (no wildcard). litellm-secrets is
envFrom-mounted and record.go stores entries verbatim, so new providers
persist across stack up with zero changes to those layers.
- --model omitted → registry Default, else live GET <base>/v1/models
(TTY picker / non-TTY error naming real ids) so we never ship guessed,
rotating model ids as a hardcoded default.
- --free seeds OpenRouter's curated free-tier snapshot (mapped from
hermes-agent's free list).
- detectCredentials + detectProvider are now registry-driven (BYOK env keys
auto-detected; aggregator models labelled by api_base, not 'openai').
Providers seeded: venice, openrouter, nvidia, gmi, novita, huggingface
(plus existing anthropic/openai/ollama). All OpenAI-compatible, so they are
pure data — no new wiring.
…URL onboarding
Adds the requested getting-started verb: `obol buy inference venice`,
`obol buy inference openrouter --free`, etc. Dispatch keys on whether the
positional arg matches a registry provider id — provider → BYOK onboarding,
URL/none → the existing x402 crypto-paid seller flow (unchanged).
Onboarding mirrors hermes-agent's api-key UX: resolve the key (--api-key →
provider env var → prompt), open the provider's SignupURL in the browser
(reuses openBrowser; skipped when a key is already in hand or non-TTY), then
delegate to the shared setupCloudProvider engine (model resolution, --free
seeding, LiteLLM patch, agent sync). No wallet, no x402 for this path.
Also adds --api-key/--free to buy inference and corrects the CLAUDE.md
framing (buy inference is now BOTH the BYOK door and the x402 door).
@bussyjdbussyjd changed the title feat(model): BYOK provider registry — one-command setup for Venice, OpenRouter & co.feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registryJun 12, 2026
@OisinKyne

Copy link
Copy Markdown
Contributor

Brave to add this many providers in one go, you're telling me all 6 of them work? Have you tested them or are you hoping?

@OisinKyne

OisinKyne commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

How does obol buy inference <provider> differ from obol model setup provider? it feels like we're just configuring them, is there a way to actually pay for them from the agents balance?

Seems to me that maybe we mostly change obol model setup and save obol buy inference for if we're paying from the agent? or maybe we just loop our DIY inference as a service into an obol model setup custom or something and use obol buy differently?

I'm thinking if we do have obol buy inference ` it should probably be more of an 'add credit/top up' type of experience rather than an api key setup?

@OisinKyne

Copy link
Copy Markdown
Contributor

added to my pr based on above thoughts. happy to merge that in here, and both into main. #644

@OisinKyne
OisinKyne merged commit f3bc655 into mainJun 15, 2026
8 checks passed
@OisinKyne
OisinKyne deleted the feat/byok-provider-registry branch June 15, 2026 21:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry - #638

Merged
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry
Jun 15, 2026
Merged

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry#638
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Why

Give a new user the shortest path to a working agent: bring an OpenRouter or Venice key and go. The headline command is the requested verb —

obol buy inference venice
obol buy inference openrouter --free

— which opens the provider's API-key page in the browser (hermes-agent-style), takes the key, and wires the agent's LiteLLM gateway to it. obol buy inference <seller-url> (or no arg) is unchanged — still the x402 crypto-paid seller flow. Dispatch keys on whether the positional argument matches a known provider id, so the two doors coexist behind one verb.

Two front doors, one engine

  • obol buy inference <provider> — friendly onboarding. Resolves the key (--api-key → provider env var → prompt), opens SignupURL via openBrowser (skipped when a key is already in hand or non-TTY), then wires LiteLLM and syncs agents.
  • obol model setup <provider> --api-key <key> — the scriptable, no-browser equivalent (CI/automation).

Both call the same setupCloudProvider engine, so behavior can't drift.

The registry refactor (the DB smell)

Provider knowledge was smeared across five sites — adding one provider meant editing all of them: knownProviders, the setup dispatch switch, a default-model switch, detectCredentials, and buildModelEntries. This PR collapses them into one source of truth: a richer ProviderInfo row (id, EnvVar/AltEnvVars, Mode, BaseURL, Default, SignupURL, Free) + ProviderByID. Every layer — buy/setup dispatch, default-model selection, credential auto-detection, LiteLLM entry shaping, and model status labelling — reads the registry. Adding a provider is one row, no switch edits.

What's added

providerbase_urlenv var
venicehttps://api.venice.ai/api/v1VENICE_API_KEY
openrouterhttps://openrouter.ai/api/v1OPENROUTER_API_KEY
nvidiahttps://integrate.api.nvidia.com/v1NVIDIA_API_KEY
gmihttps://api.gmi-serving.com/v1GMI_API_KEY
novitahttps://api.novita.ai/openai/v1NOVITA_API_KEY
huggingfacehttps://router.huggingface.co/v1HF_TOKEN

All Mode=openai-compatible: entries are openai/<id> + explicit api_base + key from env, no wildcard. litellm-secrets is envFrom-mounted and record.go persists model_list verbatim, so new providers authenticate and survive obol stack up with zero changes to the secret/record layers.

Model resolution (no guessed ids)

Aggregator catalogs rotate, so hardcoding model ids would ship 404s. With --model omitted: the registry Default is used if set (openrouter/auto; native flagships for anthropic/openai), otherwise setup lists the live GET <base>/v1/models — a picker in a TTY, or a non-TTY error naming real ids. --free seeds OpenRouter's curated free snapshot (mapped from our hermes-agent free list), documented as rotating.

Tests

buildModelEntries openai-compatible shape + legacy fallback; ProviderByID; detectProvider labelling aggregators by api_base; registry invariants; the model setup and buy inference flag surfaces (incl. --free/--api-key) and the provider-vs-ollama dispatch gate. Existing anthropic/openai/ollama entry tests pass unchanged. Full go build + vet + test ./... green.

Follow-ups (not here)

  • OAuth PKCE for OpenRouter (hermes uses api-key today, so this matches; PKCE is a nice-to-have).
  • Cache /v1/models; live free-tier discovery (filter pricing==0) instead of a static snapshot.

🤖 Generated with Claude Code

…penRouter & co.
Collapses provider knowledge (previously smeared across knownProviders, the
setup dispatch switch, a default-model switch, detectCredentials, and
buildModelEntries) into a single source-of-truth registry: one ProviderInfo
row per provider carries id/env-var/Mode/BaseURL/Default/SignupURL/Free, and
every layer reads from it. Adding a provider is now one row, no switch edits.
New BYOK getting-started path (distinct from `obol buy inference`, which is
x402 crypto-paid sellers):
obol model setup venice --api-key $VENICE_API_KEY
obol model setup openrouter --api-key $OPENROUTER_API_KEY --free
- Mode=openai-compatible providers emit model_list entries openai/<id> +
explicit api_base + key from env var (no wildcard). litellm-secrets is
envFrom-mounted and record.go stores entries verbatim, so new providers
persist across stack up with zero changes to those layers.
- --model omitted → registry Default, else live GET <base>/v1/models
(TTY picker / non-TTY error naming real ids) so we never ship guessed,
rotating model ids as a hardcoded default.
- --free seeds OpenRouter's curated free-tier snapshot (mapped from
hermes-agent's free list).
- detectCredentials + detectProvider are now registry-driven (BYOK env keys
auto-detected; aggregator models labelled by api_base, not 'openai').
Providers seeded: venice, openrouter, nvidia, gmi, novita, huggingface
(plus existing anthropic/openai/ollama). All OpenAI-compatible, so they are
pure data — no new wiring.
…URL onboarding
Adds the requested getting-started verb: `obol buy inference venice`,
`obol buy inference openrouter --free`, etc. Dispatch keys on whether the
positional arg matches a registry provider id — provider → BYOK onboarding,
URL/none → the existing x402 crypto-paid seller flow (unchanged).
Onboarding mirrors hermes-agent's api-key UX: resolve the key (--api-key →
provider env var → prompt), open the provider's SignupURL in the browser
(reuses openBrowser; skipped when a key is already in hand or non-TTY), then
delegate to the shared setupCloudProvider engine (model resolution, --free
seeding, LiteLLM patch, agent sync). No wallet, no x402 for this path.
Also adds --api-key/--free to buy inference and corrects the CLAUDE.md
framing (buy inference is now BOTH the BYOK door and the x402 door).
@bussyjdbussyjd changed the title feat(model): BYOK provider registry — one-command setup for Venice, OpenRouter & co.feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registryJun 12, 2026
@OisinKyne

Copy link
Copy Markdown
Contributor

Brave to add this many providers in one go, you're telling me all 6 of them work? Have you tested them or are you hoping?

@OisinKyne

OisinKyne commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

How does obol buy inference <provider> differ from obol model setup provider? it feels like we're just configuring them, is there a way to actually pay for them from the agents balance?

Seems to me that maybe we mostly change obol model setup and save obol buy inference for if we're paying from the agent? or maybe we just loop our DIY inference as a service into an obol model setup custom or something and use obol buy differently?

I'm thinking if we do have obol buy inference ` it should probably be more of an 'add credit/top up' type of experience rather than an api key setup?

@OisinKyne

Copy link
Copy Markdown
Contributor

added to my pr based on above thoughts. happy to merge that in here, and both into main. #644

@OisinKyne
OisinKyne merged commit f3bc655 into mainJun 15, 2026
8 checks passed
@OisinKyne
OisinKyne deleted the feat/byok-provider-registry branch June 15, 2026 21:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry - #638

Merged
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry
Jun 15, 2026
Merged

feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registry#638
OisinKyne merged 4 commits into
mainfrom
feat/byok-provider-registry

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Why

Give a new user the shortest path to a working agent: bring an OpenRouter or Venice key and go. The headline command is the requested verb —

obol buy inference venice
obol buy inference openrouter --free

— which opens the provider's API-key page in the browser (hermes-agent-style), takes the key, and wires the agent's LiteLLM gateway to it. obol buy inference <seller-url> (or no arg) is unchanged — still the x402 crypto-paid seller flow. Dispatch keys on whether the positional argument matches a known provider id, so the two doors coexist behind one verb.

Two front doors, one engine

  • obol buy inference <provider> — friendly onboarding. Resolves the key (--api-key → provider env var → prompt), opens SignupURL via openBrowser (skipped when a key is already in hand or non-TTY), then wires LiteLLM and syncs agents.
  • obol model setup <provider> --api-key <key> — the scriptable, no-browser equivalent (CI/automation).

Both call the same setupCloudProvider engine, so behavior can't drift.

The registry refactor (the DB smell)

Provider knowledge was smeared across five sites — adding one provider meant editing all of them: knownProviders, the setup dispatch switch, a default-model switch, detectCredentials, and buildModelEntries. This PR collapses them into one source of truth: a richer ProviderInfo row (id, EnvVar/AltEnvVars, Mode, BaseURL, Default, SignupURL, Free) + ProviderByID. Every layer — buy/setup dispatch, default-model selection, credential auto-detection, LiteLLM entry shaping, and model status labelling — reads the registry. Adding a provider is one row, no switch edits.

What's added

providerbase_urlenv var
venicehttps://api.venice.ai/api/v1VENICE_API_KEY
openrouterhttps://openrouter.ai/api/v1OPENROUTER_API_KEY
nvidiahttps://integrate.api.nvidia.com/v1NVIDIA_API_KEY
gmihttps://api.gmi-serving.com/v1GMI_API_KEY
novitahttps://api.novita.ai/openai/v1NOVITA_API_KEY
huggingfacehttps://router.huggingface.co/v1HF_TOKEN

All Mode=openai-compatible: entries are openai/<id> + explicit api_base + key from env, no wildcard. litellm-secrets is envFrom-mounted and record.go persists model_list verbatim, so new providers authenticate and survive obol stack up with zero changes to the secret/record layers.

Model resolution (no guessed ids)

Aggregator catalogs rotate, so hardcoding model ids would ship 404s. With --model omitted: the registry Default is used if set (openrouter/auto; native flagships for anthropic/openai), otherwise setup lists the live GET <base>/v1/models — a picker in a TTY, or a non-TTY error naming real ids. --free seeds OpenRouter's curated free snapshot (mapped from our hermes-agent free list), documented as rotating.

Tests

buildModelEntries openai-compatible shape + legacy fallback; ProviderByID; detectProvider labelling aggregators by api_base; registry invariants; the model setup and buy inference flag surfaces (incl. --free/--api-key) and the provider-vs-ollama dispatch gate. Existing anthropic/openai/ollama entry tests pass unchanged. Full go build + vet + test ./... green.

Follow-ups (not here)

  • OAuth PKCE for OpenRouter (hermes uses api-key today, so this matches; PKCE is a nice-to-have).
  • Cache /v1/models; live free-tier discovery (filter pricing==0) instead of a static snapshot.

🤖 Generated with Claude Code

…penRouter & co.
Collapses provider knowledge (previously smeared across knownProviders, the
setup dispatch switch, a default-model switch, detectCredentials, and
buildModelEntries) into a single source-of-truth registry: one ProviderInfo
row per provider carries id/env-var/Mode/BaseURL/Default/SignupURL/Free, and
every layer reads from it. Adding a provider is now one row, no switch edits.
New BYOK getting-started path (distinct from `obol buy inference`, which is
x402 crypto-paid sellers):
obol model setup venice --api-key $VENICE_API_KEY
obol model setup openrouter --api-key $OPENROUTER_API_KEY --free
- Mode=openai-compatible providers emit model_list entries openai/<id> +
explicit api_base + key from env var (no wildcard). litellm-secrets is
envFrom-mounted and record.go stores entries verbatim, so new providers
persist across stack up with zero changes to those layers.
- --model omitted → registry Default, else live GET <base>/v1/models
(TTY picker / non-TTY error naming real ids) so we never ship guessed,
rotating model ids as a hardcoded default.
- --free seeds OpenRouter's curated free-tier snapshot (mapped from
hermes-agent's free list).
- detectCredentials + detectProvider are now registry-driven (BYOK env keys
auto-detected; aggregator models labelled by api_base, not 'openai').
Providers seeded: venice, openrouter, nvidia, gmi, novita, huggingface
(plus existing anthropic/openai/ollama). All OpenAI-compatible, so they are
pure data — no new wiring.
…URL onboarding
Adds the requested getting-started verb: `obol buy inference venice`,
`obol buy inference openrouter --free`, etc. Dispatch keys on whether the
positional arg matches a registry provider id — provider → BYOK onboarding,
URL/none → the existing x402 crypto-paid seller flow (unchanged).
Onboarding mirrors hermes-agent's api-key UX: resolve the key (--api-key →
provider env var → prompt), open the provider's SignupURL in the browser
(reuses openBrowser; skipped when a key is already in hand or non-TTY), then
delegate to the shared setupCloudProvider engine (model resolution, --free
seeding, LiteLLM patch, agent sync). No wallet, no x402 for this path.
Also adds --api-key/--free to buy inference and corrects the CLAUDE.md
framing (buy inference is now BOTH the BYOK door and the x402 door).
@bussyjdbussyjd changed the title feat(model): BYOK provider registry — one-command setup for Venice, OpenRouter & co.feat: obol buy inference <provider> — BYOK onboarding (Venice, OpenRouter & co.) on a single provider registryJun 12, 2026
@OisinKyne

Copy link
Copy Markdown
Contributor

Brave to add this many providers in one go, you're telling me all 6 of them work? Have you tested them or are you hoping?

@OisinKyne

OisinKyne commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

How does obol buy inference <provider> differ from obol model setup provider? it feels like we're just configuring them, is there a way to actually pay for them from the agents balance?

Seems to me that maybe we mostly change obol model setup and save obol buy inference for if we're paying from the agent? or maybe we just loop our DIY inference as a service into an obol model setup custom or something and use obol buy differently?

I'm thinking if we do have obol buy inference ` it should probably be more of an 'add credit/top up' type of experience rather than an api key setup?

@OisinKyne

Copy link
Copy Markdown
Contributor

added to my pr based on above thoughts. happy to merge that in here, and both into main. #644

@OisinKyne
OisinKyne merged commit f3bc655 into mainJun 15, 2026
8 checks passed
@OisinKyne
OisinKyne deleted the feat/byok-provider-registry branch June 15, 2026 21:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne