@adminforth/completion-adapter-openai-responses 2.0.24 → 2.0.26
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/README.md +13 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +105 -20
- package/dist/types.d.ts +20 -0
- package/index.ts +147 -20
- package/package.json +1 -1
- package/types.ts +24 -1
package/README.md
CHANGED
|
@@ -24,6 +24,19 @@ const adapter = new CompletionAdapterOpenAIResponses({
|
|
|
24
24
|
});
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
OpenAI-compatible providers can be used by overriding the base URL:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
const adapter = new CompletionAdapterOpenAIResponses({
|
|
31
|
+
openAiApiKey: process.env.OVH_AI_ENDPOINTS_ACCESS_TOKEN as string,
|
|
32
|
+
baseUrl: "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1",
|
|
33
|
+
model: "gpt-oss-20b",
|
|
34
|
+
extraRequestBodyParameters: {
|
|
35
|
+
store: false,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
27
40
|
The adapter supports:
|
|
28
41
|
|
|
29
42
|
- regular text completion
|
package/dist/index.d.ts
CHANGED
|
@@ -19,6 +19,15 @@ export default class CompletionAdapterOpenAIResponses implements CompletionAdapt
|
|
|
19
19
|
constructor(options: AdapterOptions);
|
|
20
20
|
validate(): void;
|
|
21
21
|
measureTokensCount(content: string): number;
|
|
22
|
+
private getConfiguredBaseUrl;
|
|
23
|
+
private shouldUseComplitionApi;
|
|
24
|
+
private shouldDumpRawRequest;
|
|
25
|
+
private getClientConfiguration;
|
|
26
|
+
private createResponsesDebugFetch;
|
|
27
|
+
private getFetchUrl;
|
|
28
|
+
private isResponsesUrl;
|
|
29
|
+
private dumpRawRequest;
|
|
30
|
+
private getResponsesUrl;
|
|
22
31
|
getLangChainAgentSpec(params: {
|
|
23
32
|
maxTokens: number;
|
|
24
33
|
purpose: AgentModelPurpose;
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,8 @@ import { AIMessage } from "@langchain/core/messages";
|
|
|
22
22
|
import { ChatOpenAI } from "@langchain/openai";
|
|
23
23
|
import { createMiddleware } from "langchain";
|
|
24
24
|
import { encoding_for_model } from "tiktoken";
|
|
25
|
+
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
26
|
+
const RAW_REQUEST_LOG_PREFIX = "[CompletionAdapterOpenAIResponses] Raw /responses request";
|
|
25
27
|
function extractOutputText(data) {
|
|
26
28
|
var _a;
|
|
27
29
|
let text = "";
|
|
@@ -97,6 +99,10 @@ function parseSseBlock(block) {
|
|
|
97
99
|
function getAgentReasoningEffort(purpose) {
|
|
98
100
|
return purpose === "summary" ? "minimal" : "low";
|
|
99
101
|
}
|
|
102
|
+
function buildReasoningConfig(params) {
|
|
103
|
+
var _a;
|
|
104
|
+
return Object.assign({ summary: "detailed", effort: params.effort }, ((_a = params.reasoning) !== null && _a !== void 0 ? _a : {}));
|
|
105
|
+
}
|
|
100
106
|
function getTurnKey(context) {
|
|
101
107
|
return `${context.sessionId}:${context.turnId}`;
|
|
102
108
|
}
|
|
@@ -172,6 +178,7 @@ export default class CompletionAdapterOpenAIResponses {
|
|
|
172
178
|
const model = this.options.model || "gpt-5-nano";
|
|
173
179
|
const isStreaming = typeof streamChunkCallback === "function";
|
|
174
180
|
const extra = this.options.extraRequestBodyParameters;
|
|
181
|
+
const _d = extra !== null && extra !== void 0 ? extra : {}, { reasoning: extraReasoning } = _d, extraWithoutReasoning = __rest(_d, ["reasoning"]);
|
|
175
182
|
let openAiTools = undefined;
|
|
176
183
|
if (tools && tools.length > 0) {
|
|
177
184
|
openAiTools = tools.map((tool) => ({
|
|
@@ -179,7 +186,7 @@ export default class CompletionAdapterOpenAIResponses {
|
|
|
179
186
|
name: tool.name,
|
|
180
187
|
description: tool.description,
|
|
181
188
|
parameters: tool.input_schema,
|
|
182
|
-
strict:
|
|
189
|
+
strict: false,
|
|
183
190
|
}));
|
|
184
191
|
}
|
|
185
192
|
const body = Object.assign({ model, input: content, max_output_tokens: requestMaxTokens, stream: isStreaming, text: requestOutputSchema
|
|
@@ -190,17 +197,21 @@ export default class CompletionAdapterOpenAIResponses {
|
|
|
190
197
|
format: {
|
|
191
198
|
type: "text",
|
|
192
199
|
},
|
|
193
|
-
}, reasoning: {
|
|
200
|
+
}, reasoning: Object.assign({}, buildReasoningConfig({
|
|
201
|
+
reasoning: extraReasoning,
|
|
194
202
|
effort: requestReasoningEffort,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
203
|
+
})), tools: openAiTools }, extraWithoutReasoning);
|
|
204
|
+
const serializedBody = JSON.stringify(body);
|
|
205
|
+
if (this.shouldDumpRawRequest()) {
|
|
206
|
+
this.dumpRawRequest(this.getResponsesUrl(), serializedBody);
|
|
207
|
+
}
|
|
208
|
+
const resp = yield fetch(this.getResponsesUrl(), {
|
|
198
209
|
method: "POST",
|
|
199
210
|
headers: {
|
|
200
211
|
"Content-Type": "application/json",
|
|
201
212
|
Authorization: `Bearer ${this.options.openAiApiKey}`,
|
|
202
213
|
},
|
|
203
|
-
body:
|
|
214
|
+
body: serializedBody,
|
|
204
215
|
});
|
|
205
216
|
if (!resp.ok) {
|
|
206
217
|
let errorMessage = `OpenAI request failed with status ${resp.status}`;
|
|
@@ -209,7 +220,7 @@ export default class CompletionAdapterOpenAIResponses {
|
|
|
209
220
|
if ((_a = errorData.error) === null || _a === void 0 ? void 0 : _a.message)
|
|
210
221
|
errorMessage = errorData.error.message;
|
|
211
222
|
}
|
|
212
|
-
catch (
|
|
223
|
+
catch (_e) { }
|
|
213
224
|
return { error: errorMessage };
|
|
214
225
|
}
|
|
215
226
|
if (!isStreaming) {
|
|
@@ -331,7 +342,7 @@ export default class CompletionAdapterOpenAIResponses {
|
|
|
331
342
|
try {
|
|
332
343
|
event = JSON.parse(parsedBlock.data);
|
|
333
344
|
}
|
|
334
|
-
catch (
|
|
345
|
+
catch (_f) {
|
|
335
346
|
continue;
|
|
336
347
|
}
|
|
337
348
|
if ((_c = event === null || event === void 0 ? void 0 : event.error) === null || _c === void 0 ? void 0 : _c.message) {
|
|
@@ -421,22 +432,96 @@ export default class CompletionAdapterOpenAIResponses {
|
|
|
421
432
|
measureTokensCount(content) {
|
|
422
433
|
return this.encoding.encode(content).length;
|
|
423
434
|
}
|
|
435
|
+
getConfiguredBaseUrl() {
|
|
436
|
+
return this.options.baseUrl;
|
|
437
|
+
}
|
|
438
|
+
shouldUseComplitionApi() {
|
|
439
|
+
if (typeof this.options.useComplitionApi === "boolean") {
|
|
440
|
+
return this.options.useComplitionApi;
|
|
441
|
+
}
|
|
442
|
+
return Boolean(this.getConfiguredBaseUrl());
|
|
443
|
+
}
|
|
444
|
+
shouldDumpRawRequest() {
|
|
445
|
+
return this.options.dumpRawRequest === true;
|
|
446
|
+
}
|
|
447
|
+
getClientConfiguration() {
|
|
448
|
+
const configuredBaseUrl = this.getConfiguredBaseUrl();
|
|
449
|
+
const debugFetch = this.shouldDumpRawRequest()
|
|
450
|
+
? this.createResponsesDebugFetch()
|
|
451
|
+
: undefined;
|
|
452
|
+
if (!configuredBaseUrl && !debugFetch) {
|
|
453
|
+
return undefined;
|
|
454
|
+
}
|
|
455
|
+
return Object.assign(Object.assign({}, (configuredBaseUrl ? { baseURL: configuredBaseUrl } : {})), (debugFetch ? { fetch: debugFetch } : {}));
|
|
456
|
+
}
|
|
457
|
+
createResponsesDebugFetch() {
|
|
458
|
+
return (input, init) => __awaiter(this, void 0, void 0, function* () {
|
|
459
|
+
const url = this.getFetchUrl(input);
|
|
460
|
+
if (this.isResponsesUrl(url) && typeof (init === null || init === void 0 ? void 0 : init.body) === "string") {
|
|
461
|
+
this.dumpRawRequest(url, init.body);
|
|
462
|
+
}
|
|
463
|
+
return fetch(input, init);
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
getFetchUrl(input) {
|
|
467
|
+
if (typeof input === "string") {
|
|
468
|
+
return input;
|
|
469
|
+
}
|
|
470
|
+
if (input instanceof URL) {
|
|
471
|
+
return input.toString();
|
|
472
|
+
}
|
|
473
|
+
return input.url;
|
|
474
|
+
}
|
|
475
|
+
isResponsesUrl(url) {
|
|
476
|
+
try {
|
|
477
|
+
return new URL(url).pathname.endsWith("/responses");
|
|
478
|
+
}
|
|
479
|
+
catch (_a) {
|
|
480
|
+
return url.endsWith("/responses") || url.includes("/responses?");
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
dumpRawRequest(url, body) {
|
|
484
|
+
console.info(`${RAW_REQUEST_LOG_PREFIX} ${url}`);
|
|
485
|
+
try {
|
|
486
|
+
console.info(JSON.stringify(JSON.parse(body), null, 2));
|
|
487
|
+
}
|
|
488
|
+
catch (_a) {
|
|
489
|
+
console.info(body);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
getResponsesUrl() {
|
|
493
|
+
const baseUrl = this.getConfiguredBaseUrl() || DEFAULT_OPENAI_BASE_URL;
|
|
494
|
+
const normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
495
|
+
return new URL("responses", normalizedBaseUrl).toString();
|
|
496
|
+
}
|
|
424
497
|
getLangChainAgentSpec(params) {
|
|
425
498
|
const extraRequestBodyParameters = (this.options.extraRequestBodyParameters || {});
|
|
426
499
|
const { reasoning } = extraRequestBodyParameters, modelKwargs = __rest(extraRequestBodyParameters, ["reasoning"]);
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
500
|
+
const configuredBaseUrl = this.getConfiguredBaseUrl();
|
|
501
|
+
const normalizedModelKwargs = Object.assign({}, modelKwargs);
|
|
502
|
+
const clientConfiguration = this.getClientConfiguration();
|
|
503
|
+
const useComplitionApi = this.shouldUseComplitionApi();
|
|
504
|
+
const chatOpenAiOptions = {
|
|
505
|
+
model: this.options.model || "gpt-5-nano",
|
|
506
|
+
apiKey: this.options.openAiApiKey,
|
|
507
|
+
maxTokens: params.maxTokens,
|
|
508
|
+
reasoning: buildReasoningConfig({
|
|
509
|
+
reasoning,
|
|
510
|
+
effort: getAgentReasoningEffort(params.purpose),
|
|
438
511
|
}),
|
|
439
|
-
|
|
512
|
+
modelKwargs: normalizedModelKwargs,
|
|
513
|
+
};
|
|
514
|
+
chatOpenAiOptions.useResponsesApi = !useComplitionApi;
|
|
515
|
+
let supportsResponseContinuation = true;
|
|
516
|
+
if (configuredBaseUrl || useComplitionApi) {
|
|
517
|
+
supportsResponseContinuation = false;
|
|
518
|
+
}
|
|
519
|
+
if (clientConfiguration) {
|
|
520
|
+
chatOpenAiOptions.configuration = clientConfiguration;
|
|
521
|
+
}
|
|
522
|
+
return {
|
|
523
|
+
model: new ChatOpenAI(chatOpenAiOptions),
|
|
524
|
+
middleware: params.purpose === "primary" && supportsResponseContinuation
|
|
440
525
|
? [createOpenAiResponsesContinuationMiddleware()]
|
|
441
526
|
: [],
|
|
442
527
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -5,6 +5,21 @@ export interface AdapterOptions {
|
|
|
5
5
|
* Set openAiApiKey: process.env.OPENAI_API_KEY to access it
|
|
6
6
|
*/
|
|
7
7
|
openAiApiKey: string;
|
|
8
|
+
/**
|
|
9
|
+
* Optional OpenAI-compatible base URL.
|
|
10
|
+
*
|
|
11
|
+
* Example: `https://oai.endpoints.kepler.ai.cloud.ovh.net/v1`
|
|
12
|
+
*/
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Forces LangChain agent mode to use the Chat Completions API instead of the
|
|
16
|
+
* Responses API.
|
|
17
|
+
*
|
|
18
|
+
* When omitted, the adapter keeps the current default behavior:
|
|
19
|
+
* - official OpenAI uses the Responses API
|
|
20
|
+
* - custom `baseUrl` providers use the Chat Completions API
|
|
21
|
+
*/
|
|
22
|
+
useComplitionApi?: boolean;
|
|
8
23
|
/**
|
|
9
24
|
* Model name. Go to https://platform.openai.com/docs/models, select model and copy name.
|
|
10
25
|
* Default is `gpt-5-nano`.
|
|
@@ -14,4 +29,9 @@ export interface AdapterOptions {
|
|
|
14
29
|
* Additional request body parameters to include in the API request.
|
|
15
30
|
*/
|
|
16
31
|
extraRequestBodyParameters?: Record<string, unknown>;
|
|
32
|
+
/**
|
|
33
|
+
* Logs the exact JSON body sent to the OpenAI Responses endpoint.
|
|
34
|
+
* Authorization headers are not logged.
|
|
35
|
+
*/
|
|
36
|
+
dumpRawRequest?: boolean;
|
|
17
37
|
}
|
package/index.ts
CHANGED
|
@@ -61,6 +61,12 @@ type OpenAiResponsesContext = {
|
|
|
61
61
|
turnId: string;
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
+
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
65
|
+
const RAW_REQUEST_LOG_PREFIX = "[CompletionAdapterOpenAIResponses] Raw /responses request";
|
|
66
|
+
|
|
67
|
+
type FetchInput = Parameters<typeof fetch>[0];
|
|
68
|
+
type FetchInit = Parameters<typeof fetch>[1];
|
|
69
|
+
|
|
64
70
|
function extractOutputText(data: OpenAIResponsesSuccess): string {
|
|
65
71
|
let text = "";
|
|
66
72
|
|
|
@@ -147,6 +153,17 @@ function getAgentReasoningEffort(
|
|
|
147
153
|
return purpose === "summary" ? "minimal" : "low";
|
|
148
154
|
}
|
|
149
155
|
|
|
156
|
+
function buildReasoningConfig(params: {
|
|
157
|
+
reasoning?: Record<string, unknown>;
|
|
158
|
+
effort: Exclude<ReasoningEffort, "none"> | ReasoningEffort;
|
|
159
|
+
}) {
|
|
160
|
+
return {
|
|
161
|
+
summary: "detailed",
|
|
162
|
+
effort: params.effort,
|
|
163
|
+
...(params.reasoning ?? {}),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
150
167
|
function getTurnKey(context: OpenAiResponsesContext) {
|
|
151
168
|
return `${context.sessionId}:${context.turnId}`;
|
|
152
169
|
}
|
|
@@ -258,6 +275,86 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
258
275
|
return this.encoding.encode(content).length;
|
|
259
276
|
}
|
|
260
277
|
|
|
278
|
+
private getConfiguredBaseUrl() {
|
|
279
|
+
return this.options.baseUrl;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private shouldUseComplitionApi() {
|
|
283
|
+
if (typeof this.options.useComplitionApi === "boolean") {
|
|
284
|
+
return this.options.useComplitionApi;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return Boolean(this.getConfiguredBaseUrl());
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private shouldDumpRawRequest() {
|
|
291
|
+
return this.options.dumpRawRequest === true;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private getClientConfiguration() {
|
|
295
|
+
const configuredBaseUrl = this.getConfiguredBaseUrl();
|
|
296
|
+
const debugFetch = this.shouldDumpRawRequest()
|
|
297
|
+
? this.createResponsesDebugFetch()
|
|
298
|
+
: undefined;
|
|
299
|
+
|
|
300
|
+
if (!configuredBaseUrl && !debugFetch) {
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
...(configuredBaseUrl ? { baseURL: configuredBaseUrl } : {}),
|
|
306
|
+
...(debugFetch ? { fetch: debugFetch } : {}),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private createResponsesDebugFetch() {
|
|
311
|
+
return async (input: FetchInput, init?: FetchInit) => {
|
|
312
|
+
const url = this.getFetchUrl(input);
|
|
313
|
+
|
|
314
|
+
if (this.isResponsesUrl(url) && typeof init?.body === "string") {
|
|
315
|
+
this.dumpRawRequest(url, init.body);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return fetch(input, init);
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private getFetchUrl(input: FetchInput) {
|
|
323
|
+
if (typeof input === "string") {
|
|
324
|
+
return input;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (input instanceof URL) {
|
|
328
|
+
return input.toString();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return input.url;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private isResponsesUrl(url: string) {
|
|
335
|
+
try {
|
|
336
|
+
return new URL(url).pathname.endsWith("/responses");
|
|
337
|
+
} catch {
|
|
338
|
+
return url.endsWith("/responses") || url.includes("/responses?");
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private dumpRawRequest(url: string, body: string) {
|
|
343
|
+
console.info(`${RAW_REQUEST_LOG_PREFIX} ${url}`);
|
|
344
|
+
try {
|
|
345
|
+
console.info(JSON.stringify(JSON.parse(body), null, 2));
|
|
346
|
+
} catch {
|
|
347
|
+
console.info(body);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
private getResponsesUrl() {
|
|
352
|
+
const baseUrl = this.getConfiguredBaseUrl() || DEFAULT_OPENAI_BASE_URL;
|
|
353
|
+
const normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
354
|
+
|
|
355
|
+
return new URL("responses", normalizedBaseUrl).toString();
|
|
356
|
+
}
|
|
357
|
+
|
|
261
358
|
getLangChainAgentSpec(params: {
|
|
262
359
|
maxTokens: number;
|
|
263
360
|
purpose: AgentModelPurpose;
|
|
@@ -265,23 +362,41 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
265
362
|
const extraRequestBodyParameters =
|
|
266
363
|
(this.options.extraRequestBodyParameters || {}) as Record<string, unknown> & {
|
|
267
364
|
reasoning?: Record<string, unknown>;
|
|
365
|
+
text?: Record<string, unknown>;
|
|
268
366
|
};
|
|
269
367
|
const { reasoning, ...modelKwargs } = extraRequestBodyParameters;
|
|
368
|
+
const configuredBaseUrl = this.getConfiguredBaseUrl();
|
|
369
|
+
const normalizedModelKwargs = { ...modelKwargs };
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
const clientConfiguration = this.getClientConfiguration();
|
|
373
|
+
const useComplitionApi = this.shouldUseComplitionApi();
|
|
374
|
+
const chatOpenAiOptions: Record<string, unknown> = {
|
|
375
|
+
model: this.options.model || "gpt-5-nano",
|
|
376
|
+
apiKey: this.options.openAiApiKey,
|
|
377
|
+
maxTokens: params.maxTokens,
|
|
378
|
+
reasoning: buildReasoningConfig({
|
|
379
|
+
reasoning,
|
|
380
|
+
effort: getAgentReasoningEffort(params.purpose),
|
|
381
|
+
}),
|
|
382
|
+
modelKwargs: normalizedModelKwargs,
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
chatOpenAiOptions.useResponsesApi = !useComplitionApi;
|
|
386
|
+
|
|
387
|
+
let supportsResponseContinuation = true;
|
|
388
|
+
if (configuredBaseUrl || useComplitionApi) {
|
|
389
|
+
supportsResponseContinuation = false;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (clientConfiguration) {
|
|
393
|
+
chatOpenAiOptions.configuration = clientConfiguration;
|
|
394
|
+
}
|
|
270
395
|
|
|
271
396
|
return {
|
|
272
|
-
model: new ChatOpenAI(
|
|
273
|
-
model: this.options.model || "gpt-5-nano",
|
|
274
|
-
apiKey: this.options.openAiApiKey,
|
|
275
|
-
useResponsesApi: true,
|
|
276
|
-
maxTokens: params.maxTokens,
|
|
277
|
-
reasoning: reasoning ?? {
|
|
278
|
-
effort: getAgentReasoningEffort(params.purpose),
|
|
279
|
-
summary: "auto",
|
|
280
|
-
},
|
|
281
|
-
modelKwargs,
|
|
282
|
-
} as any),
|
|
397
|
+
model: new ChatOpenAI(chatOpenAiOptions as any),
|
|
283
398
|
middleware:
|
|
284
|
-
params.purpose === "primary"
|
|
399
|
+
params.purpose === "primary" && supportsResponseContinuation
|
|
285
400
|
? [createOpenAiResponsesContinuationMiddleware()]
|
|
286
401
|
: [],
|
|
287
402
|
};
|
|
@@ -323,7 +438,11 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
323
438
|
} = request;
|
|
324
439
|
const model = this.options.model || "gpt-5-nano";
|
|
325
440
|
const isStreaming = typeof streamChunkCallback === "function";
|
|
326
|
-
const extra =
|
|
441
|
+
const extra =
|
|
442
|
+
this.options.extraRequestBodyParameters as
|
|
443
|
+
| (Record<string, unknown> & { reasoning?: Record<string, unknown> })
|
|
444
|
+
| undefined;
|
|
445
|
+
const { reasoning: extraReasoning, ...extraWithoutReasoning } = extra ?? {};
|
|
327
446
|
let openAiTools: OpenAITool[] | undefined = undefined;
|
|
328
447
|
if (tools && tools.length > 0) {
|
|
329
448
|
openAiTools = tools.map((tool) => ({
|
|
@@ -331,7 +450,7 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
331
450
|
name: tool.name,
|
|
332
451
|
description: tool.description,
|
|
333
452
|
parameters: tool.input_schema,
|
|
334
|
-
strict:
|
|
453
|
+
strict: false,
|
|
335
454
|
}));
|
|
336
455
|
}
|
|
337
456
|
|
|
@@ -353,20 +472,28 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
353
472
|
},
|
|
354
473
|
},
|
|
355
474
|
reasoning: {
|
|
356
|
-
|
|
357
|
-
|
|
475
|
+
...buildReasoningConfig({
|
|
476
|
+
reasoning: extraReasoning,
|
|
477
|
+
effort: requestReasoningEffort,
|
|
478
|
+
}),
|
|
358
479
|
},
|
|
359
480
|
tools: openAiTools,
|
|
360
|
-
...
|
|
481
|
+
...extraWithoutReasoning,
|
|
361
482
|
} as ResponseCreateBody;
|
|
362
483
|
|
|
363
|
-
const
|
|
484
|
+
const serializedBody = JSON.stringify(body);
|
|
485
|
+
|
|
486
|
+
if (this.shouldDumpRawRequest()) {
|
|
487
|
+
this.dumpRawRequest(this.getResponsesUrl(), serializedBody);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const resp = await fetch(this.getResponsesUrl(), {
|
|
364
491
|
method: "POST",
|
|
365
492
|
headers: {
|
|
366
493
|
"Content-Type": "application/json",
|
|
367
494
|
Authorization: `Bearer ${this.options.openAiApiKey}`,
|
|
368
495
|
},
|
|
369
|
-
body:
|
|
496
|
+
body: serializedBody,
|
|
370
497
|
});
|
|
371
498
|
|
|
372
499
|
if (!resp.ok) {
|
|
@@ -585,4 +712,4 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
585
712
|
reader.releaseLock();
|
|
586
713
|
}
|
|
587
714
|
};
|
|
588
|
-
}
|
|
715
|
+
}
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -6,6 +6,23 @@ export interface AdapterOptions {
|
|
|
6
6
|
*/
|
|
7
7
|
openAiApiKey: string;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Optional OpenAI-compatible base URL.
|
|
11
|
+
*
|
|
12
|
+
* Example: `https://oai.endpoints.kepler.ai.cloud.ovh.net/v1`
|
|
13
|
+
*/
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Forces LangChain agent mode to use the Chat Completions API instead of the
|
|
18
|
+
* Responses API.
|
|
19
|
+
*
|
|
20
|
+
* When omitted, the adapter keeps the current default behavior:
|
|
21
|
+
* - official OpenAI uses the Responses API
|
|
22
|
+
* - custom `baseUrl` providers use the Chat Completions API
|
|
23
|
+
*/
|
|
24
|
+
useComplitionApi?: boolean;
|
|
25
|
+
|
|
9
26
|
/**
|
|
10
27
|
* Model name. Go to https://platform.openai.com/docs/models, select model and copy name.
|
|
11
28
|
* Default is `gpt-5-nano`.
|
|
@@ -16,4 +33,10 @@ export interface AdapterOptions {
|
|
|
16
33
|
* Additional request body parameters to include in the API request.
|
|
17
34
|
*/
|
|
18
35
|
extraRequestBodyParameters?: Record<string, unknown>;
|
|
19
|
-
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Logs the exact JSON body sent to the OpenAI Responses endpoint.
|
|
39
|
+
* Authorization headers are not logged.
|
|
40
|
+
*/
|
|
41
|
+
dumpRawRequest?: boolean;
|
|
42
|
+
}
|