@toren-run/providers 0.1.2 → 0.1.4
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/openai.d.ts +4 -0
- package/dist/openai.js +81 -0
- package/package.json +2 -2
package/dist/openai.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ import type OpenAI from "openai";
|
|
|
2
2
|
import type { ModelProvider, ModelRequest, ModelResponse } from "@toren-run/core";
|
|
3
3
|
export declare function toOpenAIParams(req: ModelRequest): OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming;
|
|
4
4
|
export declare function fromOpenAIResponse(completion: OpenAI.Chat.Completions.ChatCompletion): ModelResponse;
|
|
5
|
+
/** Real reasoning effort ("low"/"medium"/"high") needs /v1/responses; "none" or absent works on chat/completions. */
|
|
6
|
+
export declare function usesResponsesApi(req: Pick<ModelRequest, "reasoningEffort">): boolean;
|
|
7
|
+
export declare function toResponsesParams(req: ModelRequest): OpenAI.Responses.ResponseCreateParamsNonStreaming;
|
|
8
|
+
export declare function fromResponsesResponse(res: OpenAI.Responses.Response): ModelResponse;
|
|
5
9
|
/**
|
|
6
10
|
* OpenAI adapter over toren's normalized model interface.
|
|
7
11
|
* Credentials resolve from the environment (OPENAI_API_KEY);
|
package/dist/openai.js
CHANGED
|
@@ -64,6 +64,8 @@ export function toOpenAIParams(req) {
|
|
|
64
64
|
return {
|
|
65
65
|
model: req.model.replace(/^openai\//, ""),
|
|
66
66
|
max_completion_tokens: req.maxTokens,
|
|
67
|
+
// gpt-5.6+ reject tools via chat/completions unless reasoning_effort is set.
|
|
68
|
+
...(req.reasoningEffort ? { reasoning_effort: req.reasoningEffort } : {}),
|
|
67
69
|
messages: [
|
|
68
70
|
...(req.system ? [{ role: "system", content: req.system }] : []),
|
|
69
71
|
...toSdkMessages(req.messages),
|
|
@@ -113,6 +115,82 @@ export function fromOpenAIResponse(completion) {
|
|
|
113
115
|
},
|
|
114
116
|
};
|
|
115
117
|
}
|
|
118
|
+
// ---- /v1/responses: the API that allows reasoning AND tools together.
|
|
119
|
+
// gpt-5.6+ reject function tools on chat/completions unless reasoning is off,
|
|
120
|
+
// so a request with real reasoning effort routes here; everything else stays
|
|
121
|
+
// on the battle-tested chat/completions path. The digest covers toren's
|
|
122
|
+
// normalized request, not the wire shape, so routing changes nothing in-flight.
|
|
123
|
+
/** Real reasoning effort ("low"/"medium"/"high") needs /v1/responses; "none" or absent works on chat/completions. */
|
|
124
|
+
export function usesResponsesApi(req) {
|
|
125
|
+
return Boolean(req.reasoningEffort && req.reasoningEffort !== "none");
|
|
126
|
+
}
|
|
127
|
+
export function toResponsesParams(req) {
|
|
128
|
+
const input = [];
|
|
129
|
+
for (const m of req.messages) {
|
|
130
|
+
if (m.role === "assistant") {
|
|
131
|
+
const text = m.content.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
132
|
+
if (text)
|
|
133
|
+
input.push({ role: "assistant", content: [{ type: "output_text", text, annotations: [] }] });
|
|
134
|
+
for (const b of m.content) {
|
|
135
|
+
if (b.type === "toolUse")
|
|
136
|
+
input.push({ type: "function_call", call_id: b.id, name: b.name, arguments: JSON.stringify(b.input ?? {}) });
|
|
137
|
+
}
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
let text = "";
|
|
141
|
+
for (const b of m.content) {
|
|
142
|
+
if (b.type === "toolResult")
|
|
143
|
+
input.push({ type: "function_call_output", call_id: b.toolUseId, output: b.isError ? `ERROR: ${b.content}` : b.content });
|
|
144
|
+
else if (b.type === "text")
|
|
145
|
+
text += b.text;
|
|
146
|
+
}
|
|
147
|
+
if (text)
|
|
148
|
+
input.push({ role: "user", content: [{ type: "input_text", text }] });
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
model: req.model.replace(/^openai\//, ""),
|
|
152
|
+
...(req.system ? { instructions: req.system } : {}),
|
|
153
|
+
max_output_tokens: req.maxTokens,
|
|
154
|
+
reasoning: { effort: req.reasoningEffort },
|
|
155
|
+
input,
|
|
156
|
+
...(req.tools.length
|
|
157
|
+
? { tools: req.tools.map((t) => ({ type: "function", name: t.name, description: t.description, parameters: t.inputSchema, strict: false })) }
|
|
158
|
+
: {}),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
export function fromResponsesResponse(res) {
|
|
162
|
+
const content = [];
|
|
163
|
+
for (const item of res.output ?? []) {
|
|
164
|
+
if (item.type === "message") {
|
|
165
|
+
const text = item.content.filter((c) => c.type === "output_text").map((c) => c.text).join("");
|
|
166
|
+
if (text)
|
|
167
|
+
content.push({ type: "text", text });
|
|
168
|
+
}
|
|
169
|
+
else if (item.type === "function_call") {
|
|
170
|
+
let input = {};
|
|
171
|
+
try {
|
|
172
|
+
input = JSON.parse(item.arguments || "{}");
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
input = { _raw: item.arguments };
|
|
176
|
+
}
|
|
177
|
+
content.push({ type: "toolUse", id: item.call_id, name: item.name, input });
|
|
178
|
+
}
|
|
179
|
+
// reasoning items are the model's private scratchpad: not transcript content.
|
|
180
|
+
}
|
|
181
|
+
const stopReason = content.some((b) => b.type === "toolUse")
|
|
182
|
+
? "toolUse"
|
|
183
|
+
: res.status === "incomplete" && res.incomplete_details?.reason === "max_output_tokens"
|
|
184
|
+
? "maxTokens"
|
|
185
|
+
: res.status === "incomplete" && res.incomplete_details?.reason === "content_filter"
|
|
186
|
+
? "refusal"
|
|
187
|
+
: "endTurn";
|
|
188
|
+
return {
|
|
189
|
+
content,
|
|
190
|
+
stopReason,
|
|
191
|
+
usage: { inputTokens: res.usage?.input_tokens ?? 0, outputTokens: res.usage?.output_tokens ?? 0 },
|
|
192
|
+
};
|
|
193
|
+
}
|
|
116
194
|
/**
|
|
117
195
|
* OpenAI adapter over toren's normalized model interface.
|
|
118
196
|
* Credentials resolve from the environment (OPENAI_API_KEY);
|
|
@@ -124,6 +202,9 @@ export class OpenAIProvider {
|
|
|
124
202
|
this.client = client ?? new (loadSdk())();
|
|
125
203
|
}
|
|
126
204
|
async complete(req) {
|
|
205
|
+
if (usesResponsesApi(req)) {
|
|
206
|
+
return fromResponsesResponse(await this.client.responses.create(toResponsesParams(req)));
|
|
207
|
+
}
|
|
127
208
|
const response = await this.client.chat.completions.create(toOpenAIParams(req));
|
|
128
209
|
return fromOpenAIResponse(response);
|
|
129
210
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toren-run/providers",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@anthropic-ai/sdk": "^0.110.0",
|
|
13
13
|
"openai": "^4.68.0",
|
|
14
|
-
"@toren-run/core": "^0.1.
|
|
14
|
+
"@toren-run/core": "^0.1.4"
|
|
15
15
|
},
|
|
16
16
|
"description": "Model provider adapters for Toren (Anthropic, OpenAI) — each SDK loads lazily on first use.",
|
|
17
17
|
"license": "Apache-2.0",
|