Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,12 @@ ANTHROPIC_API_KEY=sk-ant-your-key-here
# Legacy aliases also work: deepseek-chat and deepseek-reasoner
# For Z.AI GLM Coding Plan, set:
# OPENAI_BASE_URL=https://api.z.ai/api/coding/paas/v4
# OPENAI_MODEL=GLM-5.1
# Optional: OPENAI_MODEL=GLM-5-Turbo, GLM-4.7, or GLM-4.5-Air
# OPENAI_MODEL=glm-5.2
# Optional: OPENAI_MODEL=GLM-5.1, GLM-5-Turbo, GLM-4.7, or GLM-4.5-Air
# Optional GLM-5.2 thinking controls:
# OPENAI_MODEL='glm-5.2?reasoning=high' # enhanced reasoning
# OPENAI_MODEL='glm-5.2?reasoning=xhigh' # maps to Z.AI reasoning_effort=max
# OPENAI_MODEL='glm-5.2?thinking=disabled' # faster direct answers for simple tasks
# For Hicap, use the OpenAI-compatible route flag above and set:
# HICAP_API_KEY=your-hicap-key-here
# OPENAI_BASE_URL=https://api.hicap.ai/v1
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ Inside OpenClaude:
- run `/provider` for guided provider setup and saved profiles
- run `/onboard-github` for GitHub Models onboarding

> **Note:** OpenClaude does not automatically load project `.env` files. We recommend using the `/provider` command for setup, which securely stores credentials. If you prefer environment variables, export them explicitly or run `openclaude --provider-env-file .env` for provider/setup variables. Export runtime/debug knobs from your shell or launcher.
> **Note:** OpenClaude does not automatically load project `.env` files. We recommend using the `/provider` command for setup, which saves provider profiles and credentials in `.openclaude-profile.json`. If you prefer environment variables, export them explicitly or run `openclaude --provider-env-file .env` for provider/setup variables. Export runtime/debug knobs from your shell or launcher.

### Background sessions

Expand Down Expand Up @@ -195,6 +195,7 @@ Advanced and source-build guides:
| Provider | Setup Path | Notes |
| --- | --- | --- |
| OpenAI-compatible | `/provider` or env vars | Works with OpenAI, OpenRouter, DeepSeek, Groq, Mistral, LM Studio, and other compatible `/v1` servers |
| Z.AI GLM Coding Plan | `/provider` or OpenAI-compatible env vars | Uses `OPENAI_API_KEY` at `https://api.z.ai/api/coding/paas/v4` and defaults to `glm-5.2` |
| Hicap | `/provider` or OpenAI-compatible env vars | Uses `api-key` auth, discovers models from unauthenticated `/models`, and supports Responses mode for `gpt-` models |
| Fireworks AI | `/provider` or env vars | First-class provider with 276 curated models (DeepSeek, Qwen, Llama, Gemma, and more); uses `FIREWORKS_API_KEY` |
| Gemini | `/provider` or env vars | Supports API key only |
Expand Down Expand Up @@ -228,6 +229,7 @@ OpenClaude supports multiple providers, but behavior is not identical across all
- Smaller local models can struggle with long multi-step tool flows
- Some providers impose lower output caps than the CLI defaults, and OpenClaude adapts where possible
- Gitlawb Opengateway is the fresh-install startup default and requires an API key from https://gitlawb.com/opengateway/keys. It uses one OpenAI-compatible base URL; switch between `mimo-*` and `google/gemini-3.1-flash-lite-preview` with `/model`, and do not pin the base URL to `/v1/xiaomi-mimo`.
- Z.AI GLM Coding Plan uses `https://api.z.ai/api/coding/paas/v4` with `glm-5.2` by default. Use `glm-5.2?reasoning=high` for enhanced reasoning, `glm-5.2?reasoning=xhigh` to request Z.AI `reasoning_effort=max`, or `glm-5.2?thinking=disabled` for faster direct answers.
- Xiaomi MiMo uses `api-key` header auth on the direct OpenAI-compatible route and currently does not support `/usage` reporting in OpenClaude

### GitHub Copilot sub-agent optimization
Expand Down Expand Up @@ -261,7 +263,7 @@ Add to `~/.openclaude.json`:
"api_key": "sk-your-key"
},
"zai-default": {
"model": "glm-5.1",
"model": "glm-5.2",
"base_url": "https://api.z.ai/api/coding/paas/v4",
"api_key": "sk-your-key"
},
Expand All @@ -282,7 +284,7 @@ Add to `~/.openclaude.json`:

When no routing match is found, the global provider remains the fallback.

`agentRouting` values and explicit Agent tool `model` overrides match keys in `agentModels`. By default, that key is also the model string sent to the provider. Set `agentModels.<key>.model` when you want a local route key such as `zai-default` to call a different provider model name such as `glm-5.1`.
`agentRouting` values and explicit Agent tool `model` overrides match keys in `agentModels`. By default, that key is also the model string sent to the provider. Set `agentModels.<key>.model` when you want a local route key such as `zai-default` to call a different provider model name such as `glm-5.2`.

> **Note:** `/provider` changes the global/parent provider for your current session. `agentModels` and `agentRouting` are specifically for configuring per-agent provider overrides while keeping the parent session unchanged.

Expand Down
1 change: 1 addition & 0 deletions src/integrations/brands/glm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default defineBrand({
supportsPreciseTokenCount: false,
},
modelIds: [
'glm-5.2',
'GLM-5.1',
'GLM-5-Turbo',
'GLM-5',
Expand Down
3 changes: 2 additions & 1 deletion src/integrations/descriptors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ export interface OpenAIShimTransportConfig {
preserveReasoningContent?: boolean
requireReasoningContentOnAssistantMessages?: boolean
reasoningContentFallback?: '' | 'omit'
thinkingRequestFormat?: 'none' | 'deepseek-compatible'
thinkingRequestFormat?: 'none' | 'deepseek-compatible' | 'zai-compatible'
enableToolStreaming?: boolean
maxTokensField?: OpenAIShimTokenField
removeBodyFields?: string[]
/** Override the endpoint path for this model (e.g., '/responses', '/messages'). */
Expand Down
1 change: 1 addition & 0 deletions src/integrations/models/glm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function glmModel(
}

export default [
glmModel('glm-5.2', 'GLM 5.2', 1_000_000, 131_072),
glmModel('GLM-5.1', 'GLM-5.1', 202_752, 131_072),
glmModel('GLM-5-Turbo', 'GLM-5-Turbo', 202_752, 131_072),
glmModel('GLM-5', 'GLM-5', 202_752, 131_072),
Expand Down
33 changes: 33 additions & 0 deletions src/integrations/runtimeMetadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,39 @@ describe('resolveModelRuntimeLimits', () => {
).toBe(1_000_000)
})
})

it('uses built-in Z.AI GLM-5.2 runtime limits', () => {
const limits = resolveModelRuntimeLimits({
model: 'glm-5.2',
processEnv: {
OPENAI_BASE_URL: 'https://api.z.ai/api/coding/paas/v4',
},
})

expect(limits.contextWindow).toBe(1_000_000)
expect(limits.maxOutputTokens).toBe(131_072)
})
})

describe('resolveOpenAIShimRuntimeContext - Z.AI GLM-5.2', () => {
it.each([
'glm-5.2',
'glm-5.2?reasoning=high',
'glm-5.2?thinking=disabled',
])('uses Z.AI GLM-5.2 shim settings for %s', model => {
const result = resolveOpenAIShimRuntimeContext({
model,
baseUrl: 'https://api.z.ai/api/coding/paas/v4',
processEnv: {},
})

expect(result.routeId).toBe('zai')
expect(result.catalogEntry?.id).toBe('glm-5.2')
expect(result.openaiShimConfig.thinkingRequestFormat).toBe('zai-compatible')
expect(result.openaiShimConfig.preserveReasoningContent).toBe(true)
expect(result.openaiShimConfig.requireReasoningContentOnAssistantMessages).toBe(true)
expect(result.openaiShimConfig.enableToolStreaming).toBe(true)
})
})

describe('resolveOpenAIShimRuntimeContext - segment-boundary heuristic', () => {
Expand Down
29 changes: 21 additions & 8 deletions src/integrations/runtimeMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,20 @@ import { parseCustomHeadersEnv } from '../utils/providerCustomHeaders.js'
function normalizeModelApiName(
value: string | undefined,
): string | null {
const trimmed = value?.trim().toLowerCase()
return trimmed ? trimmed : null
const baseModel = getBaseModelApiName(value)
return baseModel ? baseModel.toLowerCase() : null
}

function getBaseModelApiName(value: string | undefined): string | null {
const trimmed = value?.trim()
if (!trimmed) {
return null
}

const queryIndex = trimmed.indexOf('?')
const baseModel =
queryIndex === -1 ? trimmed : trimmed.slice(0, queryIndex).trim()
return baseModel || null
}

function matchesCatalogEntryModel(
Expand Down Expand Up @@ -269,7 +281,7 @@ function findModelDescriptorForApiName(
routeId: string | null,
modelApiName: string | undefined,
) {
const trimmedModel = modelApiName?.trim()
const trimmedModel = getBaseModelApiName(modelApiName)
if (!trimmedModel) {
return null
}
Expand Down Expand Up @@ -385,22 +397,23 @@ export function resolveModelRuntimeLimits(options: {
const routeId = resolveActiveRouteIdFromEnv(runtimeEnv, {
activeProfileProvider: options.activeProfileProvider,
})
const catalogEntry = findCatalogEntryForApiName(routeId, options.model)
const modelApiName = getBaseModelApiName(options.model) ?? options.model
const catalogEntry = findCatalogEntryForApiName(routeId, modelApiName)
const cachedCatalogEntry = findCachedCatalogEntryForApiName(
routeId,
options.model,
modelApiName,
runtimeEnv,
)
const modelDescriptor =
getModelDescriptorForCatalogEntry(catalogEntry) ??
getModelDescriptorForCatalogEntry(cachedCatalogEntry) ??
findModelDescriptorForApiName(routeId, options.model)
findModelDescriptorForApiName(routeId, modelApiName)
const externalContextWindow = getOpenAIContextWindowMatches(
options.model,
modelApiName,
runtimeEnv,
)
const externalMaxOutputTokens = getOpenAIMaxOutputTokenMatches(
options.model,
modelApiName,
runtimeEnv,
)

Expand Down
15 changes: 13 additions & 2 deletions src/integrations/vendors/zai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default defineVendor({
label: 'Z.AI',
classification: 'openai-compatible',
defaultBaseUrl: 'https://api.z.ai/api/coding/paas/v4',
defaultModel: 'GLM-5.1',
defaultModel: 'glm-5.2',
requiredEnvVars: ['OPENAI_API_KEY'],
setup: {
requiresAuth: true,
Expand All @@ -18,7 +18,7 @@ export default defineVendor({
preserveReasoningContent: true,
requireReasoningContentOnAssistantMessages: true,
reasoningContentFallback: '',
thinkingRequestFormat: 'deepseek-compatible',
thinkingRequestFormat: 'zai-compatible',
maxTokensField: 'max_tokens',
removeBodyFields: ['store'],
},
Expand All @@ -44,6 +44,17 @@ export default defineVendor({
catalog: {
source: 'static',
models: [
{
id: 'glm-5.2',
apiName: 'glm-5.2',
label: 'GLM-5.2',
modelDescriptorId: 'glm-5.2',
transportOverrides: {
openaiShim: {
enableToolStreaming: true,
},
},
},
{
id: 'GLM-5.1',
apiName: 'GLM-5.1',
Expand Down
Loading