| Quick Start Guide | |
|---|---|
| Tutorial Concept | Learn how to connect the SpreadJS AI Assistance add-on to a custom-trained Ollama model so it can generate and explain application-specific spreadsheet formulas. |
| What You Will Need |
|
| Controls Referenced | |
The SpreadJS AI Assistant add-on brings AI-powered formula help directly into JavaScript spreadsheet web app experiences. Two of its most useful capabilities are Formula Generation and Formula Explanation. Formula Generation helps users create spreadsheet formulas from natural language prompts, while Formula Explanation helps users understand what an existing formula does without manually unpacking every function, reference, and operator.
Those features are especially useful in business applications where spreadsheets often contain domain-specific calculations. A finance team might use formulas for commissions, discounts, taxes, margin, or risk. An operations team might use formulas for lead time, inventory thresholds, or capacity planning. These calculations are often wrapped in custom formulas so users can work with business terms instead of long repeated expressions.
Take for example a custom NETPRICE formula:
=NETPRICE([Price],[Discount],[Tax])
The challenge is that a general AI model does not automatically know what your custom spreadsheet formulas mean. If the model has never seen NETPRICE, it may avoid it, explain it incorrectly, or generate a longer formula instead of using the function your application already provides.
Let’s build a solution: connect the SpreadJS AI Assistant add-on to a model that is trained on a custom formula. In this case, we will use a local Ollama model, but this could be done with other LLMs. The sample app uses Vite, SpreadJS, the Formula Editor Panel, and a custom NETPRICE worksheet function so formula generation and explanation can understand the application-specific calculation.
For this sample, "custom trained" means a local Ollama model created from a Modelfile and reinforced with request-time workbook metadata. It is not a full model fine-tune, which keeps the setup simple and repeatable.
How to Connect the SpreadJS AI Assistant to a Custom Formula Model
- Create the Custom Spreadsheet Function
- Put the Function in a Real Workbook Context
- Add the Formula Editor Panel and AI Assistant Add-On
- Connect SpreadJS AI to Your Own Endpoint
- Teach the Model About the Custom Formula
- Create the Local Ollama Model
- How It Works
- Other Model Training Ideas for Spreadsheet Formulas
Download this Finished Sample Application to Follow Along!
What You Will Build
The app contains:
- A SpreadJS workbook with a
Pricingworksheet - A
pricingTabletable with item, price, discount, tax, and net price columns - A custom
NETPRICE(price, discountRate, taxRate)formula - A right-side Formula Editor panel powered by the SpreadJS AI Assistant Add-On
- A local Ollama model named
spreadjs-formula-helper - An Express /api/queryAI proxy that injects custom formula metadata before calling Ollama
What You Will Need
To follow along with the tutorial, please downloaded this sample project. This project requires Node.js and NPM.
Install the sample dependencies, this include the SpreadJS NPM packages:
npm install
Create the local environment file:
cp .env.example .env
or in Powershell on Windows:
Copy-Item .env.example .env
The app uses these local AI defaults:
AI_SERVER_URL=http://localhost:11434/v1
AI_MODEL=spreadjs-formula-helper
AI_API_KEY=ollama
If you haven’t already, install Ollama:
curl -fsSL https://ollama.com/install.sh | sh
or in Powershell on Windows:
irm https://ollama.com/install.ps1 | iex
Download the latest release of SpreadJS, including the AI Assistant Add-on Resources
Step 1: Create the Custom Spreadsheet Function
The sample registers a NETPRICE function with SpreadJS. It accepts a price, a discount rate, and a tax rate, then returns the final amount after discount and tax.
class NetPriceFunction extends GC.Spread.CalcEngine.Functions.Function {
constructor() {
super('NETPRICE', 3, 3, netPriceDescription);
}
evaluate(price: unknown, discountRate: unknown, taxRate: unknown) {
return calculateNetPrice(price, discountRate, taxRate);
}
}
export function registerCustomFunctions(sheet: GC.Spread.Sheets.Worksheet) {
sheet.addCustomFunction(new NetPriceFunction());
}
The key detail is the function description metadata. SpreadJS can use this in formula-related UI, and the app can also pass richer metadata to the model.
const netPriceDescription = {
description:
'Returns the final price after applying a discount rate and then a tax rate.',
parameters: [
{ name: 'price' },
{ name: 'discountRate' },
{ name: 'taxRate' },
],
};
Step 2: Put the Function in a Real Workbook Context
The sample creates a pricing table instead of loose cell ranges. This makes generated formulas easier to read because the model can use structured references.
const pricingTable = sheet.tables.add(
'pricingTable',
0,
0,
pricingRows.length,
pricingRows[0].length,
GC.Spread.Sheets.Tables.TableThemes.medium2,
);
pricingTable.setColumnDataFormula(4, '=NETPRICE([Price],[Discount],[Tax])');
Now the workbook has both the custom function and the business context the AI needs:
Worksheet: Pricing
Table: pricingTable
Columns: Item, Price, Discount, Tax, Net Price
Formula: =NETPRICE([Price],[Discount],[Tax])
Step 3: Add the Formula Editor Panel and AI Assistant Add-On
The client imports the SpreadJS packages and attaches the Formula Editor Panel to the workbook.
import '@mescius/spread-sheets-formula-panel';
import '@mescius/spread-sheets-ai-addon';
const formulaEditor = new GC.Spread.Sheets.FormulaPanel.FormulaEditor(
formulaEditorHost,
);
formulaEditor.attach(workbook);
In this sample, the Formula Editor sits to the right of the spreadsheet. A selected-cell label shows where the generated formula will go, and an Apply button commits the generated formula to the active cell.
function applyFormulaToSelectedCell() {
const formulaText = formulaEditor.text().trim();
if (!formulaText) {
return;
}
formulaEditor.text(formulaText);
formulaEditor.commandManager().execute({ cmd: 'commitContentToActiveCell' });
workbook.focus();
}
Step 4: Connect SpreadJS AI to Your Own Endpoint
The AI add-on is connected through workbook.injectAI. The browser sends the AI request body to the local Express server instead of calling the model directly.
workbook.injectAI(async (requestBody) => {
const response = await fetch('/api/queryAI', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new Error(`AI request failed: ${response.status}`);
}
return response;
});
This keeps model credentials and model-selection logic outside the browser.
Step 5: Teach the Model About the Custom Formula
The most important part of the sample is the custom function catalog. This is the model-facing description of NETPRICE.
export const customFunctionCatalog = [
{
name: 'NETPRICE',
syntax: 'NETPRICE(price, discountRate, taxRate)',
description:
'Returns final price after applying a discount rate and then a tax rate.',
parameters: [
{
name: 'price',
type: 'number',
description: 'Original numeric price before discount or tax.',
},
{
name: 'discountRate',
type: 'number',
description: 'Decimal discount rate, e.g. 0.15 for 15%.',
},
{
name: 'taxRate',
type: 'number',
description: 'Decimal tax rate, e.g. 0.0825 for 8.25%.',
},
],
examples: [
{
formula: '=NETPRICE(100, 0.15, 0.0825)',
result: 92.0125,
explanation:
'100 less 15% discount is 85; 85 plus 8.25% tax is 92.0125.',
},
],
generationGuidance:
'Use NETPRICE when the user asks for final price, discounted price with tax, or price after discount and tax.',
},
];
The server turns that catalog into a system prompt and prepends it to every AI request.
const body = {
...req.body,
model: process.env.AI_MODEL,
messages: [
{ role: 'system', content: customFunctionSystemPrompt() },
...(Array.isArray(req.body.messages) ? req.body.messages : []),
],
};
This lets the AI assistant answer prompts like:
Create a formula for the net price using the pricing table.
With the catalog and workbook context in place, the model can return:
=NETPRICE([Price],[Discount],[Tax])
Step 6: Create the Local Ollama Model
The sample uses qwen3-coder:30b as the base model because formula generation benefits from a code-oriented model. The app-specific model is created with a small Modelfile.
FROM qwen3-coder:30b
PARAMETER temperature 0.1
PARAMETER num_ctx 16384
SYSTEM """
You are a SpreadJS formula assistant.
Generate and explain Excel-compatible formulas for a Vite app using SpreadJS.
When the user asks for a formula, return one valid formula only unless they ask for explanation.
Start generated formulas with = and do not wrap formulas in Markdown.
Use structured table references when they make the formula clearer.
Prefer the app's custom worksheet functions when they fit.
"""
Run these setup commands once per machine:
ollama pull qwen3-coder:30b
ollama create spreadjs-formula-helper -f ai/Modelfile
Run ollama create spreadjs-formula-helper -f ai/Modelfile again only when the model instructions change.
Step 7: Run the Sample
Start Ollama:
ollama serve
Start the Vite app and Express AI proxy:
npm run dev
Open the app:
http://localhost:5173
Select a spreadsheet cell, ask the AI panel for a formula, then apply the generated formula to the selected cell.
Results
After the app is running, the AI Formula panel can use the custom function metadata to understand formulas that are specific to this workbook. One useful test is formula explanation. Select or enter a formula such as:
=NETPRICE([Price],[Discount],[Tax])
Then ask the AI assistant to explain it. Because the backend injects the custom function catalog into the request, the response can describe NETPRICE in business terms: it starts with the original price, applies the discount rate, then applies the tax rate to return the final net price.

The second use case is formula generation. Ask the AI assistant to create a formula for the final price after discount and tax using this prompt:
Using the pricing table, create a formula for net price after discount and tax.
With the workbook context and pricingTable schema available, the assistant can generate a formula that uses the custom function directly:
=NETPRICE([Price],[Discount],[Tax])
After reviewing the generated formula, use the Apply button to place it in the selected cell.

Of course, you can ask if to explain or generate built-in formulas as well. For example, we can ask it to add an Average of Net Prices.

How It Works
- SpreadJS evaluates the NETPRICE custom formula inside the workbook.
- The Formula Editor panel provides a focused AI formula-generation experience.
workbook.injectAIroutes AI requests through the app's backend.- The backend adds workbook context and custom formula metadata to each request.
- Ollama returns formulas that can use both built-in functions and app-specific custom functions.
Why This Pattern Works
You do not need to fine-tune a model just to make it understand a small set of custom spreadsheet functions. For many apps, the simpler pattern is:
- Register the custom function in SpreadJS.
- Maintain a structured catalog with syntax, parameters, examples, and generation guidance.
- Add workbook-specific context such as table names and column names.
- Inject that context into the AI request.
- Use a local model with stable instructions for formula-only responses.
Other Model Training Ideas for Spreadsheet Formulas
This same pattern works for many formula-heavy spreadsheet apps. A finance workbook could teach the model custom formulas for margin, amortization, commissions, or risk scoring. An operations workbook could expose formulas for reorder points, lead-time buffers, shipping estimates, or production capacity. A healthcare or insurance workbook could document domain-specific calculations while keeping the model focused on approved formulas and valid worksheet structures.
The useful training data is usually simple: function names, parameter descriptions, examples, table schemas, expected outputs, and guidance about when each function should be used. Start with metadata and request-time context first. Move to fine-tuning only when you have a larger library of formulas, repeated prompt patterns, and enough reviewed examples to justify maintaining a specialized model.
Install the SpreadJS AI Assistant in your spreadsheet app and start building out your own ideas.
Download the latest release of SpreadJS, including the AI Assistant Add-on Resources