{ "kind": "open-flow-workflow", "version": 1, "name": "Classify Software Keywords in Google Sheets", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_7ec0fb142ecf2be0": { "name": "Classify the keyword worksheet", "kind": "start", "inputDefs": [] }, "n_843591963a1ef1b8": { "inputs": {}, "name": "Choose the keyword worksheet", "kind": "task", "task": { "inputs": [], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1, "description": "The spreadsheet ID." }, "nullable": true, "handle": "spreadsheetId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "sheetName" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to retrieve." }, "nullable": false, "handle": "range" } ], "name": "Choose the keyword worksheet", "moduleId": "n_fd9a71c6439ebb7b", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_a0f9e11480986d99": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_843591963a1ef1b8", "output": "spreadsheetId" } ] }, "range": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_843591963a1ef1b8", "output": "range" } ] }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "UNFORMATTED_VALUE" }, "dateTimeRenderOption": { "kind": "value", "value": "FORMATTED_STRING" } }, "name": "Read every keyword row", "kind": "task", "task": { "inputs": [ { "description": "The spreadsheet ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The spreadsheet ID." }, "nullable": true, "handle": "spreadsheetId", "value": null }, { "description": "The A1 notation of the range to retrieve.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to retrieve." }, "nullable": false, "handle": "range" }, { "description": "The major dimension for the returned values.", "jsonSchema": { "type": "string", "enum": [ "DIMENSION_UNSPECIFIED", "ROWS", "COLUMNS" ], "description": "The major dimension for the returned values." }, "nullable": true, "handle": "majorDimension", "value": null }, { "description": "How values should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA" ], "description": "How values should be rendered in the output." }, "nullable": true, "handle": "valueRenderOption", "value": null }, { "description": "How dates and times should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "SERIAL_NUMBER", "FORMATTED_STRING" ], "description": "How dates and times should be rendered in the output." }, "nullable": true, "handle": "dateTimeRenderOption", "value": null } ], "outputs": [ { "description": "The A1 notation of the range.", "jsonSchema": { "type": "string", "description": "The A1 notation of the range." }, "nullable": false, "handle": "range" }, { "description": "The major dimension of the values (ROWS or COLUMNS).", "jsonSchema": { "type": "string", "description": "The major dimension of the values (ROWS or COLUMNS)." }, "nullable": false, "handle": "majorDimension" }, { "description": "The data values in the range.", "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data values in the range." }, "nullable": false, "handle": "values" } ], "name": "Read every keyword row", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_3f833cce3b78cb1e": { "inputs": { "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a0f9e11480986d99", "output": "values" } ] } }, "name": "Validate the complete keyword table", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data values in the range." }, "nullable": false, "handle": "values" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "rows" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "name": "Validate the complete keyword table", "moduleId": "n_7c29849bf4b7ed11", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_6e44ff3bc638af8d": { "inputs": { "rows": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_3f833cce3b78cb1e", "output": "rows" } ] }, "sheetName": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_843591963a1ef1b8", "output": "sheetName" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6e44ff3bc638af8d", "output": "state" } ] }, "modelResponse": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9c0afede10407a83", "output": "output" } ] }, "currentValues": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_13b59fde6a7a5751", "output": "values" } ] }, "actualCells": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1364ebb2e8d72aa2", "output": "totalUpdatedCells" } ] } }, "name": "Keep every keyword and batch result", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "rows" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "sheetName" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "modelResponse" }, { "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data values in the range." }, "nullable": true, "handle": "currentValues" }, { "jsonSchema": { "type": "integer", "description": "The total number of cells updated across all ranges." }, "nullable": true, "handle": "actualCells" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "phase" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "prompt" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "range": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to write to." }, "values": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data to write into the range." }, "majorDimension": { "type": "string", "enum": [ "ROWS", "COLUMNS" ], "description": "The major dimension of the values array." } }, "required": [ "range", "values" ], "additionalProperties": false, "description": "A batch values update item." }, "description": "The list of value ranges to update." }, "nullable": false, "handle": "data" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "results" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "batches" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "name": "Keep every keyword and batch result", "moduleId": "n_1310888fa1e3e8e3", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_106820e771bca83b": { "inputs": {}, "name": "Wait before the next batch", "kind": "condition", "matchMode": "first", "cases": [ { "output": "wait", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_6e44ff3bc638af8d", "output": "phase" } }, "operator": "==", "right": { "kind": "value", "value": "wait" } } ] } ] } ] }, "n_21d35d88b9e02534": { "inputs": {}, "name": "Another keyword to classify", "kind": "condition", "matchMode": "first", "cases": [ { "output": "classify", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_6e44ff3bc638af8d", "output": "phase" } }, "operator": "==", "right": { "kind": "value", "value": "classify" } } ] } ] } ] }, "n_b4375001239242f4": { "inputs": {}, "name": "Read current Number matches now", "kind": "condition", "matchMode": "first", "cases": [ { "output": "refresh", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_6e44ff3bc638af8d", "output": "phase" } }, "operator": "==", "right": { "kind": "value", "value": "refresh" } } ] } ] } ] }, "n_ca87996e659400d9": { "inputs": {}, "name": "Any matching cells to write", "kind": "condition", "matchMode": "first", "cases": [ { "output": "write", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_6e44ff3bc638af8d", "output": "phase" } }, "operator": "==", "right": { "kind": "value", "value": "write" } } ] } ] } ] }, "n_b10c5b4be6dd4e82": { "inputs": {}, "name": "Wait five seconds for this batch", "kind": "wait", "inputDefinitions": [], "prompt": "Wait five seconds for this batch", "autoContinue": { "kind": "after", "durationMs": 5000 } }, "n_9c0afede10407a83": { "inputs": { "prompt": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6e44ff3bc638af8d", "output": "prompt" } ] } }, "name": "Identify known software names", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "prompt" } ], "outputs": [ { "description": "The final result produced by the Agent.", "jsonSchema": { "type": "object", "properties": { "Isservice": { "type": "string", "enum": [ "yes", "no" ] } }, "required": [ "Isservice" ], "additionalProperties": false }, "nullable": false, "handle": "output" } ], "name": "Identify known software names", "executor": { "kind": "agent", "code": false, "model": "gpt-4o-mini", "prompt": "Check the keyword I provided and define if this keyword has a name of the known IT software, service, tool or app as a part of it (for example, ServiceNow or Salesforce) and return yes or no.\n\n{{prompt}}", "maxRounds": 1, "tools": [] } } }, "n_13b59fde6a7a5751": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_843591963a1ef1b8", "output": "spreadsheetId" } ] }, "range": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_843591963a1ef1b8", "output": "range" } ] }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "FORMATTED_VALUE" }, "dateTimeRenderOption": { "kind": "value", "value": "FORMATTED_STRING" } }, "name": "Read current Number matches", "kind": "task", "task": { "inputs": [ { "description": "The spreadsheet ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The spreadsheet ID." }, "nullable": true, "handle": "spreadsheetId", "value": null }, { "description": "The A1 notation of the range to retrieve.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to retrieve." }, "nullable": false, "handle": "range" }, { "description": "The major dimension for the returned values.", "jsonSchema": { "type": "string", "enum": [ "DIMENSION_UNSPECIFIED", "ROWS", "COLUMNS" ], "description": "The major dimension for the returned values." }, "nullable": true, "handle": "majorDimension", "value": null }, { "description": "How values should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA" ], "description": "How values should be rendered in the output." }, "nullable": true, "handle": "valueRenderOption", "value": null }, { "description": "How dates and times should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "SERIAL_NUMBER", "FORMATTED_STRING" ], "description": "How dates and times should be rendered in the output." }, "nullable": true, "handle": "dateTimeRenderOption", "value": null } ], "outputs": [ { "description": "The A1 notation of the range.", "jsonSchema": { "type": "string", "description": "The A1 notation of the range." }, "nullable": false, "handle": "range" }, { "description": "The major dimension of the values (ROWS or COLUMNS).", "jsonSchema": { "type": "string", "description": "The major dimension of the values (ROWS or COLUMNS)." }, "nullable": false, "handle": "majorDimension" }, { "description": "The data values in the range.", "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data values in the range." }, "nullable": false, "handle": "values" } ], "name": "Read current Number matches", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_325fcc81afdd43a5": { "inputs": { "data": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6e44ff3bc638af8d", "output": "data" } ] } }, "name": "Keep the complete write request", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "range": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to write to." }, "values": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data to write into the range." }, "majorDimension": { "type": "string", "enum": [ "ROWS", "COLUMNS" ], "description": "The major dimension of the values array." } }, "required": [ "range", "values" ], "additionalProperties": false, "description": "A batch values update item." }, "description": "The list of value ranges to update." }, "nullable": false, "handle": "data" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "range": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to write to." }, "values": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data to write into the range." }, "majorDimension": { "type": "string", "enum": [ "ROWS", "COLUMNS" ], "description": "The major dimension of the values array." } }, "required": [ "range", "values" ], "additionalProperties": false, "description": "A batch values update item." }, "minItems": 1, "description": "The list of value ranges to update." }, "nullable": false, "handle": "data" } ], "name": "Keep the complete write request", "moduleId": "n_8c1c01c168c63de3", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_1364ebb2e8d72aa2": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_843591963a1ef1b8", "output": "spreadsheetId" } ] }, "valueInputOption": { "kind": "value", "value": "USER_ENTERED" }, "data": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_325fcc81afdd43a5", "output": "data" } ] }, "includeValuesInResponse": { "kind": "value", "value": false } }, "name": "Update this batch of Service cells", "kind": "task", "task": { "inputs": [ { "description": "The spreadsheet ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The spreadsheet ID." }, "nullable": true, "handle": "spreadsheetId", "value": null }, { "description": "The list of value ranges to update.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "range": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to write to." }, "values": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data to write into the range." }, "majorDimension": { "type": "string", "enum": [ "ROWS", "COLUMNS" ], "description": "The major dimension of the values array." } }, "required": [ "range", "values" ], "additionalProperties": false, "description": "A batch values update item." }, "minItems": 1, "description": "The list of value ranges to update." }, "nullable": false, "handle": "data" }, { "description": "How the input data should be interpreted.", "jsonSchema": { "type": "string", "enum": [ "INPUT_VALUE_OPTION_UNSPECIFIED", "RAW", "USER_ENTERED" ], "description": "How the input data should be interpreted." }, "nullable": true, "handle": "valueInputOption", "value": null }, { "description": "Whether to include the written values in the response.", "jsonSchema": { "type": "boolean", "description": "Whether to include the written values in the response." }, "nullable": true, "handle": "includeValuesInResponse", "value": null }, { "description": "How values in the response should be rendered.", "jsonSchema": { "type": "string", "enum": [ "FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA" ], "description": "How values in the response should be rendered." }, "nullable": true, "handle": "responseValueRenderOption", "value": null }, { "description": "How dates in the response should be rendered.", "jsonSchema": { "type": "string", "enum": [ "SERIAL_NUMBER", "FORMATTED_STRING" ], "description": "How dates in the response should be rendered." }, "nullable": true, "handle": "responseDateTimeRenderOption", "value": null } ], "outputs": [ { "description": "The unique ID of the spreadsheet.", "jsonSchema": { "type": "string", "description": "The unique ID of the spreadsheet." }, "nullable": false, "handle": "spreadsheetId" }, { "description": "The total number of rows updated across all ranges.", "jsonSchema": { "type": "integer", "description": "The total number of rows updated across all ranges." }, "nullable": false, "handle": "totalUpdatedRows" }, { "description": "The total number of columns updated across all ranges.", "jsonSchema": { "type": "integer", "description": "The total number of columns updated across all ranges." }, "nullable": false, "handle": "totalUpdatedColumns" }, { "description": "The total number of cells updated across all ranges.", "jsonSchema": { "type": "integer", "description": "The total number of cells updated across all ranges." }, "nullable": false, "handle": "totalUpdatedCells" }, { "description": "The per-range update responses.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "updatedRange": { "type": "string", "description": "The A1 notation of the range that was updated." }, "updatedRows": { "type": "integer", "description": "The number of rows that were updated." }, "updatedColumns": { "type": "integer", "description": "The number of columns that were updated." }, "updatedCells": { "type": "integer", "description": "The number of cells that were updated." }, "updatedData": { "type": "object", "properties": { "range": { "type": "string", "description": "The A1 notation of the range." }, "majorDimension": { "type": "string", "description": "The major dimension of the values (ROWS or COLUMNS)." }, "values": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data values in the range." } }, "required": [ "range", "majorDimension", "values" ], "additionalProperties": false, "description": "The values of the cells after the update." } }, "required": [ "updatedRange", "updatedRows", "updatedColumns", "updatedCells" ], "additionalProperties": false, "description": "The batch update item schema." }, "description": "The per-range update responses." }, "nullable": false, "handle": "responses" } ], "name": "Update this batch of Service cells", "executor": { "kind": "connector", "action": "googlesheets.update_values_batch" } } }, "n_aa4e15703d55a4cb": { "name": "All classified keyword results", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "results" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "batches" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "outputMappings": { "results": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6e44ff3bc638af8d", "output": "results" } ] }, "batches": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6e44ff3bc638af8d", "output": "batches" } ] }, "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6e44ff3bc638af8d", "output": "count" } ] } } } }, "edges": [ { "source": "n_7ec0fb142ecf2be0", "target": "n_843591963a1ef1b8" }, { "source": "n_843591963a1ef1b8", "target": "n_a0f9e11480986d99" }, { "source": "n_a0f9e11480986d99", "target": "n_3f833cce3b78cb1e" }, { "source": "n_3f833cce3b78cb1e", "target": "n_6e44ff3bc638af8d" }, { "source": "n_6e44ff3bc638af8d", "target": "n_106820e771bca83b" }, { "source": "n_106820e771bca83b", "target": "n_b10c5b4be6dd4e82", "sourceHandle": "wait" }, { "source": "n_b10c5b4be6dd4e82", "target": "n_6e44ff3bc638af8d", "sourceHandle": "continue" }, { "source": "n_21d35d88b9e02534", "target": "n_9c0afede10407a83", "sourceHandle": "classify" }, { "source": "n_9c0afede10407a83", "target": "n_6e44ff3bc638af8d" }, { "source": "n_b4375001239242f4", "target": "n_13b59fde6a7a5751", "sourceHandle": "refresh" }, { "source": "n_13b59fde6a7a5751", "target": "n_6e44ff3bc638af8d" }, { "source": "n_ca87996e659400d9", "target": "n_325fcc81afdd43a5", "sourceHandle": "write" }, { "source": "n_325fcc81afdd43a5", "target": "n_1364ebb2e8d72aa2" }, { "source": "n_1364ebb2e8d72aa2", "target": "n_6e44ff3bc638af8d" }, { "source": "n_ca87996e659400d9", "target": "n_aa4e15703d55a4cb", "sourceHandle": "otherwise" }, { "source": "n_106820e771bca83b", "target": "n_21d35d88b9e02534", "sourceHandle": "otherwise" }, { "source": "n_21d35d88b9e02534", "target": "n_b4375001239242f4", "sourceHandle": "otherwise" }, { "source": "n_b4375001239242f4", "target": "n_ca87996e659400d9", "sourceHandle": "otherwise" } ] } }, "modules": { "n_fd9a71c6439ebb7b": { "name": "Choose the keyword worksheet", "imports": [], "source": "export default function(){const spreadsheetId=\"YOUR_SPREADSHEET_ID\",sheetName=\"Sheet1\";return {spreadsheetId,sheetName,range:\"'\"+sheetName.replace(/'/g,\"''\")+\"'\"};}" }, "n_7c29849bf4b7ed11": { "name": "Validate the complete keyword table", "imports": [], "source": "export default function keywordSheetInput({ values }) {\n if (!Array.isArray(values)) throw new Error('Expected the complete keyword worksheet values');\n if (values.length === 0) return { rows: [], count: 0 };\n if (!Array.isArray(values[0])) throw new Error('The keyword worksheet needs a header row');\n const headers = values[0];\n for (const name of ['Number', 'Keyword', 'Service?']) if (headers.filter(value => value === name).length !== 1) throw new Error('The first worksheet row must contain exactly one ' + name + ' header');\n const rows = [];\n for (let rowIndex = 1; rowIndex < values.length; rowIndex++) {\n const cells = values[rowIndex];\n if (!Array.isArray(cells)) throw new Error('Invalid keyword worksheet row ' + (rowIndex + 1));\n if (!cells.some(value => value !== null && value !== undefined && value !== '')) continue;\n const row = Object.create(null);\n for (let column = 0; column < cells.length; column++) {\n const header = headers[column] || 'col_' + column;\n if (!['string', 'number', 'boolean'].includes(typeof header)) throw new Error('Invalid keyword worksheet header');\n row[String(header)] = cells[column] ?? '';\n }\n const number = row.Number, keyword = row.Keyword;\n if (!['string', 'number', 'boolean'].includes(typeof number) || (typeof number === 'string' && !number.trim()) || (typeof number === 'number' && !Number.isFinite(number))) throw new Error('Keyword row ' + (rowIndex + 1) + ' needs a Number match key');\n if (!['string', 'number', 'boolean'].includes(typeof keyword) || (typeof keyword === 'string' && !keyword.trim()) || (typeof keyword === 'number' && !Number.isFinite(keyword))) throw new Error('Keyword row ' + (rowIndex + 1) + ' needs a nonblank scalar Keyword');\n rows.push({ index: rows.length, number, keyword: String(keyword), row });\n }\n return { rows, count: rows.length };\n}\n" }, "n_1310888fa1e3e8e3": { "name": "Keep every keyword and batch result", "imports": [], "source": "function keywordWritePlan({ values, results, sheetName, start }) {\n if (!Array.isArray(values) || !Array.isArray(values[0])) throw new Error('The current keyword worksheet has no header row');\n if (!Array.isArray(results) || results.length < 1 || results.length > 6) throw new Error('Expected a complete classified batch');\n if (typeof sheetName !== 'string' || !sheetName.trim()) throw new Error('Configure the keyword worksheet name');\n const headers = values[0], key = headers.indexOf('Number'), target = headers.indexOf('Service?');\n if (key < 0 || target < 0) throw new Error('The worksheet needs Number and Service? headers');\n const column = index => { let label = ''; for (let n = index + 1; n; n = Math.floor((n - 1) / 26)) label = String.fromCharCode(65 + (n - 1) % 26) + label; return label; };\n const tab = \"'\" + sheetName.replace(/'/g, \"''\") + \"'\", data = [], unmatched = [];\n for (const result of results) {\n if (!result || !Number.isInteger(result.index) || !['string', 'number', 'boolean'].includes(typeof result.number) || !['yes', 'no'].includes(result.classification)) throw new Error('Invalid keyword classification or match key');\n const row = values.findIndex((cells, index) => index > 0 && Array.isArray(cells) && cells[key] !== null && cells[key] !== undefined && String(cells[key]) === String(result.number));\n if (row < 0) unmatched.push(result.index);\n else data.push({ range: tab + '!' + column(target) + (row + 1), values: [[result.classification]] });\n }\n return { data, hasUpdates: data.length > 0, expectedCells: data.length, unmatched, results, start, count: results.length };\n}\n\nfunction keywordWriteReceipt({ results, start, count, expectedCells, unmatched, actualCells }) {\n if (!Array.isArray(results) || results.length !== count || !Number.isInteger(start) || !Number.isInteger(count) || count < 1 || count > 6 || !Array.isArray(unmatched) || !Number.isInteger(expectedCells) || expectedCells !== count - unmatched.length) throw new Error('Invalid keyword batch write plan');\n if (expectedCells > 0 && actualCells !== expectedCells) throw new Error('Google Sheets did not confirm every planned Service? cell; check the worksheet before replaying this batch');\n return { results, start, count, updated: expectedCells, unmatched };\n}\n\nexport default function keywordImportProgress({ rows, sheetName, previousState, modelResponse, currentValues, actualCells }) {\n if (!Array.isArray(rows)) throw new Error('Expected the complete original keyword rows');\n const fresh = previousState === null || previousState === undefined;\n const state = fresh ? { offset: 0, within: 0, stage: rows.length ? 'wait' : 'done', batchResults: [], results: [], batches: [], plan: null } : previousState;\n if (!state || !Number.isInteger(state.offset) || state.offset < 0 || state.offset > rows.length || !Number.isInteger(state.within) || state.within < 0 || state.within > 6 || !Array.isArray(state.batchResults) || !Array.isArray(state.results) || !Array.isArray(state.batches) || !['wait', 'classify', 'refresh', 'write', 'done'].includes(state.stage)) throw new Error('Invalid keyword import state');\n const completeBatch = receipt => {\n state.results.push(...receipt.results);\n state.batches.push({ start: receipt.start, count: receipt.count, updated: receipt.updated, unmatched: receipt.unmatched });\n state.offset += receipt.count;\n state.within = 0;\n state.batchResults = [];\n state.plan = null;\n state.stage = state.offset < rows.length ? 'wait' : 'done';\n };\n if (!fresh) {\n if (state.stage === 'wait') state.stage = 'classify';\n else if (state.stage === 'classify') {\n if (!modelResponse || typeof modelResponse !== 'object' || !['yes', 'no'].includes(modelResponse.Isservice)) throw new Error('The classifier must return Isservice as yes or no; this batch has not been written');\n const row = rows[state.offset + state.within];\n if (!row) throw new Error('Missing original keyword for this classification');\n state.batchResults.push({ ...row, classification: modelResponse.Isservice });\n state.within += 1;\n state.stage = state.within === Math.min(6, rows.length - state.offset) ? 'refresh' : 'classify';\n } else if (state.stage === 'refresh') {\n state.plan = keywordWritePlan({ values: currentValues, results: state.batchResults, sheetName, start: state.offset });\n if (state.plan.hasUpdates) state.stage = 'write';\n else completeBatch(keywordWriteReceipt({ ...state.plan, actualCells: null }));\n } else if (state.stage === 'write') completeBatch(keywordWriteReceipt({ ...state.plan, actualCells }));\n else throw new Error('A completed keyword import must not be restarted inside the same run');\n }\n const row = rows[state.offset + state.within];\n return { state, phase: state.stage, prompt: state.stage === 'classify' ? 'keyword: ' + row.keyword : '', data: state.plan?.data ?? [], results: state.results, batches: state.batches, count: state.results.length };\n}\n" }, "n_8c1c01c168c63de3": { "name": "Keep the complete write request", "imports": [], "source": "export default function({data}){if(!Array.isArray(data)||!data.length)throw new Error(\"Expected nonempty Service? updates\");return {data};}" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }