Ver Fonte

feat(nodes): expose content hash and resolve images from nested input

Two changes that let a node sit between a binary fetch and a consumer.

http-request already hashed the payload for stored downloads but kept the
result private. The SHA-256 is now returned as result.checksum and on the
file object, so downstream nodes can deduplicate on content without
re-hashing the image.

ollama-chat only looked for image data at input and input.data. Inside a
loop, or whenever another node sits between the fetch and the model call,
the file object is nested deeper than that and was missed. It now walks the
input breadth-first to find it, the same approach the workflow's code nodes
already needed.
fszontagh há 1 mês atrás
pai
commit
617db18e4c
2 ficheiros alterados com 34 adições e 4 exclusões
  1. 22 1
      nodes/ai/ollama-chat.js
  2. 12 3
      nodes/core/http-request.js

+ 22 - 1
nodes/ai/ollama-chat.js

@@ -202,7 +202,28 @@ function findImage(input, override) {
         }
     }
 
-    const roots = [input, input && input.data];
+    // Collect candidate roots breadth-first: inside a loop, or when another node
+    // sits between the fetch and this one, the file object is nested rather than
+    // sitting at input or input.data.
+    const roots = [];
+    const queue = [input];
+    let guard = 0;
+    while (queue.length > 0 && guard < 64) {
+        guard++;
+        const node = queue.shift();
+        if (!node || typeof node !== 'object') {
+            continue;
+        }
+        roots.push(node);
+        const keys = Object.keys(node);
+        for (let k = 0; k < keys.length; k++) {
+            const child = node[keys[k]];
+            if (child && typeof child === 'object' && keys[k] !== 'file') {
+                queue.push(child);
+            }
+        }
+    }
+
     for (let i = 0; i < roots.length; i++) {
         const root = roots[i];
         if (!root || typeof root !== 'object') {

+ 12 - 3
nodes/core/http-request.js

@@ -101,6 +101,7 @@ const outputSchema = {
   type: 'object',
   properties: {
     url: { type: 'string', description: 'Resolved request URL after interpolation' },
+    checksum: { type: 'string', description: 'SHA-256 content hash (binary mode only)' },
     statusCode: { type: 'number' },
     headers: { type: 'object' },
     body: { type: 'any' },
@@ -113,7 +114,8 @@ const outputSchema = {
         data: { type: 'string', description: 'Base64-encoded content' },
         mimeType: { type: 'string' },
         filename: { type: 'string' },
-        size: { type: 'number' }
+        size: { type: 'number' },
+        checksum: { type: 'string', description: 'SHA-256 of the base64 payload' }
       }
     },
     storage: {
@@ -312,14 +314,21 @@ async function execute(config, input, context) {
       const filename = extractFilename(responseHeaders, url);
       const mimeType = contentType.split(';')[0].trim() || 'application/octet-stream';
 
+      // Content hash of the payload, so downstream nodes can deduplicate without
+      // re-hashing the image themselves.
+      const contentHash = smartbotic.utils.sha256(base64Data);
+
       const fileObject = {
         type: 'binary',
         data: base64Data,
         mimeType: mimeType,
         filename: filename,
-        size: byteLength
+        size: byteLength,
+        checksum: contentHash
       };
 
+      result.checksum = contentHash;
+
       result.file = fileObject;
       result.body = null; // Don't include raw body for binary
 
@@ -329,7 +338,7 @@ async function execute(config, input, context) {
         const ttlHours = config.downloadTtlHours || 0;
         const ttlMs = ttlHours > 0 ? ttlHours * 60 * 60 * 1000 : 0;
 
-        const checksum = smartbotic.utils.sha256(base64Data);
+        const checksum = contentHash;
 
         const storageDoc = {
           filename: filename,