@@ -7,13 +7,15 @@ import { type ApiHandlerOptions, getModelMaxOutputTokens } from "../../shared/ap
77import { TagMatcher } from "../../utils/tag-matcher"
88import { ApiStream , ApiStreamUsageChunk } from "../transform/stream"
99import { convertToOpenAiMessages } from "../transform/openai-format"
10+ import { convertToZAiFormat } from "../transform/zai-format"
1011
1112import type { SingleCompletionHandler , ApiHandlerCreateMessageMetadata } from "../index"
1213import { DEFAULT_HEADERS } from "./constants"
1314import { BaseProvider } from "./base-provider"
1415import { handleOpenAIError } from "./utils/openai-error-handler"
1516import { calculateApiCostOpenAI } from "../../shared/cost"
1617import { getApiRequestTimeout } from "./utils/timeout-config"
18+ import { detectGlmModel , logGlmDetection , type GlmModelConfig } from "./utils/glm-model-detection"
1719
1820type BaseOpenAiCompatibleProviderOptions < ModelName extends string > = ApiHandlerOptions & {
1921 providerName : string
@@ -36,6 +38,7 @@ export abstract class BaseOpenAiCompatibleProvider<ModelName extends string>
3638 protected readonly options : ApiHandlerOptions
3739
3840 protected client : OpenAI
41+ protected glmConfig : GlmModelConfig | null = null
3942
4043 constructor ( {
4144 providerName,
@@ -65,6 +68,13 @@ export abstract class BaseOpenAiCompatibleProvider<ModelName extends string>
6568 defaultHeaders : DEFAULT_HEADERS ,
6669 timeout : getApiRequestTimeout ( ) ,
6770 } )
71+
72+ // Detect GLM model on construction if model ID is available
73+ const modelId = this . options . apiModelId || ""
74+ if ( modelId ) {
75+ this . glmConfig = detectGlmModel ( modelId )
76+ logGlmDetection ( this . providerName , modelId , this . glmConfig )
77+ }
6878 }
6979
7080 protected createStream (
@@ -75,6 +85,12 @@ export abstract class BaseOpenAiCompatibleProvider<ModelName extends string>
7585 ) {
7686 const { id : model , info } = this . getModel ( )
7787
88+ // Re-detect GLM model if not already done or if model ID changed
89+ if ( ! this . glmConfig || this . glmConfig . originalModelId !== model ) {
90+ this . glmConfig = detectGlmModel ( model )
91+ logGlmDetection ( this . providerName , model , this . glmConfig )
92+ }
93+
7894 // Centralized cap: clamp to 20% of the context window (unless provider-specific exceptions apply)
7995 const max_tokens =
8096 getModelMaxOutputTokens ( {
@@ -86,23 +102,48 @@ export abstract class BaseOpenAiCompatibleProvider<ModelName extends string>
86102
87103 const temperature = this . options . modelTemperature ?? info . defaultTemperature ?? this . defaultTemperature
88104
105+ // Convert messages based on whether this is a GLM model
106+ // GLM models benefit from mergeToolResultText to prevent reasoning_content loss
107+ const convertedMessages = this . glmConfig . isGlmModel
108+ ? convertToZAiFormat ( messages , { mergeToolResultText : this . glmConfig . mergeToolResultText } )
109+ : convertToOpenAiMessages ( messages )
110+
111+ // Determine parallel_tool_calls setting
112+ // Disable for GLM models as they may not support it properly
113+ let parallelToolCalls : boolean
114+ if ( this . glmConfig . isGlmModel && this . glmConfig . disableParallelToolCalls ) {
115+ parallelToolCalls = false
116+ console . log ( `[${ this . providerName } ] parallel_tool_calls disabled for GLM model` )
117+ } else {
118+ parallelToolCalls = metadata ?. parallelToolCalls ?? true
119+ }
120+
89121 const params : OpenAI . Chat . Completions . ChatCompletionCreateParamsStreaming = {
90122 model,
91123 max_tokens,
92124 temperature,
93- messages : [ { role : "system" , content : systemPrompt } , ...convertToOpenAiMessages ( messages ) ] ,
125+ messages : [ { role : "system" , content : systemPrompt } , ...convertedMessages ] ,
94126 stream : true ,
95127 stream_options : { include_usage : true } ,
96128 tools : this . convertToolsForOpenAI ( metadata ?. tools ) ,
97129 tool_choice : metadata ?. tool_choice ,
98- parallel_tool_calls : metadata ?. parallelToolCalls ?? true ,
130+ parallel_tool_calls : parallelToolCalls ,
99131 }
100132
101133 // Add thinking parameter if reasoning is enabled and model supports it
102134 if ( this . options . enableReasoningEffort && info . supportsReasoningBinary ) {
103135 ; ( params as any ) . thinking = { type : "enabled" }
104136 }
105137
138+ // For GLM-4.7 models with thinking support, add thinking parameter
139+ if ( this . glmConfig . isGlmModel && this . glmConfig . supportsThinking ) {
140+ const useReasoning = this . options . enableReasoningEffort !== false // Default to enabled for GLM-4.7
141+ ; ( params as any ) . thinking = useReasoning ? { type : "enabled" } : { type : "disabled" }
142+ console . log (
143+ `[${ this . providerName } ] GLM thinking mode: ${ useReasoning ? "enabled" : "disabled" } for ${ this . glmConfig . displayName } ` ,
144+ )
145+ }
146+
106147 try {
107148 return this . client . chat . completions . create ( params , requestOptions )
108149 } catch ( error ) {
@@ -222,6 +263,12 @@ export abstract class BaseOpenAiCompatibleProvider<ModelName extends string>
222263 async completePrompt ( prompt : string ) : Promise < string > {
223264 const { id : modelId , info : modelInfo } = this . getModel ( )
224265
266+ // Re-detect GLM model if not already done or if model ID changed
267+ if ( ! this . glmConfig || this . glmConfig . originalModelId !== modelId ) {
268+ this . glmConfig = detectGlmModel ( modelId )
269+ logGlmDetection ( this . providerName , modelId , this . glmConfig )
270+ }
271+
225272 const params : OpenAI . Chat . Completions . ChatCompletionCreateParams = {
226273 model : modelId ,
227274 messages : [ { role : "user" , content : prompt } ] ,
@@ -232,6 +279,12 @@ export abstract class BaseOpenAiCompatibleProvider<ModelName extends string>
232279 ; ( params as any ) . thinking = { type : "enabled" }
233280 }
234281
282+ // For GLM-4.7 models with thinking support, add thinking parameter
283+ if ( this . glmConfig . isGlmModel && this . glmConfig . supportsThinking ) {
284+ const useReasoning = this . options . enableReasoningEffort !== false
285+ ; ( params as any ) . thinking = useReasoning ? { type : "enabled" } : { type : "disabled" }
286+ }
287+
235288 try {
236289 const response = await this . client . chat . completions . create ( params )
237290
0 commit comments