@yeaft/webchat-agent 0.1.416 → 0.1.418
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.
package/package.json
CHANGED
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
* Response: delta.tool_calls[i].function.arguments (JSON string) → accumulate → JSON.parse → UnifiedToolCall
|
|
13
13
|
* Result: UnifiedToolResult → { role: "tool", tool_call_id, content }
|
|
14
14
|
* Finish: "tool_calls" → "tool_use", "stop" → "end_turn", "length" → "max_tokens"
|
|
15
|
+
*
|
|
16
|
+
* max_tokens strategy (based on model ID):
|
|
17
|
+
* OpenAI models (gpt-*, o1*, o3*, o4*) use "max_completion_tokens" (new standard).
|
|
18
|
+
* All other models (DeepSeek, Gemini, etc.) use "max_tokens" (legacy/compat).
|
|
19
|
+
* CopilotProxy transparently forwards whatever the client sends.
|
|
20
|
+
* Callers can override via extraBody to pass any parameter directly.
|
|
15
21
|
*/
|
|
16
22
|
|
|
17
23
|
import {
|
|
@@ -23,6 +29,26 @@ import {
|
|
|
23
29
|
LLMAbortError,
|
|
24
30
|
} from './adapter.js';
|
|
25
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Check if a model ID is an OpenAI model that supports max_completion_tokens.
|
|
34
|
+
* OpenAI introduced max_completion_tokens with o1 and made it standard for
|
|
35
|
+
* GPT-4.1+, o-series, and GPT-5+. Other OpenAI-compatible APIs (DeepSeek,
|
|
36
|
+
* Gemini, Ollama) still only understand max_tokens.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} model — The model ID (e.g. "gpt-5", "deepseek-chat", "o3")
|
|
39
|
+
* @returns {boolean} true = use max_completion_tokens
|
|
40
|
+
*/
|
|
41
|
+
export function useNewMaxTokensParam(model) {
|
|
42
|
+
if (!model) return false;
|
|
43
|
+
const m = model.toLowerCase();
|
|
44
|
+
// GPT-4.1+ and GPT-5+
|
|
45
|
+
if (m.startsWith('gpt-')) return true;
|
|
46
|
+
// o-series reasoning models (o1, o3, o4-mini, etc.)
|
|
47
|
+
if (/^o\d/.test(m)) return true;
|
|
48
|
+
// Everything else (deepseek-*, gemini-*, claude-*, custom models): legacy
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
26
52
|
/**
|
|
27
53
|
* ChatCompletionsAdapter — Talks to OpenAI Chat Completions API and compatibles.
|
|
28
54
|
*/
|
|
@@ -39,6 +65,24 @@ export class ChatCompletionsAdapter extends LLMAdapter {
|
|
|
39
65
|
this.#baseUrl = baseUrl.replace(/\/+$/, ''); // strip trailing slash
|
|
40
66
|
}
|
|
41
67
|
|
|
68
|
+
/** Expose baseUrl for testing. */
|
|
69
|
+
get baseUrl() { return this.#baseUrl; }
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Build the max-tokens portion of the request body.
|
|
73
|
+
* Uses max_completion_tokens for OpenAI models, max_tokens for others.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} model
|
|
76
|
+
* @param {number} maxTokens
|
|
77
|
+
* @returns {object}
|
|
78
|
+
*/
|
|
79
|
+
#maxTokensBody(model, maxTokens) {
|
|
80
|
+
if (useNewMaxTokensParam(model)) {
|
|
81
|
+
return { max_completion_tokens: maxTokens };
|
|
82
|
+
}
|
|
83
|
+
return { max_tokens: maxTokens };
|
|
84
|
+
}
|
|
85
|
+
|
|
42
86
|
/**
|
|
43
87
|
* Translate UnifiedToolDef[] → Chat Completions tool format.
|
|
44
88
|
* @param {import('./adapter.js').UnifiedToolDef[]} tools
|
|
@@ -140,16 +184,16 @@ export class ChatCompletionsAdapter extends LLMAdapter {
|
|
|
140
184
|
}
|
|
141
185
|
|
|
142
186
|
/**
|
|
143
|
-
* @param {{ model: string, system: string, messages: import('./adapter.js').UnifiedMessage[], tools?: import('./adapter.js').UnifiedToolDef[], maxTokens?: number, signal?: AbortSignal }} params
|
|
187
|
+
* @param {{ model: string, system: string, messages: import('./adapter.js').UnifiedMessage[], tools?: import('./adapter.js').UnifiedToolDef[], maxTokens?: number, extraBody?: object, signal?: AbortSignal }} params
|
|
144
188
|
* @returns {AsyncGenerator<import('./adapter.js').StreamEvent>}
|
|
145
189
|
*/
|
|
146
|
-
async *stream({ model, system, messages, tools, maxTokens = 16384, signal }) {
|
|
190
|
+
async *stream({ model, system, messages, tools, maxTokens = 16384, extraBody, signal }) {
|
|
147
191
|
if (signal?.aborted) throw new LLMAbortError();
|
|
148
192
|
|
|
149
193
|
const body = {
|
|
150
194
|
model,
|
|
151
195
|
messages: this.#translateMessages(system, messages),
|
|
152
|
-
|
|
196
|
+
...this.#maxTokensBody(model, maxTokens),
|
|
153
197
|
stream: true,
|
|
154
198
|
stream_options: { include_usage: true },
|
|
155
199
|
};
|
|
@@ -157,6 +201,9 @@ export class ChatCompletionsAdapter extends LLMAdapter {
|
|
|
157
201
|
const translatedTools = this.#translateTools(tools);
|
|
158
202
|
if (translatedTools) body.tools = translatedTools;
|
|
159
203
|
|
|
204
|
+
// extraBody allows callers to pass through any additional/override parameters
|
|
205
|
+
if (extraBody) Object.assign(body, extraBody);
|
|
206
|
+
|
|
160
207
|
const response = await fetch(`${this.#baseUrl}/chat/completions`, {
|
|
161
208
|
method: 'POST',
|
|
162
209
|
headers: {
|
|
@@ -277,15 +324,18 @@ export class ChatCompletionsAdapter extends LLMAdapter {
|
|
|
277
324
|
/**
|
|
278
325
|
* Non-streaming call for side queries.
|
|
279
326
|
*/
|
|
280
|
-
async call({ model, system, messages, maxTokens = 4096, signal }) {
|
|
327
|
+
async call({ model, system, messages, maxTokens = 4096, extraBody, signal }) {
|
|
281
328
|
if (signal?.aborted) throw new LLMAbortError();
|
|
282
329
|
|
|
283
330
|
const body = {
|
|
284
331
|
model,
|
|
285
332
|
messages: this.#translateMessages(system, messages),
|
|
286
|
-
|
|
333
|
+
...this.#maxTokensBody(model, maxTokens),
|
|
287
334
|
};
|
|
288
335
|
|
|
336
|
+
// extraBody allows callers to pass through any additional/override parameters
|
|
337
|
+
if (extraBody) Object.assign(body, extraBody);
|
|
338
|
+
|
|
289
339
|
const response = await fetch(`${this.#baseUrl}/chat/completions`, {
|
|
290
340
|
method: 'POST',
|
|
291
341
|
headers: {
|