mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-07-06 22:55:56 +08:00
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:
@@ -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',
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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-]+)?',
|
||||
|
||||
@@ -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') {
|
||||
|
||||
Reference in New Issue
Block a user