feat(zhipu): add GLM-5 model support (#12942)

Add support for Zhipu's new flagship GLM-5 model, which is designed for
Agentic Engineering with SOTA coding and agent capabilities.

Changes:
- Add GLM-5 to model definitions in default.ts
- Add GLM-5 to ZhipuAPIClient listModels()
- Enable thinking token support for GLM-5 in reasoning.ts
- Enable function calling support for GLM-5 in tooluse.ts
- Add GLM-5 to interleaved thinking model regex
- Remove zhipu web search support (provider does not have built-in
search)

GLM-5 features:
- Agentic Engineering oriented
- SOTA coding and agent capabilities
- Deep thinking mode support (thinking parameter)
- Max output: 65536 tokens

Reference: https://docs.bigmodel.cn/cn/guide/models/text/glm-5

### What this PR does

Before this PR:
Cherry Studio did not include Zhipu's GLM-5 model. Users could not
select it from the default model list, and the zhipu provider
incorrectly reported web search support.

After this PR:
Users can select and use the GLM-5 model from the Zhipu provider with
full support for thinking tokens, interleaved thinking, and function
calling. The zhipu provider's web search support is corrected to return
`false` since it does not have built-in search capability.

### Why we need it and why it was done in this way

Zhipu released GLM-5 as their new flagship model focused on Agentic
Engineering. Adding it to Cherry Studio allows users to leverage its
advanced coding and agent capabilities.

The implementation follows the existing patterns for Zhipu models:
- Model definition added to `default.ts` alongside existing GLM models
- `listModels()` updated so the model appears when fetching available
models
- Thinking token and function calling support enabled via existing
config patterns
- The `INTERLEAVED_THINKING_MODEL_REGEX` was extended with
`glm-5(?:.\d+)?(?:-[\w-]+)?` to match glm-5 and potential future
versions (glm-5.0, glm-5.1, glm-5-pro, etc.)

### Breaking changes

The zhipu provider's `isWebSearchModel` now returns `false` for all
models instead of `true` for `glm-4-*` models. This corrects incorrect
behavior since zhipu does not have built-in web search capability.

### Special notes for your reviewer

- The regex `glm-5(?:.\d+)?(?:-[\w-]+)?` intentionally matches future
glm-5.x versions (glm-5.0, glm-5.1) as they are expected to maintain the
same interleaved thinking behavior.
- A `glm-4.7` entry was also added to `default.ts` to keep the model
list current.
- Test cases cover positive matches (glm-5, glm-5-pro, glm-5.0, glm-5.1)
and verify zhipu web search returns false.

### Checklist

- [x] PR: The PR description is expressive enough and will help future
contributors
- [x] Code: [Write code that humans can
understand](https://en.wikiquote.org/wiki/Martin_Fowler#code-for-humans)
and [Keep it simple](https://en.wikipedia.org/wiki/KISS_principle)
- [x] Refactor: You have [left the code cleaner than you found it (Boy
Scout
Rule)](https://learning.oreilly.com/library/view/97-things-every/9780596809515/ch08.html)
- [x] Upgrade: Impact of this change on upgrade flows was considered and
addressed if required
- [ ] Documentation: A [user-guide update](https://docs.cherry-ai.com)
was considered and is present (link) or not required. You want a
user-guide update if it's a user facing feature.

### Release note

```release-note
feat(zhipu): add GLM-5 model support with thinking tokens, interleaved thinking, and function calling
```

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Wike
2026-02-17 12:05:53 +08:00
committed by GitHub
parent 4bc38daae0
commit a6cfe6ba0f
7 changed files with 38 additions and 8 deletions

View File

@@ -66,6 +66,7 @@ export class ZhipuAPIClient extends OpenAIAPIClient {
public async listModels(): Promise<OpenAI.Models.Model[]> {
const models = [
'glm-5',
'glm-4.7',
'glm-4.6',
'glm-4.6v',

View File

@@ -2280,7 +2280,6 @@ describe('isInterleavedThinkingModel', () => {
it('should return false for other glm models', () => {
expect(isInterleavedThinkingModel(createModel({ id: 'glm-3.5' }))).toBe(false)
expect(isInterleavedThinkingModel(createModel({ id: 'glm-5.0' }))).toBe(false)
expect(isInterleavedThinkingModel(createModel({ id: 'glm-zero-preview' }))).toBe(false)
})
@@ -2288,6 +2287,20 @@ describe('isInterleavedThinkingModel', () => {
expect(isInterleavedThinkingModel(createModel({ id: 'GLM-4.5' }))).toBe(true)
expect(isInterleavedThinkingModel(createModel({ id: 'Glm-4.6-Pro' }))).toBe(true)
})
it('should return true for glm-5', () => {
expect(isInterleavedThinkingModel(createModel({ id: 'glm-5' }))).toBe(true)
})
it('should return true for glm-5 with suffixes', () => {
expect(isInterleavedThinkingModel(createModel({ id: 'glm-5-pro' }))).toBe(true)
expect(isInterleavedThinkingModel(createModel({ id: 'glm-5-lite' }))).toBe(true)
})
it('should return true for glm-5.x versions (future versions maintain same behavior)', () => {
expect(isInterleavedThinkingModel(createModel({ id: 'glm-5.0' }))).toBe(true)
expect(isInterleavedThinkingModel(createModel({ id: 'glm-5.1' }))).toBe(true)
})
})
describe('Non-matching models', () => {

View File

@@ -219,14 +219,11 @@ describe('websearch helpers', () => {
expect(isWebSearchModel(createModel({ id: 'gemini-2.0-flash-latest' }))).toBe(true)
})
it('evaluates hunyuan/zhipu/dashscope/openrouter/grok providers', () => {
it('evaluates hunyuan/dashscope/openrouter/grok providers', () => {
providerMock.mockReturnValueOnce(createProvider({ id: 'hunyuan' }))
expect(isWebSearchModel(createModel({ id: 'hunyuan-pro' }))).toBe(true)
expect(isWebSearchModel(createModel({ id: 'hunyuan-lite', provider: 'hunyuan' }))).toBe(false)
providerMock.mockReturnValueOnce(createProvider({ id: 'zhipu' }))
expect(isWebSearchModel(createModel({ id: 'glm-4-air' }))).toBe(true)
providerMock.mockReturnValueOnce(createProvider({ id: 'dashscope' }))
expect(isWebSearchModel(createModel({ id: 'qwen-max-latest' }))).toBe(true)
@@ -235,6 +232,12 @@ describe('websearch helpers', () => {
providerMock.mockReturnValueOnce(createProvider({ id: 'grok' }))
expect(isWebSearchModel(createModel({ id: 'grok-2' }))).toBe(true)
// zhipu provider does not have built-in search capability
providerMock.mockReturnValueOnce(createProvider({ id: 'zhipu' }))
expect(isWebSearchModel(createModel({ id: 'glm-4-air' }))).toBe(false)
providerMock.mockReturnValueOnce(createProvider({ id: 'zhipu' }))
expect(isWebSearchModel(createModel({ id: 'glm-5' }))).toBe(false)
})
})

View File

@@ -611,6 +611,18 @@ export const SYSTEM_MODELS: Record<SystemProviderId | 'defaultModel', Model[]> =
{ id: 'yi-vision-v2', name: 'Yi Vision v2', provider: 'yi', group: 'yi-vision', owned_by: '01.ai' }
],
zhipu: [
{
id: 'glm-5',
provider: 'zhipu',
name: 'GLM-5',
group: 'GLM-5'
},
{
id: 'glm-4.7',
provider: 'zhipu',
name: 'GLM-4.7',
group: 'GLM-4.7'
},
{
id: 'glm-4.5-flash',
provider: 'zhipu',

View File

@@ -567,7 +567,7 @@ export const isSupportedReasoningEffortPerplexityModel = (model: Model): boolean
export const isSupportedThinkingTokenZhipuModel = (model: Model): boolean => {
const modelId = getLowerBaseModelName(model.id, '/')
return ['glm-4.5', 'glm-4.6', 'glm-4.7'].some((id) => modelId.includes(id))
return ['glm-5', 'glm-4.5', 'glm-4.6', 'glm-4.7'].some((id) => modelId.includes(id))
}
export const isSupportedThinkingTokenMiMoModel = (model: Model): boolean => {
@@ -803,7 +803,7 @@ export const isFixedReasoningModel = (model: Model) =>
// https://docs.z.ai/guides/capabilities/thinking-mode
// https://platform.moonshot.cn/docs/guide/use-kimi-k2-thinking-model#%E5%A4%9A%E6%AD%A5%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8
const INTERLEAVED_THINKING_MODEL_REGEX =
/minimax-m2(.(\d+))?(?:-[\w-]+)?|mimo-v2-flash|glm-4.(\d+)(?:-[\w-]+)?|kimi-k2-thinking?|kimi-k2.5$/i
/minimax-m2(.(\d+))?(?:-[\w-]+)?|mimo-v2-flash|glm-5(?:.\d+)?(?:-[\w-]+)?|glm-4.(\d+)(?:-[\w-]+)?|kimi-k2-thinking?|kimi-k2.5$/i
/**
* Determines whether the given model supports interleaved thinking.

View File

@@ -23,6 +23,7 @@ export const FUNCTION_CALLING_MODELS = [
'glm-4(?:-[\\w-]+)?',
'glm-4.5(?:-[\\w-]+)?',
'glm-4.7(?:-[\\w-]+)?',
'glm-5(?:-[\\w-]+)?',
'learnlm(?:-[\\w-]+)?',
'gemini(?:-[\\w-]+)?', // 提前排除了gemini的嵌入模型
'grok-3(?:-[\\w-]+)?',

View File

@@ -103,7 +103,7 @@ export function isWebSearchModel(model: Model): boolean {
}
if (provider.id === 'zhipu') {
return modelId?.startsWith('glm-4-')
return false
}
if (provider.id === 'dashscope') {