@pencil-agent/nano-pencil 2.0.0-beta.2 → 2.0.0-beta.3
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/build-meta.json +3 -3
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-continuations.d.ts +17 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-continuations.js +60 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-stream-events.d.ts +19 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-stream-events.js +55 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-tool-results.d.ts +10 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-tool-results.js +137 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-tool-summaries.d.ts +22 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop-tool-summaries.js +64 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop.d.ts +26 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop.js +913 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-run-result.d.ts +9 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-run-result.js +32 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent.d.ts +215 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent.js +522 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/errors.d.ts +62 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/errors.js +146 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/index.d.ts +14 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/index.js +19 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/proxy.d.ts +91 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/proxy.js +279 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-agent-loop.d.ts +15 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-agent-loop.js +625 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-streaming-tool-executor.d.ts +33 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-streaming-tool-executor.js +189 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-tool-orchestration.d.ts +35 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-tool-orchestration.js +319 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/types.d.ts +417 -0
- package/dist/node_modules/@pencil-agent/agent-core/dist/types.js +13 -0
- package/dist/node_modules/@pencil-agent/agent-core/package.json +28 -0
- package/dist/node_modules/@pencil-agent/ai/dist/api-registry.d.ts +27 -0
- package/dist/node_modules/@pencil-agent/ai/dist/api-registry.js +152 -0
- package/dist/node_modules/@pencil-agent/ai/dist/cli.d.ts +2 -0
- package/dist/node_modules/@pencil-agent/ai/dist/cli.js +121 -0
- package/dist/node_modules/@pencil-agent/ai/dist/config-path.d.ts +1 -0
- package/dist/node_modules/@pencil-agent/ai/dist/config-path.js +17 -0
- package/dist/node_modules/@pencil-agent/ai/dist/debug-logger.d.ts +94 -0
- package/dist/node_modules/@pencil-agent/ai/dist/debug-logger.js +218 -0
- package/dist/node_modules/@pencil-agent/ai/dist/env-api-keys.d.ts +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/env-api-keys.js +107 -0
- package/dist/node_modules/@pencil-agent/ai/dist/env.d.ts +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/env.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/events.d.ts +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/events.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/index.d.ts +27 -0
- package/dist/node_modules/@pencil-agent/ai/dist/index.js +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/json.d.ts +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/json.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/models.d.ts +31 -0
- package/dist/node_modules/@pencil-agent/ai/dist/models.generated.d.ts +15159 -0
- package/dist/node_modules/@pencil-agent/ai/dist/models.generated.js +14928 -0
- package/dist/node_modules/@pencil-agent/ai/dist/models.js +60 -0
- package/dist/node_modules/@pencil-agent/ai/dist/overflow.d.ts +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/overflow.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/amazon-bedrock.d.ts +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/amazon-bedrock.js +606 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/anthropic.d.ts +38 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/anthropic.js +737 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/azure-openai-responses.d.ts +21 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/azure-openai-responses.js +193 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/github-copilot-headers.d.ts +13 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/github-copilot-headers.js +34 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-gemini-cli.d.ts +79 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-gemini-cli.js +753 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-shared.d.ts +70 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-shared.js +311 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-vertex.d.ts +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google-vertex.js +380 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google.d.ts +18 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/google.js +360 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-codex-responses.d.ts +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-codex-responses.js +704 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-completions.d.ts +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-completions.js +870 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-responses-shared.d.ts +22 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-responses-shared.js +432 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-responses.d.ts +19 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-responses.js +207 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/register-builtins.d.ts +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/register-builtins.js +86 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/simple-options.d.ts +13 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/simple-options.js +40 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/transform-messages.d.ts +13 -0
- package/dist/node_modules/@pencil-agent/ai/dist/providers/transform-messages.js +175 -0
- package/dist/node_modules/@pencil-agent/ai/dist/registry.d.ts +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/registry.js +8 -0
- package/dist/node_modules/@pencil-agent/ai/dist/schema.d.ts +10 -0
- package/dist/node_modules/@pencil-agent/ai/dist/schema.js +9 -0
- package/dist/node_modules/@pencil-agent/ai/dist/stream.d.ts +25 -0
- package/dist/node_modules/@pencil-agent/ai/dist/stream.js +324 -0
- package/dist/node_modules/@pencil-agent/ai/dist/types.d.ts +306 -0
- package/dist/node_modules/@pencil-agent/ai/dist/types.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/event-stream-types.d.ts +12 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/event-stream-types.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/event-stream.d.ts +31 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/event-stream.js +98 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/http-proxy.d.ts +13 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/http-proxy.js +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/json-parse.d.ts +14 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/json-parse.js +34 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/anthropic.d.ts +22 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/anthropic.js +109 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/decode-credential.d.ts +12 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/decode-credential.js +25 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/github-copilot.d.ts +35 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/github-copilot.js +286 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/google-antigravity.d.ts +31 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/google-antigravity.js +378 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/google-gemini-cli.d.ts +31 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/google-gemini-cli.js +483 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/index.d.ts +60 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/index.js +131 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/openai-codex.d.ts +39 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/openai-codex.js +385 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/pkce.d.ts +18 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/pkce.js +36 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/types.d.ts +52 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/oauth/types.js +7 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/overflow.d.ts +57 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/overflow.js +120 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/sanitize-unicode.d.ts +16 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/sanitize-unicode.js +20 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/typebox-helpers.d.ts +22 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/typebox-helpers.js +26 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/validation.d.ts +23 -0
- package/dist/node_modules/@pencil-agent/ai/dist/utils/validation.js +78 -0
- package/dist/node_modules/@pencil-agent/ai/package.json +94 -0
- package/dist/node_modules/@pencil-agent/tui/dist/autocomplete.d.ts +62 -0
- package/dist/node_modules/@pencil-agent/tui/dist/autocomplete.js +624 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/box.d.ts +27 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/box.js +109 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/cancellable-loader.d.ts +27 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/cancellable-loader.js +40 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/editor.d.ts +218 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/editor.js +1697 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/image.d.ts +33 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/image.js +74 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/input.d.ts +42 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/input.js +438 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/loader.d.ts +26 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/loader.js +54 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/markdown.d.ts +100 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/markdown.js +634 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/select-list.d.ts +37 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/select-list.js +157 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/settings-list.d.ts +55 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/settings-list.js +190 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/spacer.d.ts +17 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/spacer.js +28 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/text.d.ts +24 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/text.js +94 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/truncated-text.d.ts +18 -0
- package/dist/node_modules/@pencil-agent/tui/dist/components/truncated-text.js +56 -0
- package/dist/node_modules/@pencil-agent/tui/dist/editor-component.d.ts +51 -0
- package/dist/node_modules/@pencil-agent/tui/dist/editor-component.js +7 -0
- package/dist/node_modules/@pencil-agent/tui/dist/fuzzy.d.ts +32 -0
- package/dist/node_modules/@pencil-agent/tui/dist/fuzzy.js +152 -0
- package/dist/node_modules/@pencil-agent/tui/dist/index.d.ts +28 -0
- package/dist/node_modules/@pencil-agent/tui/dist/index.js +37 -0
- package/dist/node_modules/@pencil-agent/tui/dist/keybindings.d.ts +44 -0
- package/dist/node_modules/@pencil-agent/tui/dist/keybindings.js +119 -0
- package/dist/node_modules/@pencil-agent/tui/dist/keys.d.ts +149 -0
- package/dist/node_modules/@pencil-agent/tui/dist/keys.js +948 -0
- package/dist/node_modules/@pencil-agent/tui/dist/kill-ring.d.ts +33 -0
- package/dist/node_modules/@pencil-agent/tui/dist/kill-ring.js +49 -0
- package/dist/node_modules/@pencil-agent/tui/dist/stdin-buffer.d.ts +38 -0
- package/dist/node_modules/@pencil-agent/tui/dist/stdin-buffer.js +307 -0
- package/dist/node_modules/@pencil-agent/tui/dist/terminal-image.d.ts +73 -0
- package/dist/node_modules/@pencil-agent/tui/dist/terminal-image.js +287 -0
- package/dist/node_modules/@pencil-agent/tui/dist/terminal.d.ts +86 -0
- package/dist/node_modules/@pencil-agent/tui/dist/terminal.js +266 -0
- package/dist/node_modules/@pencil-agent/tui/dist/tui.d.ts +219 -0
- package/dist/node_modules/@pencil-agent/tui/dist/tui.js +1001 -0
- package/dist/node_modules/@pencil-agent/tui/dist/undo-stack.d.ts +22 -0
- package/dist/node_modules/@pencil-agent/tui/dist/undo-stack.js +30 -0
- package/dist/node_modules/@pencil-agent/tui/dist/utils.d.ts +83 -0
- package/dist/node_modules/@pencil-agent/tui/dist/utils.js +811 -0
- package/dist/node_modules/@pencil-agent/tui/package.json +37 -0
- package/package.json +3 -2
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: OpenAIResponsesStreamOptions, ConvertResponsesMessagesOptions, ConvertResponsesToolsOptions, convertResponsesMessages, convertResponsesTools
|
|
3
|
+
* [FROM]: Depends on ../models.js, ../utils/json-parse.js, ../utils/sanitize-unicode.js, ./transform-messages.js
|
|
4
|
+
* [TO]: Consumed by core/lib/ai/src/index.ts
|
|
5
|
+
* [HERE]: core/lib/ai/src/providers/openai-responses-shared.ts -
|
|
6
|
+
*/
|
|
7
|
+
import type { Tool as OpenAITool, ResponseCreateParamsStreaming, ResponseInput, ResponseStreamEvent } from "openai/resources/responses/responses.js";
|
|
8
|
+
import type { Api, AssistantMessage, Context, Model, Tool, Usage } from "../types.js";
|
|
9
|
+
import type { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
10
|
+
export interface OpenAIResponsesStreamOptions {
|
|
11
|
+
serviceTier?: ResponseCreateParamsStreaming["service_tier"];
|
|
12
|
+
applyServiceTierPricing?: (usage: Usage, serviceTier: ResponseCreateParamsStreaming["service_tier"] | undefined) => void;
|
|
13
|
+
}
|
|
14
|
+
export interface ConvertResponsesMessagesOptions {
|
|
15
|
+
includeSystemPrompt?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface ConvertResponsesToolsOptions {
|
|
18
|
+
strict?: boolean | null;
|
|
19
|
+
}
|
|
20
|
+
export declare function convertResponsesMessages<TApi extends Api>(model: Model<TApi>, context: Context, allowedToolCallProviders: ReadonlySet<string>, options?: ConvertResponsesMessagesOptions): ResponseInput;
|
|
21
|
+
export declare function convertResponsesTools(tools: Tool[], options?: ConvertResponsesToolsOptions): OpenAITool[];
|
|
22
|
+
export declare function processResponsesStream<TApi extends Api>(openaiStream: AsyncIterable<ResponseStreamEvent>, output: AssistantMessage, stream: AssistantMessageEventStream, model: Model<TApi>, options?: OpenAIResponsesStreamOptions): Promise<void>;
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: OpenAIResponsesStreamOptions, ConvertResponsesMessagesOptions, ConvertResponsesToolsOptions, convertResponsesMessages, convertResponsesTools
|
|
3
|
+
* [FROM]: Depends on ../models.js, ../utils/json-parse.js, ../utils/sanitize-unicode.js, ./transform-messages.js
|
|
4
|
+
* [TO]: Consumed by core/lib/ai/src/index.ts
|
|
5
|
+
* [HERE]: core/lib/ai/src/providers/openai-responses-shared.ts -
|
|
6
|
+
*/
|
|
7
|
+
import { calculateCost } from "../models.js";
|
|
8
|
+
import { parseStreamingJson } from "../utils/json-parse.js";
|
|
9
|
+
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
|
10
|
+
import { transformMessages } from "./transform-messages.js";
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Utilities
|
|
13
|
+
// =============================================================================
|
|
14
|
+
/** Fast deterministic hash to shorten long strings */
|
|
15
|
+
function shortHash(str) {
|
|
16
|
+
let h1 = 0xdeadbeef;
|
|
17
|
+
let h2 = 0x41c6ce57;
|
|
18
|
+
for (let i = 0; i < str.length; i++) {
|
|
19
|
+
const ch = str.charCodeAt(i);
|
|
20
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
21
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
22
|
+
}
|
|
23
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
24
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
25
|
+
return (h2 >>> 0).toString(36) + (h1 >>> 0).toString(36);
|
|
26
|
+
}
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// Message conversion
|
|
29
|
+
// =============================================================================
|
|
30
|
+
export function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
|
|
31
|
+
const messages = [];
|
|
32
|
+
const normalizeToolCallId = (id) => {
|
|
33
|
+
if (!allowedToolCallProviders.has(model.provider))
|
|
34
|
+
return id;
|
|
35
|
+
if (!id.includes("|"))
|
|
36
|
+
return id;
|
|
37
|
+
const [callId, itemId] = id.split("|");
|
|
38
|
+
const sanitizedCallId = callId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
39
|
+
let sanitizedItemId = itemId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
40
|
+
// OpenAI Responses API requires item id to start with "fc"
|
|
41
|
+
if (!sanitizedItemId.startsWith("fc")) {
|
|
42
|
+
sanitizedItemId = `fc_${sanitizedItemId}`;
|
|
43
|
+
}
|
|
44
|
+
// Truncate to 64 chars and strip trailing underscores (OpenAI Codex rejects them)
|
|
45
|
+
let normalizedCallId = sanitizedCallId.length > 64 ? sanitizedCallId.slice(0, 64) : sanitizedCallId;
|
|
46
|
+
let normalizedItemId = sanitizedItemId.length > 64 ? sanitizedItemId.slice(0, 64) : sanitizedItemId;
|
|
47
|
+
normalizedCallId = normalizedCallId.replace(/_+$/, "");
|
|
48
|
+
normalizedItemId = normalizedItemId.replace(/_+$/, "");
|
|
49
|
+
return `${normalizedCallId}|${normalizedItemId}`;
|
|
50
|
+
};
|
|
51
|
+
const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
|
|
52
|
+
const includeSystemPrompt = options?.includeSystemPrompt ?? true;
|
|
53
|
+
if (includeSystemPrompt && context.systemPrompt) {
|
|
54
|
+
const role = model.reasoning ? "developer" : "system";
|
|
55
|
+
messages.push({
|
|
56
|
+
role,
|
|
57
|
+
content: sanitizeSurrogates(context.systemPrompt),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
let msgIndex = 0;
|
|
61
|
+
for (const msg of transformedMessages) {
|
|
62
|
+
if (msg.role === "user") {
|
|
63
|
+
if (typeof msg.content === "string") {
|
|
64
|
+
messages.push({
|
|
65
|
+
role: "user",
|
|
66
|
+
content: [{ type: "input_text", text: sanitizeSurrogates(msg.content) }],
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const content = msg.content.map((item) => {
|
|
71
|
+
if (item.type === "text") {
|
|
72
|
+
return {
|
|
73
|
+
type: "input_text",
|
|
74
|
+
text: sanitizeSurrogates(item.text),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
type: "input_image",
|
|
79
|
+
detail: "auto",
|
|
80
|
+
image_url: `data:${item.mimeType};base64,${item.data}`,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
const filteredContent = !model.input.includes("image")
|
|
84
|
+
? content.filter((c) => c.type !== "input_image")
|
|
85
|
+
: content;
|
|
86
|
+
if (filteredContent.length === 0)
|
|
87
|
+
continue;
|
|
88
|
+
messages.push({
|
|
89
|
+
role: "user",
|
|
90
|
+
content: filteredContent,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else if (msg.role === "assistant") {
|
|
95
|
+
const output = [];
|
|
96
|
+
const assistantMsg = msg;
|
|
97
|
+
const isDifferentModel = assistantMsg.model !== model.id &&
|
|
98
|
+
assistantMsg.provider === model.provider &&
|
|
99
|
+
assistantMsg.api === model.api;
|
|
100
|
+
for (const block of msg.content) {
|
|
101
|
+
if (block.type === "thinking") {
|
|
102
|
+
if (block.thinkingSignature) {
|
|
103
|
+
const reasoningItem = JSON.parse(block.thinkingSignature);
|
|
104
|
+
output.push(reasoningItem);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (block.type === "text") {
|
|
108
|
+
const textBlock = block;
|
|
109
|
+
// OpenAI requires id to be max 64 characters
|
|
110
|
+
let msgId = textBlock.textSignature;
|
|
111
|
+
if (!msgId) {
|
|
112
|
+
msgId = `msg_${msgIndex}`;
|
|
113
|
+
}
|
|
114
|
+
else if (msgId.length > 64) {
|
|
115
|
+
msgId = `msg_${shortHash(msgId)}`;
|
|
116
|
+
}
|
|
117
|
+
output.push({
|
|
118
|
+
type: "message",
|
|
119
|
+
role: "assistant",
|
|
120
|
+
content: [{ type: "output_text", text: sanitizeSurrogates(textBlock.text), annotations: [] }],
|
|
121
|
+
status: "completed",
|
|
122
|
+
id: msgId,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
else if (block.type === "toolCall") {
|
|
126
|
+
const toolCall = block;
|
|
127
|
+
const [callId, itemIdRaw] = toolCall.id.split("|");
|
|
128
|
+
let itemId = itemIdRaw;
|
|
129
|
+
// For different-model messages, set id to undefined to avoid pairing validation.
|
|
130
|
+
// OpenAI tracks which fc_xxx IDs were paired with rs_xxx reasoning items.
|
|
131
|
+
// By omitting the id, we avoid triggering that validation (like cross-provider does).
|
|
132
|
+
if (isDifferentModel && itemId?.startsWith("fc_")) {
|
|
133
|
+
itemId = undefined;
|
|
134
|
+
}
|
|
135
|
+
output.push({
|
|
136
|
+
type: "function_call",
|
|
137
|
+
id: itemId,
|
|
138
|
+
call_id: callId,
|
|
139
|
+
name: toolCall.name,
|
|
140
|
+
arguments: JSON.stringify(toolCall.arguments),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (output.length === 0)
|
|
145
|
+
continue;
|
|
146
|
+
messages.push(...output);
|
|
147
|
+
}
|
|
148
|
+
else if (msg.role === "toolResult") {
|
|
149
|
+
// Extract text and image content
|
|
150
|
+
const textResult = msg.content
|
|
151
|
+
.filter((c) => c.type === "text")
|
|
152
|
+
.map((c) => c.text)
|
|
153
|
+
.join("\n");
|
|
154
|
+
const hasImages = msg.content.some((c) => c.type === "image");
|
|
155
|
+
// Always send function_call_output with text (or placeholder if only images)
|
|
156
|
+
const hasText = textResult.length > 0;
|
|
157
|
+
const [callId] = msg.toolCallId.split("|");
|
|
158
|
+
messages.push({
|
|
159
|
+
type: "function_call_output",
|
|
160
|
+
call_id: callId,
|
|
161
|
+
output: sanitizeSurrogates(hasText ? textResult : "(see attached image)"),
|
|
162
|
+
});
|
|
163
|
+
// If there are images and model supports them, send a follow-up user message with images
|
|
164
|
+
if (hasImages && model.input.includes("image")) {
|
|
165
|
+
const contentParts = [];
|
|
166
|
+
// Add text prefix
|
|
167
|
+
contentParts.push({
|
|
168
|
+
type: "input_text",
|
|
169
|
+
text: "Attached image(s) from tool result:",
|
|
170
|
+
});
|
|
171
|
+
// Add images
|
|
172
|
+
for (const block of msg.content) {
|
|
173
|
+
if (block.type === "image") {
|
|
174
|
+
contentParts.push({
|
|
175
|
+
type: "input_image",
|
|
176
|
+
detail: "auto",
|
|
177
|
+
image_url: `data:${block.mimeType};base64,${block.data}`,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
messages.push({
|
|
182
|
+
role: "user",
|
|
183
|
+
content: contentParts,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
msgIndex++;
|
|
188
|
+
}
|
|
189
|
+
return messages;
|
|
190
|
+
}
|
|
191
|
+
// =============================================================================
|
|
192
|
+
// Tool conversion
|
|
193
|
+
// =============================================================================
|
|
194
|
+
export function convertResponsesTools(tools, options) {
|
|
195
|
+
const strict = options?.strict === undefined ? false : options.strict;
|
|
196
|
+
return tools.map((tool) => ({
|
|
197
|
+
type: "function",
|
|
198
|
+
name: tool.name,
|
|
199
|
+
description: tool.description,
|
|
200
|
+
parameters: tool.parameters, // TypeBox already generates JSON Schema
|
|
201
|
+
strict,
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
// =============================================================================
|
|
205
|
+
// Stream processing
|
|
206
|
+
// =============================================================================
|
|
207
|
+
export async function processResponsesStream(openaiStream, output, stream, model, options) {
|
|
208
|
+
let currentItem = null;
|
|
209
|
+
let currentBlock = null;
|
|
210
|
+
const blocks = output.content;
|
|
211
|
+
const blockIndex = () => blocks.length - 1;
|
|
212
|
+
for await (const event of openaiStream) {
|
|
213
|
+
if (event.type === "response.output_item.added") {
|
|
214
|
+
const item = event.item;
|
|
215
|
+
if (item.type === "reasoning") {
|
|
216
|
+
currentItem = item;
|
|
217
|
+
currentBlock = { type: "thinking", thinking: "" };
|
|
218
|
+
output.content.push(currentBlock);
|
|
219
|
+
stream.push({ type: "thinking_start", contentIndex: blockIndex(), partial: output });
|
|
220
|
+
}
|
|
221
|
+
else if (item.type === "message") {
|
|
222
|
+
currentItem = item;
|
|
223
|
+
currentBlock = { type: "text", text: "" };
|
|
224
|
+
output.content.push(currentBlock);
|
|
225
|
+
stream.push({ type: "text_start", contentIndex: blockIndex(), partial: output });
|
|
226
|
+
}
|
|
227
|
+
else if (item.type === "function_call") {
|
|
228
|
+
currentItem = item;
|
|
229
|
+
currentBlock = {
|
|
230
|
+
type: "toolCall",
|
|
231
|
+
id: `${item.call_id}|${item.id}`,
|
|
232
|
+
name: item.name,
|
|
233
|
+
arguments: {},
|
|
234
|
+
partialJson: item.arguments || "",
|
|
235
|
+
};
|
|
236
|
+
output.content.push(currentBlock);
|
|
237
|
+
stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else if (event.type === "response.reasoning_summary_part.added") {
|
|
241
|
+
if (currentItem && currentItem.type === "reasoning") {
|
|
242
|
+
currentItem.summary = currentItem.summary || [];
|
|
243
|
+
currentItem.summary.push(event.part);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
else if (event.type === "response.reasoning_summary_text.delta") {
|
|
247
|
+
if (currentItem?.type === "reasoning" && currentBlock?.type === "thinking") {
|
|
248
|
+
currentItem.summary = currentItem.summary || [];
|
|
249
|
+
const lastPart = currentItem.summary[currentItem.summary.length - 1];
|
|
250
|
+
if (lastPart) {
|
|
251
|
+
currentBlock.thinking += event.delta;
|
|
252
|
+
lastPart.text += event.delta;
|
|
253
|
+
stream.push({
|
|
254
|
+
type: "thinking_delta",
|
|
255
|
+
contentIndex: blockIndex(),
|
|
256
|
+
delta: event.delta,
|
|
257
|
+
partial: output,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else if (event.type === "response.reasoning_summary_part.done") {
|
|
263
|
+
if (currentItem?.type === "reasoning" && currentBlock?.type === "thinking") {
|
|
264
|
+
currentItem.summary = currentItem.summary || [];
|
|
265
|
+
const lastPart = currentItem.summary[currentItem.summary.length - 1];
|
|
266
|
+
if (lastPart) {
|
|
267
|
+
currentBlock.thinking += "\n\n";
|
|
268
|
+
lastPart.text += "\n\n";
|
|
269
|
+
stream.push({
|
|
270
|
+
type: "thinking_delta",
|
|
271
|
+
contentIndex: blockIndex(),
|
|
272
|
+
delta: "\n\n",
|
|
273
|
+
partial: output,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else if (event.type === "response.content_part.added") {
|
|
279
|
+
if (currentItem?.type === "message") {
|
|
280
|
+
currentItem.content = currentItem.content || [];
|
|
281
|
+
// Filter out ReasoningText, only accept output_text and refusal
|
|
282
|
+
if (event.part.type === "output_text" || event.part.type === "refusal") {
|
|
283
|
+
currentItem.content.push(event.part);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else if (event.type === "response.output_text.delta") {
|
|
288
|
+
if (currentItem?.type === "message" && currentBlock?.type === "text") {
|
|
289
|
+
if (!currentItem.content || currentItem.content.length === 0) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const lastPart = currentItem.content[currentItem.content.length - 1];
|
|
293
|
+
if (lastPart?.type === "output_text") {
|
|
294
|
+
currentBlock.text += event.delta;
|
|
295
|
+
lastPart.text += event.delta;
|
|
296
|
+
stream.push({
|
|
297
|
+
type: "text_delta",
|
|
298
|
+
contentIndex: blockIndex(),
|
|
299
|
+
delta: event.delta,
|
|
300
|
+
partial: output,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
else if (event.type === "response.refusal.delta") {
|
|
306
|
+
if (currentItem?.type === "message" && currentBlock?.type === "text") {
|
|
307
|
+
if (!currentItem.content || currentItem.content.length === 0) {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
const lastPart = currentItem.content[currentItem.content.length - 1];
|
|
311
|
+
if (lastPart?.type === "refusal") {
|
|
312
|
+
currentBlock.text += event.delta;
|
|
313
|
+
lastPart.refusal += event.delta;
|
|
314
|
+
stream.push({
|
|
315
|
+
type: "text_delta",
|
|
316
|
+
contentIndex: blockIndex(),
|
|
317
|
+
delta: event.delta,
|
|
318
|
+
partial: output,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
else if (event.type === "response.function_call_arguments.delta") {
|
|
324
|
+
if (currentItem?.type === "function_call" && currentBlock?.type === "toolCall") {
|
|
325
|
+
currentBlock.partialJson += event.delta;
|
|
326
|
+
currentBlock.arguments = parseStreamingJson(currentBlock.partialJson);
|
|
327
|
+
stream.push({
|
|
328
|
+
type: "toolcall_delta",
|
|
329
|
+
contentIndex: blockIndex(),
|
|
330
|
+
delta: event.delta,
|
|
331
|
+
partial: output,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
else if (event.type === "response.function_call_arguments.done") {
|
|
336
|
+
if (currentItem?.type === "function_call" && currentBlock?.type === "toolCall") {
|
|
337
|
+
currentBlock.partialJson = event.arguments;
|
|
338
|
+
currentBlock.arguments = parseStreamingJson(currentBlock.partialJson);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
else if (event.type === "response.output_item.done") {
|
|
342
|
+
const item = event.item;
|
|
343
|
+
if (item.type === "reasoning" && currentBlock?.type === "thinking") {
|
|
344
|
+
currentBlock.thinking = item.summary?.map((s) => s.text).join("\n\n") || "";
|
|
345
|
+
currentBlock.thinkingSignature = JSON.stringify(item);
|
|
346
|
+
stream.push({
|
|
347
|
+
type: "thinking_end",
|
|
348
|
+
contentIndex: blockIndex(),
|
|
349
|
+
content: currentBlock.thinking,
|
|
350
|
+
partial: output,
|
|
351
|
+
});
|
|
352
|
+
currentBlock = null;
|
|
353
|
+
}
|
|
354
|
+
else if (item.type === "message" && currentBlock?.type === "text") {
|
|
355
|
+
currentBlock.text = item.content.map((c) => (c.type === "output_text" ? c.text : c.refusal)).join("");
|
|
356
|
+
currentBlock.textSignature = item.id;
|
|
357
|
+
stream.push({
|
|
358
|
+
type: "text_end",
|
|
359
|
+
contentIndex: blockIndex(),
|
|
360
|
+
content: currentBlock.text,
|
|
361
|
+
partial: output,
|
|
362
|
+
});
|
|
363
|
+
currentBlock = null;
|
|
364
|
+
}
|
|
365
|
+
else if (item.type === "function_call") {
|
|
366
|
+
const args = currentBlock?.type === "toolCall" && currentBlock.partialJson
|
|
367
|
+
? parseStreamingJson(currentBlock.partialJson)
|
|
368
|
+
: parseStreamingJson(item.arguments || "{}");
|
|
369
|
+
const toolCall = {
|
|
370
|
+
type: "toolCall",
|
|
371
|
+
id: `${item.call_id}|${item.id}`,
|
|
372
|
+
name: item.name,
|
|
373
|
+
arguments: args,
|
|
374
|
+
};
|
|
375
|
+
currentBlock = null;
|
|
376
|
+
stream.push({ type: "toolcall_end", contentIndex: blockIndex(), toolCall, partial: output });
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
else if (event.type === "response.completed") {
|
|
380
|
+
const response = event.response;
|
|
381
|
+
if (response?.usage) {
|
|
382
|
+
const cachedTokens = response.usage.input_tokens_details?.cached_tokens || 0;
|
|
383
|
+
output.usage = {
|
|
384
|
+
// OpenAI includes cached tokens in input_tokens, so subtract to get non-cached input
|
|
385
|
+
input: (response.usage.input_tokens || 0) - cachedTokens,
|
|
386
|
+
output: response.usage.output_tokens || 0,
|
|
387
|
+
cacheRead: cachedTokens,
|
|
388
|
+
cacheWrite: 0,
|
|
389
|
+
totalTokens: response.usage.total_tokens || 0,
|
|
390
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
calculateCost(model, output.usage);
|
|
394
|
+
if (options?.applyServiceTierPricing) {
|
|
395
|
+
const serviceTier = response?.service_tier ?? options.serviceTier;
|
|
396
|
+
options.applyServiceTierPricing(output.usage, serviceTier);
|
|
397
|
+
}
|
|
398
|
+
// Map status to stop reason
|
|
399
|
+
output.stopReason = mapStopReason(response?.status);
|
|
400
|
+
if (output.content.some((b) => b.type === "toolCall") && output.stopReason === "stop") {
|
|
401
|
+
output.stopReason = "toolUse";
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
else if (event.type === "error") {
|
|
405
|
+
throw new Error(`Error Code ${event.code}: ${event.message}` || "Unknown error");
|
|
406
|
+
}
|
|
407
|
+
else if (event.type === "response.failed") {
|
|
408
|
+
throw new Error("Unknown error");
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function mapStopReason(status) {
|
|
413
|
+
if (!status)
|
|
414
|
+
return "stop";
|
|
415
|
+
switch (status) {
|
|
416
|
+
case "completed":
|
|
417
|
+
return "stop";
|
|
418
|
+
case "incomplete":
|
|
419
|
+
return "length";
|
|
420
|
+
case "failed":
|
|
421
|
+
case "cancelled":
|
|
422
|
+
return "error";
|
|
423
|
+
// These two are wonky ...
|
|
424
|
+
case "in_progress":
|
|
425
|
+
case "queued":
|
|
426
|
+
return "stop";
|
|
427
|
+
default: {
|
|
428
|
+
const _exhaustive = status;
|
|
429
|
+
throw new Error(`Unhandled stop reason: ${_exhaustive}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: OpenAIResponsesOptions, streamOpenAIResponses, streamSimpleOpenAIResponses
|
|
3
|
+
* [FROM]: Depends on openai, ../env-api-keys.js, ../models.js, ../utils/event-stream.js, ./github-copilot-headers.js
|
|
4
|
+
* [TO]: Consumed by core/lib/ai/src/index.ts
|
|
5
|
+
* [HERE]: core/lib/ai/src/providers/openai-responses.ts -
|
|
6
|
+
*/
|
|
7
|
+
import type { ResponseCreateParamsStreaming } from "openai/resources/responses/responses.js";
|
|
8
|
+
import type { SimpleStreamOptions, StreamFunction, StreamOptions } from "../types.js";
|
|
9
|
+
export interface OpenAIResponsesOptions extends StreamOptions {
|
|
10
|
+
reasoningEffort?: "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
11
|
+
reasoningSummary?: "auto" | "detailed" | "concise" | null;
|
|
12
|
+
serviceTier?: ResponseCreateParamsStreaming["service_tier"];
|
|
13
|
+
toolChoice?: unknown;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generate function for OpenAI Responses API
|
|
17
|
+
*/
|
|
18
|
+
export declare const streamOpenAIResponses: StreamFunction<"openai-responses", OpenAIResponsesOptions>;
|
|
19
|
+
export declare const streamSimpleOpenAIResponses: StreamFunction<"openai-responses", SimpleStreamOptions>;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: OpenAIResponsesOptions, streamOpenAIResponses, streamSimpleOpenAIResponses
|
|
3
|
+
* [FROM]: Depends on openai, ../env-api-keys.js, ../models.js, ../utils/event-stream.js, ./github-copilot-headers.js
|
|
4
|
+
* [TO]: Consumed by core/lib/ai/src/index.ts
|
|
5
|
+
* [HERE]: core/lib/ai/src/providers/openai-responses.ts -
|
|
6
|
+
*/
|
|
7
|
+
import OpenAI from "openai";
|
|
8
|
+
import { getEnvApiKey } from "../env-api-keys.js";
|
|
9
|
+
import { supportsXhigh } from "../models.js";
|
|
10
|
+
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
11
|
+
import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
|
|
12
|
+
import { convertResponsesMessages, convertResponsesTools, processResponsesStream } from "./openai-responses-shared.js";
|
|
13
|
+
import { buildBaseOptions, clampReasoning } from "./simple-options.js";
|
|
14
|
+
const OPENAI_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
|
|
15
|
+
/**
|
|
16
|
+
* Resolve cache retention preference.
|
|
17
|
+
* Defaults to "short" and uses NANOPENCIL_CACHE_RETENTION for backward compatibility.
|
|
18
|
+
*/
|
|
19
|
+
function resolveCacheRetention(cacheRetention) {
|
|
20
|
+
if (cacheRetention) {
|
|
21
|
+
return cacheRetention;
|
|
22
|
+
}
|
|
23
|
+
if (typeof process !== "undefined" && process.env.NANOPENCIL_CACHE_RETENTION === "long") {
|
|
24
|
+
return "long";
|
|
25
|
+
}
|
|
26
|
+
return "short";
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get prompt cache retention based on cacheRetention and base URL.
|
|
30
|
+
* Only applies to direct OpenAI API calls (api.openai.com).
|
|
31
|
+
*/
|
|
32
|
+
function getPromptCacheRetention(baseUrl, cacheRetention) {
|
|
33
|
+
if (cacheRetention !== "long") {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
if (baseUrl.includes("api.openai.com")) {
|
|
37
|
+
return "24h";
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Generate function for OpenAI Responses API
|
|
43
|
+
*/
|
|
44
|
+
export const streamOpenAIResponses = (model, context, options) => {
|
|
45
|
+
const stream = new AssistantMessageEventStream();
|
|
46
|
+
// Start async processing
|
|
47
|
+
(async () => {
|
|
48
|
+
const output = {
|
|
49
|
+
role: "assistant",
|
|
50
|
+
content: [],
|
|
51
|
+
api: model.api,
|
|
52
|
+
provider: model.provider,
|
|
53
|
+
model: model.id,
|
|
54
|
+
usage: {
|
|
55
|
+
input: 0,
|
|
56
|
+
output: 0,
|
|
57
|
+
cacheRead: 0,
|
|
58
|
+
cacheWrite: 0,
|
|
59
|
+
totalTokens: 0,
|
|
60
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
61
|
+
},
|
|
62
|
+
stopReason: "stop",
|
|
63
|
+
timestamp: Date.now(),
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
// Create OpenAI client
|
|
67
|
+
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
68
|
+
const client = createClient(model, context, apiKey, options?.headers);
|
|
69
|
+
const params = buildParams(model, context, options);
|
|
70
|
+
options?.onPayload?.(params);
|
|
71
|
+
const openaiStream = await client.responses.create(params, options?.signal ? { signal: options.signal } : undefined);
|
|
72
|
+
stream.push({ type: "start", partial: output });
|
|
73
|
+
await processResponsesStream(openaiStream, output, stream, model, {
|
|
74
|
+
serviceTier: options?.serviceTier,
|
|
75
|
+
applyServiceTierPricing,
|
|
76
|
+
});
|
|
77
|
+
if (options?.signal?.aborted) {
|
|
78
|
+
throw new Error("Request was aborted");
|
|
79
|
+
}
|
|
80
|
+
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
81
|
+
throw new Error("An unknown error occurred");
|
|
82
|
+
}
|
|
83
|
+
stream.push({ type: "done", reason: output.stopReason, message: output });
|
|
84
|
+
stream.end();
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
for (const block of output.content)
|
|
88
|
+
delete block.index;
|
|
89
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
90
|
+
output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
|
91
|
+
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
92
|
+
stream.end();
|
|
93
|
+
}
|
|
94
|
+
})();
|
|
95
|
+
return stream;
|
|
96
|
+
};
|
|
97
|
+
export const streamSimpleOpenAIResponses = (model, context, options) => {
|
|
98
|
+
const apiKey = options?.apiKey || getEnvApiKey(model.provider);
|
|
99
|
+
if (!apiKey) {
|
|
100
|
+
throw new Error(`No API key for provider: ${model.provider}`);
|
|
101
|
+
}
|
|
102
|
+
const base = buildBaseOptions(model, options, apiKey);
|
|
103
|
+
const reasoningEffort = supportsXhigh(model) ? options?.reasoning : clampReasoning(options?.reasoning);
|
|
104
|
+
return streamOpenAIResponses(model, context, {
|
|
105
|
+
...base,
|
|
106
|
+
reasoningEffort,
|
|
107
|
+
toolChoice: options?.toolChoice,
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
function createClient(model, context, apiKey, optionsHeaders) {
|
|
111
|
+
if (!apiKey) {
|
|
112
|
+
if (!process.env.OPENAI_API_KEY) {
|
|
113
|
+
throw new Error("OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass it as an argument.");
|
|
114
|
+
}
|
|
115
|
+
apiKey = process.env.OPENAI_API_KEY;
|
|
116
|
+
}
|
|
117
|
+
const headers = { ...model.headers };
|
|
118
|
+
if (model.provider === "github-copilot") {
|
|
119
|
+
const hasImages = hasCopilotVisionInput(context.messages);
|
|
120
|
+
const copilotHeaders = buildCopilotDynamicHeaders({
|
|
121
|
+
messages: context.messages,
|
|
122
|
+
hasImages,
|
|
123
|
+
});
|
|
124
|
+
Object.assign(headers, copilotHeaders);
|
|
125
|
+
}
|
|
126
|
+
// Merge options headers last so they can override defaults
|
|
127
|
+
if (optionsHeaders) {
|
|
128
|
+
Object.assign(headers, optionsHeaders);
|
|
129
|
+
}
|
|
130
|
+
return new OpenAI({
|
|
131
|
+
apiKey,
|
|
132
|
+
baseURL: model.baseUrl,
|
|
133
|
+
dangerouslyAllowBrowser: true,
|
|
134
|
+
defaultHeaders: headers,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function buildParams(model, context, options) {
|
|
138
|
+
const messages = convertResponsesMessages(model, context, OPENAI_TOOL_CALL_PROVIDERS);
|
|
139
|
+
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
|
|
140
|
+
const params = {
|
|
141
|
+
model: model.id,
|
|
142
|
+
input: messages,
|
|
143
|
+
stream: true,
|
|
144
|
+
prompt_cache_key: cacheRetention === "none" ? undefined : options?.sessionId,
|
|
145
|
+
prompt_cache_retention: getPromptCacheRetention(model.baseUrl, cacheRetention),
|
|
146
|
+
store: false,
|
|
147
|
+
};
|
|
148
|
+
if (options?.maxTokens) {
|
|
149
|
+
params.max_output_tokens = options?.maxTokens;
|
|
150
|
+
}
|
|
151
|
+
if (options?.temperature !== undefined) {
|
|
152
|
+
params.temperature = options?.temperature;
|
|
153
|
+
}
|
|
154
|
+
if (options?.serviceTier !== undefined) {
|
|
155
|
+
params.service_tier = options.serviceTier;
|
|
156
|
+
}
|
|
157
|
+
if (context.tools) {
|
|
158
|
+
params.tools = convertResponsesTools(context.tools);
|
|
159
|
+
}
|
|
160
|
+
if (options?.toolChoice !== undefined) {
|
|
161
|
+
params.tool_choice = options.toolChoice;
|
|
162
|
+
}
|
|
163
|
+
if (model.reasoning) {
|
|
164
|
+
if (options?.reasoningEffort || options?.reasoningSummary) {
|
|
165
|
+
params.reasoning = {
|
|
166
|
+
effort: options?.reasoningEffort || "medium",
|
|
167
|
+
summary: options?.reasoningSummary || "auto",
|
|
168
|
+
};
|
|
169
|
+
params.include = ["reasoning.encrypted_content"];
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
if (model.name.startsWith("gpt-5")) {
|
|
173
|
+
// Jesus Christ, see https://community.openai.com/t/need-reasoning-false-option-for-gpt-5/1351588/7
|
|
174
|
+
messages.push({
|
|
175
|
+
role: "developer",
|
|
176
|
+
content: [
|
|
177
|
+
{
|
|
178
|
+
type: "input_text",
|
|
179
|
+
text: "# Juice: 0 !important",
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return params;
|
|
187
|
+
}
|
|
188
|
+
function getServiceTierCostMultiplier(serviceTier) {
|
|
189
|
+
switch (serviceTier) {
|
|
190
|
+
case "flex":
|
|
191
|
+
return 0.5;
|
|
192
|
+
case "priority":
|
|
193
|
+
return 2;
|
|
194
|
+
default:
|
|
195
|
+
return 1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function applyServiceTierPricing(usage, serviceTier) {
|
|
199
|
+
const multiplier = getServiceTierCostMultiplier(serviceTier);
|
|
200
|
+
if (multiplier === 1)
|
|
201
|
+
return;
|
|
202
|
+
usage.cost.input *= multiplier;
|
|
203
|
+
usage.cost.output *= multiplier;
|
|
204
|
+
usage.cost.cacheRead *= multiplier;
|
|
205
|
+
usage.cost.cacheWrite *= multiplier;
|
|
206
|
+
usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
|
|
207
|
+
}
|