@opencode/ai 0.0.0-beta-19275
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +682 -0
- package/dist/cache-policy.d.ts +2 -0
- package/dist/cache-policy.js +129 -0
- package/dist/image-client.d.ts +19 -0
- package/dist/image-client.js +28 -0
- package/dist/image.d.ts +110 -0
- package/dist/image.js +103 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +12 -0
- package/dist/llm.d.ts +252 -0
- package/dist/llm.js +74 -0
- package/dist/protocols/anthropic-messages.d.ts +957 -0
- package/dist/protocols/anthropic-messages.js +1414 -0
- package/dist/protocols/bedrock-converse.d.ts +499 -0
- package/dist/protocols/bedrock-converse.js +643 -0
- package/dist/protocols/bedrock-event-stream.d.ts +9 -0
- package/dist/protocols/bedrock-event-stream.js +97 -0
- package/dist/protocols/gemini.d.ts +285 -0
- package/dist/protocols/gemini.js +671 -0
- package/dist/protocols/google-images.d.ts +30 -0
- package/dist/protocols/google-images.js +181 -0
- package/dist/protocols/index.d.ts +12 -0
- package/dist/protocols/index.js +12 -0
- package/dist/protocols/meta-images.d.ts +26 -0
- package/dist/protocols/meta-images.js +89 -0
- package/dist/protocols/meta-messages.d.ts +350 -0
- package/dist/protocols/meta-messages.js +41 -0
- package/dist/protocols/meta-responses.d.ts +473 -0
- package/dist/protocols/meta-responses.js +173 -0
- package/dist/protocols/mistral-chat.d.ts +540 -0
- package/dist/protocols/mistral-chat.js +624 -0
- package/dist/protocols/open-responses-channel.d.ts +23 -0
- package/dist/protocols/open-responses-channel.js +141 -0
- package/dist/protocols/open-responses-continuation.d.ts +12 -0
- package/dist/protocols/open-responses-continuation.js +161 -0
- package/dist/protocols/open-responses.d.ts +1583 -0
- package/dist/protocols/open-responses.js +1206 -0
- package/dist/protocols/openai-chat.d.ts +860 -0
- package/dist/protocols/openai-chat.js +966 -0
- package/dist/protocols/openai-compatible-chat.d.ts +109 -0
- package/dist/protocols/openai-compatible-chat.js +19 -0
- package/dist/protocols/openai-compatible-responses.d.ts +172 -0
- package/dist/protocols/openai-compatible-responses.js +18 -0
- package/dist/protocols/openai-images.d.ts +32 -0
- package/dist/protocols/openai-images.js +183 -0
- package/dist/protocols/openai-responses.d.ts +1389 -0
- package/dist/protocols/openai-responses.js +260 -0
- package/dist/protocols/shared.d.ts +193 -0
- package/dist/protocols/shared.js +290 -0
- package/dist/protocols/utils/bedrock-auth.d.ts +60 -0
- package/dist/protocols/utils/bedrock-auth.js +85 -0
- package/dist/protocols/utils/bedrock-cache.d.ts +19 -0
- package/dist/protocols/utils/bedrock-cache.js +49 -0
- package/dist/protocols/utils/bedrock-media.d.ts +55 -0
- package/dist/protocols/utils/bedrock-media.js +73 -0
- package/dist/protocols/utils/cache.d.ts +6 -0
- package/dist/protocols/utils/cache.js +5 -0
- package/dist/protocols/utils/gemini-tool-schema.d.ts +2 -0
- package/dist/protocols/utils/gemini-tool-schema.js +103 -0
- package/dist/protocols/utils/image-input.d.ts +21 -0
- package/dist/protocols/utils/image-input.js +20 -0
- package/dist/protocols/utils/lifecycle.d.ts +24 -0
- package/dist/protocols/utils/lifecycle.js +72 -0
- package/dist/protocols/utils/meta-image.d.ts +2 -0
- package/dist/protocols/utils/meta-image.js +13 -0
- package/dist/protocols/utils/open-responses-options.d.ts +56 -0
- package/dist/protocols/utils/open-responses-options.js +61 -0
- package/dist/protocols/utils/openai-image.d.ts +5 -0
- package/dist/protocols/utils/openai-image.js +18 -0
- package/dist/protocols/utils/openai-options.d.ts +16 -0
- package/dist/protocols/utils/openai-options.js +14 -0
- package/dist/protocols/utils/partial-json-options.d.ts +62 -0
- package/dist/protocols/utils/partial-json-options.js +52 -0
- package/dist/protocols/utils/partial-json.d.ts +8 -0
- package/dist/protocols/utils/partial-json.js +279 -0
- package/dist/protocols/utils/responses-checkpoint.d.ts +4 -0
- package/dist/protocols/utils/responses-checkpoint.js +107 -0
- package/dist/protocols/utils/responses-compaction.d.ts +4 -0
- package/dist/protocols/utils/responses-compaction.js +129 -0
- package/dist/protocols/utils/responses-hosted-tools.d.ts +26 -0
- package/dist/protocols/utils/responses-hosted-tools.js +32 -0
- package/dist/protocols/utils/tool-schema.d.ts +8 -0
- package/dist/protocols/utils/tool-schema.js +54 -0
- package/dist/protocols/utils/tool-stream.d.ts +710 -0
- package/dist/protocols/utils/tool-stream.js +155 -0
- package/dist/protocols/xai-images.d.ts +26 -0
- package/dist/protocols/xai-images.js +106 -0
- package/dist/protocols/xai-responses.d.ts +250 -0
- package/dist/protocols/xai-responses.js +81 -0
- package/dist/protocols/zai-images.d.ts +22 -0
- package/dist/protocols/zai-images.js +79 -0
- package/dist/protocols.d.ts +1 -0
- package/dist/protocols.js +1 -0
- package/dist/provider-error.d.ts +15 -0
- package/dist/provider-error.js +125 -0
- package/dist/provider-package.d.ts +11 -0
- package/dist/provider-package.js +1 -0
- package/dist/provider.d.ts +23 -0
- package/dist/provider.js +2 -0
- package/dist/providers/amazon-bedrock/mantle/chat.d.ts +2 -0
- package/dist/providers/amazon-bedrock/mantle/chat.js +1 -0
- package/dist/providers/amazon-bedrock/mantle/responses.d.ts +2 -0
- package/dist/providers/amazon-bedrock/mantle/responses.js +1 -0
- package/dist/providers/amazon-bedrock/mantle.d.ts +2 -0
- package/dist/providers/amazon-bedrock/mantle.js +1 -0
- package/dist/providers/amazon-bedrock-mantle.d.ts +371 -0
- package/dist/providers/amazon-bedrock-mantle.js +76 -0
- package/dist/providers/amazon-bedrock.d.ts +173 -0
- package/dist/providers/amazon-bedrock.js +41 -0
- package/dist/providers/anthropic-compatible.d.ts +304 -0
- package/dist/providers/anthropic-compatible.js +44 -0
- package/dist/providers/anthropic.d.ts +302 -0
- package/dist/providers/anthropic.js +39 -0
- package/dist/providers/azure/chat.d.ts +2 -0
- package/dist/providers/azure/chat.js +1 -0
- package/dist/providers/azure/responses.d.ts +2 -0
- package/dist/providers/azure/responses.js +1 -0
- package/dist/providers/azure.d.ts +599 -0
- package/dist/providers/azure.js +115 -0
- package/dist/providers/baseten.d.ts +229 -0
- package/dist/providers/baseten.js +38 -0
- package/dist/providers/cerebras.d.ts +228 -0
- package/dist/providers/cerebras.js +40 -0
- package/dist/providers/cloudflare-ai-gateway.d.ts +238 -0
- package/dist/providers/cloudflare-ai-gateway.js +59 -0
- package/dist/providers/cloudflare-workers-ai.d.ts +232 -0
- package/dist/providers/cloudflare-workers-ai.js +45 -0
- package/dist/providers/deepinfra.d.ts +228 -0
- package/dist/providers/deepinfra.js +43 -0
- package/dist/providers/deepseek.d.ts +229 -0
- package/dist/providers/deepseek.js +41 -0
- package/dist/providers/fireworks.d.ts +229 -0
- package/dist/providers/fireworks.js +38 -0
- package/dist/providers/google-vertex/chat.d.ts +2 -0
- package/dist/providers/google-vertex/chat.js +1 -0
- package/dist/providers/google-vertex/gemini.d.ts +2 -0
- package/dist/providers/google-vertex/gemini.js +1 -0
- package/dist/providers/google-vertex/messages.d.ts +2 -0
- package/dist/providers/google-vertex/messages.js +1 -0
- package/dist/providers/google-vertex/responses.d.ts +2 -0
- package/dist/providers/google-vertex/responses.js +1 -0
- package/dist/providers/google-vertex-chat.d.ts +133 -0
- package/dist/providers/google-vertex-chat.js +55 -0
- package/dist/providers/google-vertex-messages.d.ts +301 -0
- package/dist/providers/google-vertex-messages.js +75 -0
- package/dist/providers/google-vertex-responses.d.ts +198 -0
- package/dist/providers/google-vertex-responses.js +56 -0
- package/dist/providers/google-vertex-shared.d.ts +22 -0
- package/dist/providers/google-vertex-shared.js +61 -0
- package/dist/providers/google-vertex.d.ts +120 -0
- package/dist/providers/google-vertex.js +92 -0
- package/dist/providers/google.d.ts +115 -0
- package/dist/providers/google.js +42 -0
- package/dist/providers/groq.d.ts +315 -0
- package/dist/providers/groq.js +81 -0
- package/dist/providers/index.d.ts +28 -0
- package/dist/providers/index.js +28 -0
- package/dist/providers/meta/chat.d.ts +2 -0
- package/dist/providers/meta/chat.js +1 -0
- package/dist/providers/meta/messages.d.ts +2 -0
- package/dist/providers/meta/messages.js +1 -0
- package/dist/providers/meta/responses.d.ts +2 -0
- package/dist/providers/meta/responses.js +1 -0
- package/dist/providers/meta.d.ts +631 -0
- package/dist/providers/meta.js +113 -0
- package/dist/providers/minimax/chat.d.ts +4 -0
- package/dist/providers/minimax/chat.js +8 -0
- package/dist/providers/minimax/messages.d.ts +1 -0
- package/dist/providers/minimax/messages.js +1 -0
- package/dist/providers/minimax/responses.d.ts +4 -0
- package/dist/providers/minimax/responses.js +8 -0
- package/dist/providers/minimax.d.ts +593 -0
- package/dist/providers/minimax.js +89 -0
- package/dist/providers/mistral.d.ts +208 -0
- package/dist/providers/mistral.js +28 -0
- package/dist/providers/open-responses-options.d.ts +6 -0
- package/dist/providers/open-responses-options.js +1 -0
- package/dist/providers/openai/chat.d.ts +2 -0
- package/dist/providers/openai/chat.js +1 -0
- package/dist/providers/openai/responses.d.ts +2 -0
- package/dist/providers/openai/responses.js +1 -0
- package/dist/providers/openai-compatible/responses.d.ts +1 -0
- package/dist/providers/openai-compatible/responses.js +1 -0
- package/dist/providers/openai-compatible-responses.d.ts +196 -0
- package/dist/providers/openai-compatible-responses.js +32 -0
- package/dist/providers/openai-compatible.d.ts +131 -0
- package/dist/providers/openai-compatible.js +33 -0
- package/dist/providers/openai-options.d.ts +26 -0
- package/dist/providers/openai-options.js +41 -0
- package/dist/providers/openai.d.ts +405 -0
- package/dist/providers/openai.js +82 -0
- package/dist/providers/openrouter.d.ts +564 -0
- package/dist/providers/openrouter.js +114 -0
- package/dist/providers/togetherai.d.ts +228 -0
- package/dist/providers/togetherai.js +40 -0
- package/dist/providers/xai.d.ts +164 -0
- package/dist/providers/xai.js +84 -0
- package/dist/providers/zai.d.ts +24 -0
- package/dist/providers/zai.js +21 -0
- package/dist/providers.d.ts +1 -0
- package/dist/providers.js +1 -0
- package/dist/route/auth-options.d.ts +34 -0
- package/dist/route/auth-options.js +14 -0
- package/dist/route/auth.d.ts +51 -0
- package/dist/route/auth.js +87 -0
- package/dist/route/client.d.ts +430 -0
- package/dist/route/client.js +304 -0
- package/dist/route/endpoint.d.ts +28 -0
- package/dist/route/endpoint.js +21 -0
- package/dist/route/executor.d.ts +27 -0
- package/dist/route/executor.js +183 -0
- package/dist/route/framing.d.ts +29 -0
- package/dist/route/framing.js +14 -0
- package/dist/route/index.d.ts +16 -0
- package/dist/route/index.js +9 -0
- package/dist/route/protocol.d.ts +76 -0
- package/dist/route/protocol.js +16 -0
- package/dist/route/transport/http.d.ts +34 -0
- package/dist/route/transport/http.js +68 -0
- package/dist/route/transport/index.d.ts +41 -0
- package/dist/route/transport/index.js +4 -0
- package/dist/route/transport/websocket-channel.d.ts +57 -0
- package/dist/route/transport/websocket-channel.js +1 -0
- package/dist/route/transport/websocket.d.ts +56 -0
- package/dist/route/transport/websocket.js +366 -0
- package/dist/route.d.ts +1 -0
- package/dist/route.js +1 -0
- package/dist/schema/errors.d.ts +158 -0
- package/dist/schema/errors.js +120 -0
- package/dist/schema/events.d.ts +8123 -0
- package/dist/schema/events.js +550 -0
- package/dist/schema/ids.d.ts +17 -0
- package/dist/schema/ids.js +10 -0
- package/dist/schema/index.d.ts +5 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/messages.d.ts +646 -0
- package/dist/schema/messages.js +301 -0
- package/dist/schema/options.d.ts +157 -0
- package/dist/schema/options.js +187 -0
- package/dist/testing.d.ts +1693 -0
- package/dist/testing.js +129 -0
- package/dist/tool-history.d.ts +2 -0
- package/dist/tool-history.js +84 -0
- package/dist/tool-runtime.d.ts +15 -0
- package/dist/tool-runtime.js +62 -0
- package/dist/tool.d.ts +136 -0
- package/dist/tool.js +66 -0
- package/dist/utils/record.d.ts +2 -0
- package/dist/utils/record.js +2 -0
- package/dist/utils/sanitize.d.ts +1 -0
- package/dist/utils/sanitize.js +12 -0
- package/package.json +48 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { Effect, Encoding, Schema } from "effect";
|
|
2
|
+
import { Headers } from "effect/unstable/http";
|
|
3
|
+
import { Route } from "../route/client.js";
|
|
4
|
+
import { Auth } from "../route/auth.js";
|
|
5
|
+
import { Endpoint } from "../route/endpoint.js";
|
|
6
|
+
import { Protocol } from "../route/protocol.js";
|
|
7
|
+
import { HttpTransport } from "../route/transport/index.js";
|
|
8
|
+
import { LLMRequest, mergeJsonRecords } from "../schema/index.js";
|
|
9
|
+
import { OpenResponses } from "./open-responses.js";
|
|
10
|
+
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
|
|
11
|
+
import { OpenAIImage } from "./utils/openai-image.js";
|
|
12
|
+
import { ResponsesHostedTools } from "./utils/responses-hosted-tools.js";
|
|
13
|
+
import { ToolSchemaProjection } from "./utils/tool-schema.js";
|
|
14
|
+
import { OpenResponsesChannel } from "./open-responses-channel.js";
|
|
15
|
+
import { ResponsesCompaction } from "./utils/responses-compaction.js";
|
|
16
|
+
import { ResponsesCheckpoint } from "./utils/responses-checkpoint.js";
|
|
17
|
+
const ADAPTER = "openai-responses";
|
|
18
|
+
const NAME = "OpenAI Responses";
|
|
19
|
+
const WEBSOCKET_PROTOCOL_HEADER = "responses_websockets=2026-02-06";
|
|
20
|
+
const WEBSOCKET_ROTATE_AFTER_MS = 55 * 60 * 1000;
|
|
21
|
+
export const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
22
|
+
export const PATH = OpenResponses.PATH;
|
|
23
|
+
export const ContextManagement = Schema.Array(Schema.Struct({
|
|
24
|
+
type: Schema.Literal("compaction"),
|
|
25
|
+
compactThreshold: Schema.optional(Schema.Int.check(Schema.isGreaterThan(0))),
|
|
26
|
+
}));
|
|
27
|
+
const OpenAIResponsesImageGenerationTool = Schema.Struct({
|
|
28
|
+
type: Schema.tag("image_generation"),
|
|
29
|
+
action: Schema.optional(Schema.Literals(["auto", "generate", "edit"])),
|
|
30
|
+
background: Schema.optional(Schema.Literals(["auto", "opaque", "transparent"])),
|
|
31
|
+
input_fidelity: Schema.optional(Schema.Literals(["low", "high"])),
|
|
32
|
+
output_compression: Schema.optional(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 100 }))),
|
|
33
|
+
output_format: Schema.optional(Schema.Literals(["png", "jpeg", "webp"])),
|
|
34
|
+
partial_images: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))),
|
|
35
|
+
quality: Schema.optional(Schema.Literals(["auto", "low", "medium", "high"])),
|
|
36
|
+
size: Schema.optional(OpenAIImage.Size),
|
|
37
|
+
});
|
|
38
|
+
const OpenAIResponsesHostedToolItem = Schema.Union([
|
|
39
|
+
Schema.StructWithRest(Schema.Struct({
|
|
40
|
+
type: Schema.tag("computer_call"),
|
|
41
|
+
id: Schema.String,
|
|
42
|
+
status: Schema.optional(Schema.String),
|
|
43
|
+
call_id: Schema.optional(Schema.String),
|
|
44
|
+
action: optionalNull(JsonObject),
|
|
45
|
+
pending_safety_checks: Schema.optional(Schema.Array(JsonObject)),
|
|
46
|
+
}), [JsonObject]),
|
|
47
|
+
Schema.StructWithRest(Schema.Struct({
|
|
48
|
+
type: Schema.tag("web_search_preview_call"),
|
|
49
|
+
id: Schema.String,
|
|
50
|
+
status: Schema.optional(Schema.String),
|
|
51
|
+
action: optionalNull(JsonObject),
|
|
52
|
+
}), [JsonObject]),
|
|
53
|
+
Schema.StructWithRest(Schema.Struct({
|
|
54
|
+
type: Schema.tag("image_generation_call"),
|
|
55
|
+
id: Schema.String,
|
|
56
|
+
status: Schema.optional(Schema.String),
|
|
57
|
+
result: optionalNull(Schema.String),
|
|
58
|
+
output_format: Schema.optional(Schema.Literals(["png", "jpeg", "webp"])),
|
|
59
|
+
revised_prompt: optionalNull(Schema.String),
|
|
60
|
+
}), [JsonObject]),
|
|
61
|
+
]);
|
|
62
|
+
const OpenAIResponsesNamespace = Schema.Struct({
|
|
63
|
+
type: Schema.tag("namespace"),
|
|
64
|
+
name: Schema.String,
|
|
65
|
+
description: Schema.String,
|
|
66
|
+
tools: Schema.Array(OpenResponses.Tool),
|
|
67
|
+
});
|
|
68
|
+
const OpenAIResponsesTools = Schema.Union([
|
|
69
|
+
OpenResponses.Tool,
|
|
70
|
+
OpenAIResponsesNamespace,
|
|
71
|
+
OpenAIResponsesImageGenerationTool,
|
|
72
|
+
]);
|
|
73
|
+
const OpenAIResponsesToolChoice = Schema.Union([
|
|
74
|
+
OpenResponses.ToolChoice,
|
|
75
|
+
Schema.Struct({ type: Schema.tag("image_generation") }),
|
|
76
|
+
]);
|
|
77
|
+
const OpenAIResponsesCoreFields = {
|
|
78
|
+
...OpenResponses.coreFields,
|
|
79
|
+
input: Schema.Array(Schema.Union([OpenResponses.InputItem, OpenAIResponsesHostedToolItem])),
|
|
80
|
+
tools: optionalArray(OpenAIResponsesTools),
|
|
81
|
+
tool_choice: Schema.optional(OpenAIResponsesToolChoice),
|
|
82
|
+
context_management: Schema.optional(Schema.Array(Schema.Struct({
|
|
83
|
+
type: Schema.Literal("compaction"),
|
|
84
|
+
compact_threshold: Schema.optional(Schema.Int.check(Schema.isGreaterThan(0))),
|
|
85
|
+
}))),
|
|
86
|
+
};
|
|
87
|
+
const OpenAIResponsesBody = Schema.Struct({
|
|
88
|
+
...OpenAIResponsesCoreFields,
|
|
89
|
+
stream: Schema.Literal(true),
|
|
90
|
+
});
|
|
91
|
+
/** Request control, never conversation content. */
|
|
92
|
+
export const CompactionTrigger = Schema.Struct({ type: Schema.Literal("compaction_trigger") });
|
|
93
|
+
const CheckpointBody = Schema.Struct({
|
|
94
|
+
...OpenAIResponsesBody.fields,
|
|
95
|
+
input: Schema.Array(Schema.Union([OpenResponses.InputItem, OpenAIResponsesHostedToolItem, CompactionTrigger])),
|
|
96
|
+
store: Schema.Literal(false),
|
|
97
|
+
prompt_cache_retention: optionalNull(Schema.String),
|
|
98
|
+
prompt_cache_options: optionalNull(Schema.Struct({ mode: Schema.optional(Schema.String), ttl: Schema.optional(Schema.String) })),
|
|
99
|
+
});
|
|
100
|
+
const adapter = {
|
|
101
|
+
id: ADAPTER,
|
|
102
|
+
name: NAME,
|
|
103
|
+
restoreHostedToolItem: (item) => (Schema.is(OpenAIResponsesHostedToolItem)(item) ? item : undefined),
|
|
104
|
+
};
|
|
105
|
+
const nativeImageToolInput = (tool) => {
|
|
106
|
+
const native = tool.native?.openai;
|
|
107
|
+
return ProviderShared.isRecord(native) && native.type === "image_generation" ? native : undefined;
|
|
108
|
+
};
|
|
109
|
+
const nativeImageTool = (tool) => {
|
|
110
|
+
const native = nativeImageToolInput(tool);
|
|
111
|
+
return Schema.is(OpenAIResponsesImageGenerationTool)(native) ? native : undefined;
|
|
112
|
+
};
|
|
113
|
+
const lowerTool = Effect.fn("OpenAIResponses.lowerTool")(function* (tool, inputSchema) {
|
|
114
|
+
const native = nativeImageToolInput(tool);
|
|
115
|
+
if (native !== undefined) {
|
|
116
|
+
if (Schema.is(OpenAIResponsesImageGenerationTool)(native))
|
|
117
|
+
return native;
|
|
118
|
+
return yield* ProviderShared.invalidRequest("OpenAI Responses image generation tool options are invalid");
|
|
119
|
+
}
|
|
120
|
+
return yield* OpenResponses.lowerTool(NAME, tool, inputSchema);
|
|
121
|
+
});
|
|
122
|
+
// Native namespaces hold only function tools, so deeper levels flatten into
|
|
123
|
+
// the leaf names the same way non-native protocols flatten the whole tree.
|
|
124
|
+
const lowerToolEntry = Effect.fn("OpenAIResponses.lowerToolEntry")(function* (tool, compatibility) {
|
|
125
|
+
if (tool.type === "tool")
|
|
126
|
+
return yield* lowerTool(tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, compatibility));
|
|
127
|
+
// OpenAI requires a namespace description; fall back to a generic one so a
|
|
128
|
+
// missing description never blocks the request.
|
|
129
|
+
return {
|
|
130
|
+
type: "namespace",
|
|
131
|
+
name: tool.name,
|
|
132
|
+
description: tool.description ?? `Tools in the ${tool.name} namespace.`,
|
|
133
|
+
tools: yield* Effect.forEach(ProviderShared.flattenTools(tool.tools), (leaf) => OpenResponses.lowerTool(NAME, leaf, ToolSchemaProjection.modelCompatibility(leaf.inputSchema, compatibility))),
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
const lowerToolChoice = (toolChoice, tools) => ProviderShared.matchToolChoice(NAME, toolChoice, {
|
|
137
|
+
auto: () => "auto",
|
|
138
|
+
none: () => "none",
|
|
139
|
+
required: () => "required",
|
|
140
|
+
tool: (name) => tools.some((tool) => tool.type === "tool" && tool.name === name && nativeImageTool(tool) !== undefined)
|
|
141
|
+
? { type: "image_generation" }
|
|
142
|
+
: { type: "function", name },
|
|
143
|
+
});
|
|
144
|
+
const decodeBody = ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenAIResponsesBody));
|
|
145
|
+
const fromRequest = Effect.fn("OpenAIResponses.fromRequest")(function* (request) {
|
|
146
|
+
const management = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Schema.UndefinedOr(ContextManagement)))(request.providerOptions?.contextManagement);
|
|
147
|
+
const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
|
|
148
|
+
return yield* decodeBody({
|
|
149
|
+
...(yield* OpenResponses.lowerConversation(request, adapter)),
|
|
150
|
+
...OpenResponses.lowerGeneration(request),
|
|
151
|
+
context_management: management?.map((edit) => ({ type: edit.type, compact_threshold: edit.compactThreshold })),
|
|
152
|
+
tools: request.tools.length === 0
|
|
153
|
+
? undefined
|
|
154
|
+
: yield* Effect.forEach(request.tools, (tool) => lowerToolEntry(tool, toolSchemaCompatibility)),
|
|
155
|
+
tool_choice: OpenResponses.allowedToolChoice(request) ??
|
|
156
|
+
(request.toolChoice ? yield* lowerToolChoice(request.toolChoice, request.tools) : undefined),
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
const checkpointBody = {
|
|
160
|
+
schema: CheckpointBody,
|
|
161
|
+
from: Effect.fn("OpenAIResponses.checkpointBody")(function* (request) {
|
|
162
|
+
const native = yield* fromRequest(LLMRequest.update(request, { toolChoice: undefined }));
|
|
163
|
+
const overlay = request.http?.body;
|
|
164
|
+
// Complete history is required for stateless replay and SSE recovery. Raw input overrides bypass that contract.
|
|
165
|
+
if (overlay?.input !== undefined ||
|
|
166
|
+
overlay?.previous_response_id !== undefined ||
|
|
167
|
+
overlay?.conversation !== undefined)
|
|
168
|
+
return yield* ProviderShared.invalidRequest("Trigger compaction requires complete canonical history, not an input or continuation override");
|
|
169
|
+
return yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(CheckpointBody))({
|
|
170
|
+
...mergeJsonRecords(native, overlay),
|
|
171
|
+
input: [...native.input, { type: "compaction_trigger" }],
|
|
172
|
+
stream: true,
|
|
173
|
+
store: false,
|
|
174
|
+
parallel_tool_calls: true,
|
|
175
|
+
tool_choice: undefined,
|
|
176
|
+
context_management: undefined,
|
|
177
|
+
text: undefined,
|
|
178
|
+
max_output_tokens: undefined,
|
|
179
|
+
max_tool_calls: undefined,
|
|
180
|
+
});
|
|
181
|
+
}),
|
|
182
|
+
};
|
|
183
|
+
const hostedToolResult = Effect.fn("OpenAIResponses.hostedToolResult")(function* (item) {
|
|
184
|
+
const isError = item.error !== undefined && item.error !== null;
|
|
185
|
+
if (item.type === "image_generation_call" && item.result) {
|
|
186
|
+
yield* Effect.fromResult(Encoding.decodeBase64(item.result)).pipe(Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "OpenAI Responses returned invalid image base64", undefined, cause)));
|
|
187
|
+
const format = item.output_format ?? "png";
|
|
188
|
+
return {
|
|
189
|
+
type: "content",
|
|
190
|
+
value: [
|
|
191
|
+
{
|
|
192
|
+
type: "file",
|
|
193
|
+
uri: `data:image/${format};base64,${item.result}`,
|
|
194
|
+
mime: `image/${format}`,
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return isError ? { type: "error", value: item.error } : { type: "json", value: item };
|
|
200
|
+
});
|
|
201
|
+
const HOSTED_TOOLS = {
|
|
202
|
+
web_search_call: { name: "web_search", input: (item) => item.action ?? {} },
|
|
203
|
+
web_search_preview_call: { name: "web_search_preview", input: (item) => item.action ?? {} },
|
|
204
|
+
file_search_call: { name: "file_search", input: (item) => ({ queries: item.queries ?? [] }) },
|
|
205
|
+
code_interpreter_call: {
|
|
206
|
+
name: "code_interpreter",
|
|
207
|
+
input: (item) => ({ code: item.code, container_id: item.container_id }),
|
|
208
|
+
},
|
|
209
|
+
computer_call: { name: "computer_use", input: (item) => item.action ?? {} },
|
|
210
|
+
image_generation_call: { name: "image_generation", input: () => ({}), result: hostedToolResult },
|
|
211
|
+
mcp_call: {
|
|
212
|
+
name: "mcp",
|
|
213
|
+
input: (item) => ({ server_label: item.server_label, name: item.name, arguments: item.arguments }),
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
const step = (state, input) => {
|
|
217
|
+
const event = OpenResponses.normalize(state, input);
|
|
218
|
+
if (event.type === "response.reasoning_text.delta")
|
|
219
|
+
return event.item_id !== undefined
|
|
220
|
+
? Effect.succeed(OpenResponses.onReasoningDelta(state, event, event.item_id))
|
|
221
|
+
: ProviderShared.eventError(ADAPTER, `${event.type} is missing item_id`);
|
|
222
|
+
if (event.type === "response.output_item.done" && event.item && ResponsesHostedTools.isItem(event.item, HOSTED_TOOLS))
|
|
223
|
+
return ResponsesHostedTools.onDone(state, event.item, HOSTED_TOOLS);
|
|
224
|
+
return OpenResponses.step(state, event);
|
|
225
|
+
};
|
|
226
|
+
export const protocol = Protocol.make({
|
|
227
|
+
id: ADAPTER,
|
|
228
|
+
body: {
|
|
229
|
+
schema: OpenAIResponsesBody,
|
|
230
|
+
from: fromRequest,
|
|
231
|
+
},
|
|
232
|
+
stream: {
|
|
233
|
+
event: OpenResponses.protocol.stream.event,
|
|
234
|
+
initial: (request) => OpenResponses.initial(request, adapter),
|
|
235
|
+
step,
|
|
236
|
+
terminal: OpenResponses.terminal,
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
const endpoint = Endpoint.path(PATH, { baseURL: DEFAULT_BASE_URL });
|
|
240
|
+
const auth = Auth.none;
|
|
241
|
+
export const httpTransport = HttpTransport.sseJson.with();
|
|
242
|
+
export const channelTransport = (OpenResponsesChannel.transport);
|
|
243
|
+
export const transport = channelTransport({
|
|
244
|
+
id: ADAPTER,
|
|
245
|
+
name: NAME,
|
|
246
|
+
rotateAfterMs: WEBSOCKET_ROTATE_AFTER_MS,
|
|
247
|
+
headers: (headers) => Headers.set(headers, "openai-beta", headers["openai-beta"] ?? WEBSOCKET_PROTOCOL_HEADER),
|
|
248
|
+
});
|
|
249
|
+
export const route = Route.make({
|
|
250
|
+
compact: { endpoint: ResponsesCompaction.make(adapter), trigger: ResponsesCheckpoint.make(checkpointBody) },
|
|
251
|
+
id: ADAPTER,
|
|
252
|
+
provider: "openai",
|
|
253
|
+
providerMetadataKey: "openai",
|
|
254
|
+
protocol,
|
|
255
|
+
endpoint,
|
|
256
|
+
auth,
|
|
257
|
+
transport,
|
|
258
|
+
defaults: { providerOptions: { store: false, include: ["reasoning.encrypted_content"] } },
|
|
259
|
+
});
|
|
260
|
+
export * as OpenAIResponses from "./openai-responses.js";
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { Tool } from "@opencode/schema/tool";
|
|
2
|
+
import { Effect, Schema, Stream } from "effect";
|
|
3
|
+
import { Headers, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
|
|
4
|
+
import { AIError, LLMRequest, Message, ToolDefinition, type ContentPart, type MediaPart, type ProviderID, type ToolEntry, type ToolResultPart } from "../schema/index.js";
|
|
5
|
+
import { isRecord } from "../utils/record.js";
|
|
6
|
+
export { isRecord };
|
|
7
|
+
export declare const Json: Schema.fromJsonString<Schema.Unknown>;
|
|
8
|
+
export declare const decodeJson: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => unknown;
|
|
9
|
+
export declare const encodeJson: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => string;
|
|
10
|
+
export declare const JsonObject: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
11
|
+
export declare const optionalArray: <const S extends Schema.Top>(schema: S) => Schema.optional<Schema.$Array<S>>;
|
|
12
|
+
export declare const optionalNull: <const S extends Schema.Top>(schema: S) => Schema.optional<Schema.NullOr<S>>;
|
|
13
|
+
export declare const OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH = 64;
|
|
14
|
+
export declare const promptCacheKey: (request: LLMRequest) => string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Streaming tool-call accumulator. Adapters that build a tool call across
|
|
17
|
+
* multiple `tool-input-delta` chunks store the partial JSON input string here
|
|
18
|
+
* and finalize it with `parseToolInput` once the call completes.
|
|
19
|
+
*/
|
|
20
|
+
export interface ToolAccumulator {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly namespace?: string;
|
|
24
|
+
readonly input: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* `Usage.totalTokens` policy shared by every route. Honors a provider-
|
|
28
|
+
* supplied total; otherwise falls back to `inputTokens + outputTokens` only
|
|
29
|
+
* when at least one is defined. Returns `undefined` when neither input nor
|
|
30
|
+
* output is known so routes don't publish a misleading `0`.
|
|
31
|
+
*
|
|
32
|
+
* Under the inclusive `AI.Usage` contract, `inputTokens` includes cached input
|
|
33
|
+
* and `outputTokens` includes reasoning. Protocol mappers normalize those
|
|
34
|
+
* inclusive values before calling this helper. The provider-supplied total is
|
|
35
|
+
* the source of truth when present; otherwise their sum is the canonical total.
|
|
36
|
+
*/
|
|
37
|
+
export declare const totalTokens: (inputTokens: number | undefined, outputTokens: number | undefined, total: number | undefined) => number | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Subtract `subtrahend` from `total`, clamping to zero if the provider
|
|
40
|
+
* reports a non-sensical breakdown (e.g. `cached_tokens > prompt_tokens`).
|
|
41
|
+
* Used by protocol mappers when deriving a non-overlapping breakdown field
|
|
42
|
+
* from a provider's inclusive total — `nonCachedInputTokens` from
|
|
43
|
+
* `inputTokens - cacheReadInputTokens - cacheWriteInputTokens`.
|
|
44
|
+
*
|
|
45
|
+
* If `total` is `undefined`, returns `undefined` (we don't fabricate
|
|
46
|
+
* counts). If `subtrahend` is `undefined`, returns `total` unchanged. The
|
|
47
|
+
* provider-native breakdown stays available on `Usage.providerMetadata` for debugging.
|
|
48
|
+
*/
|
|
49
|
+
export declare const subtractTokens: (total: number | undefined, subtrahend: number | undefined) => number | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Sum a list of optional token counts, returning `undefined` only when
|
|
52
|
+
* every value is `undefined` (so we don't fabricate a `0`). Used by
|
|
53
|
+
* protocol mappers to derive the inclusive `inputTokens` total from a
|
|
54
|
+
* provider that natively reports a non-overlapping breakdown
|
|
55
|
+
* (e.g. Anthropic, whose `input_tokens` is already non-cached only).
|
|
56
|
+
*/
|
|
57
|
+
export declare const sumTokens: (...values: ReadonlyArray<number | undefined>) => number | undefined;
|
|
58
|
+
export declare const eventError: (route: string, message: string, body?: string, cause?: unknown) => AIError;
|
|
59
|
+
export declare const parseJson: (route: string, input: string, message: string) => Effect.Effect<unknown, AIError, never>;
|
|
60
|
+
/**
|
|
61
|
+
* Join the `text` field of a list of parts with newlines. Used by routes
|
|
62
|
+
* that flatten system / message content arrays into a single provider string
|
|
63
|
+
* (OpenAI Chat `system` content, OpenAI Responses `system` content, Gemini
|
|
64
|
+
* `systemInstruction.parts[].text`).
|
|
65
|
+
*/
|
|
66
|
+
export declare const joinText: (parts: ReadonlyArray<{
|
|
67
|
+
readonly text: string;
|
|
68
|
+
}>) => string;
|
|
69
|
+
/**
|
|
70
|
+
* Stable fallback representation for chronological `Message.system(...)`
|
|
71
|
+
* updates on routes that do not support that privileged role natively. The
|
|
72
|
+
* wrapper remains visibly lower-authority user text, preserves the original
|
|
73
|
+
* temporal position, and XML-escapes content so it cannot close the wrapper.
|
|
74
|
+
*/
|
|
75
|
+
export declare const wrapSystemUpdate: (parts: ReadonlyArray<{
|
|
76
|
+
readonly text: string;
|
|
77
|
+
}>) => string;
|
|
78
|
+
/**
|
|
79
|
+
* Chronological system updates deliberately accept text only. Do not insert
|
|
80
|
+
* raw retrieved, tool, or web content into privileged updates: keep untrusted
|
|
81
|
+
* data in ordinary user/tool messages instead.
|
|
82
|
+
*/
|
|
83
|
+
export declare const systemUpdateText: (route: string, message: Message) => Effect.Effect<{
|
|
84
|
+
readonly type: "text";
|
|
85
|
+
readonly text: string;
|
|
86
|
+
readonly metadata?: {
|
|
87
|
+
readonly [x: string]: unknown;
|
|
88
|
+
} | undefined;
|
|
89
|
+
readonly cache?: import("../schema/options.js").CacheHint | undefined;
|
|
90
|
+
readonly providerMetadata?: {
|
|
91
|
+
readonly [x: string]: {
|
|
92
|
+
readonly [x: string]: unknown;
|
|
93
|
+
};
|
|
94
|
+
} | undefined;
|
|
95
|
+
}[], AIError, never>;
|
|
96
|
+
/** Lower an unsupported privileged update into visible, in-order user text. */
|
|
97
|
+
export declare const wrappedSystemUpdate: (route: string, message: Message) => Effect.Effect<{
|
|
98
|
+
type: "text";
|
|
99
|
+
text: string;
|
|
100
|
+
cache: import("../schema/options.js").CacheHint | undefined;
|
|
101
|
+
}, AIError, never>;
|
|
102
|
+
/**
|
|
103
|
+
* Parse the streamed JSON input of a tool call. Treats an empty string as
|
|
104
|
+
* `"{}"` — providers occasionally finish a tool call without ever emitting
|
|
105
|
+
* input deltas (e.g. zero-arg tools). The error message is uniform across
|
|
106
|
+
* routes: `Invalid JSON input for <route> tool call <name>`.
|
|
107
|
+
*/
|
|
108
|
+
export declare const parseToolInput: (route: string, name: string, raw: string) => Effect.Effect<unknown, AIError, never>;
|
|
109
|
+
export interface NormalizedMedia {
|
|
110
|
+
readonly mime: string;
|
|
111
|
+
readonly base64: string;
|
|
112
|
+
readonly dataUrl: string;
|
|
113
|
+
}
|
|
114
|
+
export declare const normalizeMedia: (part: MediaPart) => NormalizedMedia;
|
|
115
|
+
export declare const normalizeToolFile: (part: Tool.FileContent) => NormalizedMedia;
|
|
116
|
+
export declare const trimBaseUrl: (value: string) => string;
|
|
117
|
+
export declare const toolResultText: (part: ToolResultPart) => string;
|
|
118
|
+
export declare const errorText: (error: unknown) => string;
|
|
119
|
+
/**
|
|
120
|
+
* `framing` step for Server-Sent Events. Decodes UTF-8, runs the SSE channel
|
|
121
|
+
* decoder, optionally filters named events, and drops empty events. `[DONE]`
|
|
122
|
+
* is dropped by default or retained for protocols that use it as their stream
|
|
123
|
+
* boundary. Retry control events are ignored without interrupting the stream.
|
|
124
|
+
* Decoder failures become provider output errors so the public error channel
|
|
125
|
+
* stays `AIError`.
|
|
126
|
+
*/
|
|
127
|
+
export declare const sseFraming: (bytes: Stream.Stream<Uint8Array, AIError>, events?: ReadonlySet<string>, includeDone?: boolean) => Stream.Stream<string, AIError>;
|
|
128
|
+
/**
|
|
129
|
+
* Canonical invalid-request constructor shared by protocol lowering.
|
|
130
|
+
*/
|
|
131
|
+
export declare const invalidRequest: (message: string, cause?: unknown) => AIError;
|
|
132
|
+
/**
|
|
133
|
+
* Canonical constructor for operations the selected route does not implement.
|
|
134
|
+
* Prefer this over `invalidRequest` when the failure is a missing route
|
|
135
|
+
* capability rather than a malformed caller input, so consumers can branch on
|
|
136
|
+
* `reason._tag` plus `reason.operation` instead of matching message text.
|
|
137
|
+
*/
|
|
138
|
+
export declare const unsupportedOperation: (input: {
|
|
139
|
+
readonly operation: string;
|
|
140
|
+
readonly message: string;
|
|
141
|
+
readonly provider?: ProviderID;
|
|
142
|
+
readonly route?: string;
|
|
143
|
+
readonly cause?: unknown;
|
|
144
|
+
}) => AIError;
|
|
145
|
+
/**
|
|
146
|
+
* Lower namespaces to flat definitions for protocols without a native
|
|
147
|
+
* namespace construct. Leaf names join their namespace path with `_` because
|
|
148
|
+
* `.` is not broadly accepted in provider tool names.
|
|
149
|
+
*/
|
|
150
|
+
export declare const flattenTools: (tools: ReadonlyArray<ToolEntry>, path?: ReadonlyArray<string>) => ToolDefinition[];
|
|
151
|
+
export declare const flattenToolRequest: (request: LLMRequest) => {
|
|
152
|
+
tools: ToolDefinition[];
|
|
153
|
+
request: LLMRequest<import("../schema/options.js").LanguageModel<{
|
|
154
|
+
readonly [x: string]: unknown;
|
|
155
|
+
}, import("../route.js").CompactionOperations | undefined>>;
|
|
156
|
+
};
|
|
157
|
+
export declare const imageResponse: (route: string, name: string, response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
|
|
158
|
+
body: string;
|
|
159
|
+
invalid: (message: string, cause?: unknown) => AIError;
|
|
160
|
+
}, AIError, never>;
|
|
161
|
+
export declare const matchToolChoice: <Auto, None, Required, Tool>(route: string, toolChoice: NonNullable<LLMRequest["toolChoice"]>, cases: {
|
|
162
|
+
readonly auto: () => Auto;
|
|
163
|
+
readonly none: () => None;
|
|
164
|
+
readonly required: () => Required;
|
|
165
|
+
readonly tool: (name: string) => Tool;
|
|
166
|
+
}) => Effect.Effect<Auto | None | Required | Tool, AIError, never>;
|
|
167
|
+
type ContentType = ContentPart["type"];
|
|
168
|
+
export declare const supportsContent: <const Type extends ContentType>(part: ContentPart, types: ReadonlyArray<Type>) => part is Extract<ContentPart, {
|
|
169
|
+
readonly type: Type;
|
|
170
|
+
}>;
|
|
171
|
+
export declare const unsupportedContent: (route: string, role: LLMRequest["messages"][number]["role"], types: ReadonlyArray<ContentType>) => AIError;
|
|
172
|
+
/**
|
|
173
|
+
* Build a `validate` step from a Schema decoder. Replaces the per-route
|
|
174
|
+
* lambda body `(payload) => decode(payload).pipe(Effect.mapError((e) =>
|
|
175
|
+
* invalid(e.message)))`. Any decode error is translated into
|
|
176
|
+
* `AIError` carrying the original parse-error message.
|
|
177
|
+
*/
|
|
178
|
+
export declare const validateWith: <A, I, E extends {
|
|
179
|
+
readonly message: string;
|
|
180
|
+
}>(decode: (input: I) => Effect.Effect<A, E>) => (payload: I) => Effect.Effect<A, AIError, never>;
|
|
181
|
+
/**
|
|
182
|
+
* Build an HTTP POST with a JSON body. Sets `content-type: application/json`
|
|
183
|
+
* automatically after caller-supplied headers so routes cannot accidentally
|
|
184
|
+
* send JSON with a stale content type. The body is passed pre-encoded so
|
|
185
|
+
* routes can choose between
|
|
186
|
+
* `Schema.encodeSync(payload)` and `ProviderShared.encodeJson(payload)`.
|
|
187
|
+
*/
|
|
188
|
+
export declare const jsonPost: (input: {
|
|
189
|
+
readonly url: string;
|
|
190
|
+
readonly body: string;
|
|
191
|
+
readonly headers?: Headers.Input;
|
|
192
|
+
}) => HttpClientRequest.HttpClientRequest;
|
|
193
|
+
export * as ProviderShared from "./shared.js";
|