Skip to content

feature: add Bilig WorkPaper formula readback tool#6427

Open
gregkonush wants to merge 2 commits into
FlowiseAI:mainfrom
gregkonush:feature/bilig-workpaper-formula-readback
Open

feature: add Bilig WorkPaper formula readback tool#6427
gregkonush wants to merge 2 commits into
FlowiseAI:mainfrom
gregkonush:feature/bilig-workpaper-formula-readback

Conversation

@gregkonush
Copy link
Copy Markdown

Description

Adds a Flowise marketplace custom tool for Bilig WorkPaper formula readback.

The tool writes one forecast input cell, calls Bilig to recalculate formulas server-side, rejects unverified responses, and returns a compact before/after proof object. This gives agents a spreadsheet-backed calculation tool without Excel, Google Sheets, browser automation, or trusting stale cached formula values.

Validation

  • jq empty 'packages/server/marketplaces/tools/Bilig WorkPaper Formula Readback.json'
  • jq -r '.schema' 'packages/server/marketplaces/tools/Bilig WorkPaper Formula Readback.json' | jq empty
  • Executed the embedded func from the marketplace JSON with Node against https://bilig.proompteng.ai; verified verified: true and after.expectedArr: 96000
  • Checked marketplace tool names for duplicates

Notes

This is a marketplace tool template only. It does not add dependencies or change Flowise runtime code.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new tool, bilig_workpaper_formula_readback, which enables agents to edit spreadsheet inputs and verify formula readbacks via the Bilig API. The review feedback identifies opportunities to improve the robustness of the tool's JavaScript implementation, specifically by adding a null check for the parsed JSON response to prevent potential runtime errors and adopting more idiomatic loose equality checks for nullish values.

"color": "linear-gradient(rgb(24,96,122), rgb(48,159,108))",
"iconSrc": "https://github.com/proompteng.png",
"schema": "[{\"id\":0,\"property\":\"baseUrl\",\"description\":\"Bilig base URL. Use https://bilig.proompteng.ai for the hosted demo or your self-hosted Bilig URL.\",\"type\":\"string\",\"required\":false},{\"id\":1,\"property\":\"sheetName\",\"description\":\"Worksheet containing the editable forecast input. Default: Inputs\",\"type\":\"string\",\"required\":false},{\"id\":2,\"property\":\"address\",\"description\":\"A1-style input cell address to edit. Default: B3\",\"type\":\"string\",\"required\":false},{\"id\":3,\"property\":\"value\",\"description\":\"Numeric value to write before formula readback. Default: 0.4\",\"type\":\"number\",\"required\":false}]",
"func": "const fetch = require('node-fetch');\n\nconst baseUrl = String($baseUrl || 'https://bilig.proompteng.ai').replace(/\\/$/, '');\nconst sheetName = String($sheetName || 'Inputs');\nconst address = String($address || 'B3').toUpperCase();\nconst value = $value === undefined || $value === null || $value === '' ? 0.4 : Number($value);\n\nif (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) {\n return 'Bilig WorkPaper formula readback failed: baseUrl must start with http:// or https://';\n}\n\nif (!Number.isFinite(value)) {\n return `Bilig WorkPaper formula readback failed: value must be numeric, received ${JSON.stringify($value)}`;\n}\n\nconst response = await fetch(`${baseUrl}/api/workpaper/n8n/forecast`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n 'User-Agent': 'Flowise-Bilig-WorkPaper-Tool/0.1'\n },\n body: JSON.stringify({ sheetName, address, value })\n});\n\nconst text = await response.text();\nlet proof;\ntry {\n proof = JSON.parse(text);\n} catch (error) {\n return `Bilig WorkPaper formula readback failed: expected JSON but received ${text.slice(0, 500)}`;\n}\n\nif (!response.ok) {\n return `Bilig WorkPaper formula readback failed: HTTP ${response.status} ${response.statusText} - ${JSON.stringify(proof)}`;\n}\n\nif (proof.verified !== true) {\n return `Bilig WorkPaper formula readback failed: unverified response ${JSON.stringify(proof)}`;\n}\n\nconst checks = proof.checks || {};\nconst before = proof.before || {};\nconst after = proof.after || {};\nconst compactProof = {\n verified: true,\n editedCell: proof.editedCell,\n before: {\n expectedArr: before.expectedArr,\n targetGap: before.targetGap\n },\n after: {\n expectedArr: after.expectedArr,\n targetGap: after.targetGap\n },\n checks: {\n formulasPersisted: checks.formulasPersisted === true,\n restoredMatchesAfter: checks.restoredMatchesAfter === true,\n computedOutputChanged: checks.computedOutputChanged === true\n },\n source: 'Bilig WorkPaper',\n github: 'https://github.com/proompteng/bilig'\n};\n\nreturn JSON.stringify(compactProof, null, 2);"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There are two improvements for the func implementation:

  1. Defensive Programming: JSON.parse() can return null (e.g., if the response body is the string "null"). Adding a check for proof before accessing proof.verified prevents a potential TypeError.
  2. Idiomatic Style: Per the repository's general rules, use loose equality (== null) for nullish checks instead of checking undefined and null separately.

Additionally, ensure that the proof object is checked for existence before accessing properties like editedCell.

Suggested change
"func": "const fetch = require('node-fetch');\n\nconst baseUrl = String($baseUrl || 'https://bilig.proompteng.ai').replace(/\\/$/, '');\nconst sheetName = String($sheetName || 'Inputs');\nconst address = String($address || 'B3').toUpperCase();\nconst value = $value === undefined || $value === null || $value === '' ? 0.4 : Number($value);\n\nif (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) {\n return 'Bilig WorkPaper formula readback failed: baseUrl must start with http:// or https://';\n}\n\nif (!Number.isFinite(value)) {\n return `Bilig WorkPaper formula readback failed: value must be numeric, received ${JSON.stringify($value)}`;\n}\n\nconst response = await fetch(`${baseUrl}/api/workpaper/n8n/forecast`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n 'User-Agent': 'Flowise-Bilig-WorkPaper-Tool/0.1'\n },\n body: JSON.stringify({ sheetName, address, value })\n});\n\nconst text = await response.text();\nlet proof;\ntry {\n proof = JSON.parse(text);\n} catch (error) {\n return `Bilig WorkPaper formula readback failed: expected JSON but received ${text.slice(0, 500)}`;\n}\n\nif (!response.ok) {\n return `Bilig WorkPaper formula readback failed: HTTP ${response.status} ${response.statusText} - ${JSON.stringify(proof)}`;\n}\n\nif (proof.verified !== true) {\n return `Bilig WorkPaper formula readback failed: unverified response ${JSON.stringify(proof)}`;\n}\n\nconst checks = proof.checks || {};\nconst before = proof.before || {};\nconst after = proof.after || {};\nconst compactProof = {\n verified: true,\n editedCell: proof.editedCell,\n before: {\n expectedArr: before.expectedArr,\n targetGap: before.targetGap\n },\n after: {\n expectedArr: after.expectedArr,\n targetGap: after.targetGap\n },\n checks: {\n formulasPersisted: checks.formulasPersisted === true,\n restoredMatchesAfter: checks.restoredMatchesAfter === true,\n computedOutputChanged: checks.computedOutputChanged === true\n },\n source: 'Bilig WorkPaper',\n github: 'https://github.com/proompteng/bilig'\n};\n\nreturn JSON.stringify(compactProof, null, 2);"
"func": "const fetch = require('node-fetch');\n\nconst baseUrl = String($baseUrl || 'https://bilig.proompteng.ai').replace(/\\/$/, '');\nconst sheetName = String($sheetName || 'Inputs');\nconst address = String($address || 'B3').toUpperCase();\nconst value = ($value == null || $value === '') ? 0.4 : Number($value);\n\nif (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) {\n return 'Bilig WorkPaper formula readback failed: baseUrl must start with http:// or https://';\n}\n\nif (!Number.isFinite(value)) {\n return 'Bilig WorkPaper formula readback failed: value must be numeric, received ' + JSON.stringify($value);\n}\n\nconst response = await fetch(baseUrl + '/api/workpaper/n8n/forecast', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n 'User-Agent': 'Flowise-Bilig-WorkPaper-Tool/0.1'\n },\n body: JSON.stringify({ sheetName, address, value })\n});\n\nconst text = await response.text();\nlet proof;\ntry {\n proof = JSON.parse(text);\n} catch (error) {\n return 'Bilig WorkPaper formula readback failed: expected JSON but received ' + text.slice(0, 500);\n}\n\nif (!response.ok) {\n return 'Bilig WorkPaper formula readback failed: HTTP ' + response.status + ' ' + response.statusText + ' - ' + JSON.stringify(proof);\n}\n\nif (!proof || proof.verified !== true) {\n return 'Bilig WorkPaper formula readback failed: unverified response ' + JSON.stringify(proof);\n}\n\nconst checks = proof.checks || {};\nconst before = proof.before || {};\nconst after = proof.after || {};\nconst compactProof = {\n verified: true,\n editedCell: proof.editedCell,\n before: {\n expectedArr: before.expectedArr,\n targetGap: before.targetGap\n },\n after: {\n expectedArr: after.expectedArr,\n targetGap: after.targetGap\n },\n checks: {\n formulasPersisted: checks.formulasPersisted === true,\n restoredMatchesAfter: checks.restoredMatchesAfter === true,\n computedOutputChanged: checks.computedOutputChanged === true\n },\n source: 'Bilig WorkPaper',\n github: 'https://github.com/proompteng/bilig'\n};\n\nreturn JSON.stringify(compactProof, null, 2);"
References
  1. In JavaScript/TypeScript, use loose equality (== null) as a standard idiom for a 'nullish' check that covers both null and undefined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant