Skip to content

Commit 7016a35

Browse files
authored
Merge pull request #427 from SudoJacky/feat/zai-glm-provider
feat: add Z.ai GLM provider and vision model support
2 parents 9523073 + 044bfad commit 7016a35

72 files changed

Lines changed: 2457 additions & 344 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/api/agent-runtime.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ Optional provider features must be declared explicitly on the selected provider
7676
`structured_output` (camel-case spellings are also accepted). A requested undeclared feature fails
7777
with the resolved provider ID and missing capability. Built-in profile capabilities fall back to the
7878
provider catalog when the profile omits the field; an explicit profile value overrides that default.
79-
Custom OpenAI-compatible profiles are the exception for reasoning effort: they default
79+
Reasoning effort is not capability-gated: all OpenAI-compatible profiles default
8080
`supportsReasoningEffort` to `true`. Setting it to `false` omits effort instead of failing the Turn;
81-
reasoning summaries still require the `reasoning` capability.
81+
reasoning summaries still require the separate `reasoning` capability.
8282
Declared settings map to Chat Completions fields as follows: service tier to `service_tier`, reasoning
8383
effort to `reasoning_effort`, reasoning summary configuration to `reasoning`, and output schemas to
8484
`response_format.type = "json_schema"`. For Responses requests, effort and summary map to

docs/api/desktop.md

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -491,15 +491,19 @@ configuration for a separate backend process. Secret fields
491491
return `value: null` with `secret` metadata and must remain redacted in exported/public config.
492492
Provider selection is profile-based. New config should use `agents.defaults.activeProfile` and
493493
`providers.profiles.<profileId>.provider`; `agents.defaults.provider: "auto"` is a legacy value only.
494+
The Provider & Models default selector updates `agents.defaults.activeProfile` and
495+
`agents.defaults.model` in one native patch so Profile-based endpoint resolution and global model
496+
fallbacks cannot diverge.
494497
Reasoning effort is not an Agent Defaults setting. A legacy `agents.defaults.reasoningEffort` value
495498
may remain in raw config for read compatibility, but the settings registry does not expose it and the
496499
agent runtime does not apply it to model requests.
497-
The built-in provider catalog currently exposes only `deepseek`, `dashscope`, and `openai`.
500+
The built-in provider catalog currently exposes `deepseek`, `dashscope`, `openai`, and `zai`.
498501
Profiles are not limited to that catalog: a profile with a custom provider ID, explicit `apiBase`,
499502
and at least one model is resolved as an OpenAI-compatible provider. Its optional API key remains on
500503
the existing secret/redaction path, and `supportsModelDiscovery` controls `/models` discovery.
501-
`supportsReasoningEffort` defaults to `true`; set it to `false` to omit effort from both Chat
502-
Completions and Responses requests for endpoints that reject the field.
504+
Every built-in and custom Provider profile defaults `supportsReasoningEffort` to `true`; set it to
505+
`false` to omit effort from both Chat Completions and Responses requests for endpoints that reject
506+
the field.
503507
Context windows are model-specific. A provider profile can store explicit overrides as
504508
`modelContextWindows`, for example:
505509

@@ -513,11 +517,38 @@ Context windows are model-specific. A provider profile can store explicit overri
513517

514518
The runtime prefers a turn override, then the active profile's model override, then Tinybot's
515519
known-model default. `deepseek-v4-flash`, `deepseek-v4-flash-vision-exp`, and
516-
`deepseek-v4-pro` default to `1000000`; unknown models use the legacy
517-
`agents.defaults.contextWindowTokens` value when present and otherwise fall back to `128000`.
520+
`deepseek-v4-pro`, plus `glm-5.3` and `glm-5.3-flash`, default to `1000000`;
521+
unknown models use the legacy `agents.defaults.contextWindowTokens` value when present and
522+
otherwise fall back to `128000`.
518523
The settings UI edits these values per model instead of applying one global window to every model.
524+
Provider profiles can also separate their discovered model catalog from the models shown in model
525+
selectors. `models` retains the available IDs, while `enabledModels` contains only the IDs exposed
526+
in Chat, Agent Graphs, and other shared model pickers. When `enabledModels` is absent, existing
527+
profile `models` remain enabled for backward compatibility. Models newly returned by a refresh are
528+
added to the catalog but remain disabled until selected in Provider & Models settings.
529+
530+
Image input is a model capability rather than an API-mode capability. Profiles can override it with
531+
`modelCapabilities`:
532+
533+
```json
534+
{
535+
"enabledModels": ["custom-vision"],
536+
"modelCapabilities": [
537+
{ "model": "custom-vision", "inputModalities": ["image"] }
538+
]
539+
}
540+
```
541+
542+
Missing capability entries use Tinybot's known-model defaults. `glm-5.3-flash` and
543+
`deepseek-v4-flash-vision-exp` support image input by default; unknown models default to text-only
544+
until enabled explicitly. The runtime enforces the resolved model capability before either Chat
545+
Completions or Responses requests are constructed.
519546
Each profile defaults to Chat Completions. Set `apiMode` to `responses` (or enable **Use Responses
520547
API** in provider settings) only when its endpoint supports `/responses`.
548+
The built-in `zai` profile is restricted to Chat Completions. It uses
549+
`https://open.bigmodel.cn/api/paas/v4` and maps Tinybot's maximum-output setting to Z.ai's
550+
`max_tokens` field. Z.ai profiles reject unsupported Responses mode, out-of-range temperature,
551+
and explicitly requested parallel tool calls before a request is sent.
521552

522553
OpenAI-compatible provider profiles accept separate network deadlines:
523554

@@ -541,6 +572,9 @@ Provider model discovery:
541572
- `dashscope` uses the same OpenAI-compatible model discovery shape against its configured
542573
`apiBase`, so the default discovery URL is
543574
`https://dashscope.aliyuncs.com/compatible-mode/v1/models`.
575+
- `zai` uses the static `glm-5.3`, `glm-5.3-flash`, and `glm-5.2` model list. Live `/models`
576+
discovery is disabled because the supported integration contract only guarantees Chat
577+
Completions.
544578

545579
`POST /api/provider-models` accepts `{ provider, profile, apiBase, refreshLive }`. When
546580
`refreshLive: true` is used for an OpenAI-compatible provider, the backend reads the configured

docs/api/events.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ catalog, and legacy Agent defaults:
104104
- Turn `contextWindowTokens` / `context_window_tokens`: an explicit override for that turn.
105105
- Profile `modelContextWindows`: per-model overrides containing `model` and
106106
`contextWindowTokens`. When unset, `deepseek-v4-flash`, `deepseek-v4-flash-vision-exp`, and
107-
`deepseek-v4-pro` use `1000000` automatically.
107+
`deepseek-v4-pro`, plus `glm-5.3` and `glm-5.3-flash`, use `1000000` automatically.
108108
- `agents.defaults.contextWindowTokens` / `context_window_tokens`: legacy fallback for unknown
109109
models only. Unknown models fall back to `128000` when it is absent.
110110
- `contextWindowStrategy` / `context_window_strategy`: `discard` or `compact`. The fallback is

docs/api/threads-and-memory.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ src-tauri/src/threads/domain/types/records.rs
1010
src-tauri/src/threads/domain/types/requests.rs
1111
src-tauri/tests/crate/threads.rs
1212
-->
13-
<!-- tinybot-doc-fingerprint: sha256:14fba07c2b89b249048fabd2c90c08f1d92430b30e2b441b28c105b86c2c0896 -->
13+
<!-- tinybot-doc-fingerprint: sha256:c46a50f6806b4ad0ba04e02a97083d4bae810bc6045ada37e9ae57794d2fc60e -->
1414

1515
This document covers Thread queries, memory, persistence, and project grouping.
1616
It is part of the [Rust backend API reference](rust-backend-api.md), which
@@ -248,6 +248,11 @@ is materialized before a later append.
248248
Thread discovery metadata, checkpoint pointers, and Rollout heads are maintained only in memory and
249249
rebuilt from those files when the process starts.
250250

251+
`thread.create` pins the Thread's provider API mode in `metadata.extra.apiMode` and the Rollout
252+
session metadata. When `metadata.extra.modelProvider` explicitly selects a provider, that provider's
253+
profile determines the mode; otherwise creation falls back to the active provider profile. Later
254+
turns must use the pinned mode, and a mismatch fails before the provider is called.
255+
251256
The durable hierarchy is strict: a Thread may exist without an active Turn, but every persisted
252257
`ThreadItem` and every Turn checkpoint has one non-empty `turnId`. Thread-level metadata updates made
253258
while no Turn is active update Thread metadata without manufacturing a turnless Item. A Rollout
@@ -384,7 +389,7 @@ message verbatim and does not copy attachment files. The agent reads a mentioned
384389
```
385390

386391
The renderer sends the composer's effort choice as `spec.reasoningEffort`. Composer values are `low`,
387-
`medium`, `high`, `xhigh`, and `max`; a missing or invalid local preference starts at `medium`. Model
392+
`medium`, `high`, `xhigh`, and `max`; a missing or invalid local preference starts at `high`. Model
388393
support varies, and an unsupported explicit value remains a provider request error rather than being
389394
silently downgraded.
390395

docs/architecture/agent-turn-lifecycle.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ src-tauri/src/runtime/README.md
1111
src-tauri/src/threads/domain/README.md
1212
src-tauri/src/threads/rollout/store/README.md
1313
-->
14-
<!-- tinybot-doc-fingerprint: sha256:549fd76f9c28d4743007f8f9053fed32e671b6de7ebe4d3a821fe7d2e299aea7 -->
14+
<!-- tinybot-doc-fingerprint: sha256:242038ad076dad391f3a9aa0b30b90cad72c8d045e552b09e1a9581ec91a3660 -->
1515

1616
A Turn begins with one user request and contains all provider iterations,
1717
reasoning records, tool calls, tool results, form checkpoints, and the terminal

docs/architecture/context-and-instructions.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ src-tauri/src/runtime/working_directory.rs
1111
src-tauri/src/system_prompt.rs
1212
src-tauri/src/workspace/README.md
1313
-->
14-
<!-- tinybot-doc-fingerprint: sha256:4740b7f5bf36ce5691febc3b89b54bdbd77ea348e35ca1b8eb498bfdd10e73e5 -->
14+
<!-- tinybot-doc-fingerprint: sha256:fbca262683bcc87d45f9c2fbc286d38a9ec94db6d2f1d92adc4d197a194dcf5c -->
1515

1616
Tinybot composes model-visible instructions from explicit, traceable sources
1717
before the Agent Runtime builds the bounded provider request. Instruction
@@ -109,10 +109,11 @@ unbounded prompt text in diagnostics.
109109

110110
Managed image attachments remain typed references on the originating user
111111
message. Rollouts retain only the managed path, MIME type, byte size, and
112-
content hash. When Responses history is encoded, the runtime revalidates the
113-
local file and creates a request-local Base64 data URL; later Turns repeat that
114-
encoding while the message remains in replayed context. Chat Completions fails
115-
explicitly when such a reference is present.
112+
content hash. Before constructing a provider request, the runtime verifies that
113+
the selected model declares image-input support. It then revalidates the local
114+
file and creates a request-local Base64 data URL; later Turns repeat that
115+
encoding while the message remains in replayed context. Chat Completions emits
116+
`image_url` content, while Responses emits `input_image` content.
116117

117118
Trusted lifecycle command hooks may add bounded developer context after static
118119
instruction composition: at initial prompt submission, around a completed tool

docs/architecture/system-overview.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ src/react-workbench/agent-graph/README.md
1616
src/react-workbench/shell/README.md
1717
src/react-workbench/sidecar/README.md
1818
-->
19-
<!-- tinybot-doc-fingerprint: sha256:703f7c0745a79d90c144dd769971c79478584a5617dc1cceae0d70cdefa0668f -->
19+
<!-- tinybot-doc-fingerprint: sha256:a366d810c4cf5b2f00ffdc371b01e1210499a377d3b45d17a8243cb80d0a077c -->
2020

2121
Tinybot Desktop is a local-first React and Rust application. The renderer owns
2222
presentation, the application core owns framework-independent UI contracts,
@@ -108,8 +108,9 @@ Desktop Commands / Desktop Host
108108
budget. The first visit to an Agent node creates a canonical Thread; later
109109
visits in the same Run continue it. Per-node role
110110
instructions and optional Provider, model, and reasoning-effort settings use
111-
the existing Turn interfaces; the renderer only offers models from available
112-
Provider connections, while absent settings inherit application defaults.
111+
the existing Turn interfaces; the renderer only offers profile-enabled models
112+
from available Provider connections and carries their image-input capability,
113+
while absent settings inherit application defaults.
113114
Router decisions use a separate, single-shot, tool-free provider request and
114115
do not create Threads or load Agent instruction sources.
115116
Ordinary Chat Turns expose Graphs from their exact working directory as

docs/architecture/tool-execution-and-permissions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ src-tauri/src/tools/registry/README.md
1111
src-tauri/src/tools/registry/mod.rs
1212
src-tauri/src/workspace/README.md
1313
-->
14-
<!-- tinybot-doc-fingerprint: sha256:9f4a8625b8ad11297f66d665d46d7cd090a8bc650a849fde2f082d0be7b8ace5 -->
14+
<!-- tinybot-doc-fingerprint: sha256:e83304a4f4b61acb66490f3d0962f567588e53e380eff3411556590c3109719e -->
1515

1616
Tinybot exposes one protocol-neutral tool registry to the Agent Runtime. Tool
1717
metadata, per-Turn exposure, capability policy, execution routing, lifecycle,

docs/desktop.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ The page also offers an explicit Agent-assisted migration for standalone Skills,
111111

112112
## Conversation Models
113113

114-
Model selection belongs to the conversation rather than a separate editable global default. Tinybot stores the recently used model as the starting choice for a new conversation, then persists the selected model in that Thread's metadata. Switching conversations restores each Thread's model, and changing the Composer model updates both the Thread and the recently used choice.
114+
Model selection belongs to the conversation after a Thread is created. The Provider & Models page, Chat drafts, empty Threads, and desktop-pet quick chat all use one Settings-store operation for the starting model: it saves the native `activeProfile`/`model` pair before mirroring that choice as the renderer's recently used preference. A first send waits for this save. The native pair is also the coherent fallback for background model work such as long-term Memory. When model options load, an explicitly selected native Profile paired with another Provider's model is repaired from a valid renderer preference or that Profile's default model and emits a structured warning. Switching populated conversations restores each Thread's own model, and changing their Composer model updates only the Thread.
115115

116116
Desktop turn submission resolves models in this order:
117117

@@ -120,7 +120,7 @@ Desktop turn submission resolves models in this order:
120120
3. the recently used model for new conversations;
121121
4. the native runtime's configured fallback when no user selection exists.
122122

123-
Automatic turns, including Agent-assisted plugin migration, use the same resolution path. Provider profiles keep a provider-specific fallback model for connection setup and native runtime recovery, but that value is not presented as the user's current conversation model.
123+
Automatic turns, including Agent-assisted plugin migration, use the same resolution path. Provider profiles keep a provider-specific fallback model for connection setup and native runtime recovery, while background work without a Thread-specific selection uses the native default Profile/model pair.
124124

125125
## Desktop Adapters
126126

public/assets/providers/zai.svg

Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)