gitlab-ai-provider 6.13.0 → 6.15.0

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/index.mjs CHANGED
@@ -891,7 +891,12 @@ ${message.content}` : message.content;
891
891
  } else if (message.role === "assistant") {
892
892
  const content = [];
893
893
  for (const part of message.content) {
894
- if (part.type === "text") {
894
+ if (part.type === "reasoning") {
895
+ const thinkingBlock = this.convertReasoningPart(part);
896
+ if (thinkingBlock) {
897
+ content.push(thinkingBlock);
898
+ }
899
+ } else if (part.type === "text") {
895
900
  content.push({ type: "text", text: part.text });
896
901
  } else if (part.type === "tool-call") {
897
902
  let toolInput = part.input;
@@ -1006,17 +1011,97 @@ ${message.content}` : message.content;
1006
1011
  raw: params?.raw
1007
1012
  };
1008
1013
  }
1014
+ /**
1015
+ * Translates the camelCase GitLab thinking config into the Anthropic Messages
1016
+ * request fields. `adaptive` is required by effort-based models such as Claude
1017
+ * Opus 4.8 and pairs `thinking: { type: 'adaptive' }` with a sibling
1018
+ * `output_config: { effort }`; `enabled`/`disabled` map through directly.
1019
+ */
1020
+ buildThinkingParams(config) {
1021
+ if (!config) return {};
1022
+ if (config.type === "adaptive") {
1023
+ return {
1024
+ // Newer adaptive-only models (Claude Sonnet 5+, Opus 4.7+) default
1025
+ // `thinking.display` to `'omitted'`, which returns *empty* thinking
1026
+ // blocks — the model still reasons and effort still affects
1027
+ // behavior/token usage, but no visible chain-of-thought text comes
1028
+ // back. Older models already default to `'summarized'`, so it's
1029
+ // safe to always request it explicitly rather than gating on model
1030
+ // version.
1031
+ thinking: { type: "adaptive", display: "summarized" },
1032
+ output_config: { effort: config.effort }
1033
+ };
1034
+ }
1035
+ if (config.type === "enabled") {
1036
+ return { thinking: { type: "enabled", budget_tokens: config.budgetTokens } };
1037
+ }
1038
+ return { thinking: { type: "disabled" } };
1039
+ }
1040
+ /** Per-call thinking configuration takes precedence over the model-level default. */
1041
+ resolveThinkingConfig(options) {
1042
+ const gitlabOptions = options.providerOptions?.["gitlab"];
1043
+ return gitlabOptions?.thinking ?? this.config.thinking;
1044
+ }
1045
+ validateThinkingConfig(config, maxTokens) {
1046
+ if (config?.type === "enabled" && config.budgetTokens >= maxTokens) {
1047
+ throw new GitLabError({
1048
+ message: `Anthropic thinking budgetTokens (${config.budgetTokens}) must be less than maxTokens (${maxTokens}).`,
1049
+ statusCode: 400
1050
+ });
1051
+ }
1052
+ }
1053
+ /**
1054
+ * Rebuild an Anthropic thinking block from a prior-turn reasoning part.
1055
+ * Anthropic requires thinking blocks to be replayed unmodified (with their
1056
+ * signature) during tool-use loops; the signature / redacted payload are
1057
+ * carried in the reasoning part's provider metadata under `gitlab`.
1058
+ */
1059
+ convertReasoningPart(part) {
1060
+ const meta = part.providerOptions?.["gitlab"];
1061
+ const redactedData = meta?.["redactedData"];
1062
+ if (typeof redactedData === "string") {
1063
+ return { type: "redacted_thinking", data: redactedData };
1064
+ }
1065
+ const signature = meta?.["signature"];
1066
+ if (typeof signature !== "string") {
1067
+ return void 0;
1068
+ }
1069
+ return { type: "thinking", thinking: part.text, signature };
1070
+ }
1071
+ isThinkingActive(config) {
1072
+ return config?.type === "enabled" || config?.type === "adaptive";
1073
+ }
1074
+ /**
1075
+ * Anthropic rejects `temperature` changes while thinking is active and requires
1076
+ * `top_p` within [0.95, 1]; drop those params when thinking is engaged so the
1077
+ * gateway does not 400. Undefined values are omitted either way.
1078
+ */
1079
+ buildSamplingParams(options, thinkingConfig) {
1080
+ const params = {};
1081
+ const thinkingActive = this.isThinkingActive(thinkingConfig);
1082
+ if (options.temperature != null && !thinkingActive) {
1083
+ params.temperature = options.temperature;
1084
+ }
1085
+ if (options.topP != null) {
1086
+ if (!thinkingActive || options.topP >= 0.95 && options.topP <= 1) {
1087
+ params.top_p = options.topP;
1088
+ }
1089
+ }
1090
+ return params;
1091
+ }
1009
1092
  async doGenerate(options) {
1010
1093
  return this.doGenerateWithRetry(options, false);
1011
1094
  }
1012
1095
  async doGenerateWithRetry(options, isRetry) {
1013
- const client = await this.getAnthropicClient(isRetry);
1014
1096
  const { system, messages } = this.convertPrompt(options.prompt);
1015
1097
  const toolsDisabled = options.toolChoice?.type === "none";
1016
1098
  const tools = toolsDisabled ? void 0 : this.convertTools(options.tools);
1017
1099
  const toolChoice = toolsDisabled ? void 0 : this.convertToolChoice(options.toolChoice);
1018
1100
  const anthropicModel = this.config.anthropicModel || "claude-sonnet-4-5-20250929";
1019
1101
  const maxTokens = options.maxOutputTokens || this.config.maxTokens || 8192;
1102
+ const thinkingConfig = this.resolveThinkingConfig(options);
1103
+ this.validateThinkingConfig(thinkingConfig, maxTokens);
1104
+ const client = await this.getAnthropicClient(isRetry);
1020
1105
  const generateParams = {
1021
1106
  model: anthropicModel,
1022
1107
  max_tokens: maxTokens,
@@ -1024,15 +1109,27 @@ ${message.content}` : message.content;
1024
1109
  messages,
1025
1110
  tools,
1026
1111
  tool_choice: tools ? toolChoice : void 0,
1027
- temperature: options.temperature,
1028
- top_p: options.topP,
1029
- stop_sequences: options.stopSequences
1112
+ ...this.buildSamplingParams(options, thinkingConfig),
1113
+ stop_sequences: options.stopSequences,
1114
+ ...this.buildThinkingParams(thinkingConfig)
1030
1115
  };
1031
1116
  try {
1032
1117
  const response = options.abortSignal ? await client.messages.create(generateParams, { signal: options.abortSignal }) : await client.messages.create(generateParams);
1033
1118
  const content = [];
1034
1119
  for (const block of response.content) {
1035
- if (block.type === "text") {
1120
+ if (block.type === "thinking") {
1121
+ content.push({
1122
+ type: "reasoning",
1123
+ text: block.thinking,
1124
+ providerMetadata: { gitlab: { signature: block.signature } }
1125
+ });
1126
+ } else if (block.type === "redacted_thinking") {
1127
+ content.push({
1128
+ type: "reasoning",
1129
+ text: "",
1130
+ providerMetadata: { gitlab: { redactedData: block.data } }
1131
+ });
1132
+ } else if (block.type === "text") {
1036
1133
  content.push({
1037
1134
  type: "text",
1038
1135
  text: block.text
@@ -1086,13 +1183,15 @@ ${message.content}` : message.content;
1086
1183
  return this.doStreamWithRetry(options, false);
1087
1184
  }
1088
1185
  async doStreamWithRetry(options, isRetry) {
1089
- const client = await this.getAnthropicClient(isRetry);
1090
1186
  const { system, messages } = this.convertPrompt(options.prompt);
1091
1187
  const toolsDisabled = options.toolChoice?.type === "none";
1092
1188
  const tools = toolsDisabled ? void 0 : this.convertTools(options.tools);
1093
1189
  const toolChoice = toolsDisabled ? void 0 : this.convertToolChoice(options.toolChoice);
1094
1190
  const anthropicModel = this.config.anthropicModel || "claude-sonnet-4-5-20250929";
1095
1191
  const maxTokens = options.maxOutputTokens || this.config.maxTokens || 8192;
1192
+ const thinkingConfig = this.resolveThinkingConfig(options);
1193
+ this.validateThinkingConfig(thinkingConfig, maxTokens);
1194
+ const client = await this.getAnthropicClient(isRetry);
1096
1195
  const requestBody = {
1097
1196
  model: anthropicModel,
1098
1197
  max_tokens: maxTokens,
@@ -1100,9 +1199,9 @@ ${message.content}` : message.content;
1100
1199
  messages,
1101
1200
  tools,
1102
1201
  tool_choice: tools ? toolChoice : void 0,
1103
- temperature: options.temperature,
1104
- top_p: options.topP,
1202
+ ...this.buildSamplingParams(options, thinkingConfig),
1105
1203
  stop_sequences: options.stopSequences,
1204
+ ...this.buildThinkingParams(thinkingConfig),
1106
1205
  stream: true
1107
1206
  };
1108
1207
  const self = this;
@@ -1201,6 +1300,24 @@ ${message.content}` : message.content;
1201
1300
  type: "text-start",
1202
1301
  id: textId
1203
1302
  });
1303
+ } else if (event.content_block.type === "thinking") {
1304
+ const reasoningId = `reasoning-${event.index}`;
1305
+ contentBlocks[event.index] = { type: "reasoning", id: reasoningId };
1306
+ safeEnqueue({
1307
+ type: "reasoning-start",
1308
+ id: reasoningId
1309
+ });
1310
+ } else if (event.content_block.type === "redacted_thinking") {
1311
+ const reasoningId = `reasoning-${event.index}`;
1312
+ contentBlocks[event.index] = {
1313
+ type: "reasoning",
1314
+ id: reasoningId,
1315
+ redactedData: event.content_block.data
1316
+ };
1317
+ safeEnqueue({
1318
+ type: "reasoning-start",
1319
+ id: reasoningId
1320
+ });
1204
1321
  } else if (event.content_block.type === "tool_use") {
1205
1322
  contentBlocks[event.index] = {
1206
1323
  type: "tool-call",
@@ -1223,6 +1340,14 @@ ${message.content}` : message.content;
1223
1340
  id: block.id,
1224
1341
  delta: event.delta.text
1225
1342
  });
1343
+ } else if (event.delta.type === "thinking_delta" && block?.type === "reasoning") {
1344
+ safeEnqueue({
1345
+ type: "reasoning-delta",
1346
+ id: block.id,
1347
+ delta: event.delta.thinking
1348
+ });
1349
+ } else if (event.delta.type === "signature_delta" && block?.type === "reasoning") {
1350
+ block.signature = event.delta.signature;
1226
1351
  } else if (event.delta.type === "input_json_delta" && block?.type === "tool-call") {
1227
1352
  block.input += event.delta.partial_json;
1228
1353
  safeEnqueue({
@@ -1240,6 +1365,13 @@ ${message.content}` : message.content;
1240
1365
  type: "text-end",
1241
1366
  id: block.id
1242
1367
  });
1368
+ } else if (block?.type === "reasoning") {
1369
+ const gitlabMeta = block.redactedData != null ? { redactedData: block.redactedData } : block.signature != null ? { signature: block.signature } : void 0;
1370
+ safeEnqueue({
1371
+ type: "reasoning-end",
1372
+ id: block.id,
1373
+ ...gitlabMeta && { providerMetadata: { gitlab: gitlabMeta } }
1374
+ });
1243
1375
  } else if (block?.type === "tool-call") {
1244
1376
  safeEnqueue({
1245
1377
  type: "tool-input-end",
@@ -1400,6 +1532,10 @@ var MODEL_MAPPINGS = {
1400
1532
  "duo-chat-opus-4-5": { provider: "anthropic", model: "claude-opus-4-5-20251101" },
1401
1533
  "duo-chat-sonnet-4-5": { provider: "anthropic", model: "claude-sonnet-4-5-20250929" },
1402
1534
  "duo-chat-haiku-4-5": { provider: "anthropic", model: "claude-haiku-4-5-20251001" },
1535
+ // OpenAI models - Responses API
1536
+ // GPT-6 Astra rejects `reasoning_effort` with function tools on Chat Completions;
1537
+ // it must use the Responses API (matches OpenAI's own routing for GPT-5.x/GPT-6).
1538
+ "duo-chat-gpt-6-astra": { provider: "openai", model: "gpt-6-astra", openaiApiType: "responses" },
1403
1539
  // OpenAI models - Chat Completions API
1404
1540
  "duo-chat-gpt-5-1": { provider: "openai", model: "gpt-5.1-2025-11-13", openaiApiType: "chat" },
1405
1541
  "duo-chat-gpt-5-2": { provider: "openai", model: "gpt-5.2-2025-12-11", openaiApiType: "chat" },
@@ -1592,6 +1728,17 @@ var GitLabOpenAILanguageModel = class {
1592
1728
  }
1593
1729
  return parts.join(" | ");
1594
1730
  }
1731
+ /**
1732
+ * Per-call `providerOptions.gitlab.reasoningEffort` takes precedence over the
1733
+ * model-level default.
1734
+ */
1735
+ resolveReasoningEffort(options) {
1736
+ const gitlabOptions = options.providerOptions?.["gitlab"];
1737
+ return gitlabOptions?.reasoningEffort ?? this.config.reasoningEffort;
1738
+ }
1739
+ asOpenAIReasoningEffort(effort) {
1740
+ return effort;
1741
+ }
1595
1742
  convertTools(tools) {
1596
1743
  if (!tools || tools.length === 0) {
1597
1744
  return void 0;
@@ -1866,6 +2013,7 @@ var GitLabOpenAILanguageModel = class {
1866
2013
  const toolChoice = toolsDisabled ? void 0 : this.convertToolChoice(options.toolChoice);
1867
2014
  const openaiModel = this.config.openaiModel || "gpt-4o";
1868
2015
  const maxTokens = options.maxOutputTokens || this.config.maxTokens || 8192;
2016
+ const reasoningEffort = this.resolveReasoningEffort(options);
1869
2017
  const generateParams = {
1870
2018
  model: openaiModel,
1871
2019
  max_completion_tokens: maxTokens,
@@ -1874,7 +2022,10 @@ var GitLabOpenAILanguageModel = class {
1874
2022
  tool_choice: tools ? toolChoice : void 0,
1875
2023
  temperature: options.temperature,
1876
2024
  top_p: options.topP,
1877
- stop: options.stopSequences
2025
+ stop: options.stopSequences,
2026
+ ...reasoningEffort && {
2027
+ reasoning_effort: this.asOpenAIReasoningEffort(reasoningEffort)
2028
+ }
1878
2029
  };
1879
2030
  try {
1880
2031
  const response = options.abortSignal ? await client.chat.completions.create(generateParams, { signal: options.abortSignal }) : await client.chat.completions.create(generateParams);
@@ -1937,6 +2088,7 @@ var GitLabOpenAILanguageModel = class {
1937
2088
  const instructions = this.extractSystemInstructions(options.prompt);
1938
2089
  const openaiModel = this.config.openaiModel || "gpt-5-codex";
1939
2090
  const maxTokens = options.maxOutputTokens || this.config.maxTokens || 8192;
2091
+ const reasoningEffort = this.resolveReasoningEffort(options);
1940
2092
  const generateParams = {
1941
2093
  model: openaiModel,
1942
2094
  input,
@@ -1945,7 +2097,18 @@ var GitLabOpenAILanguageModel = class {
1945
2097
  max_output_tokens: maxTokens,
1946
2098
  temperature: options.temperature,
1947
2099
  top_p: options.topP,
1948
- store: false
2100
+ store: false,
2101
+ ...reasoningEffort && {
2102
+ // `summary: 'auto'` opts into visible reasoning summary text.
2103
+ // Without it, the Responses API never returns summary content —
2104
+ // the model still reasons and effort still affects behavior, but
2105
+ // no chain-of-thought text comes back (mirrors the Anthropic
2106
+ // `thinking.display` gap fixed in buildThinkingParams).
2107
+ reasoning: {
2108
+ effort: this.asOpenAIReasoningEffort(reasoningEffort),
2109
+ summary: "auto"
2110
+ }
2111
+ }
1949
2112
  };
1950
2113
  try {
1951
2114
  const response = options.abortSignal ? await client.responses.create(generateParams, { signal: options.abortSignal }) : await client.responses.create(generateParams);
@@ -1966,6 +2129,12 @@ var GitLabOpenAILanguageModel = class {
1966
2129
  toolName: item.name,
1967
2130
  input: item.arguments
1968
2131
  });
2132
+ } else if (item.type === "reasoning") {
2133
+ for (const summary of item.summary || []) {
2134
+ if (summary.type === "summary_text" && summary.text) {
2135
+ content.push({ type: "reasoning", text: summary.text });
2136
+ }
2137
+ }
1969
2138
  }
1970
2139
  }
1971
2140
  const usage = this.createUsage({
@@ -2018,6 +2187,7 @@ var GitLabOpenAILanguageModel = class {
2018
2187
  const toolChoice = toolsDisabled ? void 0 : this.convertToolChoice(options.toolChoice);
2019
2188
  const openaiModel = this.config.openaiModel || "gpt-4o";
2020
2189
  const maxTokens = options.maxOutputTokens || this.config.maxTokens || 8192;
2190
+ const reasoningEffort = this.resolveReasoningEffort(options);
2021
2191
  const requestBody = {
2022
2192
  model: openaiModel,
2023
2193
  max_completion_tokens: maxTokens,
@@ -2027,6 +2197,9 @@ var GitLabOpenAILanguageModel = class {
2027
2197
  temperature: options.temperature,
2028
2198
  top_p: options.topP,
2029
2199
  stop: options.stopSequences,
2200
+ ...reasoningEffort && {
2201
+ reasoning_effort: this.asOpenAIReasoningEffort(reasoningEffort)
2202
+ },
2030
2203
  stream: true,
2031
2204
  stream_options: { include_usage: true }
2032
2205
  };
@@ -2234,6 +2407,7 @@ var GitLabOpenAILanguageModel = class {
2234
2407
  const instructions = this.extractSystemInstructions(options.prompt);
2235
2408
  const openaiModel = this.config.openaiModel || "gpt-5-codex";
2236
2409
  const maxTokens = options.maxOutputTokens || this.config.maxTokens || 8192;
2410
+ const reasoningEffort = this.resolveReasoningEffort(options);
2237
2411
  const requestBody = {
2238
2412
  model: openaiModel,
2239
2413
  input,
@@ -2243,6 +2417,17 @@ var GitLabOpenAILanguageModel = class {
2243
2417
  temperature: options.temperature,
2244
2418
  top_p: options.topP,
2245
2419
  store: false,
2420
+ ...reasoningEffort && {
2421
+ // `summary: 'auto'` opts into visible reasoning summary text.
2422
+ // Without it, the Responses API never returns summary content —
2423
+ // the model still reasons and effort still affects behavior, but
2424
+ // no chain-of-thought text comes back (mirrors the Anthropic
2425
+ // `thinking.display` gap fixed in buildThinkingParams).
2426
+ reasoning: {
2427
+ effort: this.asOpenAIReasoningEffort(reasoningEffort),
2428
+ summary: "auto"
2429
+ }
2430
+ },
2246
2431
  stream: true
2247
2432
  };
2248
2433
  const self = this;
@@ -2281,11 +2466,38 @@ var GitLabOpenAILanguageModel = class {
2281
2466
  }
2282
2467
  };
2283
2468
  const toolCalls = {};
2469
+ const activeReasoning = /* @__PURE__ */ new Set();
2470
+ const knownReasoning = /* @__PURE__ */ new Set();
2471
+ const reasoningWithText = /* @__PURE__ */ new Set();
2284
2472
  let usage = self.createUsage();
2285
2473
  let finishReason = { unified: "other", raw: void 0 };
2286
2474
  let textStarted = false;
2287
2475
  let contentEmitted = false;
2288
2476
  const textId = "text-0";
2477
+ const reasoningId = (outputIndex, summaryIndex) => `reasoning-${outputIndex}-${summaryIndex}`;
2478
+ const startReasoning = (outputIndex, summaryIndex) => {
2479
+ const id = reasoningId(outputIndex, summaryIndex);
2480
+ if (!knownReasoning.has(id)) {
2481
+ knownReasoning.add(id);
2482
+ activeReasoning.add(id);
2483
+ contentEmitted = true;
2484
+ safeEnqueue({ type: "reasoning-start", id });
2485
+ }
2486
+ return id;
2487
+ };
2488
+ const emitReasoningDelta = (outputIndex, summaryIndex, delta) => {
2489
+ const id = startReasoning(outputIndex, summaryIndex);
2490
+ if (activeReasoning.has(id) && delta) {
2491
+ reasoningWithText.add(id);
2492
+ safeEnqueue({ type: "reasoning-delta", id, delta });
2493
+ }
2494
+ };
2495
+ const endReasoning = (outputIndex, summaryIndex) => {
2496
+ const id = reasoningId(outputIndex, summaryIndex);
2497
+ if (activeReasoning.delete(id)) {
2498
+ safeEnqueue({ type: "reasoning-end", id });
2499
+ }
2500
+ };
2289
2501
  try {
2290
2502
  const openaiStream = await client.responses.create(
2291
2503
  {
@@ -2318,6 +2530,30 @@ var GitLabOpenAILanguageModel = class {
2318
2530
  toolName: event.item.name
2319
2531
  });
2320
2532
  }
2533
+ } else if (event.type === "response.reasoning_summary_part.added") {
2534
+ startReasoning(event.output_index, event.summary_index);
2535
+ } else if (event.type === "response.reasoning_summary_text.delta") {
2536
+ emitReasoningDelta(event.output_index, event.summary_index, event.delta);
2537
+ } else if (event.type === "response.reasoning_summary_text.done") {
2538
+ const id = startReasoning(event.output_index, event.summary_index);
2539
+ if (!reasoningWithText.has(id)) {
2540
+ emitReasoningDelta(event.output_index, event.summary_index, event.text);
2541
+ }
2542
+ endReasoning(event.output_index, event.summary_index);
2543
+ } else if (event.type === "response.reasoning_summary_part.done") {
2544
+ const id = startReasoning(event.output_index, event.summary_index);
2545
+ if (!reasoningWithText.has(id)) {
2546
+ emitReasoningDelta(event.output_index, event.summary_index, event.part.text);
2547
+ }
2548
+ endReasoning(event.output_index, event.summary_index);
2549
+ } else if (event.type === "response.output_item.done" && event.item.type === "reasoning") {
2550
+ const prefix = `reasoning-${event.output_index}-`;
2551
+ for (const id of [...activeReasoning]) {
2552
+ if (id.startsWith(prefix)) {
2553
+ activeReasoning.delete(id);
2554
+ safeEnqueue({ type: "reasoning-end", id });
2555
+ }
2556
+ }
2321
2557
  } else if (event.type === "response.output_text.delta") {
2322
2558
  contentEmitted = true;
2323
2559
  if (!textStarted) {
@@ -2360,6 +2596,10 @@ var GitLabOpenAILanguageModel = class {
2360
2596
  }
2361
2597
  }
2362
2598
  }
2599
+ for (const id of activeReasoning) {
2600
+ safeEnqueue({ type: "reasoning-end", id });
2601
+ }
2602
+ activeReasoning.clear();
2363
2603
  if (textStarted) {
2364
2604
  safeEnqueue({ type: "text-end", id: textId });
2365
2605
  }
@@ -2452,7 +2692,7 @@ import { AsyncResource } from "async_hooks";
2452
2692
  import WebSocket from "isomorphic-ws";
2453
2693
 
2454
2694
  // src/version.ts
2455
- var VERSION = true ? "6.12.2" : "0.0.0-dev";
2695
+ var VERSION = true ? "6.14.0" : "0.0.0-dev";
2456
2696
 
2457
2697
  // src/gitlab-workflow-client.ts
2458
2698
  var WS_CONNECT_TIMEOUT_MS = 3e4;
@@ -5299,12 +5539,14 @@ function createGitLab(options = {}) {
5299
5539
  if (mapping.provider === "openai") {
5300
5540
  return new GitLabOpenAILanguageModel(modelId, {
5301
5541
  ...baseConfig,
5302
- openaiModel: agenticOptions?.providerModel ?? mapping.model
5542
+ openaiModel: agenticOptions?.providerModel ?? mapping.model,
5543
+ reasoningEffort: agenticOptions?.reasoningEffort
5303
5544
  });
5304
5545
  }
5305
5546
  return new GitLabAnthropicLanguageModel(modelId, {
5306
5547
  ...baseConfig,
5307
- anthropicModel: agenticOptions?.providerModel ?? mapping.model
5548
+ anthropicModel: agenticOptions?.providerModel ?? mapping.model,
5549
+ thinking: agenticOptions?.thinking
5308
5550
  });
5309
5551
  };
5310
5552
  const createWorkflowChatModel = (modelId, workflowOptions) => {