@veryfront/ext-llm-openai 0.1.985
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/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +129 -0
- package/esm/index.d.ts +12 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/index.js +32 -0
- package/esm/openai-chat-request-builder.d.ts +67 -0
- package/esm/openai-chat-request-builder.d.ts.map +1 -0
- package/esm/openai-chat-request-builder.js +126 -0
- package/esm/openai-chat-stream.d.ts +2 -0
- package/esm/openai-chat-stream.d.ts.map +1 -0
- package/esm/openai-chat-stream.js +235 -0
- package/esm/openai-provider.d.ts +29 -0
- package/esm/openai-provider.d.ts.map +1 -0
- package/esm/openai-provider.js +436 -0
- package/esm/openai-responses-request-builder.d.ts +42 -0
- package/esm/openai-responses-request-builder.d.ts.map +1 -0
- package/esm/openai-responses-request-builder.js +220 -0
- package/esm/openai-responses-stream.d.ts +16 -0
- package/esm/openai-responses-stream.d.ts.map +1 -0
- package/esm/openai-responses-stream.js +235 -0
- package/esm/package.json +3 -0
- package/package.json +58 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { parseSseChunk, readGatewayBillingMode, readRecord } from "veryfront/provider/shared";
|
|
2
|
+
function normalizeOpenAIFinishReason(raw) {
|
|
3
|
+
if (typeof raw !== "string") {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
if (raw === "tool_calls") {
|
|
7
|
+
return { unified: "tool-calls", raw };
|
|
8
|
+
}
|
|
9
|
+
if (raw === "content_filter") {
|
|
10
|
+
return { unified: "content-filter", raw };
|
|
11
|
+
}
|
|
12
|
+
return raw;
|
|
13
|
+
}
|
|
14
|
+
function extractOpenAIUsage(payload) {
|
|
15
|
+
const record = readRecord(payload);
|
|
16
|
+
const usage = readRecord(record?.usage);
|
|
17
|
+
if (!usage) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const inputTokens = usage.prompt_tokens;
|
|
21
|
+
const outputTokens = usage.completion_tokens;
|
|
22
|
+
const totalTokens = usage.total_tokens;
|
|
23
|
+
const promptTokensDetails = readRecord(usage.prompt_tokens_details);
|
|
24
|
+
const cachedTokens = promptTokensDetails?.cached_tokens;
|
|
25
|
+
const completionTokensDetails = readRecord(usage.completion_tokens_details);
|
|
26
|
+
const reasoningTokens = completionTokensDetails?.reasoning_tokens;
|
|
27
|
+
const veryfront = readRecord(usage.veryfront);
|
|
28
|
+
const costSource = veryfront?.cost_source;
|
|
29
|
+
const billingMode = readGatewayBillingMode(veryfront?.billing_mode);
|
|
30
|
+
const usageCaptureStatus = veryfront?.usage_capture_status;
|
|
31
|
+
return {
|
|
32
|
+
inputTokens: typeof inputTokens === "number" ? inputTokens : undefined,
|
|
33
|
+
outputTokens: typeof outputTokens === "number" ? outputTokens : undefined,
|
|
34
|
+
totalTokens: typeof totalTokens === "number" ? totalTokens : undefined,
|
|
35
|
+
...(typeof cachedTokens === "number" ? { cacheReadInputTokens: cachedTokens } : {}),
|
|
36
|
+
...(typeof reasoningTokens === "number" ? { reasoningTokens } : {}),
|
|
37
|
+
...(typeof veryfront?.billable_input_tokens === "number"
|
|
38
|
+
? { billableInputTokens: veryfront.billable_input_tokens }
|
|
39
|
+
: {}),
|
|
40
|
+
...(typeof veryfront?.billable_output_tokens === "number"
|
|
41
|
+
? { billableOutputTokens: veryfront.billable_output_tokens }
|
|
42
|
+
: {}),
|
|
43
|
+
...(typeof veryfront?.cost_usd === "number" ? { costUsd: veryfront.cost_usd } : {}),
|
|
44
|
+
...(typeof veryfront?.provider_input_cost_usd === "number"
|
|
45
|
+
? { providerInputCostUsd: veryfront.provider_input_cost_usd }
|
|
46
|
+
: {}),
|
|
47
|
+
...(typeof veryfront?.provider_output_cost_usd === "number"
|
|
48
|
+
? { providerOutputCostUsd: veryfront.provider_output_cost_usd }
|
|
49
|
+
: {}),
|
|
50
|
+
...(typeof veryfront?.provider_cost_usd === "number"
|
|
51
|
+
? { providerCostUsd: veryfront.provider_cost_usd }
|
|
52
|
+
: {}),
|
|
53
|
+
...(typeof veryfront?.veryfront_input_charge_usd === "number"
|
|
54
|
+
? { veryfrontInputChargeUsd: veryfront.veryfront_input_charge_usd }
|
|
55
|
+
: {}),
|
|
56
|
+
...(typeof veryfront?.veryfront_output_charge_usd === "number"
|
|
57
|
+
? { veryfrontOutputChargeUsd: veryfront.veryfront_output_charge_usd }
|
|
58
|
+
: {}),
|
|
59
|
+
...(typeof veryfront?.veryfront_charge_usd === "number"
|
|
60
|
+
? { veryfrontChargeUsd: veryfront.veryfront_charge_usd }
|
|
61
|
+
: {}),
|
|
62
|
+
...(typeof veryfront?.veryfront_billed_usd === "number"
|
|
63
|
+
? { veryfrontBilledUsd: veryfront.veryfront_billed_usd }
|
|
64
|
+
: {}),
|
|
65
|
+
...(typeof veryfront?.cost_credits === "number" ? { costCredits: veryfront.cost_credits } : {}),
|
|
66
|
+
...(costSource === "gateway" || costSource === "missing" || costSource === "partial"
|
|
67
|
+
? { costSource }
|
|
68
|
+
: {}),
|
|
69
|
+
...(billingMode !== undefined ? { billingMode } : {}),
|
|
70
|
+
...(usageCaptureStatus === "complete" ||
|
|
71
|
+
usageCaptureStatus === "missing" ||
|
|
72
|
+
usageCaptureStatus === "partial"
|
|
73
|
+
? { usageCaptureStatus }
|
|
74
|
+
: {}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function extractOpenAIContentText(content) {
|
|
78
|
+
if (typeof content === "string") {
|
|
79
|
+
return content;
|
|
80
|
+
}
|
|
81
|
+
if (!Array.isArray(content)) {
|
|
82
|
+
return "";
|
|
83
|
+
}
|
|
84
|
+
let text = "";
|
|
85
|
+
for (const part of content) {
|
|
86
|
+
const record = readRecord(part);
|
|
87
|
+
const type = record?.type;
|
|
88
|
+
if (type === "text" && typeof record?.text === "string") {
|
|
89
|
+
text += record.text;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return text;
|
|
93
|
+
}
|
|
94
|
+
function extractFirstChoice(payload) {
|
|
95
|
+
const record = readRecord(payload);
|
|
96
|
+
const choices = record?.choices;
|
|
97
|
+
if (!Array.isArray(choices) || choices.length === 0) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
const first = readRecord(choices[0]);
|
|
101
|
+
if (!first) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
return first;
|
|
105
|
+
}
|
|
106
|
+
export async function* streamOpenAICompatibleParts(stream) {
|
|
107
|
+
const decoder = new TextDecoder();
|
|
108
|
+
let buffer = "";
|
|
109
|
+
const toolCalls = new Map();
|
|
110
|
+
let reasoningId = null;
|
|
111
|
+
let reasoningIndex = 0;
|
|
112
|
+
let finishReason = null;
|
|
113
|
+
let usage;
|
|
114
|
+
for await (const chunk of stream) {
|
|
115
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
116
|
+
const parsed = parseSseChunk(buffer);
|
|
117
|
+
buffer = parsed.remainder;
|
|
118
|
+
for (const event of parsed.events) {
|
|
119
|
+
if (event === "[DONE]") {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const record = readRecord(event);
|
|
123
|
+
usage = extractOpenAIUsage(record) ?? usage;
|
|
124
|
+
const choice = extractFirstChoice(record);
|
|
125
|
+
if (!choice) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const delta = readRecord(choice.delta);
|
|
129
|
+
if (typeof delta?.reasoning_content === "string" && delta.reasoning_content.length > 0) {
|
|
130
|
+
if (!reasoningId) {
|
|
131
|
+
reasoningId = `reasoning-${reasoningIndex++}`;
|
|
132
|
+
yield {
|
|
133
|
+
type: "reasoning-start",
|
|
134
|
+
id: reasoningId,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
yield {
|
|
138
|
+
type: "reasoning-delta",
|
|
139
|
+
id: reasoningId,
|
|
140
|
+
delta: delta.reasoning_content,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const textDelta = extractOpenAIContentText(delta?.content);
|
|
144
|
+
if (textDelta.length > 0) {
|
|
145
|
+
if (reasoningId) {
|
|
146
|
+
yield {
|
|
147
|
+
type: "reasoning-end",
|
|
148
|
+
id: reasoningId,
|
|
149
|
+
};
|
|
150
|
+
reasoningId = null;
|
|
151
|
+
}
|
|
152
|
+
yield { type: "text-delta", delta: textDelta };
|
|
153
|
+
}
|
|
154
|
+
const rawToolCalls = Array.isArray(delta?.tool_calls) ? delta.tool_calls : [];
|
|
155
|
+
for (const rawToolCall of rawToolCalls) {
|
|
156
|
+
if (reasoningId) {
|
|
157
|
+
yield {
|
|
158
|
+
type: "reasoning-end",
|
|
159
|
+
id: reasoningId,
|
|
160
|
+
};
|
|
161
|
+
reasoningId = null;
|
|
162
|
+
}
|
|
163
|
+
const toolCallRecord = readRecord(rawToolCall);
|
|
164
|
+
const index = typeof toolCallRecord?.index === "number" ? toolCallRecord.index : 0;
|
|
165
|
+
const current = toolCalls.get(index) ?? {
|
|
166
|
+
id: typeof toolCallRecord?.id === "string" ? toolCallRecord.id : `tool-${index}`,
|
|
167
|
+
name: "",
|
|
168
|
+
arguments: "",
|
|
169
|
+
started: false,
|
|
170
|
+
};
|
|
171
|
+
if (typeof toolCallRecord?.id === "string") {
|
|
172
|
+
current.id = toolCallRecord.id;
|
|
173
|
+
}
|
|
174
|
+
const fn = readRecord(toolCallRecord?.function);
|
|
175
|
+
if (typeof fn?.name === "string") {
|
|
176
|
+
current.name = fn.name;
|
|
177
|
+
}
|
|
178
|
+
if (!current.started && current.name.length > 0) {
|
|
179
|
+
current.started = true;
|
|
180
|
+
yield {
|
|
181
|
+
type: "tool-input-start",
|
|
182
|
+
id: current.id,
|
|
183
|
+
toolName: current.name,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
if (typeof fn?.arguments === "string" && fn.arguments.length > 0) {
|
|
187
|
+
current.arguments += fn.arguments;
|
|
188
|
+
yield {
|
|
189
|
+
type: "tool-input-delta",
|
|
190
|
+
id: current.id,
|
|
191
|
+
delta: fn.arguments,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
toolCalls.set(index, current);
|
|
195
|
+
}
|
|
196
|
+
const normalizedFinishReason = normalizeOpenAIFinishReason(choice.finish_reason);
|
|
197
|
+
if (normalizedFinishReason) {
|
|
198
|
+
finishReason = normalizedFinishReason;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (buffer.trim().length > 0) {
|
|
203
|
+
const parsed = parseSseChunk(`${buffer}\n\n`);
|
|
204
|
+
for (const event of parsed.events) {
|
|
205
|
+
if (event === "[DONE]") {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const record = readRecord(event);
|
|
209
|
+
usage = extractOpenAIUsage(record) ?? usage;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (reasoningId) {
|
|
213
|
+
yield {
|
|
214
|
+
type: "reasoning-end",
|
|
215
|
+
id: reasoningId,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (finishReason &&
|
|
219
|
+
typeof finishReason === "object" &&
|
|
220
|
+
finishReason.unified === "tool-calls") {
|
|
221
|
+
for (const toolCall of toolCalls.values()) {
|
|
222
|
+
yield {
|
|
223
|
+
type: "tool-call",
|
|
224
|
+
toolCallId: toolCall.id,
|
|
225
|
+
toolName: toolCall.name,
|
|
226
|
+
input: toolCall.arguments,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
yield {
|
|
231
|
+
type: "finish",
|
|
232
|
+
finishReason,
|
|
233
|
+
...(usage ? { usage } : {}),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI provider, implements the {@link LLMProvider} contract for OpenAI,
|
|
3
|
+
* OpenAI-compatible endpoints (Azure OpenAI, Moonshot AI), and OpenAI's
|
|
4
|
+
* Responses API.
|
|
5
|
+
*
|
|
6
|
+
* Ported from `src/provider/runtime-loader.ts` as part of PR 11.
|
|
7
|
+
*
|
|
8
|
+
* @module extensions/ext-llm-openai/openai-provider
|
|
9
|
+
*/
|
|
10
|
+
import type { LLMProvider, LLMProviderConfig } from "veryfront/extensions/llm";
|
|
11
|
+
import type { EmbeddingRuntime, ModelRuntime } from "veryfront/provider/types";
|
|
12
|
+
import { buildProviderError, isNumberArray, mergeUsage, parseRetryAfterMs, ProviderError, ProviderOverloadedError, ProviderQuotaError, ProviderRateLimitError, ProviderRequestError, TOOL_INPUT_PENDING_THRESHOLD_MS, withToolInputStatusTransitions } from "veryfront/provider/shared";
|
|
13
|
+
export { buildProviderError, isNumberArray, mergeUsage, parseRetryAfterMs, ProviderError, ProviderOverloadedError, ProviderQuotaError, ProviderRateLimitError, ProviderRequestError, TOOL_INPUT_PENDING_THRESHOLD_MS, withToolInputStatusTransitions, };
|
|
14
|
+
export interface OpenAIRuntimeConfig {
|
|
15
|
+
apiKey: string;
|
|
16
|
+
baseURL?: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
fetch?: typeof globalThis.fetch;
|
|
19
|
+
}
|
|
20
|
+
export declare function createOpenAIModelRuntime(config: OpenAIRuntimeConfig, modelId: string): ModelRuntime;
|
|
21
|
+
export declare function createOpenAIResponsesRuntime(config: OpenAIRuntimeConfig, modelId: string): ModelRuntime;
|
|
22
|
+
export declare function createOpenAIEmbeddingRuntime(config: OpenAIRuntimeConfig, modelId: string): EmbeddingRuntime;
|
|
23
|
+
export declare class OpenAIProvider implements LLMProvider {
|
|
24
|
+
readonly id = "openai";
|
|
25
|
+
createModel(modelId: string, config: LLMProviderConfig): ModelRuntime;
|
|
26
|
+
createEmbedding(modelId: string, config: LLMProviderConfig): EmbeddingRuntime;
|
|
27
|
+
createResponses(modelId: string, config: LLMProviderConfig): ModelRuntime;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=openai-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-provider.d.ts","sourceRoot":"","sources":["../src/openai-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EACL,kBAAkB,EAMlB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EAMpB,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,2BAA2B,CAAC;AAcnC,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EACpB,+BAA+B,EAC/B,8BAA8B,GAC/B,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAkVD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,YAAY,CAsEd;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,YAAY,CAsEd;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,gBAAgB,CAuClB;AAED,qBAAa,cAAe,YAAW,WAAW;IAChD,QAAQ,CAAC,EAAE,YAAY;IAEvB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,YAAY;IAYrE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,gBAAgB;IAY7E,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,YAAY;CAW1E"}
|