diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte
index d16bca3203..96397490f8 100644
--- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte
+++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte
@@ -371,6 +371,7 @@
delete editableColumn.relationshipType
delete editableColumn.formulaType
delete editableColumn.constraints
+ delete editableColumn.responseType
// Add in defaults and initial definition
const definition = fieldDefinitions[type?.toUpperCase()]
@@ -386,6 +387,7 @@
editableColumn.relationshipType = RelationshipType.MANY_TO_MANY
} else if (editableColumn.type === FieldType.FORMULA) {
editableColumn.formulaType = "dynamic"
+ editableColumn.responseType = FIELDS.STRING.type
}
}
@@ -767,6 +769,25 @@
{/if}
+
diff --git a/packages/frontend-core/src/components/grid/cells/FormulaCell.svelte b/packages/frontend-core/src/components/grid/cells/FormulaCell.svelte
index b4db795e44..3dabbb94c0 100644
--- a/packages/frontend-core/src/components/grid/cells/FormulaCell.svelte
+++ b/packages/frontend-core/src/components/grid/cells/FormulaCell.svelte
@@ -1,5 +1,21 @@
-
+{#if responseType === FieldType.NUMBER}
+
+{:else if responseType === FieldType.BOOLEAN}
+
+{:else if responseType === FieldType.DATETIME}
+
+{:else}
+
+{/if}
diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts
index 910e9d220f..e21cd81e3f 100644
--- a/packages/server/src/utilities/rowProcessor/index.ts
+++ b/packages/server/src/utilities/rowProcessor/index.ts
@@ -163,33 +163,33 @@ async function processDefaultValues(table: Table, row: Row) {
/**
* This will coerce a value to the correct types based on the type transform map
- * @param row The value to coerce
+ * @param value The value to coerce
* @param type The type fo coerce to
* @returns The coerced value
*/
-export function coerce(row: any, type: string) {
+export function coerce(value: unknown, type: string) {
// no coercion specified for type, skip it
if (!TYPE_TRANSFORM_MAP[type]) {
- return row
+ return value
}
// eslint-disable-next-line no-prototype-builtins
- if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(row)) {
+ if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(value)) {
// @ts-ignore
- return TYPE_TRANSFORM_MAP[type][row]
+ return TYPE_TRANSFORM_MAP[type][value]
} else if (TYPE_TRANSFORM_MAP[type].parse) {
// @ts-ignore
- return TYPE_TRANSFORM_MAP[type].parse(row)
+ return TYPE_TRANSFORM_MAP[type].parse(value)
}
- return row
+ return value
}
/**
* Given an input route this function will apply all the necessary pre-processing to it, such as coercion
* of column values or adding auto-column values.
- * @param user the user which is performing the input.
+ * @param userId the ID of the user which is performing the input.
* @param row the row which is being created/updated.
- * @param table the table which the row is being saved to.
+ * @param source the table/view which the row is being saved to.
* @param opts some input processing options (like disabling auto-column relationships).
* @returns the row which has been prepared to be written to the DB.
*/
diff --git a/packages/server/src/utilities/rowProcessor/utils.ts b/packages/server/src/utilities/rowProcessor/utils.ts
index 33aba5eb3a..15c0612fae 100644
--- a/packages/server/src/utilities/rowProcessor/utils.ts
+++ b/packages/server/src/utilities/rowProcessor/utils.ts
@@ -10,11 +10,13 @@ import {
FieldType,
OperationFieldTypeEnum,
AIOperationEnum,
+ AIFieldMetadata,
} from "@budibase/types"
import { OperationFields } from "@budibase/shared-core"
import tracer from "dd-trace"
import { context } from "@budibase/backend-core"
import * as pro from "@budibase/pro"
+import { coerce } from "./index"
interface FormulaOpts {
dynamic?: boolean
@@ -67,7 +69,18 @@ export async function processFormulas(
continue
}
+ const responseType = schema.responseType
const isStatic = schema.formulaType === FormulaType.STATIC
+ const formula = schema.formula
+
+ // coerce static values
+ if (isStatic) {
+ rows.forEach(row => {
+ if (row[column] && responseType) {
+ row[column] = coerce(row[column], responseType)
+ }
+ })
+ }
if (
schema.formula == null ||
@@ -80,12 +93,17 @@ export async function processFormulas(
for (let i = 0; i < rows.length; i++) {
let row = rows[i]
let context = contextRows ? contextRows[i] : row
- let formula = schema.formula
rows[i] = {
...row,
[column]: tracer.trace("processStringSync", {}, span => {
span?.addTags({ table_id: table._id, column, static: isStatic })
- return processStringSync(formula, context)
+ const result = processStringSync(formula, context)
+ try {
+ return responseType ? coerce(result, responseType) : result
+ } catch (err) {
+ // if the coercion fails, we return empty row contents
+ return undefined
+ }
}),
}
}
@@ -117,12 +135,13 @@ export async function processAIColumns(
continue
}
+ const operation = schema.operation
+ const aiSchema: AIFieldMetadata = schema
const rowUpdates = rows.map((row, i) => {
const contextRow = contextRows ? contextRows[i] : row
// Check if the type is bindable and pass through HBS if so
- const operationField =
- OperationFields[schema.operation as AIOperationEnum]
+ const operationField = OperationFields[operation as AIOperationEnum]
for (const key in schema) {
const fieldType = operationField[key as keyof typeof operationField]
if (fieldType === OperationFieldTypeEnum.BINDABLE_TEXT) {
@@ -131,7 +150,10 @@ export async function processAIColumns(
}
}
- const prompt = llm.buildPromptFromAIOperation({ schema, row })
+ const prompt = llm.buildPromptFromAIOperation({
+ schema: aiSchema,
+ row,
+ })
return tracer.trace("processAIColumn", {}, async span => {
span?.addTags({ table_id: table._id, column })
diff --git a/packages/types/src/documents/app/table/schema.ts b/packages/types/src/documents/app/table/schema.ts
index 7e79902a49..00e119669b 100644
--- a/packages/types/src/documents/app/table/schema.ts
+++ b/packages/types/src/documents/app/table/schema.ts
@@ -115,6 +115,11 @@ export interface FormulaFieldMetadata extends BaseFieldSchema {
type: FieldType.FORMULA
formula: string
formulaType?: FormulaType
+ responseType?:
+ | FieldType.STRING
+ | FieldType.NUMBER
+ | FieldType.BOOLEAN
+ | FieldType.DATETIME
}
export interface AIFieldMetadata extends BaseFieldSchema {