Problem
The review model is currently pinned in lib/agent.ts:
return new DurableAgent({
model: "anthropic/claude-sonnet-4.6",
...
});DurableAgent resolves that string through the Vercel AI Gateway. For a project whose selling point is that it is self-hosted, that leaves two gaps:
- The model cannot be changed without editing source. Someone who wants Opus for a security-sensitive repo, or Haiku for a high-traffic one, has to fork.
- The gateway cannot be changed. A team that already routes its AI spend through another provider has to pay through a second one to run OpenReview.
The second point has cost weight here that it would not have in a smaller app. runAgent budgets 200K tokens per review, and the agent is deliberately exploratory — it clones the repo, greps, reads files, runs linters. On a busy repo that adds up quickly, and the model that reviews a dependency bump does not have to be the model that reviews an auth refactor.
Proposal
Two optional environment variables, no change in behaviour when neither is set:
| Variable | Effect |
|---|---|
MODEL | Model id on either path. Defaults to the current anthropic/claude-sonnet-4.6. |
MODEL_BASE_URL | Routes calls through an OpenAI-compatible /chat/completions endpoint instead of the Vercel AI Gateway. |
MODEL_API_KEY | Optional key for that endpoint. |
MODEL_API_KEY being optional is deliberate: it lets an unauthenticated endpoint — a local vLLM or Ollama server on a private network — work by setting MODEL_BASE_URL alone. That covers the self-hosting interest in #6 without touching the Vercel-native path, which stays the default.
Implementation is small: @ai-sdk/openai-compatible behind a resolveModel() helper in a new lib/model.ts. DurableAgent already accepts a model factory alongside a model string, so nothing in the agent or the workflow changes. Two details worth noting, since neither is obvious:
- The factory has to be a workflow step.
DurableAgentpasses the model todoStreamStepas a step argument, and the runtime cannot serialize a plain closure. Marking it"use step"makes the compiler register it (step//./lib/model//customGatewayModel), and the provider instance is built inside the streaming step, so it is never serialized. includeUsage: trueis required. Withoutstream_options.include_usage, streamed responses carry no usage, and the 200K token budget inrunAgentwould silently never trigger.
Disclosure and motivation
I run LLMTR, one such gateway, so I have an obvious interest here — flagging that up front. That is also why the proposal is written provider-neutral rather than as an integration: MODEL_BASE_URL takes any OpenAI-compatible endpoint, and the README example lists LLMTR alongside OpenRouter, Groq, vLLM and Ollama. Nothing in the diff names or favours a specific service.
For what it is worth on the cost point above, gateways in this space do differ in what they make reachable — LLMTR currently carries 11 genuinely free models and the GLM family (zai/glm-5.3 at $1.26/M input, $3.96/M output) next to the frontier ones, and hosts part of its catalog in Türkiye for teams with data-residency requirements. But the value of the change is the seam, not any one gateway behind it.
Status
Implemented and tested against a live OpenAI-compatible gateway — streaming, tool calling across a multi-step agent loop, and usage reporting all verified. PR opened alongside this issue so the diff is there to look at, but I am aware unsolicited vendor-adjacent work has landed here before (#13, #14). If the direction is not wanted, or you would rather it were shaped differently, say so and I will close it — no hard feelings.
Activity
0No activity yet.