copilot-custom-endpoint 1.3.10 → 1.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@
11
11
  | Tool calling | ✅ Yes |
12
12
  | Context | 1M |
13
13
  | Required `requestBody` (direct) | `enable_thinking: false` |
14
- | Required `requestBody` (proxy) | none — proxy injects based on `tools` presence |
14
+ | Required `requestBody` (proxy) | none — proxy injects based on tool activity in the conversation |
15
15
  | Endpoint | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions` |
16
16
  | Proxy endpoint | `http://127.0.0.1:3458/v1/chat/completions` |
17
17
 
@@ -165,12 +165,16 @@ All can be set in a `.env` file at the repo root (both proxies `import 'dotenv/c
165
165
 
166
166
  #### Proxy request rewriting rules
167
167
 
168
- | Condition | Action |
169
- | ---------------------------------------- | ----------------------------------------------------------- |
170
- | `body.tools` is a non-empty array | Set `body.enable_thinking = false` |
171
- | `body.tools` is missing, empty, or falsy | Delete `body.enable_thinking` (let model default to `true`) |
168
+ The proxy detects active tool use by examining the conversation state, not just the `tools` array:
169
+
170
+ | Condition | Action |
171
+ | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
172
+ | A `"tool"`-role message exists in the conversation **or** `tool_choice` is set to a non-default value | Set `body.enable_thinking = false` |
173
+ | No tool-role messages and no non-default `tool_choice` (plain chat) | Delete `body.enable_thinking` (let model default to `true`) |
172
174
 
173
175
  > **Why delete rather than set `true`?** Omitting the key lets Qwen use its built-in default (`true`). Deletion is closer to "don't interfere."
176
+ >
177
+ > **Why not check `body.tools`?** The proxy checks for tool _activity_ — tool results in the message history or an explicit `tool_choice` directive — rather than the mere presence of a tools array. This correctly handles tool-enabled conversations even when the client sends `tools` in an earlier request but omits it from subsequent turns.
174
178
 
175
179
  ### API key
176
180
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "copilot-custom-endpoint",
3
- "version": "1.3.10",
3
+ "version": "1.3.11",
4
4
  "description": "Local proxies for VS Code Copilot custom endpoints — Kimi K2 & Qwen 3.x",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -55,4 +55,4 @@
55
55
  "dependencies": {
56
56
  "dotenv": "^17.4.2"
57
57
  }
58
- }
58
+ }
@@ -104,7 +104,18 @@ function rewriteKimi(payload) {
104
104
  const incomingTemperature = payload.temperature
105
105
  const incomingTopP = payload.top_p
106
106
  const incomingThinkingType = payload?.thinking?.type
107
- const hasTools = Array.isArray(payload.tools) && payload.tools.length > 0
107
+
108
+ // Determine if a tool is actually being invoked:
109
+ // - tool_choice is set and not "none"
110
+ // - OR there is a "tool" role message in the conversation
111
+ const messages = Array.isArray(payload.messages) ? payload.messages : []
112
+ const hasToolRole = messages.some((message) => message?.role === 'tool')
113
+ const toolChoice = payload.tool_choice
114
+ const hasActiveToolCall =
115
+ hasToolRole ||
116
+ (toolChoice !== undefined && toolChoice !== 'none' && toolChoice !== null)
117
+ const hasTools = hasActiveToolCall
118
+
108
119
  const useNonThinkingMode = disableThinkingWithTools && hasTools
109
120
  const rewrittenTemperature = useNonThinkingMode
110
121
  ? forcedNonThinkingTemperature
@@ -133,7 +144,8 @@ function rewriteKimi(payload) {
133
144
 
134
145
  const summary = summarizePayload(payload, hasTools, rewriteInfo)
135
146
 
136
- const consoleMsg = `temperature ${String(incomingTemperature)} -> ${String(rewrittenTemperature)}, top_p ${String(incomingTopP)} -> ${String(forcedTopP)}, thinking ${String(incomingThinkingType)} -> ${String(rewrittenThinkingType)}`
147
+ const modeTag = hasTools ? '[tools]' : '[chat]'
148
+ const consoleMsg = `${modeTag} temperature ${String(incomingTemperature)} -> ${String(rewrittenTemperature)}, top_p ${String(incomingTopP)} -> ${String(forcedTopP)}, thinking ${String(incomingThinkingType)} -> ${String(rewrittenThinkingType)}`
137
149
 
138
150
  // Clean up internal key before forwarding
139
151
  delete payload.__incomingThinkingType
@@ -72,7 +72,16 @@ function summarizePayload(payload, hasTools, rewriteInfo) {
72
72
  }
73
73
 
74
74
  function rewriteQwen(payload) {
75
- const hasTools = Array.isArray(payload.tools) && payload.tools.length > 0
75
+ // Determine if a tool is actually being invoked:
76
+ // - tool_choice is set and not "none"
77
+ // - OR there is a "tool" role message in the conversation
78
+ const messages = Array.isArray(payload.messages) ? payload.messages : []
79
+ const hasToolRole = messages.some((message) => message?.role === 'tool')
80
+ const toolChoice = payload.tool_choice
81
+ const hasActiveToolCall =
82
+ hasToolRole ||
83
+ (toolChoice !== undefined && toolChoice !== 'none' && toolChoice !== null)
84
+ const hasTools = hasActiveToolCall
76
85
  const incomingEnableThinking = payload.enable_thinking
77
86
 
78
87
  if (disableThinkingWithTools && hasTools) {
@@ -91,7 +100,8 @@ function rewriteQwen(payload) {
91
100
  rewrittenEnableThinking
92
101
  })
93
102
 
94
- const consoleMsg = `tools=${String(hasTools)} enable_thinking=${String(incomingEnableThinking)} -> ${
103
+ const modeTag = hasTools ? '[tools]' : '[chat]'
104
+ const consoleMsg = `${modeTag} enable_thinking=${String(incomingEnableThinking)} -> ${
95
105
  hasTools && disableThinkingWithTools ? 'false' : '<deleted>'
96
106
  }, model=${payload.model ?? '?'}`
97
107