@cognipeer/agent-sdk 0.10.4 → 0.10.5
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/dist/index.cjs +276 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -1
- package/dist/index.d.ts +47 -1
- package/dist/index.mjs +276 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -3874,6 +3874,37 @@ type OpenAIProviderConfig = {
|
|
|
3874
3874
|
* where it matters most: a self-hosted model is named by whoever served it.
|
|
3875
3875
|
*/
|
|
3876
3876
|
responsesApi?: "auto" | "never" | "always";
|
|
3877
|
+
/**
|
|
3878
|
+
* Models that go to the Responses API, named explicitly. Opt-in; unset by
|
|
3879
|
+
* default, and nothing here is inferred from a model name.
|
|
3880
|
+
*
|
|
3881
|
+
* A string matches the request's model (or `defaultModel`) exactly; a RegExp
|
|
3882
|
+
* is tested against it. A match routes the request to Responses whether or
|
|
3883
|
+
* not it carries `reasoning` — the case this exists for is a model that
|
|
3884
|
+
* reasons by default and rejects function tools on Chat Completions, where
|
|
3885
|
+
* sending no reasoning field does not help. `responsesApi: "never"` still
|
|
3886
|
+
* wins; under `"always"` the routing is unchanged.
|
|
3887
|
+
*
|
|
3888
|
+
* Requests for a matched model also get, on the Responses body only:
|
|
3889
|
+
* - Chat Completions spellings arriving through `extra`, which /responses
|
|
3890
|
+
* rejects with a 400, re-spelled with their values unchanged:
|
|
3891
|
+
* `reasoning_effort` → `reasoning.effort` (a request `reasoning` effort
|
|
3892
|
+
* still wins), `max_tokens` / `max_completion_tokens` →
|
|
3893
|
+
* `max_output_tokens`, `response_format` → `text.format`, and
|
|
3894
|
+
* `stream_options.include_usage` dropped.
|
|
3895
|
+
* - real SSE streaming, when `responsesStreaming` is also true.
|
|
3896
|
+
*
|
|
3897
|
+
* Honoured by `OpenAIProvider` itself only; `AzureProvider` and
|
|
3898
|
+
* `OpenAICompatibleProvider` ignore it.
|
|
3899
|
+
*/
|
|
3900
|
+
responsesApiModels?: Array<string | RegExp>;
|
|
3901
|
+
/**
|
|
3902
|
+
* Stream Responses API output as it arrives (text and tool-call argument
|
|
3903
|
+
* deltas) for models in `responsesApiModels`. Default false, which keeps the
|
|
3904
|
+
* existing behaviour: one non-streaming Responses call, emitted as a single
|
|
3905
|
+
* chunk. Has no effect on any other request.
|
|
3906
|
+
*/
|
|
3907
|
+
responsesStreaming?: boolean;
|
|
3877
3908
|
};
|
|
3878
3909
|
type AnthropicProviderConfig = {
|
|
3879
3910
|
provider: "anthropic";
|
|
@@ -5509,7 +5540,11 @@ declare class OpenAIProvider extends BaseProvider {
|
|
|
5509
5540
|
protected readonly defaultModel: string;
|
|
5510
5541
|
protected readonly defaultHeaders: Record<string, string>;
|
|
5511
5542
|
protected readonly responsesApi: "auto" | "never" | "always";
|
|
5543
|
+
private readonly responsesApiModels;
|
|
5544
|
+
private readonly responsesStreaming;
|
|
5512
5545
|
constructor(config: OpenAIProviderConfig);
|
|
5546
|
+
/** True when the caller listed this request's model in `responsesApiModels`. */
|
|
5547
|
+
private isResponsesApiModel;
|
|
5513
5548
|
/** True when this request should go to the Responses API rather than Chat
|
|
5514
5549
|
* Completions — which exposes `reasoning.effort` and reasoning summaries that
|
|
5515
5550
|
* Chat Completions silently drops.
|
|
@@ -5518,12 +5553,23 @@ declare class OpenAIProvider extends BaseProvider {
|
|
|
5518
5553
|
* carries a reasoning config AND the model name looks like an o-series/gpt-5
|
|
5519
5554
|
* model. `never`/`always` let a caller state the answer instead, because the
|
|
5520
5555
|
* name check is a guess and the two APIs differ in far more than reasoning —
|
|
5521
|
-
* see `responsesApi` in OpenAIProviderConfig.
|
|
5556
|
+
* see `responsesApi` in OpenAIProviderConfig.
|
|
5557
|
+
*
|
|
5558
|
+
* A model listed in `responsesApiModels` is the caller stating the answer
|
|
5559
|
+
* for that model alone; `never` still overrides it. */
|
|
5522
5560
|
protected useResponsesApi(request: ChatCompletionRequest): boolean;
|
|
5523
5561
|
complete(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
|
|
5524
5562
|
completeStream(request: ChatCompletionRequest): AsyncGenerator<ChatCompletionChunk, void, unknown>;
|
|
5525
5563
|
protected buildRequestBody(request: ChatCompletionRequest, stream: boolean): Record<string, any>;
|
|
5526
5564
|
protected completeResponses(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
|
|
5565
|
+
/** Real Responses SSE streaming, for models opted in through
|
|
5566
|
+
* `responsesApiModels` with `responsesStreaming` on. Emits the same chunk
|
|
5567
|
+
* shapes as the Chat Completions stream: text deltas as they arrive, a tool
|
|
5568
|
+
* call's id + name when it opens and its argument fragments under that id,
|
|
5569
|
+
* then one final chunk with usage, finish reason and any reasoning summary
|
|
5570
|
+
* (taken from the completed response, exactly as the non-streaming parse
|
|
5571
|
+
* reads it). */
|
|
5572
|
+
private streamResponses;
|
|
5527
5573
|
protected buildResponsesBody(request: ChatCompletionRequest): Record<string, any>;
|
|
5528
5574
|
/** Maps a unified message to a Responses API `input` item. */
|
|
5529
5575
|
protected toResponsesInput(m: UnifiedMessage): Record<string, any>;
|
package/dist/index.d.ts
CHANGED
|
@@ -3874,6 +3874,37 @@ type OpenAIProviderConfig = {
|
|
|
3874
3874
|
* where it matters most: a self-hosted model is named by whoever served it.
|
|
3875
3875
|
*/
|
|
3876
3876
|
responsesApi?: "auto" | "never" | "always";
|
|
3877
|
+
/**
|
|
3878
|
+
* Models that go to the Responses API, named explicitly. Opt-in; unset by
|
|
3879
|
+
* default, and nothing here is inferred from a model name.
|
|
3880
|
+
*
|
|
3881
|
+
* A string matches the request's model (or `defaultModel`) exactly; a RegExp
|
|
3882
|
+
* is tested against it. A match routes the request to Responses whether or
|
|
3883
|
+
* not it carries `reasoning` — the case this exists for is a model that
|
|
3884
|
+
* reasons by default and rejects function tools on Chat Completions, where
|
|
3885
|
+
* sending no reasoning field does not help. `responsesApi: "never"` still
|
|
3886
|
+
* wins; under `"always"` the routing is unchanged.
|
|
3887
|
+
*
|
|
3888
|
+
* Requests for a matched model also get, on the Responses body only:
|
|
3889
|
+
* - Chat Completions spellings arriving through `extra`, which /responses
|
|
3890
|
+
* rejects with a 400, re-spelled with their values unchanged:
|
|
3891
|
+
* `reasoning_effort` → `reasoning.effort` (a request `reasoning` effort
|
|
3892
|
+
* still wins), `max_tokens` / `max_completion_tokens` →
|
|
3893
|
+
* `max_output_tokens`, `response_format` → `text.format`, and
|
|
3894
|
+
* `stream_options.include_usage` dropped.
|
|
3895
|
+
* - real SSE streaming, when `responsesStreaming` is also true.
|
|
3896
|
+
*
|
|
3897
|
+
* Honoured by `OpenAIProvider` itself only; `AzureProvider` and
|
|
3898
|
+
* `OpenAICompatibleProvider` ignore it.
|
|
3899
|
+
*/
|
|
3900
|
+
responsesApiModels?: Array<string | RegExp>;
|
|
3901
|
+
/**
|
|
3902
|
+
* Stream Responses API output as it arrives (text and tool-call argument
|
|
3903
|
+
* deltas) for models in `responsesApiModels`. Default false, which keeps the
|
|
3904
|
+
* existing behaviour: one non-streaming Responses call, emitted as a single
|
|
3905
|
+
* chunk. Has no effect on any other request.
|
|
3906
|
+
*/
|
|
3907
|
+
responsesStreaming?: boolean;
|
|
3877
3908
|
};
|
|
3878
3909
|
type AnthropicProviderConfig = {
|
|
3879
3910
|
provider: "anthropic";
|
|
@@ -5509,7 +5540,11 @@ declare class OpenAIProvider extends BaseProvider {
|
|
|
5509
5540
|
protected readonly defaultModel: string;
|
|
5510
5541
|
protected readonly defaultHeaders: Record<string, string>;
|
|
5511
5542
|
protected readonly responsesApi: "auto" | "never" | "always";
|
|
5543
|
+
private readonly responsesApiModels;
|
|
5544
|
+
private readonly responsesStreaming;
|
|
5512
5545
|
constructor(config: OpenAIProviderConfig);
|
|
5546
|
+
/** True when the caller listed this request's model in `responsesApiModels`. */
|
|
5547
|
+
private isResponsesApiModel;
|
|
5513
5548
|
/** True when this request should go to the Responses API rather than Chat
|
|
5514
5549
|
* Completions — which exposes `reasoning.effort` and reasoning summaries that
|
|
5515
5550
|
* Chat Completions silently drops.
|
|
@@ -5518,12 +5553,23 @@ declare class OpenAIProvider extends BaseProvider {
|
|
|
5518
5553
|
* carries a reasoning config AND the model name looks like an o-series/gpt-5
|
|
5519
5554
|
* model. `never`/`always` let a caller state the answer instead, because the
|
|
5520
5555
|
* name check is a guess and the two APIs differ in far more than reasoning —
|
|
5521
|
-
* see `responsesApi` in OpenAIProviderConfig.
|
|
5556
|
+
* see `responsesApi` in OpenAIProviderConfig.
|
|
5557
|
+
*
|
|
5558
|
+
* A model listed in `responsesApiModels` is the caller stating the answer
|
|
5559
|
+
* for that model alone; `never` still overrides it. */
|
|
5522
5560
|
protected useResponsesApi(request: ChatCompletionRequest): boolean;
|
|
5523
5561
|
complete(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
|
|
5524
5562
|
completeStream(request: ChatCompletionRequest): AsyncGenerator<ChatCompletionChunk, void, unknown>;
|
|
5525
5563
|
protected buildRequestBody(request: ChatCompletionRequest, stream: boolean): Record<string, any>;
|
|
5526
5564
|
protected completeResponses(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
|
|
5565
|
+
/** Real Responses SSE streaming, for models opted in through
|
|
5566
|
+
* `responsesApiModels` with `responsesStreaming` on. Emits the same chunk
|
|
5567
|
+
* shapes as the Chat Completions stream: text deltas as they arrive, a tool
|
|
5568
|
+
* call's id + name when it opens and its argument fragments under that id,
|
|
5569
|
+
* then one final chunk with usage, finish reason and any reasoning summary
|
|
5570
|
+
* (taken from the completed response, exactly as the non-streaming parse
|
|
5571
|
+
* reads it). */
|
|
5572
|
+
private streamResponses;
|
|
5527
5573
|
protected buildResponsesBody(request: ChatCompletionRequest): Record<string, any>;
|
|
5528
5574
|
/** Maps a unified message to a Responses API `input` item. */
|
|
5529
5575
|
protected toResponsesInput(m: UnifiedMessage): Record<string, any>;
|
package/dist/index.mjs
CHANGED
|
@@ -14981,8 +14981,127 @@ function sanitizeBedrockDocName(name, fallback = "document") {
|
|
|
14981
14981
|
return cleaned || fallback;
|
|
14982
14982
|
}
|
|
14983
14983
|
|
|
14984
|
+
// src/providers/utils/jsonSchemaRefs.ts
|
|
14985
|
+
var DEFINITION_KEYS = ["definitions", "$defs"];
|
|
14986
|
+
function inlineJsonSchemaRefs(schema) {
|
|
14987
|
+
if (!isObject(schema) || !containsRef(schema)) return schema;
|
|
14988
|
+
const root = structuredClone(schema);
|
|
14989
|
+
const selfReaching = /* @__PURE__ */ new Map();
|
|
14990
|
+
const targetOf = (ref) => {
|
|
14991
|
+
const target = resolvePointer(root, ref);
|
|
14992
|
+
return isObject(target) ? target : void 0;
|
|
14993
|
+
};
|
|
14994
|
+
const reachesItself = (target) => {
|
|
14995
|
+
const known = selfReaching.get(target);
|
|
14996
|
+
if (known !== void 0) return known;
|
|
14997
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14998
|
+
const walk = (node) => {
|
|
14999
|
+
if (Array.isArray(node)) return node.some(walk);
|
|
15000
|
+
if (!isObject(node)) return false;
|
|
15001
|
+
if (typeof node.$ref === "string") {
|
|
15002
|
+
const next = targetOf(node.$ref);
|
|
15003
|
+
if (next === target) return true;
|
|
15004
|
+
if (next && !seen.has(next)) {
|
|
15005
|
+
seen.add(next);
|
|
15006
|
+
if (walk(next)) return true;
|
|
15007
|
+
}
|
|
15008
|
+
}
|
|
15009
|
+
return Object.entries(node).some(([key, value]) => key !== "$ref" && walk(value));
|
|
15010
|
+
};
|
|
15011
|
+
const result2 = walk(target);
|
|
15012
|
+
selfReaching.set(target, result2);
|
|
15013
|
+
return result2;
|
|
15014
|
+
};
|
|
15015
|
+
const keptRefs = /* @__PURE__ */ new Set();
|
|
15016
|
+
const expand = (node) => {
|
|
15017
|
+
if (Array.isArray(node)) return node.map(expand);
|
|
15018
|
+
if (!isObject(node)) return node;
|
|
15019
|
+
if (typeof node.$ref === "string") {
|
|
15020
|
+
const target = targetOf(node.$ref);
|
|
15021
|
+
if (target && !reachesItself(target)) {
|
|
15022
|
+
const { $ref: _inlined, ...siblings } = node;
|
|
15023
|
+
return { ...expand(target), ...expandEntries(siblings) };
|
|
15024
|
+
}
|
|
15025
|
+
keptRefs.add(node.$ref);
|
|
15026
|
+
return expandEntries(node);
|
|
15027
|
+
}
|
|
15028
|
+
return expandEntries(node);
|
|
15029
|
+
};
|
|
15030
|
+
const expandEntries = (node) => {
|
|
15031
|
+
const out = {};
|
|
15032
|
+
for (const [key, value] of Object.entries(node)) out[key] = key === "$ref" ? value : expand(value);
|
|
15033
|
+
return out;
|
|
15034
|
+
};
|
|
15035
|
+
const { definitions: _definitions, $defs: _defs, ...rootBody } = root;
|
|
15036
|
+
const result = typeof root.$ref === "string" ? expand(rootBody) : expandEntries(rootBody);
|
|
15037
|
+
for (const key of DEFINITION_KEYS) delete result[key];
|
|
15038
|
+
for (const key of DEFINITION_KEYS) {
|
|
15039
|
+
const block = root[key];
|
|
15040
|
+
if (!isObject(block)) continue;
|
|
15041
|
+
const needed = neededEntries(key, block, keptRefs);
|
|
15042
|
+
if (needed.length === 0) continue;
|
|
15043
|
+
const kept = {};
|
|
15044
|
+
for (const name of Object.keys(block)) if (needed.includes(name)) kept[name] = block[name];
|
|
15045
|
+
result[key] = kept;
|
|
15046
|
+
}
|
|
15047
|
+
return result;
|
|
15048
|
+
}
|
|
15049
|
+
function neededEntries(key, block, keptRefs) {
|
|
15050
|
+
const needed = [];
|
|
15051
|
+
const queue = [...keptRefs];
|
|
15052
|
+
while (queue.length > 0) {
|
|
15053
|
+
const ref = queue.shift();
|
|
15054
|
+
const segments = pointerSegments(ref);
|
|
15055
|
+
if (!segments || segments[0] !== key || segments.length < 2) continue;
|
|
15056
|
+
const name = segments[1];
|
|
15057
|
+
if (needed.includes(name) || !(name in block)) continue;
|
|
15058
|
+
needed.push(name);
|
|
15059
|
+
collectRefs(block[name], queue);
|
|
15060
|
+
}
|
|
15061
|
+
return needed;
|
|
15062
|
+
}
|
|
15063
|
+
function collectRefs(node, into) {
|
|
15064
|
+
if (Array.isArray(node)) {
|
|
15065
|
+
node.forEach((child) => collectRefs(child, into));
|
|
15066
|
+
return;
|
|
15067
|
+
}
|
|
15068
|
+
if (!isObject(node)) return;
|
|
15069
|
+
if (typeof node.$ref === "string") into.push(node.$ref);
|
|
15070
|
+
for (const [k, value] of Object.entries(node)) if (k !== "$ref") collectRefs(value, into);
|
|
15071
|
+
}
|
|
15072
|
+
function containsRef(node) {
|
|
15073
|
+
if (Array.isArray(node)) return node.some(containsRef);
|
|
15074
|
+
if (!isObject(node)) return false;
|
|
15075
|
+
if (typeof node.$ref === "string") return true;
|
|
15076
|
+
return Object.values(node).some(containsRef);
|
|
15077
|
+
}
|
|
15078
|
+
function pointerSegments(ref) {
|
|
15079
|
+
if (ref === "#") return [];
|
|
15080
|
+
if (!ref.startsWith("#/")) return null;
|
|
15081
|
+
return ref.slice(2).split("/").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
15082
|
+
}
|
|
15083
|
+
function resolvePointer(document, ref) {
|
|
15084
|
+
const segments = pointerSegments(ref);
|
|
15085
|
+
if (!segments) return void 0;
|
|
15086
|
+
let current = document;
|
|
15087
|
+
for (const segment of segments) {
|
|
15088
|
+
if (Array.isArray(current)) {
|
|
15089
|
+
if (!/^\d+$/.test(segment)) return void 0;
|
|
15090
|
+
current = current[Number(segment)];
|
|
15091
|
+
} else if (isObject(current) && Object.prototype.hasOwnProperty.call(current, segment)) {
|
|
15092
|
+
current = current[segment];
|
|
15093
|
+
} else {
|
|
15094
|
+
return void 0;
|
|
15095
|
+
}
|
|
15096
|
+
}
|
|
15097
|
+
return current;
|
|
15098
|
+
}
|
|
15099
|
+
function isObject(value) {
|
|
15100
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15101
|
+
}
|
|
15102
|
+
|
|
14984
15103
|
// src/providers/openai.ts
|
|
14985
|
-
var OpenAIProvider = class extends BaseProvider {
|
|
15104
|
+
var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
14986
15105
|
constructor(config) {
|
|
14987
15106
|
super();
|
|
14988
15107
|
this.providerName = "openai";
|
|
@@ -14994,8 +15113,21 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
14994
15113
|
...config.organization ? { "OpenAI-Organization": config.organization } : {}
|
|
14995
15114
|
};
|
|
14996
15115
|
this.responsesApi = config.responsesApi ?? "auto";
|
|
15116
|
+
const ownInstance = new.target === _OpenAIProvider;
|
|
15117
|
+
this.responsesApiModels = ownInstance ? [...config.responsesApiModels ?? []] : [];
|
|
15118
|
+
this.responsesStreaming = ownInstance && config.responsesStreaming === true;
|
|
14997
15119
|
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
14998
15120
|
}
|
|
15121
|
+
/** True when the caller listed this request's model in `responsesApiModels`. */
|
|
15122
|
+
isResponsesApiModel(request) {
|
|
15123
|
+
if (this.responsesApiModels.length === 0) return false;
|
|
15124
|
+
const model = request.model || this.defaultModel;
|
|
15125
|
+
return this.responsesApiModels.some((entry) => {
|
|
15126
|
+
if (typeof entry === "string") return entry === model;
|
|
15127
|
+
entry.lastIndex = 0;
|
|
15128
|
+
return entry.test(model);
|
|
15129
|
+
});
|
|
15130
|
+
}
|
|
14999
15131
|
/** True when this request should go to the Responses API rather than Chat
|
|
15000
15132
|
* Completions — which exposes `reasoning.effort` and reasoning summaries that
|
|
15001
15133
|
* Chat Completions silently drops.
|
|
@@ -15004,10 +15136,14 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
15004
15136
|
* carries a reasoning config AND the model name looks like an o-series/gpt-5
|
|
15005
15137
|
* model. `never`/`always` let a caller state the answer instead, because the
|
|
15006
15138
|
* name check is a guess and the two APIs differ in far more than reasoning —
|
|
15007
|
-
* see `responsesApi` in OpenAIProviderConfig.
|
|
15139
|
+
* see `responsesApi` in OpenAIProviderConfig.
|
|
15140
|
+
*
|
|
15141
|
+
* A model listed in `responsesApiModels` is the caller stating the answer
|
|
15142
|
+
* for that model alone; `never` still overrides it. */
|
|
15008
15143
|
useResponsesApi(request) {
|
|
15009
15144
|
if (this.responsesApi === "never") return false;
|
|
15010
15145
|
if (this.responsesApi === "always") return true;
|
|
15146
|
+
if (this.isResponsesApiModel(request)) return true;
|
|
15011
15147
|
if (!request.reasoning) return false;
|
|
15012
15148
|
return isReasoningModel(request.model || this.defaultModel);
|
|
15013
15149
|
}
|
|
@@ -15022,6 +15158,10 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
15022
15158
|
}
|
|
15023
15159
|
async *completeStream(request) {
|
|
15024
15160
|
if (this.useResponsesApi(request)) {
|
|
15161
|
+
if (this.responsesStreaming && this.isResponsesApiModel(request)) {
|
|
15162
|
+
yield* this.streamResponses(request);
|
|
15163
|
+
return;
|
|
15164
|
+
}
|
|
15025
15165
|
const response = await this.completeResponses(request);
|
|
15026
15166
|
yield {
|
|
15027
15167
|
id: response.id,
|
|
@@ -15137,10 +15277,101 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
15137
15277
|
// ─── Responses API (reasoning models) ──────────────────────────────────────
|
|
15138
15278
|
async completeResponses(request) {
|
|
15139
15279
|
const body = this.buildResponsesBody(request);
|
|
15280
|
+
if (this.isResponsesApiModel(request)) adaptChatExtrasForResponses(body, request.extra);
|
|
15140
15281
|
const res = await this.responsesFetch(body);
|
|
15141
15282
|
const json = await res.json();
|
|
15142
15283
|
return this.parseResponsesResponse(json, request.model || this.defaultModel);
|
|
15143
15284
|
}
|
|
15285
|
+
/** Real Responses SSE streaming, for models opted in through
|
|
15286
|
+
* `responsesApiModels` with `responsesStreaming` on. Emits the same chunk
|
|
15287
|
+
* shapes as the Chat Completions stream: text deltas as they arrive, a tool
|
|
15288
|
+
* call's id + name when it opens and its argument fragments under that id,
|
|
15289
|
+
* then one final chunk with usage, finish reason and any reasoning summary
|
|
15290
|
+
* (taken from the completed response, exactly as the non-streaming parse
|
|
15291
|
+
* reads it). */
|
|
15292
|
+
async *streamResponses(request) {
|
|
15293
|
+
const modelId = request.model || this.defaultModel;
|
|
15294
|
+
const body = this.buildResponsesBody(request);
|
|
15295
|
+
adaptChatExtrasForResponses(body, request.extra);
|
|
15296
|
+
body.stream = true;
|
|
15297
|
+
const res = await this.responsesFetch(body);
|
|
15298
|
+
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
15299
|
+
let id = "";
|
|
15300
|
+
let model = modelId;
|
|
15301
|
+
const calls = /* @__PURE__ */ new Map();
|
|
15302
|
+
let final;
|
|
15303
|
+
for await (const event of parseSSEStream(res.body)) {
|
|
15304
|
+
if (event.data === "[DONE]") break;
|
|
15305
|
+
let data;
|
|
15306
|
+
try {
|
|
15307
|
+
data = JSON.parse(event.data);
|
|
15308
|
+
} catch {
|
|
15309
|
+
continue;
|
|
15310
|
+
}
|
|
15311
|
+
switch (data.type ?? event.event) {
|
|
15312
|
+
case "response.created":
|
|
15313
|
+
case "response.in_progress":
|
|
15314
|
+
id = data.response?.id ?? id;
|
|
15315
|
+
model = data.response?.model ?? model;
|
|
15316
|
+
break;
|
|
15317
|
+
case "response.output_text.delta":
|
|
15318
|
+
if (data.delta) yield { id, model, delta: { content: data.delta } };
|
|
15319
|
+
break;
|
|
15320
|
+
case "response.output_item.added": {
|
|
15321
|
+
const item = data.item;
|
|
15322
|
+
if (item?.type !== "function_call") break;
|
|
15323
|
+
const call = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: item.arguments ?? "" };
|
|
15324
|
+
calls.set(item.id ?? call.callId, call);
|
|
15325
|
+
yield { id, model, delta: { toolCalls: [{ id: call.callId, name: call.name, arguments: call.arguments }] } };
|
|
15326
|
+
break;
|
|
15327
|
+
}
|
|
15328
|
+
case "response.function_call_arguments.delta": {
|
|
15329
|
+
const call = calls.get(data.item_id);
|
|
15330
|
+
if (!call || !data.delta) break;
|
|
15331
|
+
call.arguments += data.delta;
|
|
15332
|
+
yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: data.delta }] } };
|
|
15333
|
+
break;
|
|
15334
|
+
}
|
|
15335
|
+
case "response.output_item.done": {
|
|
15336
|
+
const item = data.item;
|
|
15337
|
+
if (item?.type !== "function_call") break;
|
|
15338
|
+
const full = typeof item.arguments === "string" ? item.arguments : "";
|
|
15339
|
+
const call = calls.get(item.id ?? item.call_id);
|
|
15340
|
+
if (!call) {
|
|
15341
|
+
const added = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: full };
|
|
15342
|
+
calls.set(item.id ?? added.callId, added);
|
|
15343
|
+
yield { id, model, delta: { toolCalls: [{ id: added.callId, name: added.name, arguments: full }] } };
|
|
15344
|
+
} else if (full.length > call.arguments.length && full.startsWith(call.arguments)) {
|
|
15345
|
+
const rest = full.slice(call.arguments.length);
|
|
15346
|
+
call.arguments = full;
|
|
15347
|
+
yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: rest }] } };
|
|
15348
|
+
}
|
|
15349
|
+
break;
|
|
15350
|
+
}
|
|
15351
|
+
case "response.completed":
|
|
15352
|
+
case "response.incomplete":
|
|
15353
|
+
final = this.parseResponsesResponse(data.response ?? {}, modelId);
|
|
15354
|
+
break;
|
|
15355
|
+
case "response.failed":
|
|
15356
|
+
case "error": {
|
|
15357
|
+
const err = data.response?.error ?? data.error ?? data;
|
|
15358
|
+
throw new ProviderError(
|
|
15359
|
+
`OpenAI Responses API stream error: ${err?.message ?? "unknown error"}`,
|
|
15360
|
+
this.providerName,
|
|
15361
|
+
void 0,
|
|
15362
|
+
data
|
|
15363
|
+
);
|
|
15364
|
+
}
|
|
15365
|
+
}
|
|
15366
|
+
}
|
|
15367
|
+
yield {
|
|
15368
|
+
id: final?.id || id,
|
|
15369
|
+
model: final?.model || model,
|
|
15370
|
+
delta: final?.reasoning ? { reasoning: final.reasoning } : {},
|
|
15371
|
+
usage: final?.usage,
|
|
15372
|
+
finishReason: final?.finishReason ?? (calls.size > 0 ? "tool_calls" : "stop")
|
|
15373
|
+
};
|
|
15374
|
+
}
|
|
15144
15375
|
buildResponsesBody(request) {
|
|
15145
15376
|
const body = {
|
|
15146
15377
|
model: request.model || this.defaultModel,
|
|
@@ -15170,7 +15401,7 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
15170
15401
|
format: {
|
|
15171
15402
|
type: "json_schema",
|
|
15172
15403
|
name: request.responseFormat.name ?? "response",
|
|
15173
|
-
schema: request.responseFormat.schema,
|
|
15404
|
+
schema: inlineJsonSchemaRefs(request.responseFormat.schema),
|
|
15174
15405
|
strict: true
|
|
15175
15406
|
}
|
|
15176
15407
|
};
|
|
@@ -15425,6 +15656,48 @@ function isReasoningModel(model) {
|
|
|
15425
15656
|
const m = model.toLowerCase();
|
|
15426
15657
|
return /(^|[/_-])o[0-9]/.test(m) || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("o4") || m.includes("gpt-5") || m.includes("gpt5");
|
|
15427
15658
|
}
|
|
15659
|
+
function adaptChatExtrasForResponses(body, extra) {
|
|
15660
|
+
const native = extra ?? {};
|
|
15661
|
+
if ("reasoning_effort" in body) {
|
|
15662
|
+
const effort = body.reasoning_effort;
|
|
15663
|
+
delete body.reasoning_effort;
|
|
15664
|
+
if (effort != null) {
|
|
15665
|
+
const reasoning = body.reasoning && typeof body.reasoning === "object" ? body.reasoning : {};
|
|
15666
|
+
if (reasoning.effort == null) body.reasoning = { ...reasoning, effort };
|
|
15667
|
+
}
|
|
15668
|
+
}
|
|
15669
|
+
for (const key of ["max_tokens", "max_completion_tokens"]) {
|
|
15670
|
+
if (!(key in body)) continue;
|
|
15671
|
+
const value = body[key];
|
|
15672
|
+
delete body[key];
|
|
15673
|
+
if (value != null && !("max_output_tokens" in native)) body.max_output_tokens = value;
|
|
15674
|
+
}
|
|
15675
|
+
if ("response_format" in body) {
|
|
15676
|
+
const format = toResponsesTextFormat(body.response_format);
|
|
15677
|
+
delete body.response_format;
|
|
15678
|
+
if (format && !("text" in native)) body.text = { ...body.text, format };
|
|
15679
|
+
}
|
|
15680
|
+
if (body.stream_options && typeof body.stream_options === "object" && "include_usage" in body.stream_options) {
|
|
15681
|
+
const { include_usage: _dropped, ...rest } = body.stream_options;
|
|
15682
|
+
if (Object.keys(rest).length > 0) body.stream_options = rest;
|
|
15683
|
+
else delete body.stream_options;
|
|
15684
|
+
}
|
|
15685
|
+
}
|
|
15686
|
+
function toResponsesTextFormat(rf) {
|
|
15687
|
+
if (!rf || typeof rf !== "object") return void 0;
|
|
15688
|
+
if (rf.type === "json_schema") {
|
|
15689
|
+
const js = rf.json_schema ?? {};
|
|
15690
|
+
return {
|
|
15691
|
+
type: "json_schema",
|
|
15692
|
+
name: js.name ?? "response",
|
|
15693
|
+
schema: inlineJsonSchemaRefs(js.schema),
|
|
15694
|
+
...js.description != null ? { description: js.description } : {},
|
|
15695
|
+
...js.strict != null ? { strict: js.strict } : {}
|
|
15696
|
+
};
|
|
15697
|
+
}
|
|
15698
|
+
if (rf.type === "json_object" || rf.type === "text") return { type: rf.type };
|
|
15699
|
+
return void 0;
|
|
15700
|
+
}
|
|
15428
15701
|
function expandResponsesInput(input) {
|
|
15429
15702
|
const out = [];
|
|
15430
15703
|
for (const item of input) {
|