@workglow/openai 0.3.23 → 0.3.25
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/ai/OpenAiProvider.d.ts +15 -0
- package/dist/ai/OpenAiProvider.d.ts.map +1 -1
- package/dist/ai/OpenAiQueuedProvider.d.ts +15 -0
- package/dist/ai/OpenAiQueuedProvider.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_Client.d.ts +31 -0
- package/dist/ai/common/OpenAI_Client.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_ModelSchema.d.ts +66 -0
- package/dist/ai/common/OpenAI_ModelSchema.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_ModelSearch.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_StructuredGeneration.d.ts +9 -0
- package/dist/ai/common/OpenAI_StructuredGeneration.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_TextGeneration.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_TextRewriter.d.ts +2 -1
- package/dist/ai/common/OpenAI_TextRewriter.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_TextSummary.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_ToolCalling.d.ts +2 -2
- package/dist/ai/common/OpenAI_ToolCalling.d.ts.map +1 -1
- package/dist/ai/index.d.ts +10 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai-runtime.browser.js +154 -78
- package/dist/ai-runtime.browser.js.map +9 -9
- package/dist/ai-runtime.js +154 -78
- package/dist/ai-runtime.js.map +9 -9
- package/dist/ai.browser.js +56 -1
- package/dist/ai.browser.js.map +4 -4
- package/dist/ai.js +177 -79
- package/dist/ai.js.map +11 -11
- package/package.json +11 -11
package/dist/ai.browser.js
CHANGED
|
@@ -49,6 +49,39 @@ function getModelName(model) {
|
|
|
49
49
|
}
|
|
50
50
|
return name;
|
|
51
51
|
}
|
|
52
|
+
function getReasoningConfig(model) {
|
|
53
|
+
const reasoning = model?.provider_config?.reasoning;
|
|
54
|
+
if (!reasoning || reasoning.effort === undefined && reasoning.mode === undefined) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
return reasoning;
|
|
58
|
+
}
|
|
59
|
+
function fnv1aHex(input) {
|
|
60
|
+
let hash = 2166136261;
|
|
61
|
+
for (let i = 0;i < input.length; i++) {
|
|
62
|
+
hash ^= input.charCodeAt(i);
|
|
63
|
+
hash = Math.imul(hash, 16777619);
|
|
64
|
+
}
|
|
65
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
66
|
+
}
|
|
67
|
+
function resolvePromptCacheKey(model, params) {
|
|
68
|
+
const override = model?.provider_config?.prompt_cache_key;
|
|
69
|
+
if (override)
|
|
70
|
+
return override;
|
|
71
|
+
const material = JSON.stringify([
|
|
72
|
+
params.model ?? "",
|
|
73
|
+
params.instructions ?? "",
|
|
74
|
+
params.tools ?? null
|
|
75
|
+
]);
|
|
76
|
+
return `wg-${fnv1aHex(material)}`;
|
|
77
|
+
}
|
|
78
|
+
function finalizeResponsesRequest(model, params) {
|
|
79
|
+
const reasoning = getReasoningConfig(model);
|
|
80
|
+
if (reasoning !== undefined)
|
|
81
|
+
params.reasoning = reasoning;
|
|
82
|
+
params.prompt_cache_key = resolvePromptCacheKey(model, params);
|
|
83
|
+
return params;
|
|
84
|
+
}
|
|
52
85
|
// src/ai/common/OpenAI_Constants.ts
|
|
53
86
|
var OPENAI = "OPENAI";
|
|
54
87
|
// src/ai/common/OpenAI_ModelSchema.ts
|
|
@@ -88,6 +121,28 @@ var OpenAiModelSchema = {
|
|
|
88
121
|
organization: {
|
|
89
122
|
type: "string",
|
|
90
123
|
description: "OpenAI organization ID (optional)."
|
|
124
|
+
},
|
|
125
|
+
prompt_cache_key: {
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "Overrides the auto-derived Responses prompt_cache_key. Requests sharing a key share a cached prefix; leave unset to derive a stable key from the model + system instructions + tools.",
|
|
128
|
+
"x-ui-hidden": true
|
|
129
|
+
},
|
|
130
|
+
reasoning: {
|
|
131
|
+
type: "object",
|
|
132
|
+
description: "Reasoning controls for reasoning-capable models (e.g. the GPT-5.6 sol/terra/luna family), sent on the Responses API.",
|
|
133
|
+
properties: {
|
|
134
|
+
effort: {
|
|
135
|
+
type: "string",
|
|
136
|
+
enum: ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
137
|
+
description: "Reasoning effort. Higher effort trades latency and cost for quality."
|
|
138
|
+
},
|
|
139
|
+
mode: {
|
|
140
|
+
type: "string",
|
|
141
|
+
enum: ["pro"],
|
|
142
|
+
description: "Set to 'pro' for the quality-first pro configuration on supported models."
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
additionalProperties: false
|
|
91
146
|
}
|
|
92
147
|
},
|
|
93
148
|
required: ["model_name"],
|
|
@@ -228,4 +283,4 @@ export {
|
|
|
228
283
|
OPENAI
|
|
229
284
|
};
|
|
230
285
|
|
|
231
|
-
//# debugId=
|
|
286
|
+
//# debugId=C376FBCA13DE0E7964756E2164756E21
|
package/dist/ai.browser.js.map
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/ai/common/OpenAI_Client.ts", "../src/ai/common/OpenAI_Constants.ts", "../src/ai/common/OpenAI_ModelSchema.ts", "../src/ai/registerOpenAi.ts", "../src/ai/OpenAiQueuedProvider.ts", "../src/ai/common/OpenAI_CapabilitySets.ts", "../src/ai/common/OpenAI_Capabilities.ts", "../src/ai/common/OpenAI_ImageValidation.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { isBrowserLike, resolveApiKey, validateProviderBaseUrl } from \"@workglow/ai/provider-utils\";\nimport type { OpenAiModelConfig } from \"./OpenAI_ModelSchema\";\n\n/**\n * Hostnames (or hostname suffixes) accepted for OpenAI `base_url` without\n * the explicit `trustedBaseUrl` opt-out. Includes Azure OpenAI tenants.\n */\nexport const OPENAI_ALLOWED_HOSTS: readonly string[] = [\"api.openai.com\", \".openai.azure.com\"];\n\ntype OpenAIClientClass = new (config: any) => any;\n\nlet _loadPromise: Promise<OpenAIClientClass> | undefined;\n\n// NOTE: we do not want to de-dup this in the provider-utils, vite wants direct import with string literals.\nexport async function loadOpenAISDK(): Promise<OpenAIClientClass> {\n _loadPromise ??= import(/* @vite-ignore */ \"openai\")\n\n .then((mod) => mod.default as OpenAIClientClass)\n .catch(() => {\n _loadPromise = undefined;\n throw new Error(\"openai is required for OpenAI tasks. Install it with: bun add openai\");\n });\n return _loadPromise;\n}\n\ninterface ResolvedProviderConfig {\n readonly credential_key?: string;\n readonly api_key?: string;\n readonly model_name?: string;\n readonly base_url?: string;\n readonly organization?: string;\n /**\n * When `true`, accept the `base_url` even if its hostname is not in\n * {@link OPENAI_ALLOWED_HOSTS}. Use only for known-good custom enterprise\n * gateways. The URL still has to parse and use a safe scheme.\n */\n readonly trustedBaseUrl?: boolean;\n}\n\nexport async function getClient(model: OpenAiModelConfig | undefined) {\n const OpenAI = await loadOpenAISDK();\n const config = model?.provider_config as ResolvedProviderConfig | undefined;\n const apiKey = resolveApiKey({\n config,\n envVar: \"OPENAI_API_KEY\",\n providerLabel: \"OpenAI\",\n });\n // Throw before SDK construction on a rejected base_url so the API key is\n // never sent to an unvalidated host.\n const baseURL = validateProviderBaseUrl(config?.base_url, {\n vendor: \"openai\",\n allowHosts: OPENAI_ALLOWED_HOSTS,\n trustedBaseUrl: config?.trustedBaseUrl,\n providerLabel: \"OpenAI\",\n });\n try {\n return new OpenAI({\n apiKey,\n baseURL,\n organization: config?.organization || undefined,\n dangerouslyAllowBrowser: isBrowserLike(),\n });\n } catch (err) {\n throw new Error(\n `Failed to create OpenAI client: ${err instanceof Error ? err.message : \"unknown error\"}`\n );\n }\n}\n\nexport function getModelName(model: OpenAiModelConfig | undefined): string {\n const name = model?.provider_config?.model_name;\n if (!name) {\n throw new Error(\"Missing model name in provider_config.model_name.\");\n }\n return name;\n}\n",
|
|
5
|
+
"/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { isBrowserLike, resolveApiKey, validateProviderBaseUrl } from \"@workglow/ai/provider-utils\";\nimport type { OpenAiModelConfig } from \"./OpenAI_ModelSchema\";\n\n/**\n * Hostnames (or hostname suffixes) accepted for OpenAI `base_url` without\n * the explicit `trustedBaseUrl` opt-out. Includes Azure OpenAI tenants.\n */\nexport const OPENAI_ALLOWED_HOSTS: readonly string[] = [\"api.openai.com\", \".openai.azure.com\"];\n\ntype OpenAIClientClass = new (config: any) => any;\n\nlet _loadPromise: Promise<OpenAIClientClass> | undefined;\n\n// NOTE: we do not want to de-dup this in the provider-utils, vite wants direct import with string literals.\nexport async function loadOpenAISDK(): Promise<OpenAIClientClass> {\n _loadPromise ??= import(/* @vite-ignore */ \"openai\")\n\n .then((mod) => mod.default as OpenAIClientClass)\n .catch(() => {\n _loadPromise = undefined;\n throw new Error(\"openai is required for OpenAI tasks. Install it with: bun add openai\");\n });\n return _loadPromise;\n}\n\ninterface ResolvedProviderConfig {\n readonly credential_key?: string;\n readonly api_key?: string;\n readonly model_name?: string;\n readonly base_url?: string;\n readonly organization?: string;\n readonly prompt_cache_key?: string;\n readonly reasoning?: { readonly effort?: string; readonly mode?: string };\n /**\n * When `true`, accept the `base_url` even if its hostname is not in\n * {@link OPENAI_ALLOWED_HOSTS}. Use only for known-good custom enterprise\n * gateways. The URL still has to parse and use a safe scheme.\n */\n readonly trustedBaseUrl?: boolean;\n}\n\nexport async function getClient(model: OpenAiModelConfig | undefined) {\n const OpenAI = await loadOpenAISDK();\n const config = model?.provider_config as ResolvedProviderConfig | undefined;\n const apiKey = resolveApiKey({\n config,\n envVar: \"OPENAI_API_KEY\",\n providerLabel: \"OpenAI\",\n });\n // Throw before SDK construction on a rejected base_url so the API key is\n // never sent to an unvalidated host.\n const baseURL = validateProviderBaseUrl(config?.base_url, {\n vendor: \"openai\",\n allowHosts: OPENAI_ALLOWED_HOSTS,\n trustedBaseUrl: config?.trustedBaseUrl,\n providerLabel: \"OpenAI\",\n });\n try {\n return new OpenAI({\n apiKey,\n baseURL,\n organization: config?.organization || undefined,\n dangerouslyAllowBrowser: isBrowserLike(),\n });\n } catch (err) {\n throw new Error(\n `Failed to create OpenAI client: ${err instanceof Error ? err.message : \"unknown error\"}`\n );\n }\n}\n\nexport function getModelName(model: OpenAiModelConfig | undefined): string {\n const name = model?.provider_config?.model_name;\n if (!name) {\n throw new Error(\"Missing model name in provider_config.model_name.\");\n }\n return name;\n}\n\n/**\n * Resolves the configured `reasoning` object for reasoning-capable models\n * (the GPT-5.6 sol/terra/luna family and the o-series), sent verbatim as the\n * Responses `reasoning` parameter. Returns `undefined` when unset so\n * non-reasoning models and callers that don't opt in send no reasoning field.\n */\nexport function getReasoningConfig(\n model: OpenAiModelConfig | undefined\n): { effort?: string; mode?: string } | undefined {\n const reasoning = (model?.provider_config as ResolvedProviderConfig | undefined)?.reasoning;\n if (!reasoning || (reasoning.effort === undefined && reasoning.mode === undefined)) {\n return undefined;\n }\n return reasoning;\n}\n\n/** Deterministic 32-bit FNV-1a hash → 8-char hex. Worker-safe (no crypto import). */\nfunction fnv1aHex(input: string): string {\n let hash = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Resolves the Responses `prompt_cache_key`. Uses an explicit\n * `provider_config.prompt_cache_key` override when set, otherwise derives a\n * stable key from the request's cache-relevant prefix (model + system\n * instructions + tools) so requests sharing that prefix converge on one key and\n * hit the cache. GPT-5.6 bills cache writes, so a stable key (not a random one)\n * is the cost-correct default.\n */\nexport function resolvePromptCacheKey(\n model: OpenAiModelConfig | undefined,\n params: { model?: unknown; instructions?: unknown; tools?: unknown }\n): string {\n const override = (model?.provider_config as ResolvedProviderConfig | undefined)?.prompt_cache_key;\n if (override) return override;\n const material = JSON.stringify([\n params.model ?? \"\",\n params.instructions ?? \"\",\n params.tools ?? null,\n ]);\n return `wg-${fnv1aHex(material)}`;\n}\n\n/**\n * Applies the per-request Responses fields common to every OpenAI text run-fn:\n * the model's `reasoning` config (when set) and a stable `prompt_cache_key`.\n * Mutates and returns `params` so callers can inline it into the create call.\n * Call this last, after model/instructions/tools are populated, so the cache\n * key sees the full prefix.\n */\nexport function finalizeResponsesRequest(\n model: OpenAiModelConfig | undefined,\n params: Record<string, unknown>\n): Record<string, unknown> {\n const reasoning = getReasoningConfig(model);\n if (reasoning !== undefined) params.reasoning = reasoning;\n params.prompt_cache_key = resolvePromptCacheKey(model, params);\n return params;\n}\n",
|
|
6
6
|
"/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nexport const OPENAI = \"OPENAI\";\n",
|
|
7
|
-
"/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { ModelConfigSchema, ModelRecordSchema } from \"@workglow/ai/worker\";\nimport { DataPortSchemaObject, FromSchema } from \"@workglow/util/worker\";\nimport { OPENAI } from \"./OpenAI_Constants\";\n\nexport const OpenAiModelSchema = {\n type: \"object\",\n properties: {\n provider: {\n const: OPENAI,\n description: \"Discriminator: OpenAI cloud provider.\",\n },\n provider_config: {\n type: \"object\",\n description: \"OpenAI-specific configuration.\",\n properties: {\n model_name: {\n type: \"string\",\n description: \"The OpenAI model identifier (e.g., 'gpt-4o', 'text-embedding-3-small').\",\n },\n credential_key: {\n type: \"string\",\n format: \"credential\",\n description: \"Key to look up in the credential store for the API key.\",\n \"x-ui-hidden\": true,\n },\n base_url: {\n type: \"string\",\n description: \"Base URL for the OpenAI API. Useful for Azure OpenAI or proxy servers.\",\n default: \"https://api.openai.com/v1\",\n },\n trustedBaseUrl: {\n type: \"boolean\",\n description:\n \"When true, accept a base_url whose hostname is not in the built-in allow-list. Use only for known-good custom enterprise gateways — otherwise an attacker can exfiltrate the API key by pointing base_url at their own server.\",\n default: false,\n \"x-ui-hidden\": true,\n },\n organization: {\n type: \"string\",\n description: \"OpenAI organization ID (optional).\",\n },\n },\n required: [\"model_name\"],\n additionalProperties: false,\n },\n },\n required: [\"provider\", \"provider_config\"],\n additionalProperties: true,\n} as const satisfies DataPortSchemaObject;\n\nexport const OpenAiModelRecordSchema = {\n type: \"object\",\n properties: {\n ...ModelRecordSchema.properties,\n ...OpenAiModelSchema.properties,\n },\n required: [...ModelRecordSchema.required, ...OpenAiModelSchema.required],\n additionalProperties: false,\n} as const satisfies DataPortSchemaObject;\n\nexport type OpenAiModelRecord = FromSchema<typeof OpenAiModelRecordSchema>;\n\nexport const OpenAiModelConfigSchema = {\n type: \"object\",\n properties: {\n ...ModelConfigSchema.properties,\n ...OpenAiModelSchema.properties,\n },\n required: [...ModelConfigSchema.required, ...OpenAiModelSchema.required],\n additionalProperties: false,\n} as const satisfies DataPortSchemaObject;\n\nexport type OpenAiModelConfig = FromSchema<typeof OpenAiModelConfigSchema>;\n",
|
|
7
|
+
"/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { ModelConfigSchema, ModelRecordSchema } from \"@workglow/ai/worker\";\nimport { DataPortSchemaObject, FromSchema } from \"@workglow/util/worker\";\nimport { OPENAI } from \"./OpenAI_Constants\";\n\nexport const OpenAiModelSchema = {\n type: \"object\",\n properties: {\n provider: {\n const: OPENAI,\n description: \"Discriminator: OpenAI cloud provider.\",\n },\n provider_config: {\n type: \"object\",\n description: \"OpenAI-specific configuration.\",\n properties: {\n model_name: {\n type: \"string\",\n description: \"The OpenAI model identifier (e.g., 'gpt-4o', 'text-embedding-3-small').\",\n },\n credential_key: {\n type: \"string\",\n format: \"credential\",\n description: \"Key to look up in the credential store for the API key.\",\n \"x-ui-hidden\": true,\n },\n base_url: {\n type: \"string\",\n description: \"Base URL for the OpenAI API. Useful for Azure OpenAI or proxy servers.\",\n default: \"https://api.openai.com/v1\",\n },\n trustedBaseUrl: {\n type: \"boolean\",\n description:\n \"When true, accept a base_url whose hostname is not in the built-in allow-list. Use only for known-good custom enterprise gateways — otherwise an attacker can exfiltrate the API key by pointing base_url at their own server.\",\n default: false,\n \"x-ui-hidden\": true,\n },\n organization: {\n type: \"string\",\n description: \"OpenAI organization ID (optional).\",\n },\n prompt_cache_key: {\n type: \"string\",\n description:\n \"Overrides the auto-derived Responses prompt_cache_key. Requests sharing a key share a cached prefix; leave unset to derive a stable key from the model + system instructions + tools.\",\n \"x-ui-hidden\": true,\n },\n reasoning: {\n type: \"object\",\n description:\n \"Reasoning controls for reasoning-capable models (e.g. the GPT-5.6 sol/terra/luna family), sent on the Responses API.\",\n properties: {\n effort: {\n type: \"string\",\n enum: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"],\n description: \"Reasoning effort. Higher effort trades latency and cost for quality.\",\n },\n mode: {\n type: \"string\",\n enum: [\"pro\"],\n description:\n \"Set to 'pro' for the quality-first pro configuration on supported models.\",\n },\n },\n additionalProperties: false,\n },\n },\n required: [\"model_name\"],\n additionalProperties: false,\n },\n },\n required: [\"provider\", \"provider_config\"],\n additionalProperties: true,\n} as const satisfies DataPortSchemaObject;\n\nexport const OpenAiModelRecordSchema = {\n type: \"object\",\n properties: {\n ...ModelRecordSchema.properties,\n ...OpenAiModelSchema.properties,\n },\n required: [...ModelRecordSchema.required, ...OpenAiModelSchema.required],\n additionalProperties: false,\n} as const satisfies DataPortSchemaObject;\n\nexport type OpenAiModelRecord = FromSchema<typeof OpenAiModelRecordSchema>;\n\nexport const OpenAiModelConfigSchema = {\n type: \"object\",\n properties: {\n ...ModelConfigSchema.properties,\n ...OpenAiModelSchema.properties,\n },\n required: [...ModelConfigSchema.required, ...OpenAiModelSchema.required],\n additionalProperties: false,\n} as const satisfies DataPortSchemaObject;\n\nexport type OpenAiModelConfig = FromSchema<typeof OpenAiModelConfigSchema>;\n",
|
|
8
8
|
"/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { AiProviderRegisterOptions } from \"@workglow/ai\";\nimport { registerProviderWithWorker } from \"@workglow/ai/provider-utils\";\nimport { OpenAiQueuedProvider } from \"./OpenAiQueuedProvider\";\nimport { registerOpenAiImageValidator } from \"./common/OpenAI_ImageValidation\";\n\nexport async function registerOpenAi(\n options: AiProviderRegisterOptions & {\n worker: Worker | (() => Worker);\n }\n): Promise<void> {\n registerOpenAiImageValidator();\n await registerProviderWithWorker(new OpenAiQueuedProvider(), \"OpenAI\", options);\n}\n",
|
|
9
9
|
"/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { Capability, ModelRecord } from \"@workglow/ai\";\nimport { AiProvider } from \"@workglow/ai\";\nimport { createCloudProviderClass } from \"@workglow/ai/provider-utils\";\nimport { inferOpenAiCapabilities, openAiWorkerRunFnSpecs } from \"./common/OpenAI_Capabilities\";\nimport { OPENAI } from \"./common/OpenAI_Constants\";\nimport type { OpenAiModelConfig } from \"./common/OpenAI_ModelSchema\";\n\n/**\n * Main-thread registration shell for OpenAI. Used both for inline mode\n * (constructed with the run-fn registrations array) and worker-backed mode\n * (constructed empty so the base class registers worker proxies). No queue\n * is created — OpenAI uses {@link DirectExecutionStrategy}.\n */\nexport class OpenAiQueuedProvider extends createCloudProviderClass<OpenAiModelConfig>(AiProvider, {\n name: OPENAI,\n displayName: \"OpenAI\",\n}) {\n override inferCapabilities(model: ModelRecord): readonly Capability[] {\n return inferOpenAiCapabilities(model);\n }\n\n protected override workerRunFnSpecs(): readonly { serves: readonly Capability[] }[] {\n return openAiWorkerRunFnSpecs();\n }\n}\n",
|
|
10
10
|
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { Capability } from \"@workglow/ai/worker\";\n\n/**\n * Single source of truth for OpenAI's capability sets.\n *\n * Both `OPENAI_RUN_FNS` (the worker-side registration list) and\n * `workerRunFnSpecs()` (the main-thread proxy declaration) derive their\n * `serves` arrays from these named exports. SDK-free so the main thread\n * can import without paying the OpenAI client cost.\n *\n * To add a new capability set: declare a new `as const` constant here,\n * then reference it from both `OPENAI_RUN_FNS` and `OPENAI_RUN_FN_SPECS`.\n */\nexport const OPENAI_TEXT_GENERATION = [\"text.generation\"] as const satisfies Capability[];\nexport const OPENAI_TOOL_USE = [\"text.generation\", \"tool-use\"] as const satisfies Capability[];\nexport const OPENAI_JSON_MODE = [\"text.generation\", \"json-mode\"] as const satisfies Capability[];\nexport const OPENAI_TEXT_REWRITER = [\"text.rewriter\"] as const satisfies Capability[];\nexport const OPENAI_TEXT_SUMMARY = [\"text.summary\"] as const satisfies Capability[];\nexport const OPENAI_TEXT_EMBEDDING = [\"text.embedding\"] as const satisfies Capability[];\nexport const OPENAI_IMAGE_GENERATION = [\"image.generation\"] as const satisfies Capability[];\nexport const OPENAI_IMAGE_EDITING = [\"image.editing\"] as const satisfies Capability[];\nexport const OPENAI_COUNT_TOKENS = [\"model.count-tokens\"] as const satisfies Capability[];\nexport const OPENAI_MODEL_SEARCH = [\"model.search\"] as const satisfies Capability[];\nexport const OPENAI_MODEL_INFO = [\"model.info\"] as const satisfies Capability[];\n\n/** Aggregated list — for `workerRunFnSpecs()` derivation. Order MUST match `OPENAI_RUN_FNS`. */\nexport const OPENAI_CAPABILITY_SETS = [\n OPENAI_TEXT_GENERATION,\n OPENAI_TOOL_USE,\n OPENAI_JSON_MODE,\n OPENAI_TEXT_REWRITER,\n OPENAI_TEXT_SUMMARY,\n OPENAI_TEXT_EMBEDDING,\n OPENAI_IMAGE_GENERATION,\n OPENAI_IMAGE_EDITING,\n OPENAI_COUNT_TOKENS,\n OPENAI_MODEL_SEARCH,\n OPENAI_MODEL_INFO,\n] as const;\n",
|
|
11
11
|
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { Capability, ModelRecord } from \"@workglow/ai/worker\";\nimport { OPENAI_CAPABILITY_SETS } from \"./OpenAI_CapabilitySets\";\n\n/**\n * Closed list of capability-set specs the OpenAI provider serves. Derived\n * from {@link OPENAI_CAPABILITY_SETS}. Used by the main-thread provider\n * shells when registering worker-mode proxies so the dispatcher can route\n * requests to the worker proxy.\n */\nexport const OPENAI_RUN_FN_SPECS = OPENAI_CAPABILITY_SETS.map((serves) => ({ serves }));\n\nexport function openAiWorkerRunFnSpecs(): readonly { readonly serves: readonly Capability[] }[] {\n return OPENAI_RUN_FN_SPECS;\n}\n\n/**\n * Shape used by the model-name regexes — `model_id` is required, the rest\n * is loosely-typed metadata only used to opportunistically widen the\n * inferred capability set.\n */\ntype CapabilityHints = Pick<ModelRecord, \"model_id\" | \"provider_config\" | \"capabilities\">;\n\n/**\n * Heuristic capability inference for an OpenAI {@link ModelRecord}. Pattern-\n * matches the canonical OpenAI model id strings (and the `provider_config.\n * model_name` if present) to a closed set of {@link Capability}s. Falls back\n * to the model's stored `capabilities` array (or a baseline of search +\n * info) when no pattern matches.\n *\n * Main-thread method only — workers do not run capability inference.\n */\nexport function inferOpenAiCapabilities(model: CapabilityHints): readonly Capability[] {\n const id = String(\n model.model_id ??\n (model.provider_config as { model_name?: string } | undefined)?.model_name ??\n \"\"\n );\n\n // Embedding models — text-embedding-3-{small,large}, text-embedding-ada-002.\n if (/^text-embedding/i.test(id)) {\n return [\"text.embedding\", \"model.count-tokens\", \"model.info\", \"model.search\"];\n }\n\n // Image models — DALL-E and gpt-image families.\n if (/^dall-e/i.test(id)) {\n // DALL-E 2/3 do NOT support edit consistently — gpt-image-* does. Cover\n // generation only here; the gpt-image branch below adds editing.\n return [\"image.generation\", \"model.info\", \"model.search\"];\n }\n if (/^gpt-image/i.test(id)) {\n return [\"image.generation\", \"image.editing\", \"model.info\", \"model.search\"];\n }\n\n // Chat / reasoning models — gpt-3.5/4/4o/5/...; o-series reasoning models (o1, o3, o4, future).\n // GPT-4o, gpt-4-vision-*, gpt-4-turbo, and all o-series additionally accept image inputs.\n if (/^gpt-/i.test(id) || /^o\\d/i.test(id)) {\n const caps: Capability[] = [\n \"text.generation\",\n \"text.rewriter\",\n \"text.summary\",\n \"tool-use\",\n \"json-mode\",\n \"model.count-tokens\",\n \"model.info\",\n \"model.search\",\n ];\n const supportsVision =\n /gpt-4o|gpt-4\\.1|gpt-5|gpt-4-vision|gpt-4-turbo/i.test(id) || /^o\\d/i.test(id);\n if (supportsVision) {\n caps.push(\"vision-input\");\n }\n return caps;\n }\n\n // Unknown model — fall back to whatever the record declared, or just\n // expose the meta-ops so the model can still be searched / inspected.\n const declared = (model.capabilities as readonly Capability[] | undefined) ?? [];\n if (declared.length > 0) return declared;\n return [\"model.search\", \"model.info\"];\n}\n",
|
|
12
12
|
"/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { ModelConfig } from \"@workglow/ai\";\nimport { AiImageOutputTask, ProviderUnsupportedFeatureError } from \"@workglow/ai\";\n\nimport { OPENAI } from \"./OpenAI_Constants\";\n\n/**\n * Registers the OpenAI per-provider image validator. Called at provider registration time\n * (both inline and worker-backed paths) so it runs on the main thread before any dispatch.\n *\n * Currently validates:\n * - DALL-E 2 + non-empty `additionalImages` → throws (single-image edit only).\n * DALL-E 3 + ImageEditTask is rejected upstream by the model registry task-array check.\n */\nexport function registerOpenAiImageValidator(): void {\n AiImageOutputTask.registerProviderImageValidator(\n OPENAI,\n (taskType, input, model: ModelConfig) => {\n if (taskType !== \"ImageEditTask\") return;\n const modelName =\n (model.provider_config as { model_name?: string } | undefined)?.model_name ?? \"\";\n const additional = input[\"additionalImages\"] as unknown[] | undefined;\n if (modelName.startsWith(\"dall-e-2\") && Array.isArray(additional) && additional.length > 0) {\n throw new ProviderUnsupportedFeatureError(\n \"additionalImages\",\n model.model_id ?? modelName,\n \"DALL-E 2 only supports single-image edits\"\n );\n }\n }\n );\n}\n"
|
|
13
13
|
],
|
|
14
|
-
"mappings": ";;;;;;;;;AAMA;AAOO,IAAM,uBAA0C,CAAC,kBAAkB,mBAAmB;AAI7F,IAAI;AAGJ,eAAsB,aAAa,GAA+B;AAAA,EAChE,iBAA2C,iBAExC,KAAK,CAAC,QAAQ,IAAI,OAA4B,EAC9C,MAAM,MAAM;AAAA,IACX,eAAe;AAAA,IACf,MAAM,IAAI,MAAM,sEAAsE;AAAA,GACvF;AAAA,EACH,OAAO;AAAA;
|
|
15
|
-
"debugId": "
|
|
14
|
+
"mappings": ";;;;;;;;;AAMA;AAOO,IAAM,uBAA0C,CAAC,kBAAkB,mBAAmB;AAI7F,IAAI;AAGJ,eAAsB,aAAa,GAA+B;AAAA,EAChE,iBAA2C,iBAExC,KAAK,CAAC,QAAQ,IAAI,OAA4B,EAC9C,MAAM,MAAM;AAAA,IACX,eAAe;AAAA,IACf,MAAM,IAAI,MAAM,sEAAsE;AAAA,GACvF;AAAA,EACH,OAAO;AAAA;AAmBT,eAAsB,SAAS,CAAC,OAAsC;AAAA,EACpE,MAAM,SAAS,MAAM,cAAc;AAAA,EACnC,MAAM,SAAS,OAAO;AAAA,EACtB,MAAM,SAAS,cAAc;AAAA,IAC3B;AAAA,IACA,QAAQ;AAAA,IACR,eAAe;AAAA,EACjB,CAAC;AAAA,EAGD,MAAM,UAAU,wBAAwB,QAAQ,UAAU;AAAA,IACxD,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,gBAAgB,QAAQ;AAAA,IACxB,eAAe;AAAA,EACjB,CAAC;AAAA,EACD,IAAI;AAAA,IACF,OAAO,IAAI,OAAO;AAAA,MAChB;AAAA,MACA;AAAA,MACA,cAAc,QAAQ,gBAAgB;AAAA,MACtC,yBAAyB,cAAc;AAAA,IACzC,CAAC;AAAA,IACD,OAAO,KAAK;AAAA,IACZ,MAAM,IAAI,MACR,mCAAmC,eAAe,QAAQ,IAAI,UAAU,iBAC1E;AAAA;AAAA;AAIG,SAAS,YAAY,CAAC,OAA8C;AAAA,EACzE,MAAM,OAAO,OAAO,iBAAiB;AAAA,EACrC,IAAI,CAAC,MAAM;AAAA,IACT,MAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AAAA,EACA,OAAO;AAAA;AASF,SAAS,kBAAkB,CAChC,OACgD;AAAA,EAChD,MAAM,YAAa,OAAO,iBAAwD;AAAA,EAClF,IAAI,CAAC,aAAc,UAAU,WAAW,aAAa,UAAU,SAAS,WAAY;AAAA,IAClF;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAIT,SAAS,QAAQ,CAAC,OAAuB;AAAA,EACvC,IAAI,OAAO;AAAA,EACX,SAAS,IAAI,EAAG,IAAI,MAAM,QAAQ,KAAK;AAAA,IACrC,QAAQ,MAAM,WAAW,CAAC;AAAA,IAC1B,OAAO,KAAK,KAAK,MAAM,QAAU;AAAA,EACnC;AAAA,EACA,QAAQ,SAAS,GAAG,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAAA;AAW3C,SAAS,qBAAqB,CACnC,OACA,QACQ;AAAA,EACR,MAAM,WAAY,OAAO,iBAAwD;AAAA,EACjF,IAAI;AAAA,IAAU,OAAO;AAAA,EACrB,MAAM,WAAW,KAAK,UAAU;AAAA,IAC9B,OAAO,SAAS;AAAA,IAChB,OAAO,gBAAgB;AAAA,IACvB,OAAO,SAAS;AAAA,EAClB,CAAC;AAAA,EACD,OAAO,MAAM,SAAS,QAAQ;AAAA;AAUzB,SAAS,wBAAwB,CACtC,OACA,QACyB;AAAA,EACzB,MAAM,YAAY,mBAAmB,KAAK;AAAA,EAC1C,IAAI,cAAc;AAAA,IAAW,OAAO,YAAY;AAAA,EAChD,OAAO,mBAAmB,sBAAsB,OAAO,MAAM;AAAA,EAC7D,OAAO;AAAA;;AC7IF,IAAM,SAAS;;ACAtB;AAIO,IAAM,oBAAoB;AAAA,EAC/B,MAAM;AAAA,EACN,YAAY;AAAA,IACV,UAAU;AAAA,MACR,OAAO;AAAA,MACP,aAAa;AAAA,IACf;AAAA,IACA,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,aAAa;AAAA,MACb,YAAY;AAAA,QACV,YAAY;AAAA,UACV,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,gBAAgB;AAAA,UACd,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,eAAe;AAAA,QACjB;AAAA,QACA,UAAU;AAAA,UACR,MAAM;AAAA,UACN,aAAa;AAAA,UACb,SAAS;AAAA,QACX;AAAA,QACA,gBAAgB;AAAA,UACd,MAAM;AAAA,UACN,aACE;AAAA,UACF,SAAS;AAAA,UACT,eAAe;AAAA,QACjB;AAAA,QACA,cAAc;AAAA,UACZ,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,kBAAkB;AAAA,UAChB,MAAM;AAAA,UACN,aACE;AAAA,UACF,eAAe;AAAA,QACjB;AAAA,QACA,WAAW;AAAA,UACT,MAAM;AAAA,UACN,aACE;AAAA,UACF,YAAY;AAAA,YACV,QAAQ;AAAA,cACN,MAAM;AAAA,cACN,MAAM,CAAC,QAAQ,WAAW,OAAO,UAAU,QAAQ,SAAS,KAAK;AAAA,cACjE,aAAa;AAAA,YACf;AAAA,YACA,MAAM;AAAA,cACJ,MAAM;AAAA,cACN,MAAM,CAAC,KAAK;AAAA,cACZ,aACE;AAAA,YACJ;AAAA,UACF;AAAA,UACA,sBAAsB;AAAA,QACxB;AAAA,MACF;AAAA,MACA,UAAU,CAAC,YAAY;AAAA,MACvB,sBAAsB;AAAA,IACxB;AAAA,EACF;AAAA,EACA,UAAU,CAAC,YAAY,iBAAiB;AAAA,EACxC,sBAAsB;AACxB;AAEO,IAAM,0BAA0B;AAAA,EACrC,MAAM;AAAA,EACN,YAAY;AAAA,OACP,kBAAkB;AAAA,OAClB,kBAAkB;AAAA,EACvB;AAAA,EACA,UAAU,CAAC,GAAG,kBAAkB,UAAU,GAAG,kBAAkB,QAAQ;AAAA,EACvE,sBAAsB;AACxB;AAIO,IAAM,0BAA0B;AAAA,EACrC,MAAM;AAAA,EACN,YAAY;AAAA,OACP,kBAAkB;AAAA,OAClB,kBAAkB;AAAA,EACvB;AAAA,EACA,UAAU,CAAC,GAAG,kBAAkB,UAAU,GAAG,kBAAkB,QAAQ;AAAA,EACvE,sBAAsB;AACxB;;AC9FA;;;ACAA;AACA;;;ACWO,IAAM,yBAAyB,CAAC,iBAAiB;AACjD,IAAM,kBAAkB,CAAC,mBAAmB,UAAU;AACtD,IAAM,mBAAmB,CAAC,mBAAmB,WAAW;AACxD,IAAM,uBAAuB,CAAC,eAAe;AAC7C,IAAM,sBAAsB,CAAC,cAAc;AAC3C,IAAM,wBAAwB,CAAC,gBAAgB;AAC/C,IAAM,0BAA0B,CAAC,kBAAkB;AACnD,IAAM,uBAAuB,CAAC,eAAe;AAC7C,IAAM,sBAAsB,CAAC,oBAAoB;AACjD,IAAM,sBAAsB,CAAC,cAAc;AAC3C,IAAM,oBAAoB,CAAC,YAAY;AAGvC,IAAM,yBAAyB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;AC7BO,IAAM,sBAAsB,uBAAuB,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE;AAE/E,SAAS,sBAAsB,GAA0D;AAAA,EAC9F,OAAO;AAAA;AAmBF,SAAS,uBAAuB,CAAC,OAA+C;AAAA,EACrF,MAAM,KAAK,OACT,MAAM,YACH,MAAM,iBAAyD,cAChE,EACJ;AAAA,EAGA,IAAI,mBAAmB,KAAK,EAAE,GAAG;AAAA,IAC/B,OAAO,CAAC,kBAAkB,sBAAsB,cAAc,cAAc;AAAA,EAC9E;AAAA,EAGA,IAAI,WAAW,KAAK,EAAE,GAAG;AAAA,IAGvB,OAAO,CAAC,oBAAoB,cAAc,cAAc;AAAA,EAC1D;AAAA,EACA,IAAI,cAAc,KAAK,EAAE,GAAG;AAAA,IAC1B,OAAO,CAAC,oBAAoB,iBAAiB,cAAc,cAAc;AAAA,EAC3E;AAAA,EAIA,IAAI,SAAS,KAAK,EAAE,KAAK,QAAQ,KAAK,EAAE,GAAG;AAAA,IACzC,MAAM,OAAqB;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,MAAM,iBACJ,kDAAkD,KAAK,EAAE,KAAK,QAAQ,KAAK,EAAE;AAAA,IAC/E,IAAI,gBAAgB;AAAA,MAClB,KAAK,KAAK,cAAc;AAAA,IAC1B;AAAA,IACA,OAAO;AAAA,EACT;AAAA,EAIA,MAAM,WAAY,MAAM,gBAAsD,CAAC;AAAA,EAC/E,IAAI,SAAS,SAAS;AAAA,IAAG,OAAO;AAAA,EAChC,OAAO,CAAC,gBAAgB,YAAY;AAAA;;;AFjE/B,MAAM,6BAA6B,yBAA4C,YAAY;AAAA,EAChG,MAAM;AAAA,EACN,aAAa;AACf,CAAC,EAAE;AAAA,EACQ,iBAAiB,CAAC,OAA2C;AAAA,IACpE,OAAO,wBAAwB,KAAK;AAAA;AAAA,EAGnB,gBAAgB,GAAiD;AAAA,IAClF,OAAO,uBAAuB;AAAA;AAElC;;;AGvBA;AAYO,SAAS,4BAA4B,GAAS;AAAA,EACnD,kBAAkB,+BAChB,QACA,CAAC,UAAU,OAAO,UAAuB;AAAA,IACvC,IAAI,aAAa;AAAA,MAAiB;AAAA,IAClC,MAAM,YACH,MAAM,iBAAyD,cAAc;AAAA,IAChF,MAAM,aAAa,MAAM;AAAA,IACzB,IAAI,UAAU,WAAW,UAAU,KAAK,MAAM,QAAQ,UAAU,KAAK,WAAW,SAAS,GAAG;AAAA,MAC1F,MAAM,IAAI,gCACR,oBACA,MAAM,YAAY,WAClB,2CACF;AAAA,IACF;AAAA,GAEJ;AAAA;;;AJxBF,eAAsB,cAAc,CAClC,SAGe;AAAA,EACf,6BAA6B;AAAA,EAC7B,MAAM,2BAA2B,IAAI,sBAAwB,UAAU,OAAO;AAAA;",
|
|
15
|
+
"debugId": "C376FBCA13DE0E7964756E2164756E21",
|
|
16
16
|
"names": []
|
|
17
17
|
}
|
package/dist/ai.js
CHANGED
|
@@ -49,6 +49,39 @@ function getModelName(model) {
|
|
|
49
49
|
}
|
|
50
50
|
return name;
|
|
51
51
|
}
|
|
52
|
+
function getReasoningConfig(model) {
|
|
53
|
+
const reasoning = model?.provider_config?.reasoning;
|
|
54
|
+
if (!reasoning || reasoning.effort === undefined && reasoning.mode === undefined) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
return reasoning;
|
|
58
|
+
}
|
|
59
|
+
function fnv1aHex(input) {
|
|
60
|
+
let hash = 2166136261;
|
|
61
|
+
for (let i = 0;i < input.length; i++) {
|
|
62
|
+
hash ^= input.charCodeAt(i);
|
|
63
|
+
hash = Math.imul(hash, 16777619);
|
|
64
|
+
}
|
|
65
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
66
|
+
}
|
|
67
|
+
function resolvePromptCacheKey(model, params) {
|
|
68
|
+
const override = model?.provider_config?.prompt_cache_key;
|
|
69
|
+
if (override)
|
|
70
|
+
return override;
|
|
71
|
+
const material = JSON.stringify([
|
|
72
|
+
params.model ?? "",
|
|
73
|
+
params.instructions ?? "",
|
|
74
|
+
params.tools ?? null
|
|
75
|
+
]);
|
|
76
|
+
return `wg-${fnv1aHex(material)}`;
|
|
77
|
+
}
|
|
78
|
+
function finalizeResponsesRequest(model, params) {
|
|
79
|
+
const reasoning = getReasoningConfig(model);
|
|
80
|
+
if (reasoning !== undefined)
|
|
81
|
+
params.reasoning = reasoning;
|
|
82
|
+
params.prompt_cache_key = resolvePromptCacheKey(model, params);
|
|
83
|
+
return params;
|
|
84
|
+
}
|
|
52
85
|
// src/ai/common/OpenAI_Constants.ts
|
|
53
86
|
var OPENAI = "OPENAI";
|
|
54
87
|
// src/ai/common/OpenAI_ImageValidation.ts
|
|
@@ -101,6 +134,28 @@ var OpenAiModelSchema = {
|
|
|
101
134
|
organization: {
|
|
102
135
|
type: "string",
|
|
103
136
|
description: "OpenAI organization ID (optional)."
|
|
137
|
+
},
|
|
138
|
+
prompt_cache_key: {
|
|
139
|
+
type: "string",
|
|
140
|
+
description: "Overrides the auto-derived Responses prompt_cache_key. Requests sharing a key share a cached prefix; leave unset to derive a stable key from the model + system instructions + tools.",
|
|
141
|
+
"x-ui-hidden": true
|
|
142
|
+
},
|
|
143
|
+
reasoning: {
|
|
144
|
+
type: "object",
|
|
145
|
+
description: "Reasoning controls for reasoning-capable models (e.g. the GPT-5.6 sol/terra/luna family), sent on the Responses API.",
|
|
146
|
+
properties: {
|
|
147
|
+
effort: {
|
|
148
|
+
type: "string",
|
|
149
|
+
enum: ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
150
|
+
description: "Reasoning effort. Higher effort trades latency and cost for quality."
|
|
151
|
+
},
|
|
152
|
+
mode: {
|
|
153
|
+
type: "string",
|
|
154
|
+
enum: ["pro"],
|
|
155
|
+
description: "Set to 'pro' for the quality-first pro configuration on supported models."
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
additionalProperties: false
|
|
104
159
|
}
|
|
105
160
|
},
|
|
106
161
|
required: ["model_name"],
|
|
@@ -133,6 +188,10 @@ import { filterLabeledModelsByQuery } from "@workglow/ai/provider-utils";
|
|
|
133
188
|
var OPENAI_FALLBACK = [
|
|
134
189
|
{ label: "gpt-image-2", value: "gpt-image-2" },
|
|
135
190
|
{ label: "dall-e-3", value: "dall-e-3" },
|
|
191
|
+
{ label: "gpt-5.6", value: "gpt-5.6" },
|
|
192
|
+
{ label: "gpt-5.6-sol", value: "gpt-5.6-sol" },
|
|
193
|
+
{ label: "gpt-5.6-terra", value: "gpt-5.6-terra" },
|
|
194
|
+
{ label: "gpt-5.6-luna", value: "gpt-5.6-luna" },
|
|
136
195
|
{ label: "gpt-5.5", value: "gpt-5.5" },
|
|
137
196
|
{ label: "gpt-5.4-mini", value: "gpt-5.4-mini" },
|
|
138
197
|
{ label: "gpt-5.4-nano", value: "gpt-5.4-nano" },
|
|
@@ -551,36 +610,77 @@ var OpenAI_ModelInfo_Stream = async (input, model, _signal, emit) => {
|
|
|
551
610
|
|
|
552
611
|
// src/ai/common/OpenAI_StructuredGeneration.ts
|
|
553
612
|
import { parsePartialJson } from "@workglow/util/worker";
|
|
613
|
+
function isStrictCompatibleSchema(schema) {
|
|
614
|
+
if (schema === null || typeof schema !== "object")
|
|
615
|
+
return true;
|
|
616
|
+
const s = schema;
|
|
617
|
+
if (s.$ref !== undefined)
|
|
618
|
+
return false;
|
|
619
|
+
if (Array.isArray(s.anyOf) || Array.isArray(s.oneOf) || Array.isArray(s.allOf))
|
|
620
|
+
return false;
|
|
621
|
+
const isObject = s.type === "object" || s.type === undefined && s.properties !== undefined;
|
|
622
|
+
if (isObject) {
|
|
623
|
+
if (s.additionalProperties !== false)
|
|
624
|
+
return false;
|
|
625
|
+
const props = s.properties ?? {};
|
|
626
|
+
const required = Array.isArray(s.required) ? s.required : [];
|
|
627
|
+
for (const key of Object.keys(props)) {
|
|
628
|
+
if (!required.includes(key))
|
|
629
|
+
return false;
|
|
630
|
+
if (!isStrictCompatibleSchema(props[key]))
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
return true;
|
|
634
|
+
}
|
|
635
|
+
const isArray = s.type === "array" || s.items !== undefined;
|
|
636
|
+
if (isArray) {
|
|
637
|
+
if (Array.isArray(s.items))
|
|
638
|
+
return s.items.every(isStrictCompatibleSchema);
|
|
639
|
+
return isStrictCompatibleSchema(s.items);
|
|
640
|
+
}
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
554
643
|
var OpenAI_StructuredGeneration_Stream = async (input, model, signal, emit, outputSchema) => {
|
|
555
644
|
const client = await getClient(model);
|
|
556
645
|
const modelName = getModelName(model);
|
|
557
646
|
const schema = input.outputSchema ?? outputSchema;
|
|
558
|
-
const
|
|
647
|
+
const params = {
|
|
559
648
|
model: modelName,
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
649
|
+
input: input.prompt,
|
|
650
|
+
text: {
|
|
651
|
+
format: {
|
|
652
|
+
type: "json_schema",
|
|
564
653
|
name: "structured_output",
|
|
565
654
|
schema,
|
|
566
|
-
strict:
|
|
655
|
+
strict: isStrictCompatibleSchema(schema)
|
|
567
656
|
}
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
if (input.maxTokens !== undefined)
|
|
660
|
+
params.max_output_tokens = input.maxTokens;
|
|
661
|
+
if (input.temperature !== undefined)
|
|
662
|
+
params.temperature = input.temperature;
|
|
663
|
+
finalizeResponsesRequest(model, params);
|
|
664
|
+
const stream = await client.responses.create({ ...params, stream: true }, { signal });
|
|
573
665
|
let accumulatedJson = "";
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
if (delta) {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
666
|
+
let refusal = "";
|
|
667
|
+
for await (const event of stream) {
|
|
668
|
+
if (event.type === "response.output_text.delta") {
|
|
669
|
+
const delta = event.delta ?? "";
|
|
670
|
+
if (delta) {
|
|
671
|
+
accumulatedJson += delta;
|
|
672
|
+
const partial = parsePartialJson(accumulatedJson);
|
|
673
|
+
if (partial !== undefined) {
|
|
674
|
+
emit({ type: "object-delta", port: "object", objectDelta: partial });
|
|
675
|
+
}
|
|
581
676
|
}
|
|
677
|
+
} else if (event.type === "response.refusal.delta") {
|
|
678
|
+
refusal += event.delta ?? "";
|
|
582
679
|
}
|
|
583
680
|
}
|
|
681
|
+
if (refusal) {
|
|
682
|
+
emit({ type: "refusal", refusal });
|
|
683
|
+
}
|
|
584
684
|
let finalObject;
|
|
585
685
|
try {
|
|
586
686
|
finalObject = JSON.parse(accumulatedJson);
|
|
@@ -613,30 +713,34 @@ var OpenAI_TextEmbedding_Stream = async (input, model, signal, emit) => {
|
|
|
613
713
|
};
|
|
614
714
|
|
|
615
715
|
// src/ai/common/OpenAI_TextGeneration.ts
|
|
716
|
+
import { accumulateOpenAIResponsesStream, buildResponsesInput } from "@workglow/ai/provider-utils";
|
|
616
717
|
import { toOpenAIMessages } from "@workglow/ai/worker";
|
|
617
718
|
import { getLogger as getLogger2 } from "@workglow/util/worker";
|
|
618
|
-
function
|
|
719
|
+
function buildResponsesParams(input, model) {
|
|
619
720
|
const hasMessages = Array.isArray(input.messages) && input.messages.length > 0;
|
|
620
721
|
const messages = hasMessages ? toOpenAIMessages({
|
|
621
722
|
messages: input.messages,
|
|
622
723
|
systemPrompt: input.systemPrompt,
|
|
623
724
|
prompt: "",
|
|
624
725
|
tools: []
|
|
625
|
-
}) :
|
|
726
|
+
}) : undefined;
|
|
727
|
+
const { input: responsesInput, instructions } = buildResponsesInput({
|
|
728
|
+
messages,
|
|
729
|
+
prompt: hasMessages ? undefined : input.prompt,
|
|
730
|
+
systemPrompt: hasMessages ? undefined : input.systemPrompt
|
|
731
|
+
});
|
|
626
732
|
const params = {
|
|
627
733
|
model: getModelName(model),
|
|
628
|
-
|
|
734
|
+
input: responsesInput
|
|
629
735
|
};
|
|
736
|
+
if (instructions !== undefined)
|
|
737
|
+
params.instructions = instructions;
|
|
630
738
|
if (input.maxTokens !== undefined)
|
|
631
|
-
params.
|
|
739
|
+
params.max_output_tokens = input.maxTokens;
|
|
632
740
|
if (input.temperature !== undefined)
|
|
633
741
|
params.temperature = input.temperature;
|
|
634
742
|
if (input.topP !== undefined)
|
|
635
743
|
params.top_p = input.topP;
|
|
636
|
-
if (input.frequencyPenalty !== undefined)
|
|
637
|
-
params.frequency_penalty = input.frequencyPenalty;
|
|
638
|
-
if (input.presencePenalty !== undefined)
|
|
639
|
-
params.presence_penalty = input.presencePenalty;
|
|
640
744
|
return params;
|
|
641
745
|
}
|
|
642
746
|
var OpenAI_TextGeneration_Stream = async (input, model, signal, emit) => {
|
|
@@ -645,14 +749,9 @@ var OpenAI_TextGeneration_Stream = async (input, model, signal, emit) => {
|
|
|
645
749
|
logger.time(timerLabel, { model: getModelName(model) });
|
|
646
750
|
try {
|
|
647
751
|
const client = await getClient(model);
|
|
648
|
-
const params =
|
|
649
|
-
const stream = await client.
|
|
650
|
-
|
|
651
|
-
const delta = chunk.choices?.[0]?.delta?.content ?? "";
|
|
652
|
-
if (delta) {
|
|
653
|
-
emit({ type: "text-delta", port: "text", textDelta: delta });
|
|
654
|
-
}
|
|
655
|
-
}
|
|
752
|
+
const params = finalizeResponsesRequest(model, buildResponsesParams(input, model));
|
|
753
|
+
const stream = await client.responses.create({ ...params, stream: true }, { signal });
|
|
754
|
+
await accumulateOpenAIResponsesStream(stream, emit);
|
|
656
755
|
emit({ type: "finish", data: {} });
|
|
657
756
|
} finally {
|
|
658
757
|
logger.timeEnd(timerLabel, { model: getModelName(model) });
|
|
@@ -660,70 +759,66 @@ var OpenAI_TextGeneration_Stream = async (input, model, signal, emit) => {
|
|
|
660
759
|
};
|
|
661
760
|
|
|
662
761
|
// src/ai/common/OpenAI_TextRewriter.ts
|
|
762
|
+
import { accumulateOpenAIResponsesStream as accumulateOpenAIResponsesStream2 } from "@workglow/ai/provider-utils";
|
|
663
763
|
var OpenAI_TextRewriter_Stream = async (input, model, signal, emit) => {
|
|
664
764
|
const client = await getClient(model);
|
|
665
|
-
const
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
}, { signal });
|
|
674
|
-
for await (const chunk of stream) {
|
|
675
|
-
const delta = chunk.choices[0]?.delta?.content ?? "";
|
|
676
|
-
if (delta) {
|
|
677
|
-
emit({ type: "text-delta", port: "text", textDelta: delta });
|
|
678
|
-
}
|
|
679
|
-
}
|
|
765
|
+
const params = {
|
|
766
|
+
model: getModelName(model),
|
|
767
|
+
instructions: input.prompt,
|
|
768
|
+
input: input.text
|
|
769
|
+
};
|
|
770
|
+
finalizeResponsesRequest(model, params);
|
|
771
|
+
const stream = await client.responses.create({ ...params, stream: true }, { signal });
|
|
772
|
+
await accumulateOpenAIResponsesStream2(stream, emit);
|
|
680
773
|
emit({ type: "finish", data: {} });
|
|
681
774
|
};
|
|
682
775
|
|
|
683
776
|
// src/ai/common/OpenAI_TextSummary.ts
|
|
777
|
+
import { accumulateOpenAIResponsesStream as accumulateOpenAIResponsesStream3 } from "@workglow/ai/provider-utils";
|
|
684
778
|
var OpenAI_TextSummary_Stream = async (input, model, signal, emit) => {
|
|
685
779
|
const client = await getClient(model);
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
}, { signal });
|
|
695
|
-
for await (const chunk of stream) {
|
|
696
|
-
const delta = chunk.choices[0]?.delta?.content ?? "";
|
|
697
|
-
if (delta) {
|
|
698
|
-
emit({ type: "text-delta", port: "text", textDelta: delta });
|
|
699
|
-
}
|
|
700
|
-
}
|
|
780
|
+
const params = {
|
|
781
|
+
model: getModelName(model),
|
|
782
|
+
instructions: "Summarize the following text concisely.",
|
|
783
|
+
input: input.text
|
|
784
|
+
};
|
|
785
|
+
finalizeResponsesRequest(model, params);
|
|
786
|
+
const stream = await client.responses.create({ ...params, stream: true }, { signal });
|
|
787
|
+
await accumulateOpenAIResponsesStream3(stream, emit);
|
|
701
788
|
emit({ type: "finish", data: {} });
|
|
702
789
|
};
|
|
703
790
|
|
|
704
791
|
// src/ai/common/OpenAI_ToolCalling.ts
|
|
705
792
|
import {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
793
|
+
accumulateOpenAIResponsesStream as accumulateOpenAIResponsesStream4,
|
|
794
|
+
buildResponsesInput as buildResponsesInput2,
|
|
795
|
+
buildResponsesTools,
|
|
796
|
+
mapResponsesToolChoice
|
|
709
797
|
} from "@workglow/ai/provider-utils";
|
|
710
798
|
import { filterValidToolCalls, toOpenAIMessages as toOpenAIMessages2 } from "@workglow/ai/worker";
|
|
711
799
|
var OpenAI_ToolCalling_Stream = async (input, model, signal, emit) => {
|
|
712
800
|
const client = await getClient(model);
|
|
713
801
|
const modelName = getModelName(model);
|
|
714
|
-
const tools =
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
802
|
+
const tools = buildResponsesTools(input.tools);
|
|
803
|
+
const { input: responsesInput, instructions } = buildResponsesInput2({
|
|
804
|
+
messages: toOpenAIMessages2(input)
|
|
805
|
+
});
|
|
806
|
+
const toolChoice = mapResponsesToolChoice(input.toolChoice);
|
|
807
|
+
const params = {
|
|
718
808
|
model: modelName,
|
|
719
|
-
|
|
720
|
-
max_completion_tokens: input.maxTokens,
|
|
721
|
-
temperature: input.temperature,
|
|
722
|
-
stream: true,
|
|
809
|
+
input: responsesInput,
|
|
723
810
|
tools,
|
|
724
811
|
tool_choice: toolChoice
|
|
725
|
-
}
|
|
726
|
-
|
|
812
|
+
};
|
|
813
|
+
if (instructions !== undefined)
|
|
814
|
+
params.instructions = instructions;
|
|
815
|
+
if (input.maxTokens !== undefined)
|
|
816
|
+
params.max_output_tokens = input.maxTokens;
|
|
817
|
+
if (input.temperature !== undefined)
|
|
818
|
+
params.temperature = input.temperature;
|
|
819
|
+
finalizeResponsesRequest(model, params);
|
|
820
|
+
const stream = await client.responses.create({ ...params, stream: true }, { signal });
|
|
821
|
+
await accumulateOpenAIResponsesStream4(stream, (event) => {
|
|
727
822
|
if (event.type === "object-delta" && event.port === "toolCalls") {
|
|
728
823
|
const validated = filterValidToolCalls(event.objectDelta, input.tools);
|
|
729
824
|
if (validated.length > 0) {
|
|
@@ -758,7 +853,10 @@ var OPENAI_PREVIEW_TASKS = {
|
|
|
758
853
|
var _testOnly = {
|
|
759
854
|
OpenAiQueuedProvider,
|
|
760
855
|
OPENAI_RUN_FN_SPECS,
|
|
761
|
-
OPENAI_RUN_FNS
|
|
856
|
+
OPENAI_RUN_FNS,
|
|
857
|
+
getReasoningConfig,
|
|
858
|
+
resolvePromptCacheKey,
|
|
859
|
+
isStrictCompatibleSchema
|
|
762
860
|
};
|
|
763
861
|
export {
|
|
764
862
|
registerOpenAiImageValidator,
|
|
@@ -772,4 +870,4 @@ export {
|
|
|
772
870
|
OPENAI
|
|
773
871
|
};
|
|
774
872
|
|
|
775
|
-
//# debugId=
|
|
873
|
+
//# debugId=509E7AE9B7F0875564756E2164756E21
|