@@ -10,18 +10,21 @@ import { NativeToolCallParser } from "../../core/assistant-message/NativeToolCal
1010import { TagMatcher } from "../../utils/tag-matcher"
1111
1212import { convertToOpenAiMessages } from "../transform/openai-format"
13+ import { convertToZAiFormat } from "../transform/zai-format"
1314import { ApiStream } from "../transform/stream"
1415
1516import { BaseProvider } from "./base-provider"
1617import type { SingleCompletionHandler , ApiHandlerCreateMessageMetadata } from "../index"
1718import { getModelsFromCache } from "./fetchers/modelCache"
1819import { getApiRequestTimeout } from "./utils/timeout-config"
1920import { handleOpenAIError } from "./utils/openai-error-handler"
21+ import { detectGlmModel , logGlmDetection , type GlmModelConfig } from "./utils/glm-model-detection"
2022
2123export class LmStudioHandler extends BaseProvider implements SingleCompletionHandler {
2224 protected options : ApiHandlerOptions
2325 private client : OpenAI
2426 private readonly providerName = "LM Studio"
27+ private glmConfig : GlmModelConfig | null = null
2528
2629 constructor ( options : ApiHandlerOptions ) {
2730 super ( )
@@ -35,16 +38,37 @@ export class LmStudioHandler extends BaseProvider implements SingleCompletionHan
3538 apiKey : apiKey ,
3639 timeout : getApiRequestTimeout ( ) ,
3740 } )
41+
42+ // Detect GLM model on construction if model ID is available
43+ const modelId = this . options . lmStudioModelId || ""
44+ if ( modelId ) {
45+ this . glmConfig = detectGlmModel ( modelId )
46+ logGlmDetection ( this . providerName , modelId , this . glmConfig )
47+ }
3848 }
3949
4050 override async * createMessage (
4151 systemPrompt : string ,
4252 messages : Anthropic . Messages . MessageParam [ ] ,
4353 metadata ?: ApiHandlerCreateMessageMetadata ,
4454 ) : ApiStream {
55+ const model = this . getModel ( )
56+
57+ // Re-detect GLM model if not already done or if model ID changed
58+ if ( ! this . glmConfig || this . glmConfig . originalModelId !== model . id ) {
59+ this . glmConfig = detectGlmModel ( model . id )
60+ logGlmDetection ( this . providerName , model . id , this . glmConfig )
61+ }
62+
63+ // Convert messages based on whether this is a GLM model
64+ // GLM models benefit from mergeToolResultText to prevent reasoning_content loss
65+ const convertedMessages = this . glmConfig . isGlmModel
66+ ? convertToZAiFormat ( messages , { mergeToolResultText : this . glmConfig . mergeToolResultText } )
67+ : convertToOpenAiMessages ( messages )
68+
4569 const openAiMessages : OpenAI . Chat . ChatCompletionMessageParam [ ] = [
4670 { role : "system" , content : systemPrompt } ,
47- ...convertToOpenAiMessages ( messages ) ,
71+ ...convertedMessages ,
4872 ]
4973
5074 // -------------------------
@@ -83,20 +107,37 @@ export class LmStudioHandler extends BaseProvider implements SingleCompletionHan
83107 let assistantText = ""
84108
85109 try {
110+ // Determine parallel_tool_calls setting
111+ // Disable for GLM models as they may not support it properly
112+ let parallelToolCalls : boolean
113+ if ( this . glmConfig . isGlmModel && this . glmConfig . disableParallelToolCalls ) {
114+ parallelToolCalls = false
115+ console . log ( `[${ this . providerName } ] parallel_tool_calls disabled for GLM model` )
116+ } else {
117+ parallelToolCalls = metadata ?. parallelToolCalls ?? true
118+ }
119+
86120 const params : OpenAI . Chat . ChatCompletionCreateParamsStreaming & { draft_model ?: string } = {
87- model : this . getModel ( ) . id ,
121+ model : model . id ,
88122 messages : openAiMessages ,
89123 temperature : this . options . modelTemperature ?? LMSTUDIO_DEFAULT_TEMPERATURE ,
90124 stream : true ,
91125 tools : this . convertToolsForOpenAI ( metadata ?. tools ) ,
92126 tool_choice : metadata ?. tool_choice ,
93- parallel_tool_calls : metadata ?. parallelToolCalls ?? true ,
127+ parallel_tool_calls : parallelToolCalls ,
94128 }
95129
96130 if ( this . options . lmStudioSpeculativeDecodingEnabled && this . options . lmStudioDraftModelId ) {
97131 params . draft_model = this . options . lmStudioDraftModelId
98132 }
99133
134+ // For GLM-4.7 models with thinking support, add thinking parameter
135+ if ( this . glmConfig . isGlmModel && this . glmConfig . supportsThinking ) {
136+ const useReasoning = this . options . enableReasoningEffort !== false // Default to enabled for GLM-4.7
137+ ; ( params as any ) . thinking = useReasoning ? { type : "enabled" } : { type : "disabled" }
138+ console . log ( `[${ this . providerName } ] GLM-4.7 thinking mode: ${ useReasoning ? "enabled" : "disabled" } ` )
139+ }
140+
100141 let results
101142 try {
102143 results = await this . client . chat . completions . create ( params )
@@ -124,6 +165,19 @@ export class LmStudioHandler extends BaseProvider implements SingleCompletionHan
124165 }
125166 }
126167
168+ // Handle reasoning_content for GLM models (similar to Z.ai)
169+ if ( delta ) {
170+ for ( const key of [ "reasoning_content" , "reasoning" ] as const ) {
171+ if ( key in delta ) {
172+ const reasoning_content = ( ( delta as any ) [ key ] as string | undefined ) || ""
173+ if ( reasoning_content ?. trim ( ) ) {
174+ yield { type : "reasoning" , text : reasoning_content }
175+ }
176+ break
177+ }
178+ }
179+ }
180+
127181 // Handle tool calls in stream - emit partial chunks for NativeToolCallParser
128182 if ( delta ?. tool_calls ) {
129183 for ( const toolCall of delta . tool_calls ) {
0 commit comments