(core) Modify prompt so that model may say it cannot help with certain requests.

Summary:
This tweaks the prompting so that the user's message is given on its own instead of as a docstring within Python. This is so that the prompt makes sense when:

- the user asks a question such as "Can you write me a formula which does ...?" rather than describing their formula as a docstring would, or
- the user sends a message that doesn't ask for a formula at all (https://grist.slack.com/archives/C0234CPPXPA/p1687699944315069?thread_ts=1687698078.832209&cid=C0234CPPXPA)

Also added wording for the model to refuse when the user asks for something that the model cannot do.

Because the code (and maybe in some cases the model) for non-ChatGPT models relies on the prompt consisting entirely of Python code produced by the data engine (which no longer contains the user's message) those code paths have been disabled for now. Updating them now seems like undesirable drag, I think it'd be better to revisit this when iteration/experimentation has slowed down and stabilised.

Test Plan:
Added entries to the formula dataset where the response shouldn't contain a formula, indicated by the value `1` for the new column `no_formula`.

This is somewhat successful, as the model does refuse to help in some of the new test cases, but not all. Performance on existing entries also seems a bit worse, but it's hard to distinguish this from random noise. Hopefully this can be remedied in the future with more work, e.g. automatic followup messages containing example inputs and outputs.

Reviewers: paulfitz

Reviewed By: paulfitz

Subscribers: dsagal

Differential Revision: https://phab.getgrist.com/D3936
This commit is contained in:
Alex Hall 2023-06-27 13:39:15 +02:00
parent fc16b4c8f6
commit bb7cf6ba20
6 changed files with 126 additions and 118 deletions

View File

@ -46,7 +46,7 @@ export interface AssistanceSchemaPromptV1Context {
/** /**
* A flavor of assistant for use with the OpenAI API. * A flavor of assistant for use with the OpenAI API.
* Tested primarily with text-davinci-002 and gpt-3.5-turbo. * Tested primarily with gpt-3.5-turbo.
*/ */
export class OpenAIAssistant implements Assistant { export class OpenAIAssistant implements Assistant {
private _apiKey: string; private _apiKey: string;
@ -60,8 +60,11 @@ export class OpenAIAssistant implements Assistant {
throw new Error('OPENAI_API_KEY not set'); throw new Error('OPENAI_API_KEY not set');
} }
this._apiKey = apiKey; this._apiKey = apiKey;
this._model = process.env.COMPLETION_MODEL || "text-davinci-002"; this._model = process.env.COMPLETION_MODEL || "gpt-3.5-turbo-0613";
this._chatMode = this._model.includes('turbo'); this._chatMode = this._model.includes('turbo');
if (!this._chatMode) {
throw new Error('Only turbo models are currently supported');
}
this._endpoint = `https://api.openai.com/v1/${this._chatMode ? 'chat/' : ''}completions`; this._endpoint = `https://api.openai.com/v1/${this._chatMode ? 'chat/' : ''}completions`;
} }
@ -72,25 +75,27 @@ export class OpenAIAssistant implements Assistant {
if (messages.length === 0) { if (messages.length === 0) {
messages.push({ messages.push({
role: 'system', role: 'system',
content: 'The user gives you one or more Python classes, ' + content: 'You are a helpful assistant for a user of software called Grist. ' +
'with one last method that needs completing. Write the ' + 'Below are one or more Python classes. ' +
'method body as a single code block, ' + 'The last method needs completing. ' +
'including the docstring the user gave. ' + "The user will probably give a description of what they want the method (a 'formula') to return. " +
'Just give the Python code as a markdown block, ' + 'If so, your response should include the method body as Python code in a markdown block. ' +
'do not give any introduction, that will just be ' + 'Do not include the class or method signature, just the method body. ' +
'awkward for the user when copying and pasting. ' + 'If your code starts with `class`, `@dataclass`, or `def` it will fail. Only give the method body. ' +
'You are working with Grist, an environment very like ' + 'You can import modules inside the method body if needed. ' +
'regular Python except `rec` (like record) is used ' + 'You cannot define additional functions or methods. ' +
'instead of `self`. ' + 'The method should be a pure function that performs some computation and returns a result. ' +
'Include at least one `return` statement or the method ' + 'It CANNOT perform any side effects such as adding/removing/modifying rows/columns/cells/tables/etc. ' +
'will fail, disappointing the user. ' + 'It CANNOT interact with files/databases/networks/etc. ' +
'Your answer should be the body of a single method, ' + 'It CANNOT display images/charts/graphs/maps/etc. ' +
'not a class, and should not include `dataclass` or ' + 'If the user asks for these things, tell them that you cannot help. ' +
'`class` since the user is counting on you to provide ' + 'The method uses `rec` instead of `self` as the first parameter.\n\n' +
'a single method. Thanks!' '```python\n' +
await makeSchemaPromptV1(doc, request) +
'\n```',
}); });
messages.push({ messages.push({
role: 'user', content: await makeSchemaPromptV1(doc, request), role: 'user', content: request.text,
}); });
} else { } else {
if (request.regenerate) { if (request.regenerate) {
@ -257,10 +262,11 @@ function getAssistant() {
if (process.env.OPENAI_API_KEY) { if (process.env.OPENAI_API_KEY) {
return new OpenAIAssistant(); return new OpenAIAssistant();
} }
if (process.env.HUGGINGFACE_API_KEY) { // Maintaining this is too much of a burden for now.
return new HuggingFaceAssistant(); // if (process.env.HUGGINGFACE_API_KEY) {
} // return new HuggingFaceAssistant();
throw new Error('Please set OPENAI_API_KEY or HUGGINGFACE_API_KEY'); // }
throw new Error('Please set OPENAI_API_KEY');
} }
/** /**

View File

@ -1,35 +1,35 @@
# Using Large Language Models with Grist # Using Large Language Models with Grist
In this experimental Grist feature, originally developed by Alex Hall, In this experimental Grist feature, originally developed by Alex Hall,
you can hook up an AI model such as OpenAI's Codex to write formulas for you can hook up OpenAI's ChatGPT to write formulas for
you. Here's how. you. Here's how.
First, you need an API key. You'll have best results currently with an First, you need an API key. Visit https://openai.com/api/ and prepare a key, then
OpenAI model. Visit https://openai.com/api/ and prepare a key, then
store it in an environment variable `OPENAI_API_KEY`. store it in an environment variable `OPENAI_API_KEY`.
Alternatively, there are many non-proprietary models hosted on Hugging Face.
At the time of writing, none can compare with OpenAI for use with Grist.
Things can change quickly in the world of AI though. So instead of OpenAI,
you can visit https://huggingface.co/ and prepare a key, then
store it in an environment variable `HUGGINGFACE_API_KEY`.
That's all the configuration needed! That's all the configuration needed!
Currently it is only a backend feature, we are still working on the UI for it. Currently it is only a backend feature, we are still working on the UI for it.
## Trying other models ## Hugging Face and other OpenAI models (deactivated)
The model used will default to `text-davinci-002` for OpenAI. You can _Not currently available, needs some work to revive. These notes are only preserved as a reminder to ourselves of how this worked._
get better results by setting an environment variable `COMPLETION_MODEL` to
`code-davinci-002` if you have access to that model.
The model used will default to `NovelAI/genji-python-6B` for ~~To use a different OpenAI model such as `code-davinci-002` or `text-davinci-003`,
set the environment variable `COMPLETION_MODEL` to the name of the model.~~
~~Alternatively, there are many non-proprietary models hosted on Hugging Face.
At the time of writing, none can compare with OpenAI for use with Grist.
Things can change quickly in the world of AI though. So instead of OpenAI,
you can visit https://huggingface.co/ and prepare a key, then
store it in an environment variable `HUGGINGFACE_API_KEY`.~~
~~The model used will default to `NovelAI/genji-python-6B` for
Hugging Face. There's no particularly great model for this application, Hugging Face. There's no particularly great model for this application,
but you can try other models by setting an environment variable but you can try other models by setting an environment variable
`COMPLETION_MODEL` to `codeparrot/codeparrot` or `COMPLETION_MODEL` to `codeparrot/codeparrot` or
`NinedayWang/PolyCoder-2.7B` or similar. `NinedayWang/PolyCoder-2.7B` or similar.~~
If you are hosting a model yourself, host it as Hugging Face does, ~~If you are hosting a model yourself, host it as Hugging Face does,
and use `COMPLETION_URL` rather than `COMPLETION_MODEL` to and use `COMPLETION_URL` rather than `COMPLETION_MODEL` to
point to the model on your own server rather than Hugging Face. point to the model on your own server rather than Hugging Face.~~

View File

@ -150,7 +150,7 @@ def class_schema(engine, table_id, exclude_col_id=None, lookups=False):
return result return result
def get_formula_prompt(engine, table_id, col_id, description, def get_formula_prompt(engine, table_id, col_id, _description,
include_all_tables=True, include_all_tables=True,
lookups=True): lookups=True):
result = "" result = ""
@ -165,9 +165,7 @@ def get_formula_prompt(engine, table_id, col_id, description,
result += " @property\n" result += " @property\n"
result += " # rec is alias for self\n" result += " # rec is alias for self\n"
result += " def {}(rec) -> {}:\n".format(col_id, return_type) result += " def {}(rec) -> {}:\n".format(col_id, return_type)
result += ' """\n' result += " # Please fill in code only after this line, not the `def`\n"
result += '{}\n'.format(indent(description, " "))
result += ' """\n'
return result return result
def indent(text, prefix, predicate=None): def indent(text, prefix, predicate=None):

View File

@ -151,9 +151,7 @@ class Table2:
@property @property
# rec is alias for self # rec is alias for self
def new_formula(rec) -> float: def new_formula(rec) -> float:
""" # Please fill in code only after this line, not the `def`
description here
"""
''') ''')
def test_get_formula_prompt(self): def test_get_formula_prompt(self):
@ -183,9 +181,7 @@ class Table1:
@property @property
# rec is alias for self # rec is alias for self
def text(rec) -> str: def text(rec) -> str:
""" # Please fill in code only after this line, not the `def`
description here
"""
''') ''')
self.assert_prompt("Table2", "ref", '''\ self.assert_prompt("Table2", "ref", '''\
@ -199,9 +195,7 @@ class Table2:
@property @property
# rec is alias for self # rec is alias for self
def ref(rec) -> Table1: def ref(rec) -> Table1:
""" # Please fill in code only after this line, not the `def`
description here
"""
''') ''')
self.assert_prompt("Table3", "reflist", '''\ self.assert_prompt("Table3", "reflist", '''\
@ -219,9 +213,7 @@ class Table3:
@property @property
# rec is alias for self # rec is alias for self
def reflist(rec) -> List[Table2]: def reflist(rec) -> List[Table2]:
""" # Please fill in code only after this line, not the `def`
description here
"""
''') ''')
def test_convert_completion(self): def test_convert_completion(self):

View File

@ -1,69 +1,76 @@
table_id,col_id,doc_id,Description no_formula,table_id,col_id,doc_id,Description
Contacts,Send_Email,hQHXqAQXceeQBPvRw5sSs1,"Link to compose an email, if there is one" 0,Contacts,Send_Email,hQHXqAQXceeQBPvRw5sSs1,"Link to compose an email, if there is one"
Contacts,No_Notes,hQHXqAQXceeQBPvRw5sSs1,"Number of notes for this contact" 0,Contacts,No_Notes,hQHXqAQXceeQBPvRw5sSs1,"Number of notes for this contact"
Category,Contains_archived_project_,hQHXqAQXceeQBPvRw5sSs1,"Whether any projects in this category are archived" 0,Category,Contains_archived_project_,hQHXqAQXceeQBPvRw5sSs1,"Whether any projects in this category are archived"
Tasks,Today,hQHXqAQXceeQBPvRw5sSs1,Needs to be done today (or every day) 0,Tasks,Today,hQHXqAQXceeQBPvRw5sSs1,Needs to be done today (or every day)
Tasks,Week_Day,hQHXqAQXceeQBPvRw5sSs1,Full name of deadline weekday 0,Tasks,Week_Day,hQHXqAQXceeQBPvRw5sSs1,Full name of deadline weekday
Tasks,period,hQHXqAQXceeQBPvRw5sSs1,Whether this task was modified between (inclusive) the dates in the single row in Settings 0,Tasks,period,hQHXqAQXceeQBPvRw5sSs1,Whether this task was modified between (inclusive) the dates in the single row in Settings
Expenses,Month,55Q2EtTbFvB1N6iizLh4Rk,e.g. 2022-01 0,Expenses,Month,55Q2EtTbFvB1N6iizLh4Rk,e.g. 2022-01
Payroll,Date_Range,5pHLanQNThxkEaEJHKJUf5,"The start date, followed by a dash (no spaces) and the end date if there is one. Dates are month/day with no leading zeroes." 0,Payroll,Date_Range,5pHLanQNThxkEaEJHKJUf5,"The start date, followed by a dash (no spaces) and the end date if there is one. Dates are month/day with no leading zeroes."
Payroll,Per_Hour,5pHLanQNThxkEaEJHKJUf5,The hourly rate of the latest rate for this role and person that started on or before this date 0,Payroll,Per_Hour,5pHLanQNThxkEaEJHKJUf5,The hourly rate of the latest rate for this role and person that started on or before this date
Payroll,Payment,5pHLanQNThxkEaEJHKJUf5,"Total payment amount for hours worked, rounded to the nearest cent." 0,Payroll,Payment,5pHLanQNThxkEaEJHKJUf5,"Total payment amount for hours worked, rounded to the nearest cent."
Payroll_summary_Pay_Period_Person,Dates,5pHLanQNThxkEaEJHKJUf5,"All date ranges in the group, separated by a comma and a space" 0,Payroll_summary_Pay_Period_Person,Dates,5pHLanQNThxkEaEJHKJUf5,"All date ranges in the group, separated by a comma and a space"
People,Full_Name,5pHLanQNThxkEaEJHKJUf5,"e.g. Doe, John" 0,People,Full_Name,5pHLanQNThxkEaEJHKJUf5,"e.g. Doe, John"
General_Ledger,Quarter,2YwYBWpREY2a1N2NV7cb55,e.g. 2020 Q4 0,General_Ledger,Quarter,2YwYBWpREY2a1N2NV7cb55,e.g. 2020 Q4
General_Ledger,Year,2YwYBWpREY2a1N2NV7cb55,"Just the year of the date, as a string" 0,General_Ledger,Year,2YwYBWpREY2a1N2NV7cb55,"Just the year of the date, as a string"
Time_Calculator,Time_Worked,np7TVHmuvFcHmo1K8h7Ur4,Formatted as hours:minutes. No leading zeroes for hours. 0,Time_Calculator,Time_Worked,np7TVHmuvFcHmo1K8h7Ur4,Formatted as hours:minutes. No leading zeroes for hours.
Time_Calculator,Seconds_Worked,np7TVHmuvFcHmo1K8h7Ur4,"Number of seconds between start/end times, if they're both there" 0,Time_Calculator,Seconds_Worked,np7TVHmuvFcHmo1K8h7Ur4,"Number of seconds between start/end times, if they're both there"
Funding_Source,Percentage,qprycQa2TVwajAe6Hb3bUZ,Ratio of the amount to the total across all rows 0,Funding_Source,Percentage,qprycQa2TVwajAe6Hb3bUZ,Ratio of the amount to the total across all rows
Funding_Source_summary,Debt_to_Equity,qprycQa2TVwajAe6Hb3bUZ,Ratio of the total amounts in the group where the type is Debt vs Equity 0,Funding_Source_summary,Debt_to_Equity,qprycQa2TVwajAe6Hb3bUZ,Ratio of the total amounts in the group where the type is Debt vs Equity
Invoices,Client,bReAxyLmzmEQfHF5L5Sc1e,Client's name followed by their address on the next line 0,Invoices,Client,bReAxyLmzmEQfHF5L5Sc1e,Client's name followed by their address on the next line
Invoices,Hours,bReAxyLmzmEQfHF5L5Sc1e,Total duration in hours across all time logs for this invoice 0,Invoices,Hours,bReAxyLmzmEQfHF5L5Sc1e,Total duration in hours across all time logs for this invoice
Invoices,Due,bReAxyLmzmEQfHF5L5Sc1e,30 days after the invoice date 0,Invoices,Due,bReAxyLmzmEQfHF5L5Sc1e,30 days after the invoice date
Invoices,Invoice_ID,bReAxyLmzmEQfHF5L5Sc1e,Invoice date followed by the client's name in brackets 0,Invoices,Invoice_ID,bReAxyLmzmEQfHF5L5Sc1e,Invoice date followed by the client's name in brackets
Projects,Project_Name,bReAxyLmzmEQfHF5L5Sc1e,"Client name and project name, e.g. John Doe: Big project" 0,Projects,Project_Name,bReAxyLmzmEQfHF5L5Sc1e,"Client name and project name, e.g. John Doe: Big project"
Time_Log,Date,bReAxyLmzmEQfHF5L5Sc1e,Start date if there is one 0,Time_Log,Date,bReAxyLmzmEQfHF5L5Sc1e,Start date if there is one
Time_Log,Duration_hrs_,bReAxyLmzmEQfHF5L5Sc1e,Duration (if there is one) in hours rounded to two decimal places 0,Time_Log,Duration_hrs_,bReAxyLmzmEQfHF5L5Sc1e,Duration (if there is one) in hours rounded to two decimal places
Time_Log,Duration_min_,bReAxyLmzmEQfHF5L5Sc1e,"Number of minutes between start and end time. If either time is missing, leave blank. If end is before start, give 0." 0,Time_Log,Duration_min_,bReAxyLmzmEQfHF5L5Sc1e,"Number of minutes between start and end time. If either time is missing, leave blank. If end is before start, give 0."
Filtered_By_Formula,LabelCount,9nNr9uQwoXWAvxcWQDygh6,"1 if the state is CA, otherwise 0" 0,Filtered_By_Formula,LabelCount,9nNr9uQwoXWAvxcWQDygh6,"1 if the state is CA, otherwise 0"
Objects,Address,pyMHqncEspfZN5zfShCwT8,"City and state, separated by comma space" 0,Objects,Address,pyMHqncEspfZN5zfShCwT8,"City and state, separated by comma space"
Books,search_terms,hdXy57qLiyNf35oNLzzgBG,"Title and author name, with a space in between" 0,Books,search_terms,hdXy57qLiyNf35oNLzzgBG,"Title and author name, with a space in between"
BOM_Items,Cost,e4gEm7dt4cgBMkouVBNMeY,Total cost if both quantity and cost are given 0,BOM_Items,Cost,e4gEm7dt4cgBMkouVBNMeY,Total cost if both quantity and cost are given
Bill_Of_Materials,Cost,e4gEm7dt4cgBMkouVBNMeY,Total cost 0,Bill_Of_Materials,Cost,e4gEm7dt4cgBMkouVBNMeY,Total cost
All_Responses,Entry,qvND7WUcuNb2fU4n1vBJ7f,"Name and submitted date in the format ""Name - month-day""" 1,Bill_Of_Materials,Cost,e4gEm7dt4cgBMkouVBNMeY,Calculate the mean cost and add a row showing the variance from the mean
All_Responses,Month,qvND7WUcuNb2fU4n1vBJ7f,Submitted month (full name) and year 0,All_Responses,Entry,qvND7WUcuNb2fU4n1vBJ7f,"Name and submitted date in the format ""Name - month-day"""
Cap_Table,Common_Stock,iXggjrCPHut9u2BuhJxJkk,"If the class is Options, RSUs, or Option Pool, return 0, otherwise return the fully diluted value." 0,All_Responses,Month,qvND7WUcuNb2fU4n1vBJ7f,Submitted month (full name) and year
Cap_Table,Fully_Diluted,iXggjrCPHut9u2BuhJxJkk,"The granted amount, minus the total pool used if the class is Option Pool" 0,Cap_Table,Common_Stock,iXggjrCPHut9u2BuhJxJkk,"If the class is Options, RSUs, or Option Pool, return 0, otherwise return the fully diluted value."
Cap_Table,Fully_Diluted_,iXggjrCPHut9u2BuhJxJkk,Fully diluted as a fraction of the total 0,Cap_Table,Fully_Diluted,iXggjrCPHut9u2BuhJxJkk,"The granted amount, minus the total pool used if the class is Option Pool"
Classes,Spots_Left,swLvb3Fic22gVzrdczcAoZ,or Full 0,Cap_Table,Fully_Diluted_,iXggjrCPHut9u2BuhJxJkk,Fully diluted as a fraction of the total
Classes,Count,swLvb3Fic22gVzrdczcAoZ,Number of enrollments for this class where the status is Confirmed 0,Classes,Spots_Left,swLvb3Fic22gVzrdczcAoZ,or Full
All_Survey_Responses,Product_Experience_Score,4ktYzGV1mUipSiQFtkLGqm,"A number based on the experience: 0,Classes,Count,swLvb3Fic22gVzrdczcAoZ,Number of enrollments for this class where the status is Confirmed
1,Classes,Count,swLvb3Fic22gVzrdczcAoZ,Add a row at the end with the total number
0,All_Survey_Responses,Product_Experience_Score,4ktYzGV1mUipSiQFtkLGqm,"A number based on the experience:
Very Dissatisfied: 1 Very Dissatisfied: 1
Somewhat Dissatisfied: 2 Somewhat Dissatisfied: 2
Neutral: 3 Neutral: 3
Somewhat Satisfied: 4 Somewhat Satisfied: 4
Very Satisfied: 5" Very Satisfied: 5"
Time_Sheet_Entries_summary_Account_Employee_Month,Total_Spend,oGxD8EnzeVs6vSQK3QBrUv,Total hours worked times hourly rate 0,Time_Sheet_Entries_summary_Account_Employee_Month,Total_Spend,oGxD8EnzeVs6vSQK3QBrUv,Total hours worked times hourly rate
Time_Sheets,Title,oGxD8EnzeVs6vSQK3QBrUv,Month number and employee full name separated by a space 0,Time_Sheets,Title,oGxD8EnzeVs6vSQK3QBrUv,Month number and employee full name separated by a space
All_Products,SKU,sXsBGDTKau1F3fvxkCyoaJ,"Brand code, color code, and size, separated by dashes without spaces" 0,All_Products,SKU,sXsBGDTKau1F3fvxkCyoaJ,"Brand code, color code, and size, separated by dashes without spaces"
All_Products,QTY_on_Order,sXsBGDTKau1F3fvxkCyoaJ,Total quantity minus total received quantity across all incoming order line items for this product 0,All_Products,QTY_on_Order,sXsBGDTKau1F3fvxkCyoaJ,Total quantity minus total received quantity across all incoming order line items for this product
All_Products,Stock_Alert,sXsBGDTKau1F3fvxkCyoaJ,"If the amount in stock and on order is more than 5: In Stock 0,All_Products,Stock_Alert,sXsBGDTKau1F3fvxkCyoaJ,"If the amount in stock and on order is more than 5: In Stock
If it's 0: OUT OF STOCK If it's 0: OUT OF STOCK
Otherwise: Low Stock" Otherwise: Low Stock"
Incoming_Order_Line_Items,Received_Qty,sXsBGDTKau1F3fvxkCyoaJ,"The quantity, but only if the order is received" 0,Incoming_Order_Line_Items,Received_Qty,sXsBGDTKau1F3fvxkCyoaJ,"The quantity, but only if the order is received"
Theaters,Latitude2,dKztiPYamcCpttT1LT1FnU,Coordinate before the comma 0,Theaters,Latitude2,dKztiPYamcCpttT1LT1FnU,Coordinate before the comma
Theaters,Longitude,dKztiPYamcCpttT1LT1FnU,Coordinate after the comma and space 1,Theaters,Latitude2,dKztiPYamcCpttT1LT1FnU,How can I see the coordinates on a map?
Families,Amount_Due,cJcSKdUC3nLNAv4wTjAxA6,"Total charged minus total paid, capped at 0" 0,Theaters,Longitude,dKztiPYamcCpttT1LT1FnU,Coordinate after the comma and space
Families,Total_Applied,cJcSKdUC3nLNAv4wTjAxA6,Total charge for all paid sessions for this family 0,Families,Amount_Due,cJcSKdUC3nLNAv4wTjAxA6,"Total charged minus total paid, capped at 0"
Gifts_summary_Occasion_Who_Year,Over_Budget_,dr6epxpXUcy9rsFVUoXTEe,Did we spend more than the budget for this person? 0,Families,Total_Applied,cJcSKdUC3nLNAv4wTjAxA6,Total charge for all paid sessions for this family
Gifts_summary_Year,Total_Budget,dr6epxpXUcy9rsFVUoXTEe,Total budget for all important dates this year 0,Gifts_summary_Occasion_Who_Year,Over_Budget_,dr6epxpXUcy9rsFVUoXTEe,Did we spend more than the budget for this person?
Leases,Signer,5iMYwmESm33JpEECSqdZk2,The signing tenant for this lease 0,Gifts_summary_Year,Total_Budget,dr6epxpXUcy9rsFVUoXTEe,Total budget for all important dates this year
Apartments,Have_Picture,5iMYwmESm33JpEECSqdZk2,Yes or No depending on if there's a picture 0,Leases,Signer,5iMYwmESm33JpEECSqdZk2,The signing tenant for this lease
Apartments,Current_Lease,5iMYwmESm33JpEECSqdZk2,The lease for this apartment whose current status is Active 1,Leases,Signer,5iMYwmESm33JpEECSqdZk2,Show the attached photo of the signing tenant
Current_Signers,Lease_Start_Date,5iMYwmESm33JpEECSqdZk2,The start date of the lease for this apartment whose current status is Active 0,Apartments,Have_Picture,5iMYwmESm33JpEECSqdZk2,Yes or No depending on if there's a picture
Leases,Lease_End_Date,5iMYwmESm33JpEECSqdZk2,Start date plus the lease term in years minus one day 0,Apartments,Current_Lease,5iMYwmESm33JpEECSqdZk2,The lease for this apartment whose current status is Active
Tenancies,Minor,5iMYwmESm33JpEECSqdZk2,"1 if the age is less than 18, otherwise 0" 0,Current_Signers,Lease_Start_Date,5iMYwmESm33JpEECSqdZk2,The start date of the lease for this apartment whose current status is Active
Game_Schedule,Loser,1xJAp2uxM7tFCVUbEofKoF,The team that won fewer sets 0,Leases,Lease_End_Date,5iMYwmESm33JpEECSqdZk2,Start date plus the lease term in years minus one day
Standings,Win_Rate,1xJAp2uxM7tFCVUbEofKoF,Ratio of wins to total games 0,Tenancies,Minor,5iMYwmESm33JpEECSqdZk2,"1 if the age is less than 18, otherwise 0"
Standings,Wins,1xJAp2uxM7tFCVUbEofKoF,Number of games won 0,Game_Schedule,Loser,1xJAp2uxM7tFCVUbEofKoF,The team that won fewer sets
Prepare_Invoices,Due,9NH6D58FmxwPP43nw7uzQK,One month after the issued date if there is one 0,Standings,Win_Rate,1xJAp2uxM7tFCVUbEofKoF,Ratio of wins to total games
0,Standings,Wins,1xJAp2uxM7tFCVUbEofKoF,Number of games won
0,Prepare_Invoices,Due,9NH6D58FmxwPP43nw7uzQK,One month after the issued date if there is one
1,Prepare_Invoices,Due,9NH6D58FmxwPP43nw7uzQK,Hello
1,Prepare_Invoices,Due,9NH6D58FmxwPP43nw7uzQK,Can you help me?
1,Prepare_Invoices,Due,9NH6D58FmxwPP43nw7uzQK,How do I create a new table?

1 no_formula table_id col_id doc_id Description
2 0 Contacts Send_Email hQHXqAQXceeQBPvRw5sSs1 Link to compose an email, if there is one
3 0 Contacts No_Notes hQHXqAQXceeQBPvRw5sSs1 Number of notes for this contact
4 0 Category Contains_archived_project_ hQHXqAQXceeQBPvRw5sSs1 Whether any projects in this category are archived
5 0 Tasks Today hQHXqAQXceeQBPvRw5sSs1 Needs to be done today (or every day)
6 0 Tasks Week_Day hQHXqAQXceeQBPvRw5sSs1 Full name of deadline weekday
7 0 Tasks period hQHXqAQXceeQBPvRw5sSs1 Whether this task was modified between (inclusive) the dates in the single row in Settings
8 0 Expenses Month 55Q2EtTbFvB1N6iizLh4Rk e.g. 2022-01
9 0 Payroll Date_Range 5pHLanQNThxkEaEJHKJUf5 The start date, followed by a dash (no spaces) and the end date if there is one. Dates are month/day with no leading zeroes.
10 0 Payroll Per_Hour 5pHLanQNThxkEaEJHKJUf5 The hourly rate of the latest rate for this role and person that started on or before this date
11 0 Payroll Payment 5pHLanQNThxkEaEJHKJUf5 Total payment amount for hours worked, rounded to the nearest cent.
12 0 Payroll_summary_Pay_Period_Person Dates 5pHLanQNThxkEaEJHKJUf5 All date ranges in the group, separated by a comma and a space
13 0 People Full_Name 5pHLanQNThxkEaEJHKJUf5 e.g. Doe, John
14 0 General_Ledger Quarter 2YwYBWpREY2a1N2NV7cb55 e.g. 2020 Q4
15 0 General_Ledger Year 2YwYBWpREY2a1N2NV7cb55 Just the year of the date, as a string
16 0 Time_Calculator Time_Worked np7TVHmuvFcHmo1K8h7Ur4 Formatted as hours:minutes. No leading zeroes for hours.
17 0 Time_Calculator Seconds_Worked np7TVHmuvFcHmo1K8h7Ur4 Number of seconds between start/end times, if they're both there
18 0 Funding_Source Percentage qprycQa2TVwajAe6Hb3bUZ Ratio of the amount to the total across all rows
19 0 Funding_Source_summary Debt_to_Equity qprycQa2TVwajAe6Hb3bUZ Ratio of the total amounts in the group where the type is Debt vs Equity
20 0 Invoices Client bReAxyLmzmEQfHF5L5Sc1e Client's name followed by their address on the next line
21 0 Invoices Hours bReAxyLmzmEQfHF5L5Sc1e Total duration in hours across all time logs for this invoice
22 0 Invoices Due bReAxyLmzmEQfHF5L5Sc1e 30 days after the invoice date
23 0 Invoices Invoice_ID bReAxyLmzmEQfHF5L5Sc1e Invoice date followed by the client's name in brackets
24 0 Projects Project_Name bReAxyLmzmEQfHF5L5Sc1e Client name and project name, e.g. John Doe: Big project
25 0 Time_Log Date bReAxyLmzmEQfHF5L5Sc1e Start date if there is one
26 0 Time_Log Duration_hrs_ bReAxyLmzmEQfHF5L5Sc1e Duration (if there is one) in hours rounded to two decimal places
27 0 Time_Log Duration_min_ bReAxyLmzmEQfHF5L5Sc1e Number of minutes between start and end time. If either time is missing, leave blank. If end is before start, give 0.
28 0 Filtered_By_Formula LabelCount 9nNr9uQwoXWAvxcWQDygh6 1 if the state is CA, otherwise 0
29 0 Objects Address pyMHqncEspfZN5zfShCwT8 City and state, separated by comma space
30 0 Books search_terms hdXy57qLiyNf35oNLzzgBG Title and author name, with a space in between
31 0 BOM_Items Cost e4gEm7dt4cgBMkouVBNMeY Total cost if both quantity and cost are given
32 0 Bill_Of_Materials Cost e4gEm7dt4cgBMkouVBNMeY Total cost
33 1 All_Responses Bill_Of_Materials Entry Cost qvND7WUcuNb2fU4n1vBJ7f e4gEm7dt4cgBMkouVBNMeY Name and submitted date in the format "Name - month-day" Calculate the mean cost and add a row showing the variance from the mean
34 0 All_Responses Month Entry qvND7WUcuNb2fU4n1vBJ7f Submitted month (full name) and year Name and submitted date in the format "Name - month-day"
35 0 Cap_Table All_Responses Common_Stock Month iXggjrCPHut9u2BuhJxJkk qvND7WUcuNb2fU4n1vBJ7f If the class is Options, RSUs, or Option Pool, return 0, otherwise return the fully diluted value. Submitted month (full name) and year
36 0 Cap_Table Fully_Diluted Common_Stock iXggjrCPHut9u2BuhJxJkk The granted amount, minus the total pool used if the class is Option Pool If the class is Options, RSUs, or Option Pool, return 0, otherwise return the fully diluted value.
37 0 Cap_Table Fully_Diluted_ Fully_Diluted iXggjrCPHut9u2BuhJxJkk Fully diluted as a fraction of the total The granted amount, minus the total pool used if the class is Option Pool
38 0 Classes Cap_Table Spots_Left Fully_Diluted_ swLvb3Fic22gVzrdczcAoZ iXggjrCPHut9u2BuhJxJkk or Full Fully diluted as a fraction of the total
39 0 Classes Count Spots_Left swLvb3Fic22gVzrdczcAoZ Number of enrollments for this class where the status is Confirmed or Full
40 0 All_Survey_Responses Classes Product_Experience_Score Count 4ktYzGV1mUipSiQFtkLGqm swLvb3Fic22gVzrdczcAoZ A number based on the experience: Very Dissatisfied: 1 Somewhat Dissatisfied: 2 Neutral: 3 Somewhat Satisfied: 4 Very Satisfied: 5 Number of enrollments for this class where the status is Confirmed
41 1 Classes Count swLvb3Fic22gVzrdczcAoZ Add a row at the end with the total number
42 0 All_Survey_Responses Product_Experience_Score 4ktYzGV1mUipSiQFtkLGqm A number based on the experience: Very Dissatisfied: 1 Somewhat Dissatisfied: 2 Neutral: 3 Somewhat Satisfied: 4 Very Satisfied: 5
43 0 Time_Sheet_Entries_summary_Account_Employee_Month Total_Spend oGxD8EnzeVs6vSQK3QBrUv Total hours worked times hourly rate
44 0 Time_Sheets Title oGxD8EnzeVs6vSQK3QBrUv Month number and employee full name separated by a space
45 0 All_Products SKU sXsBGDTKau1F3fvxkCyoaJ Brand code, color code, and size, separated by dashes without spaces
46 0 All_Products QTY_on_Order sXsBGDTKau1F3fvxkCyoaJ Total quantity minus total received quantity across all incoming order line items for this product
47 0 All_Products Stock_Alert sXsBGDTKau1F3fvxkCyoaJ If the amount in stock and on order is more than 5: In Stock If it's 0: OUT OF STOCK Otherwise: Low Stock
48 0 Incoming_Order_Line_Items Received_Qty sXsBGDTKau1F3fvxkCyoaJ The quantity, but only if the order is received
49 0 Theaters Latitude2 dKztiPYamcCpttT1LT1FnU Coordinate before the comma
50 1 Theaters Longitude Latitude2 dKztiPYamcCpttT1LT1FnU Coordinate after the comma and space How can I see the coordinates on a map?
51 0 Families Theaters Amount_Due Longitude cJcSKdUC3nLNAv4wTjAxA6 dKztiPYamcCpttT1LT1FnU Total charged minus total paid, capped at 0 Coordinate after the comma and space
52 0 Families Total_Applied Amount_Due cJcSKdUC3nLNAv4wTjAxA6 Total charge for all paid sessions for this family Total charged minus total paid, capped at 0
53 0 Gifts_summary_Occasion_Who_Year Families Over_Budget_ Total_Applied dr6epxpXUcy9rsFVUoXTEe cJcSKdUC3nLNAv4wTjAxA6 Did we spend more than the budget for this person? Total charge for all paid sessions for this family
54 0 Gifts_summary_Year Gifts_summary_Occasion_Who_Year Total_Budget Over_Budget_ dr6epxpXUcy9rsFVUoXTEe Total budget for all important dates this year Did we spend more than the budget for this person?
55 0 Leases Gifts_summary_Year Signer Total_Budget 5iMYwmESm33JpEECSqdZk2 dr6epxpXUcy9rsFVUoXTEe The signing tenant for this lease Total budget for all important dates this year
56 0 Apartments Leases Have_Picture Signer 5iMYwmESm33JpEECSqdZk2 Yes or No depending on if there's a picture The signing tenant for this lease
57 1 Apartments Leases Current_Lease Signer 5iMYwmESm33JpEECSqdZk2 The lease for this apartment whose current status is Active Show the attached photo of the signing tenant
58 0 Current_Signers Apartments Lease_Start_Date Have_Picture 5iMYwmESm33JpEECSqdZk2 The start date of the lease for this apartment whose current status is Active Yes or No depending on if there's a picture
59 0 Leases Apartments Lease_End_Date Current_Lease 5iMYwmESm33JpEECSqdZk2 Start date plus the lease term in years minus one day The lease for this apartment whose current status is Active
60 0 Tenancies Current_Signers Minor Lease_Start_Date 5iMYwmESm33JpEECSqdZk2 1 if the age is less than 18, otherwise 0 The start date of the lease for this apartment whose current status is Active
61 0 Game_Schedule Leases Loser Lease_End_Date 1xJAp2uxM7tFCVUbEofKoF 5iMYwmESm33JpEECSqdZk2 The team that won fewer sets Start date plus the lease term in years minus one day
62 0 Standings Tenancies Win_Rate Minor 1xJAp2uxM7tFCVUbEofKoF 5iMYwmESm33JpEECSqdZk2 Ratio of wins to total games 1 if the age is less than 18, otherwise 0
63 0 Standings Game_Schedule Wins Loser 1xJAp2uxM7tFCVUbEofKoF Number of games won The team that won fewer sets
64 0 Prepare_Invoices Standings Due Win_Rate 9NH6D58FmxwPP43nw7uzQK 1xJAp2uxM7tFCVUbEofKoF One month after the issued date if there is one Ratio of wins to total games
65 0 Standings Wins 1xJAp2uxM7tFCVUbEofKoF Number of games won
66 0 Prepare_Invoices Due 9NH6D58FmxwPP43nw7uzQK One month after the issued date if there is one
67 1 Prepare_Invoices Due 9NH6D58FmxwPP43nw7uzQK Hello
68 1 Prepare_Invoices Due 9NH6D58FmxwPP43nw7uzQK Can you help me?
69 1 Prepare_Invoices Due 9NH6D58FmxwPP43nw7uzQK How do I create a new table?
70
71
72
73
74
75
76

View File

@ -53,6 +53,7 @@ const TEMPLATE_URL = "https://grist-static.com/datasets/grist_dataset_formulai_2
const oldFetch = DEPS.fetch; const oldFetch = DEPS.fetch;
interface FormulaRec { interface FormulaRec {
no_formula: string;
table_id: string; table_id: string;
col_id: string; col_id: string;
doc_id: string; doc_id: string;
@ -170,6 +171,10 @@ where c.colId = ? and t.tableId = ?
if (result.state) { if (result.state) {
history = result.state; history = result.state;
} }
if (rec.no_formula == "1") {
success = result.suggestedActions.length === 0;
return null;
}
suggestedActions = result.suggestedActions; suggestedActions = result.suggestedActions;
// apply modification // apply modification
const {actionNum} = await activeDoc.applyUserActions(session, suggestedActions); const {actionNum} = await activeDoc.applyUserActions(session, suggestedActions);