From 52f8466dc6144840091b6192996ca478dd96dbd9 Mon Sep 17 00:00:00 2001 From: aiapienthusiast <327629069+aiapienthusiast@users.noreply.github.com> Date: Wed, 21 Oct 2026 17:01:14 +0400 Subject: [PATCH] feat(ai): add a cheaperinference provider alias PGBOT_AI_PROVIDER=cheaperinference points the /chat/completions client at https://api.cheaperinference.com/v1, the same way the requesty and opper aliases pick their endpoints. The key comes from CHEAPER_INFERENCE_API_KEY or PGBOT_AI_API_KEY only, so an OpenAI or OpenRouter key in the shell is never sent to it, and it is never auto-detected. Documented in the README environment table and the explain section, and in the changelog. --- CHANGELOG.md | 7 +++++++ README.md | 7 ++++++- internal/ai/openai.go | 1 + internal/ai/resolve.go | 9 ++++++++- internal/ai/resolve_test.go | 36 +++++++++++++++++++++++++++++++++++- 5 files changed, 57 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f136d71..709b3aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ separately by `model.SchemaVersion` (currently 1.5.0). ## [Unreleased] +### Added +- **Cheaper Inference as an `explain` / `ask` provider.** + `PGBOT_AI_PROVIDER=cheaperinference` sends `/chat/completions` to + `https://api.cheaperinference.com/v1` with `CHEAPER_INFERENCE_API_KEY` (or + `PGBOT_AI_API_KEY`), the same way the `requesty` and `opper` aliases work. It + is never auto-detected, and it never picks up an OpenAI or OpenRouter key. + ## [0.9.0] - 2026-10-18 ### Added diff --git a/README.md b/README.md index 3f39509..1c875a3 100644 --- a/README.md +++ b/README.md @@ -469,6 +469,7 @@ one SSH connection serves the whole run. Raise `--timeout` if the link is slow. | `OPENAI_API_KEY` / `OPENROUTER_API_KEY` | Enables `ask` / `explain` via OpenAI or OpenRouter. Keys are never accepted as flags. | | `REQUESTY_API_KEY` | Enables `ask` / `explain` via [Requesty](https://docs.requesty.ai) with `PGBOT_AI_PROVIDER=requesty` (never auto-detected). | | `OPPER_API_KEY` | Enables `ask` / `explain` via [Opper](https://docs.opper.ai) with `PGBOT_AI_PROVIDER=opper` (never auto-detected). | +| `CHEAPER_INFERENCE_API_KEY` | Enables `ask` / `explain` via [Cheaper Inference](https://cheaperinference.com/docs) with `PGBOT_AI_PROVIDER=cheaperinference` (never auto-detected). | | `GEMINI_API_KEY` / `GOOGLE_API_KEY` | Enables `ask` / `explain` via Google Gemini. | | `ANTHROPIC_API_KEY` | Enables `ask` / `explain` via Anthropic. | | `XAI_API_KEY` / `GROK_API_KEY` | Enables `ask` / `explain` via xAI. | @@ -783,7 +784,7 @@ not require confirmation. | Bedrock Mantle | `AWS_BEARER_TOKEN_BEDROCK`, or `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` | `openai.gpt-5.6-terra` | Responses (GPT) / Messages (Claude) | The OpenAI provider also supports compatible services such as OpenRouter, -Requesty, Opper, Groq, Together, DeepSeek, Mistral, Ollama, vLLM, and LM Studio. +Requesty, Opper, Cheaper Inference, Groq, Together, DeepSeek, Mistral, Ollama, vLLM, and LM Studio. `PGBOT_AI_PROVIDER=requesty` with `REQUESTY_API_KEY` points it at `https://router.requesty.ai/v1`; set `PGBOT_AI_MODEL` to any Requesty model id (for example `openai/gpt-4o-mini`), and `PGBOT_AI_BASE_URL` to @@ -792,6 +793,10 @@ Requesty, Opper, Groq, Together, DeepSeek, Mistral, Ollama, vLLM, and LM Studio. `https://api.opper.ai/v3/compat`, an EU-hosted gateway. The default `gpt-5.6-terra` works there as is; set `PGBOT_AI_MODEL` to any Opper model id (for example `claude-sonnet-4-6`). +`PGBOT_AI_PROVIDER=cheaperinference` with `CHEAPER_INFERENCE_API_KEY` points it +at `https://api.cheaperinference.com/v1`. The default `gpt-5.6-terra` works +there as is; set `PGBOT_AI_MODEL` to any Cheaper Inference model id (for example +`gpt-5.4-mini`). ``` export OPENAI_API_KEY=… diff --git a/internal/ai/openai.go b/internal/ai/openai.go index 47c4f2c..45660f4 100644 --- a/internal/ai/openai.go +++ b/internal/ai/openai.go @@ -18,6 +18,7 @@ const ( defaultOpenRouterURL = "https://openrouter.ai/api/v1" defaultRequestyURL = "https://router.requesty.ai/v1" defaultOpperURL = "https://api.opper.ai/v3/compat" + defaultCheaperInfURL = "https://api.cheaperinference.com/v1" defaultOllamaURL = "http://localhost:11434/v1" // Applied only to reasoning models. Override with $PGBOT_AI_REASONING_EFFORT diff --git a/internal/ai/resolve.go b/internal/ai/resolve.go index d57fba4..c122f9a 100644 --- a/internal/ai/resolve.go +++ b/internal/ai/resolve.go @@ -113,7 +113,7 @@ func Resolve() (LanguageModel, error) { ReasoningEffort: envOr("PGBOT_AI_REASONING_EFFORT", ""), } - case "openai", "openrouter", "requesty", "opper", "ollama", "openai-compatible": + case "openai", "openrouter", "requesty", "opper", "cheaperinference", "ollama", "openai-compatible": // One /chat/completions client serves them all; only the endpoint and the // conventional key variable differ. if key == "" { @@ -125,6 +125,9 @@ func Resolve() (LanguageModel, error) { case "opper": // Same for Opper. key = firstEnv("OPPER_API_KEY") + case "cheaperinference": + // Same for Cheaper Inference. + key = firstEnv("CHEAPER_INFERENCE_API_KEY") default: key = firstEnv("OPENAI_API_KEY", "OPENROUTER_API_KEY") } @@ -143,6 +146,8 @@ func Resolve() (LanguageModel, error) { base = defaultRequestyURL case "opper": base = defaultOpperURL + case "cheaperinference": + base = defaultCheaperInfURL case "ollama": base = defaultOllamaURL case "openai-compatible": @@ -210,6 +215,8 @@ func keyVarsFor(name string) string { return "REQUESTY_API_KEY" case "opper": return "OPPER_API_KEY" + case "cheaperinference": + return "CHEAPER_INFERENCE_API_KEY" default: return "OPENAI_API_KEY / OPENROUTER_API_KEY" } diff --git a/internal/ai/resolve_test.go b/internal/ai/resolve_test.go index 644a890..2c3f222 100644 --- a/internal/ai/resolve_test.go +++ b/internal/ai/resolve_test.go @@ -12,7 +12,7 @@ func clearEnv(t *testing.T) { for _, k := range []string{ "PGBOT_AI_PROVIDER", "PGBOT_AI_MODEL", "PGBOT_AI_BASE_URL", "PGBOT_AI_API_KEY", "PGBOT_AI_REASONING_EFFORT", "GEMINI_API_KEY", "GOOGLE_API_KEY", "ANTHROPIC_API_KEY", - "OPENAI_API_KEY", "OPENROUTER_API_KEY", "REQUESTY_API_KEY", "OPPER_API_KEY", "XAI_API_KEY", "GROK_API_KEY", + "OPENAI_API_KEY", "OPENROUTER_API_KEY", "REQUESTY_API_KEY", "OPPER_API_KEY", "CHEAPER_INFERENCE_API_KEY", "XAI_API_KEY", "GROK_API_KEY", "AWS_BEARER_TOKEN_BEDROCK", "AWS_REGION", "AWS_DEFAULT_REGION", "PGBOT_GEMINI_MODEL", "PGBOT_GEMINI_URL", "PGBOT_OPENAI_MODEL", "PGBOT_OPENAI_URL", } { @@ -315,6 +315,40 @@ func TestResolve_opper(t *testing.T) { } } +// cheaperinference is an explicit alias only: it goes to Cheaper Inference with +// CHEAPER_INFERENCE_API_KEY or PGBOT_AI_API_KEY, never borrows an OpenAI key, +// and is never auto-detected. +func TestResolve_cheaperinference(t *testing.T) { + for _, keyVar := range []string{"CHEAPER_INFERENCE_API_KEY", "PGBOT_AI_API_KEY"} { + clearEnv(t) + t.Setenv("PGBOT_AI_PROVIDER", "cheaperinference") + t.Setenv(keyVar, "k") + m, err := Resolve() + if err != nil { + t.Fatalf("cheaperinference with %s: %v", keyVar, err) + } + if m.Provider() != "openai" { + t.Errorf("cheaperinference with %s: provider = %q, want openai", keyVar, m.Provider()) + } + if m.Endpoint() != defaultCheaperInfURL { + t.Errorf("cheaperinference with %s: endpoint = %q, want %q", keyVar, m.Endpoint(), defaultCheaperInfURL) + } + } + + clearEnv(t) + t.Setenv("PGBOT_AI_PROVIDER", "cheaperinference") + t.Setenv("OPENAI_API_KEY", "sk-openai") + if _, err := Resolve(); err == nil || !strings.Contains(err.Error(), "CHEAPER_INFERENCE_API_KEY") { + t.Errorf("cheaperinference with only OPENAI_API_KEY should ask for CHEAPER_INFERENCE_API_KEY, got: %v", err) + } + + clearEnv(t) + t.Setenv("CHEAPER_INFERENCE_API_KEY", "k") + if _, err := Resolve(); err == nil { + t.Error("CHEAPER_INFERENCE_API_KEY alone must not be auto-detected") + } +} + // A custom remote endpoint must come with a named provider. Auto-detecting one // from an ambient vendor key would send that key — say the ANTHROPIC_API_KEY // another tool left in the shell — to whatever host PGBOT_AI_BASE_URL names,