Skip to content

feature: add Bilig WorkPaper formula readback tool#6427

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

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

Conversation

@gregkonush
Copy link
Copy Markdown

@gregkonush gregkonush commented May 23, 2026

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 readback object. This gives agents a spreadsheet-backed calculation tool without Excel, Google Sheets, browser automation, or trusting stale cached formula values.

Reviewer follow-up

Addressed the Gemini review feedback in the latest branch commit:

  • uses loose nullish checks for optional numeric input
  • rejects null, arrays, and non-object JSON responses before reading fields
  • guards nested checks, before, and after objects before building the compact result
  • keeps error output compact for unverified Bilig responses

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
git diff --check
node - <<'NODE'
const fs = require('fs');
const tool = JSON.parse(fs.readFileSync('packages/server/marketplaces/tools/Bilig WorkPaper Formula Readback.json', 'utf8'));
if (!tool.func.includes('proof == null')) throw new Error('missing loose nullish proof object guard');
if (!tool.func.includes('$value == null')) throw new Error('missing loose nullish value guard');
console.log('Flowise Bilig tool JSON/schema/nullish guards ok');
NODE
npm view @bilig/workpaper version
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json

npm view @bilig/workpaper version returned 0.131.2.

The public Bilig evaluator returned verified: true for the current package.

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