Adding the ability to set formula response type - returning native values.
This commit is contained in:
parent
888820a0de
commit
7c2bfec22c
|
@ -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 @@
|
|||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="split-label">
|
||||
<div class="label-length">
|
||||
<Label size="M">Response Type</Label>
|
||||
</div>
|
||||
<div class="input-length">
|
||||
<Select
|
||||
bind:value={editableColumn.responseType}
|
||||
options={[
|
||||
FIELDS.STRING,
|
||||
FIELDS.NUMBER,
|
||||
FIELDS.BOOLEAN,
|
||||
FIELDS.DATETIME,
|
||||
]}
|
||||
getOptionLabel={option => option.name}
|
||||
getOptionValue={option => option.type}
|
||||
tooltip="Formulas by default will return a string - however if you need a native type the response can be coerced."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="split-label">
|
||||
<div class="label-length">
|
||||
<Label size="M">Formula</Label>
|
||||
|
|
|
@ -1,5 +1,21 @@
|
|||
<script>
|
||||
import TextCell from "./TextCell.svelte"
|
||||
import DateCell from "./DateCell.svelte"
|
||||
import NumberCell from "./NumberCell.svelte"
|
||||
import BooleanCell from "./BooleanCell.svelte"
|
||||
import { FieldType } from "@budibase/types"
|
||||
|
||||
export let schema
|
||||
|
||||
$: responseType = schema.responseType
|
||||
</script>
|
||||
|
||||
<TextCell {...$$props} readonly />
|
||||
{#if responseType === FieldType.NUMBER}
|
||||
<NumberCell {...$$props} readonly />
|
||||
{:else if responseType === FieldType.BOOLEAN}
|
||||
<BooleanCell {...$$props} readonly />
|
||||
{:else if responseType === FieldType.DATETIME}
|
||||
<DateCell {...$$props} readonly />
|
||||
{:else}
|
||||
<TextCell {...$$props} readonly />
|
||||
{/if}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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<T extends Row | Row[]>(
|
|||
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<T extends Row | Row[]>(
|
|||
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<T extends Row | Row[]>(
|
|||
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<T extends Row | Row[]>(
|
|||
}
|
||||
}
|
||||
|
||||
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 })
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue