@opencode-ai/ai 0.0.0-beta-17492
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 +392 -0
- package/dist/cache-policy.d.ts +2 -0
- package/dist/cache-policy.js +121 -0
- package/dist/image-client.d.ts +19 -0
- package/dist/image-client.js +19 -0
- package/dist/image.d.ts +110 -0
- package/dist/image.js +102 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +12 -0
- package/dist/llm.d.ts +230 -0
- package/dist/llm.js +77 -0
- package/dist/protocols/anthropic-messages.d.ts +633 -0
- package/dist/protocols/anthropic-messages.js +860 -0
- package/dist/protocols/bedrock-converse.d.ts +533 -0
- package/dist/protocols/bedrock-converse.js +584 -0
- package/dist/protocols/bedrock-event-stream.d.ts +9 -0
- package/dist/protocols/bedrock-event-stream.js +73 -0
- package/dist/protocols/gemini.d.ts +300 -0
- package/dist/protocols/gemini.js +512 -0
- package/dist/protocols/google-images.d.ts +30 -0
- package/dist/protocols/google-images.js +191 -0
- package/dist/protocols/index.d.ts +10 -0
- package/dist/protocols/index.js +10 -0
- package/dist/protocols/open-responses-channel.d.ts +26 -0
- package/dist/protocols/open-responses-channel.js +120 -0
- package/dist/protocols/open-responses.d.ts +951 -0
- package/dist/protocols/open-responses.js +873 -0
- package/dist/protocols/openai-chat.d.ts +795 -0
- package/dist/protocols/openai-chat.js +718 -0
- package/dist/protocols/openai-compatible-chat.d.ts +107 -0
- package/dist/protocols/openai-compatible-chat.js +20 -0
- package/dist/protocols/openai-compatible-responses.d.ts +96 -0
- package/dist/protocols/openai-compatible-responses.js +17 -0
- package/dist/protocols/openai-images.d.ts +32 -0
- package/dist/protocols/openai-images.js +188 -0
- package/dist/protocols/openai-responses-channel.d.ts +12 -0
- package/dist/protocols/openai-responses-channel.js +142 -0
- package/dist/protocols/openai-responses.d.ts +596 -0
- package/dist/protocols/openai-responses.js +208 -0
- package/dist/protocols/shared.d.ts +194 -0
- package/dist/protocols/shared.js +238 -0
- package/dist/protocols/utils/bedrock-auth.d.ts +21 -0
- package/dist/protocols/utils/bedrock-auth.js +43 -0
- package/dist/protocols/utils/bedrock-cache.d.ts +15 -0
- package/dist/protocols/utils/bedrock-cache.js +29 -0
- package/dist/protocols/utils/bedrock-media.d.ts +49 -0
- package/dist/protocols/utils/bedrock-media.js +70 -0
- package/dist/protocols/utils/cache.d.ts +6 -0
- package/dist/protocols/utils/cache.js +8 -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 +22 -0
- package/dist/protocols/utils/lifecycle.d.ts +20 -0
- package/dist/protocols/utils/lifecycle.js +68 -0
- package/dist/protocols/utils/open-responses-options.d.ts +21 -0
- package/dist/protocols/utils/open-responses-options.js +41 -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 +14 -0
- package/dist/protocols/utils/openai-options.js +14 -0
- package/dist/protocols/utils/tool-schema.d.ts +8 -0
- package/dist/protocols/utils/tool-schema.js +81 -0
- package/dist/protocols/utils/tool-stream.d.ts +639 -0
- package/dist/protocols/utils/tool-stream.js +141 -0
- package/dist/protocols/xai-images.d.ts +26 -0
- package/dist/protocols/xai-images.js +111 -0
- package/dist/protocols/zai-images.d.ts +22 -0
- package/dist/protocols/zai-images.js +84 -0
- package/dist/protocols.d.ts +1 -0
- package/dist/protocols.js +1 -0
- package/dist/provider-error.d.ts +14 -0
- package/dist/provider-error.js +130 -0
- package/dist/provider-package.d.ts +15 -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 +247 -0
- package/dist/providers/amazon-bedrock-mantle.js +68 -0
- package/dist/providers/amazon-bedrock.d.ts +172 -0
- package/dist/providers/amazon-bedrock.js +41 -0
- package/dist/providers/anthropic-compatible.d.ts +218 -0
- package/dist/providers/anthropic-compatible.js +45 -0
- package/dist/providers/anthropic.d.ts +216 -0
- package/dist/providers/anthropic.js +40 -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 +248 -0
- package/dist/providers/azure.js +90 -0
- package/dist/providers/cloudflare.d.ts +338 -0
- package/dist/providers/cloudflare.js +87 -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 +131 -0
- package/dist/providers/google-vertex-chat.js +50 -0
- package/dist/providers/google-vertex-messages.d.ts +215 -0
- package/dist/providers/google-vertex-messages.js +75 -0
- package/dist/providers/google-vertex-responses.d.ts +122 -0
- package/dist/providers/google-vertex-responses.js +51 -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 +115 -0
- package/dist/providers/google-vertex.js +64 -0
- package/dist/providers/google.d.ts +112 -0
- package/dist/providers/google.js +43 -0
- package/dist/providers/index.d.ts +18 -0
- package/dist/providers/index.js +18 -0
- package/dist/providers/open-responses-options.d.ts +16 -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-profile.d.ts +43 -0
- package/dist/providers/openai-compatible-profile.js +12 -0
- package/dist/providers/openai-compatible-responses.d.ts +120 -0
- package/dist/providers/openai-compatible-responses.js +33 -0
- package/dist/providers/openai-compatible.d.ts +167 -0
- package/dist/providers/openai-compatible.js +55 -0
- package/dist/providers/openai-options.d.ts +21 -0
- package/dist/providers/openai-options.js +41 -0
- package/dist/providers/openai.d.ts +264 -0
- package/dist/providers/openai.js +83 -0
- package/dist/providers/openrouter.d.ts +537 -0
- package/dist/providers/openrouter.js +117 -0
- package/dist/providers/xai.d.ts +249 -0
- package/dist/providers/xai.js +79 -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 +89 -0
- package/dist/route/client.d.ts +348 -0
- package/dist/route/client.js +214 -0
- package/dist/route/endpoint.d.ts +28 -0
- package/dist/route/endpoint.js +21 -0
- package/dist/route/executor.d.ts +23 -0
- package/dist/route/executor.js +227 -0
- package/dist/route/framing.d.ts +23 -0
- package/dist/route/framing.js +4 -0
- package/dist/route/index.d.ts +16 -0
- package/dist/route/index.js +9 -0
- package/dist/route/protocol.d.ts +77 -0
- package/dist/route/protocol.js +17 -0
- package/dist/route/transport/http.d.ts +34 -0
- package/dist/route/transport/http.js +63 -0
- package/dist/route/transport/index.d.ts +39 -0
- package/dist/route/transport/index.js +4 -0
- package/dist/route/transport/websocket-channel.d.ts +56 -0
- package/dist/route/transport/websocket-channel.js +1 -0
- package/dist/route/transport/websocket.d.ts +55 -0
- package/dist/route/transport/websocket.js +326 -0
- package/dist/route.d.ts +1 -0
- package/dist/route.js +1 -0
- package/dist/schema/errors.d.ts +159 -0
- package/dist/schema/errors.js +155 -0
- package/dist/schema/events.d.ts +4587 -0
- package/dist/schema/events.js +492 -0
- package/dist/schema/ids.d.ts +30 -0
- package/dist/schema/ids.js +19 -0
- package/dist/schema/index.d.ts +5 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/messages.d.ts +402 -0
- package/dist/schema/messages.js +244 -0
- package/dist/schema/options.d.ts +159 -0
- package/dist/schema/options.js +202 -0
- package/dist/testing.d.ts +1493 -0
- package/dist/testing.js +88 -0
- package/dist/tool-runtime.d.ts +15 -0
- package/dist/tool-runtime.js +54 -0
- package/dist/tool.d.ts +135 -0
- package/dist/tool.js +66 -0
- package/dist/utils/record.d.ts +2 -0
- package/dist/utils/record.js +2 -0
- package/package.json +47 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Route, type RouteRoutedLanguageModelInput } from "../route/client.js";
|
|
2
|
+
export type OpenAICompatibleChatLanguageModelInput = RouteRoutedLanguageModelInput;
|
|
3
|
+
/**
|
|
4
|
+
* Route for non-OpenAI providers that expose an OpenAI Chat-compatible
|
|
5
|
+
* `/chat/completions` endpoint. Reuses `OpenAIChat.protocol` end-to-end and
|
|
6
|
+
* overrides only the route id so providers can be resolved per-family without
|
|
7
|
+
* colliding with native OpenAI. Provider helpers configure the route endpoint
|
|
8
|
+
* before model selection.
|
|
9
|
+
*/
|
|
10
|
+
export declare const route: Route<{
|
|
11
|
+
readonly model: string;
|
|
12
|
+
readonly messages: readonly ({
|
|
13
|
+
readonly role: "system";
|
|
14
|
+
readonly content: string | readonly ({
|
|
15
|
+
readonly type: "text";
|
|
16
|
+
readonly text: string;
|
|
17
|
+
readonly cache_control?: {
|
|
18
|
+
readonly type: "ephemeral";
|
|
19
|
+
readonly ttl?: string | undefined;
|
|
20
|
+
} | undefined;
|
|
21
|
+
} | {
|
|
22
|
+
readonly type: "image_url";
|
|
23
|
+
readonly image_url: {
|
|
24
|
+
readonly url: string;
|
|
25
|
+
};
|
|
26
|
+
})[];
|
|
27
|
+
} | {
|
|
28
|
+
readonly role: "user";
|
|
29
|
+
readonly content: string | readonly ({
|
|
30
|
+
readonly type: "text";
|
|
31
|
+
readonly text: string;
|
|
32
|
+
readonly cache_control?: {
|
|
33
|
+
readonly type: "ephemeral";
|
|
34
|
+
readonly ttl?: string | undefined;
|
|
35
|
+
} | undefined;
|
|
36
|
+
} | {
|
|
37
|
+
readonly type: "image_url";
|
|
38
|
+
readonly image_url: {
|
|
39
|
+
readonly url: string;
|
|
40
|
+
};
|
|
41
|
+
})[];
|
|
42
|
+
} | {
|
|
43
|
+
readonly [x: string]: unknown;
|
|
44
|
+
readonly content: string | null;
|
|
45
|
+
readonly role: "assistant";
|
|
46
|
+
readonly reasoning?: string | undefined;
|
|
47
|
+
readonly reasoning_content?: string | undefined;
|
|
48
|
+
readonly reasoning_text?: string | undefined;
|
|
49
|
+
readonly cache_control?: {
|
|
50
|
+
readonly type: "ephemeral";
|
|
51
|
+
readonly ttl?: string | undefined;
|
|
52
|
+
} | undefined;
|
|
53
|
+
readonly tool_calls?: readonly {
|
|
54
|
+
readonly id: string;
|
|
55
|
+
readonly type: "function";
|
|
56
|
+
readonly function: {
|
|
57
|
+
readonly name: string;
|
|
58
|
+
readonly arguments: string;
|
|
59
|
+
};
|
|
60
|
+
}[] | undefined;
|
|
61
|
+
readonly reasoning_details?: unknown;
|
|
62
|
+
} | {
|
|
63
|
+
readonly content: string;
|
|
64
|
+
readonly role: "tool";
|
|
65
|
+
readonly tool_call_id: string;
|
|
66
|
+
readonly cache_control?: {
|
|
67
|
+
readonly type: "ephemeral";
|
|
68
|
+
readonly ttl?: string | undefined;
|
|
69
|
+
} | undefined;
|
|
70
|
+
})[];
|
|
71
|
+
readonly stream: true;
|
|
72
|
+
readonly stop?: readonly string[] | undefined;
|
|
73
|
+
readonly max_completion_tokens?: number | undefined;
|
|
74
|
+
readonly max_tokens?: number | undefined;
|
|
75
|
+
readonly tools?: readonly {
|
|
76
|
+
readonly function: {
|
|
77
|
+
readonly name: string;
|
|
78
|
+
readonly description: string;
|
|
79
|
+
readonly parameters: {
|
|
80
|
+
readonly [x: string]: unknown;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
readonly type: "function";
|
|
84
|
+
readonly cache_control?: {
|
|
85
|
+
readonly type: "ephemeral";
|
|
86
|
+
readonly ttl?: string | undefined;
|
|
87
|
+
} | undefined;
|
|
88
|
+
}[] | undefined;
|
|
89
|
+
readonly temperature?: number | undefined;
|
|
90
|
+
readonly seed?: number | undefined;
|
|
91
|
+
readonly tool_choice?: "required" | "none" | "auto" | {
|
|
92
|
+
readonly type: "function";
|
|
93
|
+
readonly function: {
|
|
94
|
+
readonly name: string;
|
|
95
|
+
};
|
|
96
|
+
} | undefined;
|
|
97
|
+
readonly top_p?: number | undefined;
|
|
98
|
+
readonly store?: boolean | undefined;
|
|
99
|
+
readonly stream_options?: {
|
|
100
|
+
readonly include_usage: boolean;
|
|
101
|
+
} | undefined;
|
|
102
|
+
readonly prompt_cache_key?: string | undefined;
|
|
103
|
+
readonly reasoning_effort?: string | undefined;
|
|
104
|
+
readonly frequency_penalty?: number | undefined;
|
|
105
|
+
readonly presence_penalty?: number | undefined;
|
|
106
|
+
}, import("../route/transport/http.js").HttpPrepared<string>>;
|
|
107
|
+
export * as OpenAICompatibleChat from "./openai-compatible-chat.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Route } from "../route/client.js";
|
|
2
|
+
import { Endpoint } from "../route/endpoint.js";
|
|
3
|
+
import { Framing } from "../route/framing.js";
|
|
4
|
+
import * as OpenAIChat from "./openai-chat.js";
|
|
5
|
+
const ADAPTER = "openai-compatible-chat";
|
|
6
|
+
/**
|
|
7
|
+
* Route for non-OpenAI providers that expose an OpenAI Chat-compatible
|
|
8
|
+
* `/chat/completions` endpoint. Reuses `OpenAIChat.protocol` end-to-end and
|
|
9
|
+
* overrides only the route id so providers can be resolved per-family without
|
|
10
|
+
* colliding with native OpenAI. Provider helpers configure the route endpoint
|
|
11
|
+
* before model selection.
|
|
12
|
+
*/
|
|
13
|
+
export const route = Route.make({
|
|
14
|
+
id: ADAPTER,
|
|
15
|
+
providerMetadataKey: "openai",
|
|
16
|
+
protocol: OpenAIChat.protocol,
|
|
17
|
+
endpoint: Endpoint.path("/chat/completions"),
|
|
18
|
+
framing: Framing.sse,
|
|
19
|
+
});
|
|
20
|
+
export * as OpenAICompatibleChat from "./openai-compatible-chat.js";
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Route, type RouteRoutedLanguageModelInput } from "../route/client.js";
|
|
2
|
+
export type OpenAICompatibleResponsesLanguageModelInput = RouteRoutedLanguageModelInput;
|
|
3
|
+
/**
|
|
4
|
+
* Deployment adapter for providers that expose an Open Responses-compatible
|
|
5
|
+
* `/responses` endpoint. Provider helpers configure identity, endpoint, and
|
|
6
|
+
* auth while the semantic protocol remains provider-neutral.
|
|
7
|
+
*/
|
|
8
|
+
export declare const route: Route<{
|
|
9
|
+
readonly input: readonly ({
|
|
10
|
+
readonly type: "reasoning";
|
|
11
|
+
readonly summary: readonly {
|
|
12
|
+
readonly type: "summary_text";
|
|
13
|
+
readonly text: string;
|
|
14
|
+
}[];
|
|
15
|
+
readonly id?: string | undefined;
|
|
16
|
+
readonly encrypted_content?: string | null | undefined;
|
|
17
|
+
} | {
|
|
18
|
+
readonly type: "item_reference";
|
|
19
|
+
readonly id: string;
|
|
20
|
+
} | {
|
|
21
|
+
readonly role: "system";
|
|
22
|
+
readonly content: string;
|
|
23
|
+
} | {
|
|
24
|
+
readonly role: "user";
|
|
25
|
+
readonly content: readonly ({
|
|
26
|
+
readonly type: "input_image";
|
|
27
|
+
readonly image_url: string;
|
|
28
|
+
} | {
|
|
29
|
+
readonly type: "input_file";
|
|
30
|
+
readonly filename: string;
|
|
31
|
+
readonly file_data: string;
|
|
32
|
+
readonly mime_type?: string | undefined;
|
|
33
|
+
} | {
|
|
34
|
+
readonly type: "input_text";
|
|
35
|
+
readonly text: string;
|
|
36
|
+
})[];
|
|
37
|
+
} | {
|
|
38
|
+
readonly content: readonly {
|
|
39
|
+
readonly type: "output_text";
|
|
40
|
+
readonly text: string;
|
|
41
|
+
}[];
|
|
42
|
+
readonly role: "assistant";
|
|
43
|
+
readonly phase?: "commentary" | "final_answer" | undefined;
|
|
44
|
+
} | {
|
|
45
|
+
readonly type: "function_call";
|
|
46
|
+
readonly call_id: string;
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly arguments: string;
|
|
49
|
+
} | {
|
|
50
|
+
readonly type: "function_call_output";
|
|
51
|
+
readonly call_id: string;
|
|
52
|
+
readonly output: string | readonly ({
|
|
53
|
+
readonly type: "input_image";
|
|
54
|
+
readonly image_url: string;
|
|
55
|
+
} | {
|
|
56
|
+
readonly type: "input_file";
|
|
57
|
+
readonly filename: string;
|
|
58
|
+
readonly file_data: string;
|
|
59
|
+
readonly mime_type?: string | undefined;
|
|
60
|
+
} | {
|
|
61
|
+
readonly type: "input_text";
|
|
62
|
+
readonly text: string;
|
|
63
|
+
})[];
|
|
64
|
+
})[];
|
|
65
|
+
readonly model: string;
|
|
66
|
+
readonly stream: true;
|
|
67
|
+
readonly instructions?: string | undefined;
|
|
68
|
+
readonly reasoning?: {
|
|
69
|
+
readonly summary?: "auto" | "concise" | "detailed" | undefined;
|
|
70
|
+
readonly effort?: string | undefined;
|
|
71
|
+
} | undefined;
|
|
72
|
+
readonly tools?: readonly {
|
|
73
|
+
readonly type: "function";
|
|
74
|
+
readonly description: string;
|
|
75
|
+
readonly name: string;
|
|
76
|
+
readonly parameters: {
|
|
77
|
+
readonly [x: string]: unknown;
|
|
78
|
+
};
|
|
79
|
+
readonly strict?: boolean | undefined;
|
|
80
|
+
}[] | undefined;
|
|
81
|
+
readonly text?: {
|
|
82
|
+
readonly verbosity?: "low" | "medium" | "high" | undefined;
|
|
83
|
+
} | undefined;
|
|
84
|
+
readonly temperature?: number | undefined;
|
|
85
|
+
readonly tool_choice?: "required" | "none" | "auto" | {
|
|
86
|
+
readonly type: "function";
|
|
87
|
+
readonly name: string;
|
|
88
|
+
} | undefined;
|
|
89
|
+
readonly top_p?: number | undefined;
|
|
90
|
+
readonly include?: readonly ("file_search_call.results" | "web_search_call.results" | "web_search_call.action.sources" | "message.input_image.image_url" | "computer_call_output.output.image_url" | "code_interpreter_call.outputs" | "reasoning.encrypted_content" | "message.output_text.logprobs")[] | undefined;
|
|
91
|
+
readonly store?: boolean | undefined;
|
|
92
|
+
readonly prompt_cache_key?: string | undefined;
|
|
93
|
+
readonly service_tier?: "default" | "auto" | "flex" | "priority" | undefined;
|
|
94
|
+
readonly max_output_tokens?: number | undefined;
|
|
95
|
+
}, import("../route/transport/http.js").HttpPrepared<string>>;
|
|
96
|
+
export * as OpenAICompatibleResponses from "./openai-compatible-responses.js";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Route } from "../route/client.js";
|
|
2
|
+
import { Endpoint } from "../route/endpoint.js";
|
|
3
|
+
import { OpenResponses } from "./open-responses.js";
|
|
4
|
+
const ADAPTER = "openai-compatible-responses";
|
|
5
|
+
/**
|
|
6
|
+
* Deployment adapter for providers that expose an Open Responses-compatible
|
|
7
|
+
* `/responses` endpoint. Provider helpers configure identity, endpoint, and
|
|
8
|
+
* auth while the semantic protocol remains provider-neutral.
|
|
9
|
+
*/
|
|
10
|
+
export const route = Route.make({
|
|
11
|
+
id: ADAPTER,
|
|
12
|
+
providerMetadataKey: "openresponses",
|
|
13
|
+
protocol: OpenResponses.protocol,
|
|
14
|
+
endpoint: Endpoint.path(OpenResponses.PATH),
|
|
15
|
+
transport: OpenResponses.httpTransport,
|
|
16
|
+
});
|
|
17
|
+
export * as OpenAICompatibleResponses from "./openai-compatible-responses.js";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ImageModel, type ImageInput } from "../image.js";
|
|
2
|
+
import { type Definition as AuthDefinition } from "../route/auth.js";
|
|
3
|
+
import { type HttpOptions } from "../schema/index.js";
|
|
4
|
+
export declare const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
5
|
+
export declare const PATH = "/images/generations";
|
|
6
|
+
export declare const EDIT_PATH = "/images/edits";
|
|
7
|
+
export type OpenAIImageString<Known extends string> = Known | (string & {});
|
|
8
|
+
export type OpenAIImageOptions = {
|
|
9
|
+
readonly mask?: ImageInput;
|
|
10
|
+
readonly n?: number;
|
|
11
|
+
readonly size?: OpenAIImageString<"auto" | "256x256" | "512x512" | "1024x1024" | "1536x1024" | "1024x1536" | "1792x1024" | "1024x1792">;
|
|
12
|
+
readonly quality?: OpenAIImageString<"auto" | "low" | "medium" | "high" | "standard" | "hd">;
|
|
13
|
+
readonly background?: OpenAIImageString<"auto" | "opaque" | "transparent">;
|
|
14
|
+
readonly moderation?: OpenAIImageString<"auto" | "low">;
|
|
15
|
+
readonly outputFormat?: OpenAIImageString<"png" | "jpeg" | "webp">;
|
|
16
|
+
readonly outputCompression?: number;
|
|
17
|
+
} & Record<string, unknown>;
|
|
18
|
+
export type OpenAIImageBody = Record<string, unknown> & {
|
|
19
|
+
readonly model: string;
|
|
20
|
+
readonly prompt: string;
|
|
21
|
+
};
|
|
22
|
+
export interface ModelInput {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly auth: AuthDefinition;
|
|
25
|
+
readonly baseURL?: string;
|
|
26
|
+
readonly headers?: Record<string, string>;
|
|
27
|
+
readonly http?: HttpOptions;
|
|
28
|
+
}
|
|
29
|
+
export declare const model: (input: ModelInput) => ImageModel<OpenAIImageOptions>;
|
|
30
|
+
export declare const OpenAIImages: {
|
|
31
|
+
readonly model: (input: ModelInput) => ImageModel<OpenAIImageOptions>;
|
|
32
|
+
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Effect, Encoding, Schema } from "effect";
|
|
2
|
+
import { Headers, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
|
|
3
|
+
import { ImageModel, GeneratedImage, ImageResponse, } from "../image.js";
|
|
4
|
+
import { Auth } from "../route/auth.js";
|
|
5
|
+
import { InvalidProviderOutputReason, AIError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema/index.js";
|
|
6
|
+
import { ProviderShared } from "./shared.js";
|
|
7
|
+
import { ImageInputs } from "./utils/image-input.js";
|
|
8
|
+
import { OpenAIImage } from "./utils/openai-image.js";
|
|
9
|
+
const ADAPTER = "openai-images";
|
|
10
|
+
export const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
11
|
+
export const PATH = "/images/generations";
|
|
12
|
+
export const EDIT_PATH = "/images/edits";
|
|
13
|
+
const OpenAIImageResponse = Schema.Struct({
|
|
14
|
+
data: Schema.Array(Schema.Struct({
|
|
15
|
+
b64_json: Schema.optional(Schema.String),
|
|
16
|
+
url: Schema.optional(Schema.String),
|
|
17
|
+
revised_prompt: Schema.optional(Schema.String),
|
|
18
|
+
})),
|
|
19
|
+
output_format: Schema.optional(Schema.String),
|
|
20
|
+
usage: Schema.optional(Schema.Struct({
|
|
21
|
+
input_tokens: Schema.optional(Schema.Number),
|
|
22
|
+
output_tokens: Schema.optional(Schema.Number),
|
|
23
|
+
total_tokens: Schema.optional(Schema.Number),
|
|
24
|
+
input_tokens_details: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
|
|
25
|
+
output_tokens_details: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
|
|
26
|
+
})),
|
|
27
|
+
});
|
|
28
|
+
const nativeOptions = (options) => {
|
|
29
|
+
if (!options)
|
|
30
|
+
return undefined;
|
|
31
|
+
const { mask: _, outputFormat, outputCompression, ...native } = options;
|
|
32
|
+
return {
|
|
33
|
+
output_format: outputFormat,
|
|
34
|
+
output_compression: outputCompression,
|
|
35
|
+
...native,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
const invalidOutput = (message) => new AIError({
|
|
39
|
+
module: ADAPTER,
|
|
40
|
+
method: "generate",
|
|
41
|
+
reason: new InvalidProviderOutputReason({ message, route: ADAPTER }),
|
|
42
|
+
});
|
|
43
|
+
const applyQuery = (url, query) => {
|
|
44
|
+
if (!query)
|
|
45
|
+
return url;
|
|
46
|
+
const next = new URL(url);
|
|
47
|
+
Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
|
|
48
|
+
return next.toString();
|
|
49
|
+
};
|
|
50
|
+
export const model = (input) => {
|
|
51
|
+
const route = {
|
|
52
|
+
id: ADAPTER,
|
|
53
|
+
generate: Effect.fn("OpenAIImages.generate")(function* (request, execute) {
|
|
54
|
+
const mask = request.options?.mask;
|
|
55
|
+
if (mask !== undefined && (request.images?.length ?? 0) === 0)
|
|
56
|
+
return yield* ImageInputs.invalid(ADAPTER, "An OpenAI image mask requires at least one input image");
|
|
57
|
+
const http = mergeHttpOptions(request.model.http, request.http);
|
|
58
|
+
const sourceImages = request.images ?? [];
|
|
59
|
+
const multipartImages = yield* Effect.forEach(sourceImages, (image) => {
|
|
60
|
+
if (image.type === "bytes")
|
|
61
|
+
return Effect.succeed({ data: image.data, mediaType: image.mediaType });
|
|
62
|
+
if (image.type === "url")
|
|
63
|
+
return ImageInputs.decodeDataUrl(image.url, ADAPTER);
|
|
64
|
+
return Effect.succeed(undefined);
|
|
65
|
+
});
|
|
66
|
+
const multipartMask = mask === undefined
|
|
67
|
+
? undefined
|
|
68
|
+
: mask.type === "bytes"
|
|
69
|
+
? { data: mask.data, mediaType: mask.mediaType }
|
|
70
|
+
: mask.type === "url"
|
|
71
|
+
? yield* ImageInputs.decodeDataUrl(mask.url, ADAPTER)
|
|
72
|
+
: undefined;
|
|
73
|
+
const useMultipart = sourceImages.length > 0 &&
|
|
74
|
+
multipartImages.every((image) => image !== undefined) &&
|
|
75
|
+
(mask === undefined || multipartMask !== undefined);
|
|
76
|
+
const path = sourceImages.length === 0 ? PATH : EDIT_PATH;
|
|
77
|
+
const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${path}`, http?.query);
|
|
78
|
+
if (useMultipart) {
|
|
79
|
+
const form = new FormData();
|
|
80
|
+
form.append("model", request.model.id);
|
|
81
|
+
form.append("prompt", request.prompt);
|
|
82
|
+
Object.entries(mergeJsonRecords(nativeOptions(request.options), http?.body) ?? {}).forEach(([key, value]) => {
|
|
83
|
+
if (["model", "prompt", "image", "image[]", "images", "mask"].includes(key))
|
|
84
|
+
return;
|
|
85
|
+
form.append(key, typeof value === "string" ? value : ProviderShared.encodeJson(value));
|
|
86
|
+
});
|
|
87
|
+
multipartImages.forEach((image, index) => {
|
|
88
|
+
if (image === undefined)
|
|
89
|
+
return;
|
|
90
|
+
form.append("image[]", imageBlob(image.data, image.mediaType), `image-${index}`);
|
|
91
|
+
});
|
|
92
|
+
if (multipartMask !== undefined)
|
|
93
|
+
form.append("mask", imageBlob(multipartMask.data, multipartMask.mediaType), "mask");
|
|
94
|
+
const headers = yield* Auth.toEffect(input.auth)({
|
|
95
|
+
request,
|
|
96
|
+
method: "POST",
|
|
97
|
+
url,
|
|
98
|
+
body: "[multipart/form-data]",
|
|
99
|
+
headers: Headers.remove(Headers.fromInput({ ...input.headers, ...http?.headers }), "content-type"),
|
|
100
|
+
});
|
|
101
|
+
const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyFormData(form)));
|
|
102
|
+
return yield* parseResponse(response, request.options, http?.body);
|
|
103
|
+
}
|
|
104
|
+
const references = sourceImages.map((image) => {
|
|
105
|
+
if (image.type === "bytes")
|
|
106
|
+
return { image_url: ImageInputs.dataUrl(image) };
|
|
107
|
+
if (image.type === "url")
|
|
108
|
+
return { image_url: image.url };
|
|
109
|
+
if (image.type === "file-id")
|
|
110
|
+
return { file_id: image.id };
|
|
111
|
+
return undefined;
|
|
112
|
+
});
|
|
113
|
+
if (references.some((image) => image === undefined))
|
|
114
|
+
return yield* ImageInputs.invalid(ADAPTER, "OpenAI Images accepts image URLs, data URLs, bytes, and file IDs");
|
|
115
|
+
const maskReference = mask === undefined
|
|
116
|
+
? undefined
|
|
117
|
+
: mask.type === "bytes"
|
|
118
|
+
? { image_url: ImageInputs.dataUrl(mask) }
|
|
119
|
+
: mask.type === "url"
|
|
120
|
+
? { image_url: mask.url }
|
|
121
|
+
: mask.type === "file-id"
|
|
122
|
+
? { file_id: mask.id }
|
|
123
|
+
: undefined;
|
|
124
|
+
if (mask !== undefined && maskReference === undefined)
|
|
125
|
+
return yield* ImageInputs.invalid(ADAPTER, "OpenAI Images accepts masks as URLs, data URLs, bytes, or file IDs");
|
|
126
|
+
const requestBody = mergeJsonRecords({
|
|
127
|
+
model: request.model.id,
|
|
128
|
+
prompt: request.prompt,
|
|
129
|
+
images: references.length === 0 ? undefined : references,
|
|
130
|
+
mask: maskReference,
|
|
131
|
+
}, nativeOptions(request.options), http?.body);
|
|
132
|
+
const text = ProviderShared.encodeJson(requestBody);
|
|
133
|
+
const headers = yield* Auth.toEffect(input.auth)({
|
|
134
|
+
request,
|
|
135
|
+
method: "POST",
|
|
136
|
+
url,
|
|
137
|
+
body: text,
|
|
138
|
+
headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
|
|
139
|
+
});
|
|
140
|
+
const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
|
|
141
|
+
return yield* parseResponse(response, request.options, http?.body);
|
|
142
|
+
}),
|
|
143
|
+
};
|
|
144
|
+
return ImageModel.make({ id: input.id, provider: "openai", route, http: input.http });
|
|
145
|
+
};
|
|
146
|
+
const parseResponse = Effect.fn("OpenAIImages.parseResponse")(function* (response, options, overlay) {
|
|
147
|
+
const payload = yield* response.json.pipe(Effect.mapError(() => invalidOutput("Failed to read the OpenAI Images response")));
|
|
148
|
+
const decoded = yield* Schema.decodeUnknownEffect(OpenAIImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("OpenAI Images returned an invalid response")));
|
|
149
|
+
const requestBody = mergeJsonRecords(nativeOptions(options), overlay);
|
|
150
|
+
const format = decoded.output_format ?? (typeof requestBody?.output_format === "string" ? requestBody.output_format : "png");
|
|
151
|
+
const images = yield* Effect.forEach(decoded.data, (item, index) => {
|
|
152
|
+
if (item.b64_json)
|
|
153
|
+
return Effect.fromResult(Encoding.decodeBase64(item.b64_json)).pipe(Effect.mapError(() => invalidOutput(`OpenAI Images result ${index} contains invalid base64 data`)), Effect.map((data) => new GeneratedImage({
|
|
154
|
+
mediaType: `image/${format}`,
|
|
155
|
+
data,
|
|
156
|
+
providerMetadata: item.revised_prompt === undefined ? undefined : { openai: { revisedPrompt: item.revised_prompt } },
|
|
157
|
+
})));
|
|
158
|
+
if (item.url)
|
|
159
|
+
return Effect.succeed(new GeneratedImage({
|
|
160
|
+
mediaType: `image/${format}`,
|
|
161
|
+
data: item.url,
|
|
162
|
+
providerMetadata: item.revised_prompt === undefined ? undefined : { openai: { revisedPrompt: item.revised_prompt } },
|
|
163
|
+
}));
|
|
164
|
+
return Effect.fail(invalidOutput(`OpenAI Images result ${index} has neither image data nor a URL`));
|
|
165
|
+
});
|
|
166
|
+
if (images.length === 0)
|
|
167
|
+
return yield* invalidOutput("OpenAI Images returned no images");
|
|
168
|
+
return new ImageResponse({
|
|
169
|
+
images,
|
|
170
|
+
usage: decoded.usage === undefined
|
|
171
|
+
? undefined
|
|
172
|
+
: new Usage({
|
|
173
|
+
inputTokens: decoded.usage.input_tokens,
|
|
174
|
+
outputTokens: decoded.usage.output_tokens,
|
|
175
|
+
totalTokens: decoded.usage.total_tokens,
|
|
176
|
+
providerMetadata: { openai: decoded.usage },
|
|
177
|
+
}),
|
|
178
|
+
providerMetadata: { openai: { outputFormat: format } },
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
const imageBlob = (data, mediaType) => {
|
|
182
|
+
const buffer = new ArrayBuffer(data.byteLength);
|
|
183
|
+
new Uint8Array(buffer).set(data);
|
|
184
|
+
return new Blob([buffer], { type: mediaType });
|
|
185
|
+
};
|
|
186
|
+
export const OpenAIImages = {
|
|
187
|
+
model,
|
|
188
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { WebSocketChannelDriver } from "../route/transport/index.js";
|
|
2
|
+
export interface DriverInput {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly request: Readonly<Record<string, unknown>>;
|
|
6
|
+
readonly message: string;
|
|
7
|
+
readonly base: WebSocketChannelDriver;
|
|
8
|
+
}
|
|
9
|
+
export declare const driver: (input: DriverInput) => WebSocketChannelDriver;
|
|
10
|
+
export declare const OpenAIResponsesChannel: {
|
|
11
|
+
readonly driver: (input: DriverInput) => WebSocketChannelDriver;
|
|
12
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { AIError, TransportReason } from "../schema/index.js";
|
|
2
|
+
import { Effect, Option, Schema } from "effect";
|
|
3
|
+
import * as ProviderShared from "./shared.js";
|
|
4
|
+
import { OpenResponses } from "./open-responses.js";
|
|
5
|
+
const PROTOCOL = "openai-responses.websocket.v1";
|
|
6
|
+
const VERSION = 1;
|
|
7
|
+
const decodeEvent = Schema.decodeUnknownEffect(OpenResponses.protocol.stream.event);
|
|
8
|
+
const checkpointValue = (checkpoint) => {
|
|
9
|
+
if (checkpoint?.protocol !== PROTOCOL || !ProviderShared.isRecord(checkpoint.value))
|
|
10
|
+
return undefined;
|
|
11
|
+
if (checkpoint.value.version !== VERSION)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (typeof checkpoint.value.responseID !== "string" || checkpoint.value.responseID.trim().length === 0)
|
|
14
|
+
return undefined;
|
|
15
|
+
if (!ProviderShared.isRecord(checkpoint.value.request) || !Array.isArray(checkpoint.value.output))
|
|
16
|
+
return undefined;
|
|
17
|
+
return {
|
|
18
|
+
version: VERSION,
|
|
19
|
+
responseID: checkpoint.value.responseID,
|
|
20
|
+
request: checkpoint.value.request,
|
|
21
|
+
output: checkpoint.value.output,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
const canonical = (value) => {
|
|
25
|
+
if (value === undefined)
|
|
26
|
+
return "undefined";
|
|
27
|
+
if (Array.isArray(value))
|
|
28
|
+
return `[${value.map(canonical).join(",")}]`;
|
|
29
|
+
if (!ProviderShared.isRecord(value))
|
|
30
|
+
return ProviderShared.encodeJson(value);
|
|
31
|
+
return `{${Object.keys(value)
|
|
32
|
+
.sort()
|
|
33
|
+
.map((key) => `${ProviderShared.encodeJson(key)}:${canonical(value[key])}`)
|
|
34
|
+
.join(",")}}`;
|
|
35
|
+
};
|
|
36
|
+
const json = (value) => {
|
|
37
|
+
if (typeof value !== "string")
|
|
38
|
+
return value;
|
|
39
|
+
return Option.getOrElse(Schema.decodeUnknownOption(ProviderShared.Json)(value), () => value);
|
|
40
|
+
};
|
|
41
|
+
const comparable = (value) => {
|
|
42
|
+
if (!ProviderShared.isRecord(value))
|
|
43
|
+
return value;
|
|
44
|
+
if (value.type === "message" && value.role === "assistant")
|
|
45
|
+
return {
|
|
46
|
+
role: "assistant",
|
|
47
|
+
content: value.content,
|
|
48
|
+
...(value.phase === undefined ? {} : { phase: value.phase }),
|
|
49
|
+
};
|
|
50
|
+
if (value.type === "function_call")
|
|
51
|
+
return {
|
|
52
|
+
type: value.type,
|
|
53
|
+
call_id: value.call_id,
|
|
54
|
+
name: value.name,
|
|
55
|
+
arguments: json(value.arguments),
|
|
56
|
+
};
|
|
57
|
+
if (value.type === "reasoning")
|
|
58
|
+
return {
|
|
59
|
+
type: value.type,
|
|
60
|
+
summary: value.summary,
|
|
61
|
+
encrypted_content: value.encrypted_content,
|
|
62
|
+
};
|
|
63
|
+
return value;
|
|
64
|
+
};
|
|
65
|
+
const invariant = (request) => {
|
|
66
|
+
const { type: _type, input: _input, previous_response_id: _previousResponseID, ...rest } = request;
|
|
67
|
+
return rest;
|
|
68
|
+
};
|
|
69
|
+
const incremental = (request, checkpoint) => {
|
|
70
|
+
const input = request.input;
|
|
71
|
+
const previousInput = checkpoint.request.input;
|
|
72
|
+
if (!Array.isArray(input) || !Array.isArray(previousInput))
|
|
73
|
+
return undefined;
|
|
74
|
+
if (canonical(invariant(request)) !== canonical(invariant(checkpoint.request)))
|
|
75
|
+
return undefined;
|
|
76
|
+
const baseline = [...previousInput, ...checkpoint.output];
|
|
77
|
+
if (input.length <= baseline.length)
|
|
78
|
+
return undefined;
|
|
79
|
+
if (!baseline.every((item, index) => canonical(comparable(item)) === canonical(comparable(input[index]))))
|
|
80
|
+
return undefined;
|
|
81
|
+
return input.slice(baseline.length);
|
|
82
|
+
};
|
|
83
|
+
const code = (event) => event.code || event.error?.code || event.response?.error?.code || undefined;
|
|
84
|
+
const rejected = (input, observation, recovery) => ({
|
|
85
|
+
type: "rejected",
|
|
86
|
+
recovery,
|
|
87
|
+
error: new AIError({
|
|
88
|
+
module: input.id,
|
|
89
|
+
method: "stream",
|
|
90
|
+
reason: new TransportReason({
|
|
91
|
+
message: observation.error.message,
|
|
92
|
+
transport: "websocket",
|
|
93
|
+
operation: "read",
|
|
94
|
+
phase: "receive",
|
|
95
|
+
delivery: "rejected",
|
|
96
|
+
recovery,
|
|
97
|
+
}),
|
|
98
|
+
}),
|
|
99
|
+
});
|
|
100
|
+
export const driver = (input) => {
|
|
101
|
+
const { previous_response_id: _previousResponseID, ...request } = input.request;
|
|
102
|
+
let output = [];
|
|
103
|
+
return {
|
|
104
|
+
create: (checkpoint) => Effect.sync(() => {
|
|
105
|
+
output = [];
|
|
106
|
+
const previous = checkpointValue(checkpoint);
|
|
107
|
+
const delta = previous ? incremental(request, previous) : undefined;
|
|
108
|
+
if (!previous || !delta)
|
|
109
|
+
return { message: ProviderShared.encodeJson(request), mode: "full" };
|
|
110
|
+
return {
|
|
111
|
+
message: ProviderShared.encodeJson({ ...request, input: delta, previous_response_id: previous.responseID }),
|
|
112
|
+
mode: "incremental",
|
|
113
|
+
};
|
|
114
|
+
}),
|
|
115
|
+
observe: (create, frame) => Effect.gen(function* () {
|
|
116
|
+
const event = yield* decodeEvent(frame).pipe(Effect.mapError(() => ProviderShared.eventError(input.id, `Invalid ${input.name} WebSocket event`, frame)));
|
|
117
|
+
const observation = yield* input.base.observe(create, frame);
|
|
118
|
+
if (event.type === "response.output_item.done" && event.item)
|
|
119
|
+
output.push(event.item);
|
|
120
|
+
if (observation.type === "provider-failure") {
|
|
121
|
+
const rejection = code(event);
|
|
122
|
+
if (rejection === "previous_response_not_found")
|
|
123
|
+
return rejected(input, observation, "retry-full");
|
|
124
|
+
if (rejection === "websocket_connection_limit_reached")
|
|
125
|
+
return rejected(input, observation, "rotate-and-retry-full");
|
|
126
|
+
}
|
|
127
|
+
if (observation.type !== "completed")
|
|
128
|
+
return observation;
|
|
129
|
+
const responseID = event.response?.id;
|
|
130
|
+
if (!responseID || responseID.trim().length === 0)
|
|
131
|
+
return observation;
|
|
132
|
+
return {
|
|
133
|
+
...observation,
|
|
134
|
+
checkpoint: {
|
|
135
|
+
protocol: PROTOCOL,
|
|
136
|
+
value: { version: VERSION, responseID, request, output: output.slice() },
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
export const OpenAIResponsesChannel = { driver };
|