Skip to content

Commit 2f518bb

Browse files
committed
Enable LLMs to query the sheet data
1 parent 1b039ab commit 2f518bb

File tree

2 files changed

+78
-11
lines changed

2 files changed

+78
-11
lines changed

src/Llm.svelte

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@
6666
let responsePromise = $state();
6767
let modelName = $state("Gemini");
6868
let template =
69-
$state(`You modify a spreadsheet by executing JavaScript code. You output JavaScript code in Markdown blocks. You do not output any explanation or comments. You are concise and succinct. You are a technical expert with extensive experience with JavaScript and data science. You query for more information if it would improve your response.
69+
$state(`You modify a spreadsheet by executing JavaScript code. You output JavaScript code in Markdown blocks. You do not output any explanation or comments. You are concise and succinct. You are a technical expert with extensive experience with JavaScript and data science. You query the spreadsheet for more information if it would improve your response.
7070
7171
Formulas begin with an equals sign (\\\`=\\\`), and can contain:
7272
- Numbers such as \\\`123\\\` and \\\`-3.21\\\`
7373
- Strings such as \\\`"asdf"\\\` and \\\`"multi\\\\nline"\\\`
74-
- Singleton references in R1C1 notation such as \\\`R10C3\\\` (zero-indexed) for absolute references, \\\`R[-1]c[2]\\\` for relative references, and \\\`RC\\\` for self-references
74+
- Singleton references in R1C1 notation such as \\\`R10C3\\\` (zero-indexed) and \\\`RC0\\\` for absolute references, \\\`R[-1]c[2]\\\` for relative references, and \\\`RC\\\` for self-references
7575
- Negative absolute references start from the end of a row or column, such as \\\`R-1C-1\\\` to select the cell in the bottom right corner of the sheet, and \\\`R1C0:R1C-1\\\` to select all of row 1
7676
- Ranges such as \\\`R[-3]C:R[-1]C\\\`
7777
- References and ranges across sheets like \\\`S1!R1C1\\\` and \\\`S[1]!R2C2:R2C-1\\\` and \\\`S-1R2C3\\\` (the exclamation point is optional)
@@ -81,11 +81,12 @@ Formulas begin with an equals sign (\\\`=\\\`), and can contain:
8181
Formula function definitions have access to a \\\`this\\\` object with:
8282
- this.row and this.col - readonly
8383
- this.set(value)
84-
- this.element - writable value with the HTML element that will be displayed in the cell (e.g., buttons, checkboxes, canvas, SVG, etc.)
85-
- this.style - writable value with the CSS style string for the containing \\\`<td>\\\`
84+
- this.element - writable with the HTML DOMElement that will be displayed in the cell (e.g., buttons, checkboxes, canvas, SVG, etc.)
85+
- this.style - writable with the CSS style string for the containing \\\`<td>\\\`
8686
8787
You define any formula functions you use that do not already exist. To define formula functions, they must be assigned like: "functions.formula_name = function() {}" in a call to \\\`addFunction\\\`.
8888
89+
8990
The currently available formula functions are all of the JavaScript Math.* functions and: \${Object.keys(formulaFunctions).filter(k => !(k in Math)).join(", ")}.
9091
9192
You can run the following functions:
@@ -94,12 +95,52 @@ You can run the following functions:
9495
return \`llmToolFunctions.\${name}\${args} \${f.description ?? ""}\`;
9596
}).join("\\n- ")}
9697
97-
Available spreadsheets:
98-
\${
99-
globals.sheets.map(
100-
(sheet, i) => \`\${i}. "\${sheet.name}" - \${sheet.heights.length} rows, \${sheet.widths.length} cols\`
101-
).join('\\n')
98+
Querying can only be used to get data from the environment. It cannot be used to ask the user questions. You can query multiple things at once.
99+
100+
101+
Example:
102+
103+
User:
104+
Find the receipt items that contain seafood items and make them red.
105+
106+
Model:
107+
\\\`\\\`\\\`javascript
108+
const sheets = llmToolFunctions.getSheets();
109+
llmToolFunctions.query("Sheets", sheets);
110+
let firstRows = {};
111+
sheets.forEach((sheet, sheetIndex) => {
112+
firstRows[sheet.name] = new Array(sheet.cols).fill().map(
113+
(_, i) => llmToolFunctions.getCellFormula(sheetIndex, 0, i)
114+
)
115+
});
116+
llmToolFunctions.query("First rows", firstRows);
117+
\\\`\\\`\\\`
118+
119+
User:
120+
Sheets: [{"name": "Sheet 1", "rows": 10, "cols": 3}, {"name": "Receipt", "rows": 6, "cols": 2}]
121+
First rows: {"Sheet 1": [null, null, null], "Receipt": ["=BOLD(\\\\"Item\\\\")", "=BOLD(\\\\"Cost\\\\")"]}
122+
123+
Model:
124+
\\\`\\\`\\\`javascript
125+
llmToolFunctions.query("Items", new Array(6).fill().map(
126+
(_, i) => llmToolFunctions.getCellFormula(1, i, 0)
127+
));
128+
\\\`\\\`\\\`
129+
130+
User:
131+
Items: ["=BOLD(\\\\"Items\\\\")", "shrimp", "chicken", "vegetables", "scallops", "cups"]
132+
133+
Model:
134+
\\\`\\\`\\\`javascript
135+
llmToolFunctions.addFunction(\\\`
136+
functions.red = function (s) {
137+
this.style += "color: red;"
138+
return s;
102139
}
140+
\\\`);
141+
llmToolFunctions.setCellFormula(1, 1, 0, \\\`=RED(\\\${llmToolFunctions.getCellFormula(1, 1, 0)})\\\`)
142+
llmToolFunctions.setCellFormula(1, 4, 0, \\\`=RED(\\\${llmToolFunctions.getCellFormula(1, 4, 0)})\\\`)
143+
\\\`\\\`\\\`
103144
`);
104145
let conversation = $state([
105146
{ role: "system", text: "" },
@@ -154,6 +195,20 @@ Available spreadsheets:
154195
155196
function execute(llmCode, i) {
156197
llmToolFunctions.globals = globals;
198+
199+
llmToolFunctions.query = (name, value) => {
200+
let userResponse;
201+
for (
202+
userResponse = conversation[i + 1];
203+
userResponse != null && userResponse.role != "user";
204+
i++
205+
) {}
206+
if (userResponse.text.length && !userResponse.text.endsWith("\n")) {
207+
userResponse.text += "\n";
208+
}
209+
userResponse.text += `${name}: ${JSON.stringify(value)}`;
210+
};
211+
157212
eval(
158213
llmCode +
159214
// Allows user code to show up in the devtools debugger as "llm-code.js"
@@ -210,7 +265,7 @@ Available spreadsheets:
210265
<div style="margin-left: 10%;">
211266
<Details open={part.text.startsWith("Error")}>
212267
{#snippet summary()}System prompt{/snippet}
213-
<pre>{part.text}</pre>
268+
<pre style="white-space: pre;">{part.text}</pre>
214269
</Details>
215270
</div>
216271
{:else if part.role == "user"}

src/llm.svelte.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ llmToolFunctions.getFormulaFunction = function (name) {
3333
return functions[name].toString();
3434
};
3535

36+
llmToolFunctions.getSheets = function () {
37+
return this.globals.sheets.map(({ name, cells }) => ({
38+
name,
39+
rows: cells.length,
40+
cols: cells[0]?.length ?? 0,
41+
}));
42+
};
43+
44+
llmToolFunctions.query = function (value) {
45+
throw new Error("Implemented in Llm.svelte");
46+
};
47+
3648
// TODO: Save models to IndexDB (or local storage)
3749
export const llmModels = $state({});
3850

@@ -51,7 +63,7 @@ llmModels.Gemini = {
5163
parts: conversation
5264
.filter(({ role }) => role == "system")
5365
.map(({ text }) =>
54-
text.split("\n\n").map((s) => ({ text: s.trim() })),
66+
text.split("\n\n\n").map((s) => ({ text: s.trim() })),
5567
)
5668
.flat(Infinity),
5769
},

0 commit comments

Comments
 (0)