@workglow/ai 0.2.8 → 0.2.9

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/bun.js CHANGED
@@ -322,6 +322,7 @@ class QueuedExecutionStrategy {
322
322
  concurrency;
323
323
  autoCreate;
324
324
  initPromise = null;
325
+ limiter;
325
326
  constructor(queueName, concurrency = 1, autoCreate = true) {
326
327
  this.queueName = queueName;
327
328
  this.concurrency = concurrency;
@@ -359,8 +360,42 @@ class QueuedExecutionStrategy {
359
360
  }
360
361
  abort() {}
361
362
  async* executeStream(jobInput, context, runnerId) {
362
- const result = await this.execute(jobInput, context, runnerId);
363
- yield { type: "finish", data: result };
363
+ if (context.signal.aborted) {
364
+ throw context.signal.reason ?? new AbortSignalJobError2("The operation was aborted");
365
+ }
366
+ await this.ensureQueue();
367
+ const limiter = this.limiter;
368
+ if (!limiter) {
369
+ throw new TaskConfigurationError(`QueuedExecutionStrategy: limiter was not initialized for queue "${this.queueName}"`);
370
+ }
371
+ await this.acquireLimiterSlot(limiter, context.signal);
372
+ try {
373
+ const job = new AiJob({
374
+ queueName: jobInput.aiProvider,
375
+ jobRunId: runnerId,
376
+ input: jobInput
377
+ });
378
+ yield* job.executeStream(jobInput, {
379
+ signal: context.signal,
380
+ updateProgress: context.updateProgress
381
+ });
382
+ } finally {
383
+ await limiter.recordJobCompletion();
384
+ }
385
+ }
386
+ async acquireLimiterSlot(limiter, signal) {
387
+ const poll = async () => {
388
+ while (!await limiter.canProceed()) {
389
+ if (signal.aborted) {
390
+ throw signal.reason ?? new AbortSignalJobError2("The operation was aborted");
391
+ }
392
+ const next = await limiter.getNextAvailableTime();
393
+ const delay = Math.max(0, next.getTime() - Date.now());
394
+ await new Promise((resolve) => setTimeout(resolve, Math.min(delay, 50)));
395
+ }
396
+ };
397
+ await poll();
398
+ await limiter.recordJobStart();
364
399
  }
365
400
  ensureQueue() {
366
401
  if (!this.initPromise) {
@@ -378,6 +413,7 @@ class QueuedExecutionStrategy {
378
413
  if (!existing.server.isRunning()) {
379
414
  await existing.server.start();
380
415
  }
416
+ this.limiter = existing.server.limiter;
381
417
  return existing;
382
418
  }
383
419
  if (!this.autoCreate) {
@@ -385,10 +421,11 @@ class QueuedExecutionStrategy {
385
421
  }
386
422
  const storage = new InMemoryQueueStorage(this.queueName);
387
423
  await storage.setupDatabase();
424
+ this.limiter = new ConcurrencyLimiter(this.concurrency);
388
425
  const server = new JobQueueServer(AiJob, {
389
426
  storage,
390
427
  queueName: this.queueName,
391
- limiter: new ConcurrencyLimiter(this.concurrency)
428
+ limiter: this.limiter
392
429
  });
393
430
  const client = new JobQueueClient({
394
431
  storage,
@@ -412,6 +449,7 @@ class QueuedExecutionStrategy {
412
449
  if (!raced.server.isRunning()) {
413
450
  await raced.server.start();
414
451
  }
452
+ this.limiter = raced.server.limiter;
415
453
  return raced;
416
454
  }
417
455
  }
@@ -800,8 +838,8 @@ class QueuedAiProvider extends AiProvider {
800
838
  // src/task/index.ts
801
839
  import { TaskRegistry } from "@workglow/task-graph";
802
840
 
803
- // src/task/BackgroundRemovalTask.ts
804
- import { CreateWorkflow, Workflow } from "@workglow/task-graph";
841
+ // src/task/AiChatTask.ts
842
+ import { TaskConfigSchema as TaskConfigSchema2 } from "@workglow/task-graph";
805
843
 
806
844
  // src/task/base/AiTaskSchemas.ts
807
845
  var TypeLanguage = (annotations = {}) => ({
@@ -960,8 +998,8 @@ var TypeCategory = {
960
998
  description: "Classification category with label and score"
961
999
  };
962
1000
 
963
- // src/task/base/AiVisionTask.ts
964
- import { convertImageDataToUseableForm } from "@workglow/util/media";
1001
+ // src/task/base/StreamingAiTask.ts
1002
+ import { getStreamingPorts, TaskConfigurationError as TaskConfigurationError3 } from "@workglow/task-graph";
965
1003
 
966
1004
  // src/task/base/AiTask.ts
967
1005
  import {
@@ -1147,7 +1185,393 @@ class AiTask extends Task {
1147
1185
  }
1148
1186
  }
1149
1187
 
1188
+ // src/task/base/StreamingAiTask.ts
1189
+ class StreamingAiTask extends AiTask {
1190
+ static type = "StreamingAiTask";
1191
+ async* executeStream(input, context) {
1192
+ const model = input.model;
1193
+ if (!model || typeof model !== "object") {
1194
+ throw new TaskConfigurationError3("StreamingAiTask: Model was not resolved to ModelConfig - this indicates a bug in the resolution system");
1195
+ }
1196
+ const jobInput = await this.getJobInput(input);
1197
+ const strategy = getAiProviderRegistry().getStrategy(model);
1198
+ const outSchema = this.outputSchema();
1199
+ const ports = getStreamingPorts(outSchema);
1200
+ let defaultPort = "text";
1201
+ if (ports.length > 0) {
1202
+ defaultPort = ports[0].port;
1203
+ } else {
1204
+ if (typeof outSchema === "object" && outSchema.properties) {
1205
+ const firstProp = Object.keys(outSchema.properties)[0];
1206
+ if (firstProp)
1207
+ defaultPort = firstProp;
1208
+ }
1209
+ }
1210
+ for await (const event of strategy.executeStream(jobInput, context, this.runConfig.runnerId)) {
1211
+ if (event.type === "text-delta") {
1212
+ yield { ...event, port: event.port ?? defaultPort };
1213
+ } else if (event.type === "object-delta") {
1214
+ yield { ...event, port: event.port ?? defaultPort };
1215
+ } else {
1216
+ yield event;
1217
+ }
1218
+ }
1219
+ }
1220
+ }
1221
+
1222
+ // src/task/ChatMessage.ts
1223
+ var ContentBlockTextSchema = {
1224
+ type: "object",
1225
+ properties: {
1226
+ type: { type: "string", enum: ["text"] },
1227
+ text: { type: "string" }
1228
+ },
1229
+ required: ["type", "text"],
1230
+ additionalProperties: false
1231
+ };
1232
+ var ContentBlockImageSchema = {
1233
+ type: "object",
1234
+ properties: {
1235
+ type: { type: "string", enum: ["image"] },
1236
+ mimeType: { type: "string" },
1237
+ data: { type: "string" }
1238
+ },
1239
+ required: ["type", "mimeType", "data"],
1240
+ additionalProperties: false
1241
+ };
1242
+ var ContentBlockToolUseSchema = {
1243
+ type: "object",
1244
+ properties: {
1245
+ type: { type: "string", enum: ["tool_use"] },
1246
+ id: { type: "string" },
1247
+ name: { type: "string" },
1248
+ input: { type: "object", additionalProperties: true }
1249
+ },
1250
+ required: ["type", "id", "name", "input"],
1251
+ additionalProperties: false
1252
+ };
1253
+ var ContentBlockToolResultSchema = {
1254
+ type: "object",
1255
+ properties: {
1256
+ type: { type: "string", enum: ["tool_result"] },
1257
+ tool_use_id: { type: "string" },
1258
+ content: {
1259
+ type: "array",
1260
+ items: { $ref: "#/definitions/ContentBlock" }
1261
+ },
1262
+ is_error: { type: "boolean" }
1263
+ },
1264
+ required: ["type", "tool_use_id", "content"],
1265
+ additionalProperties: false
1266
+ };
1267
+ var ContentBlockSchema = {
1268
+ oneOf: [
1269
+ ContentBlockTextSchema,
1270
+ ContentBlockImageSchema,
1271
+ ContentBlockToolUseSchema,
1272
+ ContentBlockToolResultSchema
1273
+ ],
1274
+ definitions: {
1275
+ ContentBlock: {
1276
+ oneOf: [
1277
+ ContentBlockTextSchema,
1278
+ ContentBlockImageSchema,
1279
+ ContentBlockToolUseSchema,
1280
+ ContentBlockToolResultSchema
1281
+ ]
1282
+ }
1283
+ },
1284
+ title: "ContentBlock",
1285
+ description: "A single content block within a chat message"
1286
+ };
1287
+ var ChatMessageSchema = {
1288
+ type: "object",
1289
+ properties: {
1290
+ role: { type: "string", enum: ["user", "assistant", "tool", "system"] },
1291
+ content: {
1292
+ type: "array",
1293
+ items: ContentBlockSchema
1294
+ }
1295
+ },
1296
+ required: ["role", "content"],
1297
+ additionalProperties: false,
1298
+ title: "ChatMessage",
1299
+ description: "A single chat message with role and structured content blocks"
1300
+ };
1301
+ function isContentBlock(value) {
1302
+ if (!value || typeof value !== "object")
1303
+ return false;
1304
+ const v = value;
1305
+ switch (v.type) {
1306
+ case "text":
1307
+ return typeof v.text === "string";
1308
+ case "image":
1309
+ return typeof v.mimeType === "string" && typeof v.data === "string";
1310
+ case "tool_use":
1311
+ return typeof v.id === "string" && typeof v.name === "string" && v.input !== null && typeof v.input === "object";
1312
+ case "tool_result":
1313
+ return typeof v.tool_use_id === "string" && Array.isArray(v.content) && v.content.every(isContentBlock);
1314
+ default:
1315
+ return false;
1316
+ }
1317
+ }
1318
+ function isChatMessage(value) {
1319
+ if (!value || typeof value !== "object")
1320
+ return false;
1321
+ const v = value;
1322
+ const validRole = v.role === "user" || v.role === "assistant" || v.role === "tool" || v.role === "system";
1323
+ return validRole && Array.isArray(v.content) && v.content.every(isContentBlock);
1324
+ }
1325
+ function textMessage(role, text) {
1326
+ return { role, content: [{ type: "text", text }] };
1327
+ }
1328
+
1329
+ // src/task/AiChatTask.ts
1330
+ import { resolveHumanConnector } from "@workglow/util";
1331
+ var modelSchema = TypeModel("model:AiChatTask");
1332
+ var chatConnectorContentSchema = {
1333
+ type: "object",
1334
+ properties: {
1335
+ content: {
1336
+ type: "string",
1337
+ title: "Message",
1338
+ description: "Your reply (leave blank to end the conversation)"
1339
+ }
1340
+ },
1341
+ additionalProperties: false
1342
+ };
1343
+ var AiChatInputSchema = {
1344
+ type: "object",
1345
+ properties: {
1346
+ model: modelSchema,
1347
+ prompt: {
1348
+ oneOf: [
1349
+ { type: "string", title: "Prompt", description: "The initial user message" },
1350
+ {
1351
+ type: "array",
1352
+ title: "Prompt",
1353
+ description: "The initial user message as structured content blocks",
1354
+ items: ContentBlockSchema
1355
+ }
1356
+ ],
1357
+ title: "Prompt",
1358
+ description: "The first user message to start the conversation"
1359
+ },
1360
+ messages: {
1361
+ type: "array",
1362
+ title: "Messages",
1363
+ description: "Conversation history (managed internally by the chat loop; not a user-facing input)",
1364
+ items: ChatMessageSchema,
1365
+ "x-ui-hidden": true
1366
+ },
1367
+ systemPrompt: {
1368
+ type: "string",
1369
+ title: "System Prompt",
1370
+ description: "Optional system instructions for the model"
1371
+ },
1372
+ maxTokens: {
1373
+ type: "number",
1374
+ title: "Max Tokens",
1375
+ description: "Per-turn token limit",
1376
+ minimum: 1,
1377
+ "x-ui-group": "Configuration"
1378
+ },
1379
+ temperature: {
1380
+ type: "number",
1381
+ title: "Temperature",
1382
+ description: "Sampling temperature",
1383
+ minimum: 0,
1384
+ maximum: 2,
1385
+ "x-ui-group": "Configuration"
1386
+ },
1387
+ maxIterations: {
1388
+ type: "number",
1389
+ title: "Max Iterations",
1390
+ description: "Safety cap on conversation turns",
1391
+ minimum: 1,
1392
+ default: 100,
1393
+ "x-ui-group": "Configuration"
1394
+ }
1395
+ },
1396
+ required: ["model", "prompt"],
1397
+ additionalProperties: false
1398
+ };
1399
+ var AiChatOutputSchema = {
1400
+ type: "object",
1401
+ properties: {
1402
+ text: {
1403
+ type: "string",
1404
+ title: "Text",
1405
+ description: "Last assistant response",
1406
+ "x-stream": "append"
1407
+ },
1408
+ messages: {
1409
+ type: "array",
1410
+ title: "Messages",
1411
+ description: "Full conversation history",
1412
+ items: ChatMessageSchema,
1413
+ "x-stream": "object"
1414
+ },
1415
+ iterations: {
1416
+ type: "number",
1417
+ title: "Iterations",
1418
+ description: "Number of completed turns"
1419
+ }
1420
+ },
1421
+ required: ["text", "messages", "iterations"],
1422
+ additionalProperties: false
1423
+ };
1424
+
1425
+ class AiChatTask extends StreamingAiTask {
1426
+ static type = "AiChatTask";
1427
+ static category = "AI Chat";
1428
+ static title = "AI Chat";
1429
+ static description = "Multi-turn chat with a language model, using a human connector to collect user input between turns.";
1430
+ static cacheable = false;
1431
+ static configSchema() {
1432
+ return {
1433
+ type: "object",
1434
+ properties: {
1435
+ ...TaskConfigSchema2["properties"]
1436
+ },
1437
+ additionalProperties: false
1438
+ };
1439
+ }
1440
+ static inputSchema() {
1441
+ return AiChatInputSchema;
1442
+ }
1443
+ static outputSchema() {
1444
+ return AiChatOutputSchema;
1445
+ }
1446
+ _sessionId;
1447
+ async getJobInput(input) {
1448
+ const model = input.model;
1449
+ if (!this._sessionId) {
1450
+ this._sessionId = getAiProviderRegistry().createSession(model.provider, model);
1451
+ }
1452
+ return {
1453
+ taskType: "AiChatTask",
1454
+ aiProvider: model.provider,
1455
+ taskInput: input,
1456
+ sessionId: this._sessionId
1457
+ };
1458
+ }
1459
+ async* executeStream(input, context) {
1460
+ this._sessionId = undefined;
1461
+ const model = input.model;
1462
+ if (!model || typeof model !== "object") {
1463
+ throw new Error("AiChatTask: model was not resolved to ModelConfig");
1464
+ }
1465
+ const connector = resolveHumanConnector(context);
1466
+ const history = [];
1467
+ if (input.systemPrompt) {
1468
+ history.push({ role: "system", content: [{ type: "text", text: input.systemPrompt }] });
1469
+ }
1470
+ const firstUserBlocks = typeof input.prompt === "string" ? [{ type: "text", text: input.prompt }] : input.prompt;
1471
+ history.push({ role: "user", content: firstUserBlocks });
1472
+ const workingInput = { ...input, messages: history };
1473
+ await this.getJobInput(workingInput);
1474
+ const strategy = getAiProviderRegistry().getStrategy(model);
1475
+ const maxIterations = input.maxIterations ?? 100;
1476
+ if (context.resourceScope && this._sessionId) {
1477
+ const sessionId = this._sessionId;
1478
+ context.resourceScope.register(`ai:session:${sessionId}`, async () => {
1479
+ await getAiProviderRegistry().disposeSession(model.provider, sessionId);
1480
+ });
1481
+ }
1482
+ yield {
1483
+ type: "object-delta",
1484
+ port: "messages",
1485
+ objectDelta: [...history]
1486
+ };
1487
+ let iterations = 0;
1488
+ let lastAssistantText = "";
1489
+ for (let turn = 0;turn < maxIterations; turn++) {
1490
+ const perTurnInput = { ...input, messages: [...history] };
1491
+ const turnJobInput = await this.getJobInput(perTurnInput);
1492
+ let assistantText = "";
1493
+ for await (const event of strategy.executeStream(turnJobInput, context, this.runConfig.runnerId)) {
1494
+ if (event.type === "text-delta") {
1495
+ assistantText += event.textDelta;
1496
+ yield {
1497
+ ...event,
1498
+ port: event.port ?? "text"
1499
+ };
1500
+ } else if (event.type === "finish") {} else {
1501
+ yield event;
1502
+ }
1503
+ }
1504
+ iterations++;
1505
+ lastAssistantText = assistantText;
1506
+ const assistantMsg = {
1507
+ role: "assistant",
1508
+ content: [{ type: "text", text: assistantText }]
1509
+ };
1510
+ history.push(assistantMsg);
1511
+ yield {
1512
+ type: "object-delta",
1513
+ port: "messages",
1514
+ objectDelta: [assistantMsg]
1515
+ };
1516
+ const request = {
1517
+ requestId: crypto.randomUUID(),
1518
+ targetHumanId: "default",
1519
+ kind: "elicit",
1520
+ message: "",
1521
+ contentSchema: chatConnectorContentSchema,
1522
+ contentData: undefined,
1523
+ expectsResponse: true,
1524
+ mode: "multi-turn",
1525
+ metadata: { iteration: turn, taskId: this.id }
1526
+ };
1527
+ const response = await connector.send(request, context.signal);
1528
+ if (response.action === "cancel" || response.action === "decline")
1529
+ break;
1530
+ const raw = response.content?.content;
1531
+ let userContent;
1532
+ if (typeof raw === "string") {
1533
+ const text = raw.trim();
1534
+ userContent = text.length > 0 ? [{ type: "text", text: raw }] : [];
1535
+ } else if (Array.isArray(raw)) {
1536
+ userContent = raw;
1537
+ } else {
1538
+ userContent = [];
1539
+ }
1540
+ if (userContent.length === 0)
1541
+ break;
1542
+ const userMsg = { role: "user", content: userContent };
1543
+ history.push(userMsg);
1544
+ yield {
1545
+ type: "object-delta",
1546
+ port: "messages",
1547
+ objectDelta: [userMsg]
1548
+ };
1549
+ }
1550
+ yield {
1551
+ type: "finish",
1552
+ data: {
1553
+ text: lastAssistantText,
1554
+ messages: [...history],
1555
+ iterations
1556
+ }
1557
+ };
1558
+ }
1559
+ async execute(input, context) {
1560
+ let result;
1561
+ for await (const event of this.executeStream(input, context)) {
1562
+ if (event.type === "finish") {
1563
+ result = event.data;
1564
+ }
1565
+ }
1566
+ return result;
1567
+ }
1568
+ }
1569
+
1570
+ // src/task/BackgroundRemovalTask.ts
1571
+ import { CreateWorkflow, Workflow } from "@workglow/task-graph";
1572
+
1150
1573
  // src/task/base/AiVisionTask.ts
1574
+ import { convertImageDataToUseableForm } from "@workglow/util/media";
1151
1575
  class AiVisionTask extends AiTask {
1152
1576
  static type = "AiVisionTask";
1153
1577
  async getJobInput(input) {
@@ -1168,7 +1592,7 @@ class AiVisionTask extends AiTask {
1168
1592
  }
1169
1593
 
1170
1594
  // src/task/BackgroundRemovalTask.ts
1171
- var modelSchema = TypeModel("model:BackgroundRemovalTask");
1595
+ var modelSchema2 = TypeModel("model:BackgroundRemovalTask");
1172
1596
  var processedImageSchema = {
1173
1597
  type: "string",
1174
1598
  contentEncoding: "base64",
@@ -1180,7 +1604,7 @@ var BackgroundRemovalInputSchema = {
1180
1604
  type: "object",
1181
1605
  properties: {
1182
1606
  image: TypeImageInput,
1183
- model: modelSchema
1607
+ model: modelSchema2
1184
1608
  },
1185
1609
  required: ["image", "model"],
1186
1610
  additionalProperties: false
@@ -1224,7 +1648,7 @@ import { CreateWorkflow as CreateWorkflow2, Workflow as Workflow2 } from "@workg
1224
1648
  import {
1225
1649
  TypedArraySchema
1226
1650
  } from "@workglow/util/schema";
1227
- var modelSchema2 = TypeModel("model:TextEmbeddingTask");
1651
+ var modelSchema3 = TypeModel("model:TextEmbeddingTask");
1228
1652
  var TextEmbeddingInputSchema = {
1229
1653
  type: "object",
1230
1654
  properties: {
@@ -1233,7 +1657,7 @@ var TextEmbeddingInputSchema = {
1233
1657
  title: "Text",
1234
1658
  description: "The text to embed"
1235
1659
  }),
1236
- model: modelSchema2
1660
+ model: modelSchema3
1237
1661
  },
1238
1662
  required: ["text", "model"],
1239
1663
  additionalProperties: false
@@ -2002,7 +2426,7 @@ import { CreateWorkflow as CreateWorkflow9, Task as Task7, Workflow as Workflow9
2002
2426
 
2003
2427
  // src/task/CountTokensTask.ts
2004
2428
  import { CreateWorkflow as CreateWorkflow8, Workflow as Workflow8 } from "@workglow/task-graph";
2005
- var modelSchema3 = TypeModel("model");
2429
+ var modelSchema4 = TypeModel("model");
2006
2430
  var CountTokensInputSchema = {
2007
2431
  type: "object",
2008
2432
  properties: {
@@ -2011,7 +2435,7 @@ var CountTokensInputSchema = {
2011
2435
  title: "Text",
2012
2436
  description: "The text to count tokens for"
2013
2437
  },
2014
- model: modelSchema3
2438
+ model: modelSchema4
2015
2439
  },
2016
2440
  required: ["text", "model"],
2017
2441
  additionalProperties: false
@@ -2055,7 +2479,7 @@ var ContextFormat = {
2055
2479
  MARKDOWN: "markdown",
2056
2480
  JSON: "json"
2057
2481
  };
2058
- var modelSchema4 = TypeModel("model", {
2482
+ var modelSchema5 = TypeModel("model", {
2059
2483
  title: "Model",
2060
2484
  description: "Model to use for token counting (optional, falls back to estimation)"
2061
2485
  });
@@ -2119,7 +2543,7 @@ var inputSchema6 = {
2119
2543
 
2120
2544
  `
2121
2545
  },
2122
- model: modelSchema4
2546
+ model: modelSchema5
2123
2547
  },
2124
2548
  required: ["chunks"],
2125
2549
  additionalProperties: false
@@ -2353,7 +2777,7 @@ import { CreateWorkflow as CreateWorkflow12, Task as Task8, Workflow as Workflow
2353
2777
 
2354
2778
  // src/task/TextNamedEntityRecognitionTask.ts
2355
2779
  import { CreateWorkflow as CreateWorkflow10, Workflow as Workflow10 } from "@workglow/task-graph";
2356
- var modelSchema5 = TypeModel("model:TextNamedEntityRecognitionTask");
2780
+ var modelSchema6 = TypeModel("model:TextNamedEntityRecognitionTask");
2357
2781
  var TextNamedEntityRecognitionInputSchema = {
2358
2782
  type: "object",
2359
2783
  properties: {
@@ -2372,7 +2796,7 @@ var TextNamedEntityRecognitionInputSchema = {
2372
2796
  "x-ui-group": "Configuration",
2373
2797
  "x-ui-group-open": false
2374
2798
  },
2375
- model: modelSchema5
2799
+ model: modelSchema6
2376
2800
  },
2377
2801
  required: ["text", "model"],
2378
2802
  additionalProperties: false
@@ -2431,44 +2855,7 @@ Workflow10.prototype.textNamedEntityRecognition = CreateWorkflow10(TextNamedEnti
2431
2855
 
2432
2856
  // src/task/TextSummaryTask.ts
2433
2857
  import { CreateWorkflow as CreateWorkflow11, Workflow as Workflow11 } from "@workglow/task-graph";
2434
-
2435
- // src/task/base/StreamingAiTask.ts
2436
- import { getStreamingPorts, TaskConfigurationError as TaskConfigurationError3 } from "@workglow/task-graph";
2437
- class StreamingAiTask extends AiTask {
2438
- static type = "StreamingAiTask";
2439
- async* executeStream(input, context) {
2440
- const model = input.model;
2441
- if (!model || typeof model !== "object") {
2442
- throw new TaskConfigurationError3("StreamingAiTask: Model was not resolved to ModelConfig - this indicates a bug in the resolution system");
2443
- }
2444
- const jobInput = await this.getJobInput(input);
2445
- const strategy = getAiProviderRegistry().getStrategy(model);
2446
- const outSchema = this.outputSchema();
2447
- const ports = getStreamingPorts(outSchema);
2448
- let defaultPort = "text";
2449
- if (ports.length > 0) {
2450
- defaultPort = ports[0].port;
2451
- } else {
2452
- if (typeof outSchema === "object" && outSchema.properties) {
2453
- const firstProp = Object.keys(outSchema.properties)[0];
2454
- if (firstProp)
2455
- defaultPort = firstProp;
2456
- }
2457
- }
2458
- for await (const event of strategy.executeStream(jobInput, context, this.runConfig.runnerId)) {
2459
- if (event.type === "text-delta") {
2460
- yield { ...event, port: event.port ?? defaultPort };
2461
- } else if (event.type === "object-delta") {
2462
- yield { ...event, port: event.port ?? defaultPort };
2463
- } else {
2464
- yield event;
2465
- }
2466
- }
2467
- }
2468
- }
2469
-
2470
- // src/task/TextSummaryTask.ts
2471
- var modelSchema6 = TypeModel("model:TextSummaryTask");
2858
+ var modelSchema7 = TypeModel("model:TextSummaryTask");
2472
2859
  var TextSummaryInputSchema = {
2473
2860
  type: "object",
2474
2861
  properties: {
@@ -2477,7 +2864,7 @@ var TextSummaryInputSchema = {
2477
2864
  title: "Text",
2478
2865
  description: "The text to summarize"
2479
2866
  },
2480
- model: modelSchema6
2867
+ model: modelSchema7
2481
2868
  },
2482
2869
  required: ["text", "model"],
2483
2870
  additionalProperties: false
@@ -2831,11 +3218,11 @@ Workflow13.prototype.documentUpsert = CreateWorkflow13(DocumentUpsertTask);
2831
3218
 
2832
3219
  // src/task/DownloadModelTask.ts
2833
3220
  import { CreateWorkflow as CreateWorkflow14, Workflow as Workflow14 } from "@workglow/task-graph";
2834
- var modelSchema7 = TypeModel("model");
3221
+ var modelSchema8 = TypeModel("model");
2835
3222
  var DownloadModelInputSchema = {
2836
3223
  type: "object",
2837
3224
  properties: {
2838
- model: modelSchema7
3225
+ model: modelSchema8
2839
3226
  },
2840
3227
  required: ["model"],
2841
3228
  additionalProperties: false
@@ -2843,7 +3230,7 @@ var DownloadModelInputSchema = {
2843
3230
  var DownloadModelOutputSchema = {
2844
3231
  type: "object",
2845
3232
  properties: {
2846
- model: modelSchema7
3233
+ model: modelSchema8
2847
3234
  },
2848
3235
  required: ["model"],
2849
3236
  additionalProperties: false
@@ -2901,7 +3288,7 @@ Workflow14.prototype.downloadModel = CreateWorkflow14(DownloadModelTask);
2901
3288
 
2902
3289
  // src/task/FaceDetectorTask.ts
2903
3290
  import { CreateWorkflow as CreateWorkflow15, Workflow as Workflow15 } from "@workglow/task-graph";
2904
- var modelSchema8 = TypeModel("model:FaceDetectorTask");
3291
+ var modelSchema9 = TypeModel("model:FaceDetectorTask");
2905
3292
  var TypeBoundingBox2 = {
2906
3293
  type: "object",
2907
3294
  properties: {
@@ -2974,7 +3361,7 @@ var FaceDetectorInputSchema = {
2974
3361
  type: "object",
2975
3362
  properties: {
2976
3363
  image: TypeImageInput,
2977
- model: modelSchema8,
3364
+ model: modelSchema9,
2978
3365
  minDetectionConfidence: {
2979
3366
  type: "number",
2980
3367
  minimum: 0,
@@ -3032,7 +3419,7 @@ Workflow15.prototype.faceDetector = CreateWorkflow15(FaceDetectorTask);
3032
3419
 
3033
3420
  // src/task/FaceLandmarkerTask.ts
3034
3421
  import { CreateWorkflow as CreateWorkflow16, Workflow as Workflow16 } from "@workglow/task-graph";
3035
- var modelSchema9 = TypeModel("model:FaceLandmarkerTask");
3422
+ var modelSchema10 = TypeModel("model:FaceLandmarkerTask");
3036
3423
  var TypeLandmark = {
3037
3424
  type: "object",
3038
3425
  properties: {
@@ -3104,7 +3491,7 @@ var FaceLandmarkerInputSchema = {
3104
3491
  type: "object",
3105
3492
  properties: {
3106
3493
  image: TypeImageInput,
3107
- model: modelSchema9,
3494
+ model: modelSchema10,
3108
3495
  numFaces: {
3109
3496
  type: "number",
3110
3497
  minimum: 1,
@@ -3194,7 +3581,7 @@ Workflow16.prototype.faceLandmarker = CreateWorkflow16(FaceLandmarkerTask);
3194
3581
 
3195
3582
  // src/task/GestureRecognizerTask.ts
3196
3583
  import { CreateWorkflow as CreateWorkflow17, Workflow as Workflow17 } from "@workglow/task-graph";
3197
- var modelSchema10 = TypeModel("model:GestureRecognizerTask");
3584
+ var modelSchema11 = TypeModel("model:GestureRecognizerTask");
3198
3585
  var TypeLandmark2 = {
3199
3586
  type: "object",
3200
3587
  properties: {
@@ -3286,7 +3673,7 @@ var GestureRecognizerInputSchema = {
3286
3673
  type: "object",
3287
3674
  properties: {
3288
3675
  image: TypeImageInput,
3289
- model: modelSchema10,
3676
+ model: modelSchema11,
3290
3677
  numHands: {
3291
3678
  type: "number",
3292
3679
  minimum: 1,
@@ -3362,7 +3749,7 @@ Workflow17.prototype.gestureRecognizer = CreateWorkflow17(GestureRecognizerTask)
3362
3749
 
3363
3750
  // src/task/HandLandmarkerTask.ts
3364
3751
  import { CreateWorkflow as CreateWorkflow18, Workflow as Workflow18 } from "@workglow/task-graph";
3365
- var modelSchema11 = TypeModel("model:HandLandmarkerTask");
3752
+ var modelSchema12 = TypeModel("model:HandLandmarkerTask");
3366
3753
  var TypeLandmark3 = {
3367
3754
  type: "object",
3368
3755
  properties: {
@@ -3431,7 +3818,7 @@ var HandLandmarkerInputSchema = {
3431
3818
  type: "object",
3432
3819
  properties: {
3433
3820
  image: TypeImageInput,
3434
- model: modelSchema11,
3821
+ model: modelSchema12,
3435
3822
  numHands: {
3436
3823
  type: "number",
3437
3824
  minimum: 1,
@@ -3514,7 +3901,7 @@ import {
3514
3901
  } from "@workglow/knowledge-base";
3515
3902
  import { CreateWorkflow as CreateWorkflow19, Task as Task10, Workflow as Workflow19 } from "@workglow/task-graph";
3516
3903
  import { uuid4 } from "@workglow/util";
3517
- var modelSchema12 = TypeModel("model", {
3904
+ var modelSchema13 = TypeModel("model", {
3518
3905
  title: "Model",
3519
3906
  description: "Model to use for token counting"
3520
3907
  });
@@ -3560,7 +3947,7 @@ var inputSchema9 = {
3560
3947
  description: "Strategy for chunking",
3561
3948
  default: "hierarchical"
3562
3949
  },
3563
- model: modelSchema12
3950
+ model: modelSchema13
3564
3951
  },
3565
3952
  required: ["doc_id", "documentTree"],
3566
3953
  additionalProperties: false
@@ -4005,12 +4392,12 @@ Workflow21.prototype.kbToDocuments = CreateWorkflow21(KbToDocumentsTask);
4005
4392
 
4006
4393
  // src/task/ImageClassificationTask.ts
4007
4394
  import { CreateWorkflow as CreateWorkflow22, Workflow as Workflow22 } from "@workglow/task-graph";
4008
- var modelSchema13 = TypeModel("model:ImageClassificationTask");
4395
+ var modelSchema14 = TypeModel("model:ImageClassificationTask");
4009
4396
  var ImageClassificationInputSchema = {
4010
4397
  type: "object",
4011
4398
  properties: {
4012
4399
  image: TypeImageInput,
4013
- model: modelSchema13,
4400
+ model: modelSchema14,
4014
4401
  categories: {
4015
4402
  type: "array",
4016
4403
  items: {
@@ -4071,12 +4458,12 @@ import { CreateWorkflow as CreateWorkflow23, Workflow as Workflow23 } from "@wor
4071
4458
  import {
4072
4459
  TypedArraySchema as TypedArraySchema7
4073
4460
  } from "@workglow/util/schema";
4074
- var modelSchema14 = TypeModel("model:ImageEmbeddingTask");
4461
+ var modelSchema15 = TypeModel("model:ImageEmbeddingTask");
4075
4462
  var ImageEmbeddingInputSchema = {
4076
4463
  type: "object",
4077
4464
  properties: {
4078
4465
  image: TypeSingleOrArray(TypeImageInput),
4079
- model: modelSchema14
4466
+ model: modelSchema15
4080
4467
  },
4081
4468
  required: ["image", "model"],
4082
4469
  additionalProperties: false
@@ -4112,12 +4499,12 @@ Workflow23.prototype.imageEmbedding = CreateWorkflow23(ImageEmbeddingTask);
4112
4499
 
4113
4500
  // src/task/ImageSegmentationTask.ts
4114
4501
  import { CreateWorkflow as CreateWorkflow24, Workflow as Workflow24 } from "@workglow/task-graph";
4115
- var modelSchema15 = TypeModel("model:ImageSegmentationTask");
4502
+ var modelSchema16 = TypeModel("model:ImageSegmentationTask");
4116
4503
  var ImageSegmentationInputSchema = {
4117
4504
  type: "object",
4118
4505
  properties: {
4119
4506
  image: TypeImageInput,
4120
- model: modelSchema15,
4507
+ model: modelSchema16,
4121
4508
  threshold: {
4122
4509
  type: "number",
4123
4510
  title: "Threshold",
@@ -4200,7 +4587,7 @@ Workflow24.prototype.imageSegmentation = CreateWorkflow24(ImageSegmentationTask)
4200
4587
 
4201
4588
  // src/task/ImageToTextTask.ts
4202
4589
  import { CreateWorkflow as CreateWorkflow25, Workflow as Workflow25 } from "@workglow/task-graph";
4203
- var modelSchema16 = TypeModel("model:ImageToTextTask");
4590
+ var modelSchema17 = TypeModel("model:ImageToTextTask");
4204
4591
  var generatedTextSchema = {
4205
4592
  type: "string",
4206
4593
  title: "Text",
@@ -4210,7 +4597,7 @@ var ImageToTextInputSchema = {
4210
4597
  type: "object",
4211
4598
  properties: {
4212
4599
  image: TypeImageInput,
4213
- model: modelSchema16,
4600
+ model: modelSchema17,
4214
4601
  maxTokens: {
4215
4602
  type: "number",
4216
4603
  title: "Max Tokens",
@@ -4255,11 +4642,11 @@ Workflow25.prototype.imageToText = CreateWorkflow25(ImageToTextTask);
4255
4642
 
4256
4643
  // src/task/ModelInfoTask.ts
4257
4644
  import { CreateWorkflow as CreateWorkflow26, Workflow as Workflow26 } from "@workglow/task-graph";
4258
- var modelSchema17 = TypeModel("model");
4645
+ var modelSchema18 = TypeModel("model");
4259
4646
  var ModelInfoInputSchema = {
4260
4647
  type: "object",
4261
4648
  properties: {
4262
- model: modelSchema17,
4649
+ model: modelSchema18,
4263
4650
  detail: {
4264
4651
  type: "string",
4265
4652
  enum: ["cached_status", "files", "files_with_metadata", "dimensions"],
@@ -4272,7 +4659,7 @@ var ModelInfoInputSchema = {
4272
4659
  var ModelInfoOutputSchema = {
4273
4660
  type: "object",
4274
4661
  properties: {
4275
- model: modelSchema17,
4662
+ model: modelSchema18,
4276
4663
  is_local: { type: "boolean" },
4277
4664
  is_remote: { type: "boolean" },
4278
4665
  supports_browser: { type: "boolean" },
@@ -4428,7 +4815,7 @@ Workflow27.prototype.modelSearch = CreateWorkflow27(ModelSearchTask);
4428
4815
 
4429
4816
  // src/task/ObjectDetectionTask.ts
4430
4817
  import { CreateWorkflow as CreateWorkflow28, Workflow as Workflow28 } from "@workglow/task-graph";
4431
- var modelSchema18 = TypeModel("model:ObjectDetectionTask");
4818
+ var modelSchema19 = TypeModel("model:ObjectDetectionTask");
4432
4819
  var detectionSchema = {
4433
4820
  type: "object",
4434
4821
  properties: {
@@ -4453,7 +4840,7 @@ var ObjectDetectionInputSchema = {
4453
4840
  type: "object",
4454
4841
  properties: {
4455
4842
  image: TypeImageInput,
4456
- model: modelSchema18,
4843
+ model: modelSchema19,
4457
4844
  labels: {
4458
4845
  type: "array",
4459
4846
  items: {
@@ -4511,7 +4898,7 @@ Workflow28.prototype.objectDetection = CreateWorkflow28(ObjectDetectionTask);
4511
4898
 
4512
4899
  // src/task/PoseLandmarkerTask.ts
4513
4900
  import { CreateWorkflow as CreateWorkflow29, Workflow as Workflow29 } from "@workglow/task-graph";
4514
- var modelSchema19 = TypeModel("model:PoseLandmarkerTask");
4901
+ var modelSchema20 = TypeModel("model:PoseLandmarkerTask");
4515
4902
  var TypePoseLandmark = {
4516
4903
  type: "object",
4517
4904
  properties: {
@@ -4590,7 +4977,7 @@ var PoseLandmarkerInputSchema = {
4590
4977
  type: "object",
4591
4978
  properties: {
4592
4979
  image: TypeImageInput,
4593
- model: modelSchema19,
4980
+ model: modelSchema20,
4594
4981
  numPoses: {
4595
4982
  type: "number",
4596
4983
  minimum: 1,
@@ -4889,7 +5276,7 @@ import { CreateWorkflow as CreateWorkflow32, Task as Task15, Workflow as Workflo
4889
5276
 
4890
5277
  // src/task/TextClassificationTask.ts
4891
5278
  import { CreateWorkflow as CreateWorkflow31, Workflow as Workflow31 } from "@workglow/task-graph";
4892
- var modelSchema20 = TypeModel("model:TextClassificationTask");
5279
+ var modelSchema21 = TypeModel("model:TextClassificationTask");
4893
5280
  var TextClassificationInputSchema = {
4894
5281
  type: "object",
4895
5282
  properties: {
@@ -4916,7 +5303,7 @@ var TextClassificationInputSchema = {
4916
5303
  description: "The maximum number of categories to return",
4917
5304
  "x-ui-group": "Configuration"
4918
5305
  },
4919
- model: modelSchema20
5306
+ model: modelSchema21
4920
5307
  },
4921
5308
  required: ["text", "model"],
4922
5309
  additionalProperties: false
@@ -5299,12 +5686,13 @@ var structuralParser = (input, config) => {
5299
5686
  Workflow33.prototype.structuralParser = CreateWorkflow33(StructuralParserTask);
5300
5687
 
5301
5688
  // src/task/StructuredGenerationTask.ts
5302
- import { CreateWorkflow as CreateWorkflow34, Workflow as Workflow34 } from "@workglow/task-graph";
5303
- var modelSchema21 = TypeModel("model:StructuredGenerationTask");
5689
+ import { CreateWorkflow as CreateWorkflow34, TaskConfigurationError as TaskConfigurationError4, TaskError, Workflow as Workflow34 } from "@workglow/task-graph";
5690
+ import { compileSchema as compileSchema2 } from "@workglow/util/schema";
5691
+ var modelSchema22 = TypeModel("model:StructuredGenerationTask");
5304
5692
  var StructuredGenerationInputSchema = {
5305
5693
  type: "object",
5306
5694
  properties: {
5307
- model: modelSchema21,
5695
+ model: modelSchema22,
5308
5696
  prompt: {
5309
5697
  type: "string",
5310
5698
  title: "Prompt",
@@ -5331,6 +5719,15 @@ var StructuredGenerationInputSchema = {
5331
5719
  minimum: 0,
5332
5720
  maximum: 2,
5333
5721
  "x-ui-group": "Configuration"
5722
+ },
5723
+ maxRetries: {
5724
+ type: "integer",
5725
+ title: "Max Retries",
5726
+ description: "Number of times to re-prompt the model with validation errors when its output doesn't match the schema. 0 disables retries (fail on first mismatch).",
5727
+ minimum: 0,
5728
+ maximum: 10,
5729
+ default: 2,
5730
+ "x-ui-group": "Configuration"
5334
5731
  }
5335
5732
  },
5336
5733
  required: ["model", "prompt", "outputSchema"],
@@ -5352,17 +5749,111 @@ var StructuredGenerationOutputSchema = {
5352
5749
  additionalProperties: false
5353
5750
  };
5354
5751
 
5752
+ class StructuredOutputValidationError extends TaskError {
5753
+ static type = "StructuredOutputValidationError";
5754
+ attempts;
5755
+ constructor(attempts) {
5756
+ const last = attempts[attempts.length - 1];
5757
+ const summary = last?.errors.map((e) => `${e.path || "/"}: ${e.message}`).join("; ") ?? "";
5758
+ super(`StructuredGenerationTask: model output failed validation after ${attempts.length} attempt(s). ` + `Last errors: ${summary}`);
5759
+ this.attempts = attempts;
5760
+ }
5761
+ }
5762
+ function validationErrorsFromSchemaNode(result) {
5763
+ return result.errors.map((e) => ({ path: e.data.pointer || "", message: e.message }));
5764
+ }
5765
+ function buildRetryPrompt(originalPrompt, errors) {
5766
+ const errorList = errors.map((e) => ` - ${e.path || "/"}: ${e.message}`).join(`
5767
+ `);
5768
+ return `${originalPrompt}
5769
+
5770
+ ` + `Your previous response did not conform to the required JSON schema. ` + `Validation errors:
5771
+ ${errorList}
5772
+
5773
+ ` + `Please respond again with a JSON object that satisfies the schema. ` + `Output ONLY the JSON object, no other text.`;
5774
+ }
5775
+
5355
5776
  class StructuredGenerationTask extends StreamingAiTask {
5356
5777
  static type = "StructuredGenerationTask";
5357
5778
  static category = "AI Text Model";
5358
5779
  static title = "Structured Generation";
5359
- static description = "Generates structured JSON output conforming to a provided schema using language models";
5780
+ static description = "Generates structured JSON output conforming to a provided schema using language models, with automatic validation and retry on mismatch";
5360
5781
  static inputSchema() {
5361
5782
  return StructuredGenerationInputSchema;
5362
5783
  }
5363
5784
  static outputSchema() {
5364
5785
  return StructuredGenerationOutputSchema;
5365
5786
  }
5787
+ async* executeStream(input, context) {
5788
+ let validator;
5789
+ try {
5790
+ validator = compileSchema2(input.outputSchema);
5791
+ } catch (err2) {
5792
+ const msg = err2 instanceof Error ? err2.message : String(err2);
5793
+ const configErr = new TaskConfigurationError4(`StructuredGenerationTask: invalid outputSchema \u2014 ${msg}`);
5794
+ configErr.taskType = this.type;
5795
+ configErr.taskId = this.id;
5796
+ throw configErr;
5797
+ }
5798
+ const maxRetries = Math.max(0, input.maxRetries ?? 2);
5799
+ const maxAttempts = maxRetries + 1;
5800
+ const attempts = [];
5801
+ let currentInput = input;
5802
+ for (let attempt = 1;attempt <= maxAttempts; attempt++) {
5803
+ let lastObject;
5804
+ for await (const event of super.executeStream(currentInput, context)) {
5805
+ if (event.type === "object-delta" && event.port === "object") {
5806
+ const delta = event.objectDelta;
5807
+ if (delta && !Array.isArray(delta)) {
5808
+ lastObject = delta;
5809
+ }
5810
+ yield event;
5811
+ } else if (event.type === "finish") {
5812
+ const data = event.data;
5813
+ const finalObject = data?.object ?? lastObject ?? {};
5814
+ const result = validator.validate(finalObject);
5815
+ if (result.valid) {
5816
+ yield {
5817
+ type: "finish",
5818
+ data: { object: finalObject }
5819
+ };
5820
+ return;
5821
+ }
5822
+ const errors = validationErrorsFromSchemaNode(result);
5823
+ attempts.push({ attempt, errors, object: finalObject });
5824
+ lastObject = finalObject;
5825
+ break;
5826
+ } else {
5827
+ yield event;
5828
+ }
5829
+ }
5830
+ if (attempt < maxAttempts) {
5831
+ yield {
5832
+ type: "object-delta",
5833
+ port: "object",
5834
+ objectDelta: {}
5835
+ };
5836
+ const lastErrors = attempts[attempts.length - 1].errors;
5837
+ currentInput = {
5838
+ ...input,
5839
+ prompt: buildRetryPrompt(input.prompt, lastErrors)
5840
+ };
5841
+ }
5842
+ }
5843
+ const err = new StructuredOutputValidationError(attempts);
5844
+ err.taskType = this.type;
5845
+ err.taskId = this.id;
5846
+ throw err;
5847
+ }
5848
+ async execute(input, context) {
5849
+ let result;
5850
+ for await (const event of this.executeStream(input, context)) {
5851
+ if (event.type === "finish") {
5852
+ result = event.data;
5853
+ }
5854
+ }
5855
+ return result;
5856
+ }
5366
5857
  }
5367
5858
  var structuredGeneration = (input, config) => {
5368
5859
  return new StructuredGenerationTask(config).run(input);
@@ -5623,7 +6114,7 @@ Workflow35.prototype.textChunker = CreateWorkflow35(TextChunkerTask);
5623
6114
 
5624
6115
  // src/task/TextFillMaskTask.ts
5625
6116
  import { CreateWorkflow as CreateWorkflow36, Workflow as Workflow36 } from "@workglow/task-graph";
5626
- var modelSchema22 = TypeModel("model:TextFillMaskTask");
6117
+ var modelSchema23 = TypeModel("model:TextFillMaskTask");
5627
6118
  var TextFillMaskInputSchema = {
5628
6119
  type: "object",
5629
6120
  properties: {
@@ -5632,7 +6123,7 @@ var TextFillMaskInputSchema = {
5632
6123
  title: "Text",
5633
6124
  description: "The text with a mask token to fill"
5634
6125
  },
5635
- model: modelSchema22
6126
+ model: modelSchema23
5636
6127
  },
5637
6128
  required: ["text", "model"],
5638
6129
  additionalProperties: false
@@ -5697,11 +6188,11 @@ var generatedTextSchema2 = {
5697
6188
  description: "The generated text",
5698
6189
  "x-stream": "append"
5699
6190
  };
5700
- var modelSchema23 = TypeModel("model:TextGenerationTask");
6191
+ var modelSchema24 = TypeModel("model:TextGenerationTask");
5701
6192
  var TextGenerationInputSchema = {
5702
6193
  type: "object",
5703
6194
  properties: {
5704
- model: modelSchema23,
6195
+ model: modelSchema24,
5705
6196
  prompt: {
5706
6197
  type: "string",
5707
6198
  title: "Prompt",
@@ -5779,7 +6270,7 @@ Workflow37.prototype.textGeneration = CreateWorkflow37(TextGenerationTask);
5779
6270
 
5780
6271
  // src/task/TextLanguageDetectionTask.ts
5781
6272
  import { CreateWorkflow as CreateWorkflow38, Workflow as Workflow38 } from "@workglow/task-graph";
5782
- var modelSchema24 = TypeModel("model:TextLanguageDetectionTask");
6273
+ var modelSchema25 = TypeModel("model:TextLanguageDetectionTask");
5783
6274
  var TextLanguageDetectionInputSchema = {
5784
6275
  type: "object",
5785
6276
  properties: {
@@ -5796,7 +6287,7 @@ var TextLanguageDetectionInputSchema = {
5796
6287
  title: "Max Languages",
5797
6288
  description: "The maximum number of languages to return"
5798
6289
  },
5799
- model: modelSchema24
6290
+ model: modelSchema25
5800
6291
  },
5801
6292
  required: ["text", "model"],
5802
6293
  additionalProperties: false
@@ -5866,13 +6357,13 @@ var textSchema = {
5866
6357
  description: "The generated text",
5867
6358
  "x-stream": "append"
5868
6359
  };
5869
- var modelSchema25 = TypeModel("model:TextQuestionAnswerTask");
6360
+ var modelSchema26 = TypeModel("model:TextQuestionAnswerTask");
5870
6361
  var TextQuestionAnswerInputSchema = {
5871
6362
  type: "object",
5872
6363
  properties: {
5873
6364
  context: contextSchema,
5874
6365
  question: questionSchema,
5875
- model: modelSchema25
6366
+ model: modelSchema26
5876
6367
  },
5877
6368
  required: ["context", "question", "model"],
5878
6369
  additionalProperties: false
@@ -5905,7 +6396,7 @@ Workflow39.prototype.textQuestionAnswer = CreateWorkflow39(TextQuestionAnswerTas
5905
6396
 
5906
6397
  // src/task/TextRewriterTask.ts
5907
6398
  import { CreateWorkflow as CreateWorkflow40, Workflow as Workflow40 } from "@workglow/task-graph";
5908
- var modelSchema26 = TypeModel("model:TextRewriterTask");
6399
+ var modelSchema27 = TypeModel("model:TextRewriterTask");
5909
6400
  var TextRewriterInputSchema = {
5910
6401
  type: "object",
5911
6402
  properties: {
@@ -5919,7 +6410,7 @@ var TextRewriterInputSchema = {
5919
6410
  title: "Prompt",
5920
6411
  description: "The prompt to direct the rewriting"
5921
6412
  },
5922
- model: modelSchema26
6413
+ model: modelSchema27
5923
6414
  },
5924
6415
  required: ["text", "prompt", "model"],
5925
6416
  additionalProperties: false
@@ -5957,7 +6448,7 @@ Workflow40.prototype.textRewriter = CreateWorkflow40(TextRewriterTask);
5957
6448
 
5958
6449
  // src/task/TextTranslationTask.ts
5959
6450
  import { CreateWorkflow as CreateWorkflow41, Workflow as Workflow41 } from "@workglow/task-graph";
5960
- var modelSchema27 = TypeModel("model:TextTranslationTask");
6451
+ var modelSchema28 = TypeModel("model:TextTranslationTask");
5961
6452
  var translationTextSchema = {
5962
6453
  type: "string",
5963
6454
  title: "Text",
@@ -5984,7 +6475,7 @@ var TextTranslationInputSchema = {
5984
6475
  minLength: 2,
5985
6476
  maxLength: 2
5986
6477
  }),
5987
- model: modelSchema27
6478
+ model: modelSchema28
5988
6479
  },
5989
6480
  required: ["text", "source_lang", "target_lang", "model"],
5990
6481
  additionalProperties: false
@@ -6106,11 +6597,11 @@ var ToolCallSchema = {
6106
6597
  required: ["id", "name", "input"],
6107
6598
  additionalProperties: false
6108
6599
  };
6109
- var modelSchema28 = TypeModel("model:ToolCallingTask");
6600
+ var modelSchema29 = TypeModel("model:ToolCallingTask");
6110
6601
  var ToolCallingInputSchema = {
6111
6602
  type: "object",
6112
6603
  properties: {
6113
- model: modelSchema28,
6604
+ model: modelSchema29,
6114
6605
  prompt: {
6115
6606
  oneOf: [
6116
6607
  { type: "string", title: "Prompt", description: "The prompt to send to the model" },
@@ -6145,15 +6636,7 @@ var ToolCallingInputSchema = {
6145
6636
  type: "array",
6146
6637
  title: "Messages",
6147
6638
  description: "Full conversation history for multi-turn interactions. When provided, used instead of prompt to construct the messages array sent to the provider.",
6148
- items: {
6149
- type: "object",
6150
- properties: {
6151
- role: { type: "string", enum: ["user", "assistant", "tool"] },
6152
- content: {}
6153
- },
6154
- required: ["role", "content"],
6155
- additionalProperties: true
6156
- }
6639
+ items: ChatMessageSchema
6157
6640
  },
6158
6641
  tools: {
6159
6642
  type: "array",
@@ -6549,11 +7032,11 @@ Workflow43.prototype.topicSegmenter = CreateWorkflow43(TopicSegmenterTask);
6549
7032
 
6550
7033
  // src/task/UnloadModelTask.ts
6551
7034
  import { CreateWorkflow as CreateWorkflow44, Workflow as Workflow44 } from "@workglow/task-graph";
6552
- var modelSchema29 = TypeModel("model");
7035
+ var modelSchema30 = TypeModel("model");
6553
7036
  var UnloadModelInputSchema = {
6554
7037
  type: "object",
6555
7038
  properties: {
6556
- model: modelSchema29
7039
+ model: modelSchema30
6557
7040
  },
6558
7041
  required: ["model"],
6559
7042
  additionalProperties: false
@@ -6561,7 +7044,7 @@ var UnloadModelInputSchema = {
6561
7044
  var UnloadModelOutputSchema = {
6562
7045
  type: "object",
6563
7046
  properties: {
6564
- model: modelSchema29
7047
+ model: modelSchema30
6565
7048
  },
6566
7049
  required: ["model"],
6567
7050
  additionalProperties: false
@@ -6876,7 +7359,6 @@ var similarity = (input, config) => {
6876
7359
  return new VectorSimilarityTask(config).run(input);
6877
7360
  };
6878
7361
  Workflow46.prototype.similarity = CreateWorkflow46(VectorSimilarityTask);
6879
-
6880
7362
  // src/task/MessageConversion.ts
6881
7363
  function getInputMessages(input) {
6882
7364
  const messages = input.messages;
@@ -6925,66 +7407,46 @@ function toOpenAIMessages(input) {
6925
7407
  }
6926
7408
  for (const msg of inputMessages) {
6927
7409
  if (msg.role === "user") {
6928
- if (typeof msg.content === "string") {
6929
- messages.push({ role: "user", content: msg.content });
6930
- } else if (Array.isArray(msg.content) && msg.content.length > 0 && typeof msg.content[0]?.type === "string") {
6931
- const parts = [];
6932
- for (const block of msg.content) {
6933
- const b = block;
6934
- if (b.type === "text") {
6935
- parts.push({ type: "text", text: b.text });
6936
- } else if (b.type === "image") {
6937
- parts.push({
6938
- type: "image_url",
6939
- image_url: { url: `data:${b.mimeType};base64,${b.data}` }
6940
- });
6941
- } else if (b.type === "audio") {
6942
- const format = b.mimeType.replace(/^audio\//, "");
6943
- parts.push({
6944
- type: "input_audio",
6945
- input_audio: { data: b.data, format }
6946
- });
6947
- }
6948
- }
6949
- messages.push({ role: "user", content: parts });
6950
- } else {
6951
- try {
6952
- messages.push({ role: "user", content: JSON.stringify(msg.content) });
6953
- } catch {
6954
- messages.push({ role: "user", content: String(msg.content) });
7410
+ const parts = [];
7411
+ for (const block of msg.content) {
7412
+ if (block.type === "text") {
7413
+ parts.push({ type: "text", text: block.text });
7414
+ } else if (block.type === "image") {
7415
+ parts.push({
7416
+ type: "image_url",
7417
+ image_url: { url: `data:${block.mimeType};base64,${block.data}` }
7418
+ });
6955
7419
  }
6956
7420
  }
7421
+ messages.push({ role: "user", content: parts });
6957
7422
  } else if (msg.role === "assistant") {
6958
- if (typeof msg.content === "string") {
6959
- messages.push({ role: "assistant", content: msg.content.length > 0 ? msg.content : null });
6960
- } else if (Array.isArray(msg.content)) {
6961
- const textParts = msg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
6962
- const toolCalls = msg.content.filter((b) => b.type === "tool_use").map((b) => ({
6963
- id: b.id,
6964
- type: "function",
6965
- function: {
6966
- name: b.name,
6967
- arguments: JSON.stringify(b.input)
6968
- }
6969
- }));
6970
- const entry = {
6971
- role: "assistant",
6972
- content: textParts.length > 0 ? textParts : null
6973
- };
6974
- if (toolCalls.length > 0) {
6975
- entry.tool_calls = toolCalls;
7423
+ const textParts = msg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
7424
+ const toolCalls = msg.content.filter((b) => b.type === "tool_use").map((b) => ({
7425
+ id: b.id,
7426
+ type: "function",
7427
+ function: {
7428
+ name: b.name,
7429
+ arguments: JSON.stringify(b.input)
6976
7430
  }
6977
- messages.push(entry);
7431
+ }));
7432
+ const entry = {
7433
+ role: "assistant",
7434
+ content: textParts.length > 0 ? textParts : null
7435
+ };
7436
+ if (toolCalls.length > 0) {
7437
+ entry.tool_calls = toolCalls;
6978
7438
  }
6979
- } else if (msg.role === "tool" && Array.isArray(msg.content)) {
7439
+ messages.push(entry);
7440
+ } else if (msg.role === "tool") {
6980
7441
  for (const block of msg.content) {
6981
- const b = block;
7442
+ if (block.type !== "tool_result")
7443
+ continue;
6982
7444
  let content;
6983
- if (typeof b.content === "string") {
6984
- content = b.content;
6985
- } else if (Array.isArray(b.content)) {
7445
+ if (block.content.length === 1 && block.content[0].type === "text") {
7446
+ content = block.content[0].text;
7447
+ } else {
6986
7448
  const parts = [];
6987
- for (const inner of b.content) {
7449
+ for (const inner of block.content) {
6988
7450
  if (inner.type === "text") {
6989
7451
  parts.push({ type: "text", text: inner.text });
6990
7452
  } else if (inner.type === "image") {
@@ -6995,14 +7457,8 @@ function toOpenAIMessages(input) {
6995
7457
  }
6996
7458
  }
6997
7459
  content = parts;
6998
- } else {
6999
- content = "";
7000
7460
  }
7001
- messages.push({
7002
- role: "tool",
7003
- content,
7004
- tool_call_id: b.tool_use_id
7005
- });
7461
+ messages.push({ role: "tool", content, tool_call_id: block.tool_use_id });
7006
7462
  }
7007
7463
  }
7008
7464
  }
@@ -7032,41 +7488,18 @@ function toTextFlatMessages(input) {
7032
7488
  }
7033
7489
  for (const msg of inputMessages) {
7034
7490
  if (msg.role === "user") {
7035
- let content = "";
7036
- if (typeof msg.content === "string") {
7037
- content = msg.content;
7038
- } else if (Array.isArray(msg.content) && msg.content.length > 0 && typeof msg.content[0]?.type === "string") {
7039
- content = msg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
7040
- } else if (msg.content != null) {
7041
- try {
7042
- content = JSON.stringify(msg.content);
7043
- } catch {
7044
- content = String(msg.content);
7045
- }
7046
- }
7491
+ const content = msg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
7047
7492
  messages.push({ role: "user", content });
7048
7493
  } else if (msg.role === "assistant") {
7049
- if (typeof msg.content === "string") {
7050
- if (msg.content) {
7051
- messages.push({ role: "assistant", content: msg.content });
7052
- }
7053
- } else if (Array.isArray(msg.content)) {
7054
- const text = msg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
7055
- if (text) {
7056
- messages.push({ role: "assistant", content: text });
7057
- }
7494
+ const text = msg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
7495
+ if (text) {
7496
+ messages.push({ role: "assistant", content: text });
7058
7497
  }
7059
- } else if (msg.role === "tool" && Array.isArray(msg.content)) {
7498
+ } else if (msg.role === "tool") {
7060
7499
  for (const block of msg.content) {
7061
- const b = block;
7062
- let content;
7063
- if (typeof b.content === "string") {
7064
- content = b.content;
7065
- } else if (Array.isArray(b.content)) {
7066
- content = b.content.filter((inner) => inner.type === "text").map((inner) => inner.text).join("");
7067
- } else {
7068
- content = "";
7069
- }
7500
+ if (block.type !== "tool_result")
7501
+ continue;
7502
+ const content = block.content.filter((inner) => inner.type === "text").map((inner) => inner.text).join("");
7070
7503
  messages.push({ role: "tool", content });
7071
7504
  }
7072
7505
  }
@@ -7077,6 +7510,7 @@ function toTextFlatMessages(input) {
7077
7510
  // src/task/index.ts
7078
7511
  var registerAiTasks = () => {
7079
7512
  const tasks = [
7513
+ AiChatTask,
7080
7514
  BackgroundRemovalTask,
7081
7515
  ChunkToVectorTask,
7082
7516
  CountTokensTask,
@@ -7140,6 +7574,7 @@ export {
7140
7574
  textRewriter,
7141
7575
  textQuestionAnswer,
7142
7576
  textNamedEntityRecognition,
7577
+ textMessage,
7143
7578
  textLanguageDetection,
7144
7579
  textGeneration,
7145
7580
  textFillMask,
@@ -7161,6 +7596,8 @@ export {
7161
7596
  modelSearch,
7162
7597
  modelInfo,
7163
7598
  kbToDocuments,
7599
+ isContentBlock,
7600
+ isChatMessage,
7164
7601
  isAllowedToolName,
7165
7602
  imageToText,
7166
7603
  imageSegmentation,
@@ -7234,6 +7671,7 @@ export {
7234
7671
  TextClassificationOutputSchema,
7235
7672
  TextClassificationInputSchema,
7236
7673
  TextChunkerTask,
7674
+ StructuredOutputValidationError,
7237
7675
  StructuredGenerationTask,
7238
7676
  StructuredGenerationOutputSchema,
7239
7677
  StructuredGenerationInputSchema,
@@ -7297,19 +7735,24 @@ export {
7297
7735
  CountTokensInputSchema,
7298
7736
  ContextFormat,
7299
7737
  ContextBuilderTask,
7738
+ ContentBlockSchema,
7300
7739
  ChunkingStrategy,
7301
7740
  ChunkVectorUpsertTask,
7302
7741
  ChunkVectorSearchTask,
7303
7742
  ChunkVectorHybridSearchTask,
7304
7743
  ChunkToVectorTask,
7305
7744
  ChunkRetrievalTask,
7745
+ ChatMessageSchema,
7306
7746
  BackgroundRemovalTask,
7307
7747
  BackgroundRemovalOutputSchema,
7308
7748
  BackgroundRemovalInputSchema,
7309
7749
  AiTask,
7310
7750
  AiProviderRegistry,
7311
7751
  AiProvider,
7312
- AiJob
7752
+ AiJob,
7753
+ AiChatTask,
7754
+ AiChatOutputSchema,
7755
+ AiChatInputSchema
7313
7756
  };
7314
7757
 
7315
- //# debugId=F0E9DA50FF60D85564756E2164756E21
7758
+ //# debugId=4A4F2C3AAA2FA8C064756E2164756E21