Repository navigation
Commit e240ea8
authored
Pick the model and thinking effort for every Bot, from the provider's live list (#1044)
* Pick a model and thinking effort for every built-in Bot, from the provider's live list
The model list is read from whatever serves the deployment's model (OpenAI,
Anthropic, Google and xAI directly, any OpenAI-compatible endpoint, or a
ChatGPT or Claude plan) and kept current in the background: read at start-up,
every six hours, and at once when a provider refuses a model as unknown or
unsupported. How hard each model can think comes from the provider's own
listing where it says (Anthropic, xAI, the ChatGPT plan) and from models.dev
otherwise. The default is the middle of the provider's current line by price,
never a configured model the credential cannot use.
A pick is now a model and an effort per person and conversation. The effort
reaches the model call on every path: the AI SDK's provider options for
built-in Bots (reasoning.effort, reasoning_effort, output_config.effort), and
forwardedProps.openbotModel for a plan's harness and for the harness picked in
setup when it is one of OpenBot's own. A ChatGPT plan's harness is told a
model the plan lists, so it no longer runs the gpt-5.5 the plan refuses.
One person on their own machine now has picking on by default, as the
personal edition already did.
* LangGraph AG-UI harness: answer on the server's model, and only on models a ChatGPT plan offers
On a ChatGPT plan with Compose's default BOT_MODEL=gpt-5.5, every message failed with
"The 'gpt-5.5' model is not supported when using Codex with a ChatGPT account", because the
harness sent whatever model the environment named.
- Read forwardedProps.openbotModel ({model, reasoningEffort}) on every run, keep it out of graph
state, and build the model from it: reasoning.effort on the Codex backend, the Responses API
with reasoning.effort for OpenAI models that take tools only there (the shared
requiresResponsesApi rule, ported), reasoning_effort on Chat Completions and compatible
endpoints, output_config.effort on Anthropic, thinking_level on Google. No effort is sent when
the run carries none.
- Add GET /models for a signed-in plan: the plan's own Codex listing
(chatgpt.com/backend-api/codex/models), visible models only, in Codex's priority order, as
{object: "list", data: [{id, label, efforts, default_effort, priority}]}. Cached for six hours,
the last good listing kept when a refresh fails, and read again early after a model refusal.
- With no server model, a plan answers on the configured model only when its listing offers it,
otherwise on the plan's default; with no listing, as before.
- A provider refusal now ends the run with RUN_ERROR in the provider's own words (including the
Codex backend's {"detail": ...} body, which the OpenAI SDK leaves out of its message), instead
of raising out of the stream after RUN_STARTED.
- Hold google-genai on 2.x, as in #1041: 3.0.0 moved generateContent to /v1.
* Claude Agent SDK harness: answer on the server's model and effort
Read forwardedProps.openbotModel on both paths (the /model endpoint for built-in Bots on a Claude
plan, and / when this harness is the Bot) and set ClaudeAgentOptions.model and .effort from it,
after the upstream merge so the server's choice wins. The model endpoint still drops every other
forwarded prop. Efforts Claude Code does not take are not sent.
Every query builds its options per run, so a choice changed between runs starts the next query on
it. A tool result continues the query that asked for it; when it arrives with another model, the
running query is moved to it with ClaudeSDKClient.set_model. The SDK has no live effort change,
so a changed effort applies from the next query.
* Pick a thinking effort beside the model in the composer
The composer's brain icon becomes a text button naming the model and how
hard it thinks, "GPT-6.1 Terra · High", the way VS Code's chat does. Its
popup keeps the model search and adds a Thinking group for the model in
use, listing the efforts it accepts with its own default marked. A model
whose default is unknown gets a Default row first, and a model that takes
no effort shows no group at all.
The efforts sit in the same list as the models, pinned under them, so one
keyboard model covers both and a screen reader hears them as options in a
labelled group. A listbox option cannot hold a submenu arrow, which is
why this is not VS Code's submenu.
A conversation's pick is now a model and an effort, read and saved as
{ model, effort } on /api/channels/:id/model. Each default is saved as
null, so it follows the model. Switching model keeps the effort when the
new model accepts it and drops it otherwise, so the server is never asked
for an effort it would refuse. A new conversation saves both before its
first message runs, with the same discard-and-keep-the-draft failure as
before.
Admin, Providers names whoever actually serves the models by the new
source field (OpenAI, Anthropic, Google, xAI, an OpenAI-compatible
endpoint, a ChatGPT or Claude plan), with a vendor's mark only where one
is vendored. The allowlist still uses provider/model.
* Log the model and effort each run is sent
One run-model line per run, with the Bot, the run id, the model and the
effort, so an operator can see what a conversation actually ran on. Ids
only: never the prompt, the person or a credential.
* Keep the model list's refusal signal light, list sign-in models through the proxy, and document it
The stream observers that report a refused model are imported by the
browser app's type check, so the signal moves to a module with no imports
instead of pulling in the whole model list. Google and xAI sign-ins list
their models through the local proxy, tested on their captured listings.
The changelog and configuration docs describe the model list, its refresh
and its two new settings.
* Mark Google, xAI and Claude, one Default badge, and tell a provider out of credit as that
Google, xAI and Claude marks come from @lobehub/icons-static-svg 1.97.0,
the set the OpenAI and Anthropic marks already come from, so no model row
or trigger starts on an empty gap; a model whose maker has none gets the
app's plain model icon. The Default tag is the app's own Badge on every
row, so it no longer disappears on the highlighted one.
A provider out of credit or at its spending limit is now told as that,
with its name and its own words, by built-in Bots (instead of xAI's bare
"Forbidden" or OpenAI's "Failed after 3 attempts") and by the LangGraph
harness (instead of "Sign in to your model provider again", which also
told the desktop to ask for a sign-in). Tested on OpenAI's and xAI's real
refusal bodies, captured, and Anthropic's as its API answers it.
* Keep a gateway's own model and names, and wait for the first listing instead of answering empty
A gateway that routes every vendor (OpenRouter, switchback) keeps the
model its operator named while it lists it, and its models keep the names
it gives them; one vendor's middle means nothing across all of them.
OpenRouter's tilde aliases (~openai/...) carry their vendor's mark. A read
that arrives while the boot listing is still on its way now waits for it,
where it answered with nothing, hid the picker and ran the configured
model.
* Give the compose probe the harness's new imports
The probe loads agent-langgraph-agui with stand-ins instead of its real
packages. The picker work added three things that module now imports at load
time: httpx (for the ChatGPT plan's model list), more names from
tool_runtime (billing_refusal, provider_message, provider_refusal,
current_tools), and a run's model and effort from current_tools(). CI's
python3 has no httpx, so the two probe tests failed before a model was built.
Add an httpx stand-in, the missing tool_runtime names, and a current_tools()
that returns a run with no model or effort chosen, so the deployment's own
configuration applies as before.
* An operator's model is the default, and who may pick what depends on who uses OpenBot
The operator's model wins. A model named in BOT_MODEL, or chosen on the
Desktop setup screen, is the default for every run nobody picked a model
for, on every provider, while the credential lists it. The middle of the
provider's line applies only when nobody named one, or when the named one
is not in the credential's list, which is how a ChatGPT plan given
gpt-5.5 still lands on the plan's own middle model. To tell set from
unset, nothing names a model on the operator's behalf any more:
docker-compose.yml leaves the picked harness's BOT_MODEL empty when unset,
and the Desktop no longer writes claude-sonnet-5-5 for an Anthropic key,
whose setup screen asks for no model.
One person on their own machine (OPENBOT_SINGLE_USER, the Desktop among
them) is offered every model the credential lists, and nothing narrows
it: no switch, no allowlist. In a team, an admin allows models in Admin,
Providers, and people pick among those; until one is allowed people get
the default and see no picker, so the old on/off switch is gone. The
allowed-models dialog lists the same live models with their marks and
thinking levels. An allowed model the provider stops listing drops out,
and a pick saved on it runs on the default.
* Keep the billing tests inside the default timeout, and leave the changelog to its own PR
The two 429 tests waited out the AI SDK's real backoff (2 s, then 4 s),
which CI's 5 s timeout cut short. The fake provider now answers with
retry-after-ms: 0, the SDK's own seam for that wait, so every retry still
happens and the tests assert all three attempts. The changelog for 0.3.1
belongs to its own PR.
* Say what Only allowed models off means for a team: other models aren't refused, people still pick among the allowed
* Mention the allowlist in Usage only where there is one
One person on their own machine is offered every model and has no allowlist,
but Admin > Providers' Usage still said it counted "runs the allowlist
refused". Pass the page's team flag to the Usage section and say that only
for a team.1 parent 2ee1fad commit e240ea8
59 files changed
Lines changed: 14028 additions & 662 deletions
File tree
- agent-claude-sdk
- src
- tests
- agent-langgraph-agui
- src
- tests
- app
- src
- components
- admin/controls
- channels/composer
- lib
- channels
- models
- routes/_authed/admin
- tests
- desktop/src-tauri/src
- docs
- server
- src
- admin
- agents
- channels
- db/schema
- tests
- fixtures
- billing
- model-listings
- tests
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
15 | 16 | | |
16 | 17 | | |
| 18 | + | |
17 | 19 | | |
18 | 20 | | |
19 | 21 | | |
20 | | - | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
21 | 29 | | |
22 | 30 | | |
23 | 31 | | |
24 | 32 | | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
25 | 38 | | |
26 | 39 | | |
27 | 40 | | |
28 | 41 | | |
29 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
30 | 62 | | |
31 | 63 | | |
32 | 64 | | |
| |||
47 | 79 | | |
48 | 80 | | |
49 | 81 | | |
| 82 | + | |
| 83 | + | |
50 | 84 | | |
51 | 85 | | |
52 | 86 | | |
| |||
73 | 107 | | |
74 | 108 | | |
75 | 109 | | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
76 | 114 | | |
77 | 115 | | |
78 | 116 | | |
79 | 117 | | |
80 | 118 | | |
81 | 119 | | |
82 | 120 | | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
83 | 124 | | |
84 | 125 | | |
85 | 126 | | |
| |||
313 | 354 | | |
314 | 355 | | |
315 | 356 | | |
| 357 | + | |
316 | 358 | | |
317 | 359 | | |
318 | 360 | | |
| |||
329 | 371 | | |
330 | 372 | | |
331 | 373 | | |
332 | | - | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
333 | 379 | | |
334 | 380 | | |
335 | 381 | | |
| |||
361 | 407 | | |
362 | 408 | | |
363 | 409 | | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
364 | 433 | | |
365 | 434 | | |
366 | 435 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
| 76 | + | |
76 | 77 | | |
77 | 78 | | |
78 | 79 | | |
| |||
88 | 89 | | |
89 | 90 | | |
90 | 91 | | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
91 | 96 | | |
92 | 97 | | |
93 | 98 | | |
| |||
0 commit comments