tests, pr comments
This commit is contained in:
parent
cf8f7db36d
commit
30215ea9be
|
@ -28,6 +28,7 @@ export enum Config {
|
||||||
OIDC = "oidc",
|
OIDC = "oidc",
|
||||||
OIDC_LOGOS = "logos_oidc",
|
OIDC_LOGOS = "logos_oidc",
|
||||||
SCIM = "scim",
|
SCIM = "scim",
|
||||||
|
AI = "AI",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MIN_VALID_DATE = new Date(-2147483647000)
|
export const MIN_VALID_DATE = new Date(-2147483647000)
|
||||||
|
|
|
@ -102,6 +102,14 @@ export const useAppBuilders = () => {
|
||||||
return useFeature(Feature.APP_BUILDERS)
|
return useFeature(Feature.APP_BUILDERS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useBudibaseAI = () => {
|
||||||
|
return useFeature(Feature.BUDIBASE_AI)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAICustomConfigs = () => {
|
||||||
|
return useFeature(Feature.AI_CUSTOM_CONFIGS)
|
||||||
|
}
|
||||||
|
|
||||||
// QUOTAS
|
// QUOTAS
|
||||||
|
|
||||||
export const setAutomationLogsQuota = (value: number) => {
|
export const setAutomationLogsQuota = (value: number) => {
|
||||||
|
|
|
@ -26,7 +26,9 @@
|
||||||
|
|
||||||
$: style = makeStyle($menu)
|
$: style = makeStyle($menu)
|
||||||
$: isNewRow = $focusedRowId === NewRowID
|
$: isNewRow = $focusedRowId === NewRowID
|
||||||
$: budibaseAIEnabled = $config.licensing?.budibaseAIEnabled || $config.licensing?.customAIConfigsEnabled
|
$: budibaseAIEnabled =
|
||||||
|
$config.licensing?.budibaseAIEnabled ||
|
||||||
|
$config.licensing?.customAIConfigsEnabled
|
||||||
|
|
||||||
const makeStyle = menu => {
|
const makeStyle = menu => {
|
||||||
return `left:${menu.left}px; top:${menu.top}px;`
|
return `left:${menu.left}px; top:${menu.top}px;`
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit fc4c7f4925139af078480217965c3d6338dc0a7f
|
Subproject commit a55a58fc96291ae021e61ad369a2077992fddfcd
|
|
@ -1,10 +1,6 @@
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
|
|
||||||
import {
|
import { DatabaseName, knexClient } from "../../../integrations/tests/utils"
|
||||||
DatabaseName,
|
|
||||||
getDatasource,
|
|
||||||
knexClient,
|
|
||||||
} from "../../../integrations/tests/utils"
|
|
||||||
|
|
||||||
import tk from "timekeeper"
|
import tk from "timekeeper"
|
||||||
import emitter from "../../../../src/events"
|
import emitter from "../../../../src/events"
|
||||||
|
@ -12,36 +8,37 @@ import { outputProcessing } from "../../../utilities/rowProcessor"
|
||||||
import {
|
import {
|
||||||
context,
|
context,
|
||||||
InternalTable,
|
InternalTable,
|
||||||
|
setEnv as setCoreEnv,
|
||||||
tenancy,
|
tenancy,
|
||||||
withEnv as withCoreEnv,
|
withEnv as withCoreEnv,
|
||||||
setEnv as setCoreEnv,
|
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import { quotas } from "@budibase/pro"
|
import { quotas } from "@budibase/pro"
|
||||||
import {
|
import {
|
||||||
|
AIOperationEnum,
|
||||||
AttachmentFieldMetadata,
|
AttachmentFieldMetadata,
|
||||||
AutoFieldSubType,
|
AutoFieldSubType,
|
||||||
|
BBReferenceFieldSubType,
|
||||||
Datasource,
|
Datasource,
|
||||||
DateFieldMetadata,
|
DateFieldMetadata,
|
||||||
DeleteRow,
|
DeleteRow,
|
||||||
|
FeatureFlag,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
FieldType,
|
FieldType,
|
||||||
BBReferenceFieldSubType,
|
|
||||||
FormulaType,
|
FormulaType,
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
|
JsonFieldSubType,
|
||||||
NumberFieldMetadata,
|
NumberFieldMetadata,
|
||||||
QuotaUsageType,
|
QuotaUsageType,
|
||||||
|
RelationSchemaField,
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
Row,
|
Row,
|
||||||
|
RowExportFormat,
|
||||||
SaveTableRequest,
|
SaveTableRequest,
|
||||||
StaticQuotaName,
|
StaticQuotaName,
|
||||||
Table,
|
Table,
|
||||||
|
TableSchema,
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
UpdatedRowEventEmitter,
|
UpdatedRowEventEmitter,
|
||||||
TableSchema,
|
|
||||||
JsonFieldSubType,
|
|
||||||
RowExportFormat,
|
|
||||||
FeatureFlag,
|
|
||||||
RelationSchemaField,
|
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { generator, mocks } from "@budibase/backend-core/tests"
|
import { generator, mocks } from "@budibase/backend-core/tests"
|
||||||
import _, { merge } from "lodash"
|
import _, { merge } from "lodash"
|
||||||
|
@ -51,6 +48,18 @@ import { InternalTables } from "../../../db/utils"
|
||||||
import { withEnv } from "../../../environment"
|
import { withEnv } from "../../../environment"
|
||||||
import { JsTimeoutError } from "@budibase/string-templates"
|
import { JsTimeoutError } from "@budibase/string-templates"
|
||||||
|
|
||||||
|
jest.mock("@budibase/pro", () => ({
|
||||||
|
...jest.requireActual("@budibase/pro"),
|
||||||
|
ai: {
|
||||||
|
LargeLanguageModel: {
|
||||||
|
forCurrentTenant: async () => ({
|
||||||
|
run: jest.fn(() => `Mock LLM Response`),
|
||||||
|
buildPromptFromAIOperation: jest.fn(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
const timestamp = new Date("2023-01-26T11:48:57.597Z").toISOString()
|
const timestamp = new Date("2023-01-26T11:48:57.597Z").toISOString()
|
||||||
tk.freeze(timestamp)
|
tk.freeze(timestamp)
|
||||||
interface WaitOptions {
|
interface WaitOptions {
|
||||||
|
@ -77,13 +86,13 @@ async function waitForEvent(
|
||||||
}
|
}
|
||||||
|
|
||||||
describe.each([
|
describe.each([
|
||||||
["lucene", undefined],
|
// ["lucene", undefined],
|
||||||
["sqs", undefined],
|
["sqs", undefined],
|
||||||
[DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)],
|
// [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)],
|
||||||
[DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)],
|
// [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)],
|
||||||
[DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)],
|
// [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)],
|
||||||
[DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)],
|
// [DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)],
|
||||||
[DatabaseName.ORACLE, getDatasource(DatabaseName.ORACLE)],
|
// [DatabaseName.ORACLE, getDatasource(DatabaseName.ORACLE)],
|
||||||
])("/rows (%s)", (providerType, dsProvider) => {
|
])("/rows (%s)", (providerType, dsProvider) => {
|
||||||
const isInternal = dsProvider === undefined
|
const isInternal = dsProvider === undefined
|
||||||
const isLucene = providerType === "lucene"
|
const isLucene = providerType === "lucene"
|
||||||
|
@ -1986,6 +1995,7 @@ describe.each([
|
||||||
[FieldType.ATTACHMENT_SINGLE]: setup.structures.basicAttachment(),
|
[FieldType.ATTACHMENT_SINGLE]: setup.structures.basicAttachment(),
|
||||||
[FieldType.FORMULA]: undefined, // generated field
|
[FieldType.FORMULA]: undefined, // generated field
|
||||||
[FieldType.AUTO]: undefined, // generated field
|
[FieldType.AUTO]: undefined, // generated field
|
||||||
|
[FieldType.AI]: undefined, // generated field
|
||||||
[FieldType.JSON]: { name: generator.guid() },
|
[FieldType.JSON]: { name: generator.guid() },
|
||||||
[FieldType.INTERNAL]: generator.guid(),
|
[FieldType.INTERNAL]: generator.guid(),
|
||||||
[FieldType.BARCODEQR]: generator.guid(),
|
[FieldType.BARCODEQR]: generator.guid(),
|
||||||
|
@ -2016,6 +2026,7 @@ describe.each([
|
||||||
url: expect.any(String),
|
url: expect.any(String),
|
||||||
}),
|
}),
|
||||||
[FieldType.FORMULA]: fullSchema[FieldType.FORMULA].formula,
|
[FieldType.FORMULA]: fullSchema[FieldType.FORMULA].formula,
|
||||||
|
[FieldType.AI]: fullSchema[FieldType.AI].prompt,
|
||||||
[FieldType.AUTO]: expect.any(Number),
|
[FieldType.AUTO]: expect.any(Number),
|
||||||
[FieldType.JSON]: rowValues[FieldType.JSON],
|
[FieldType.JSON]: rowValues[FieldType.JSON],
|
||||||
[FieldType.INTERNAL]: rowValues[FieldType.INTERNAL],
|
[FieldType.INTERNAL]: rowValues[FieldType.INTERNAL],
|
||||||
|
@ -2880,6 +2891,57 @@ describe.each([
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
isSqs &&
|
||||||
|
describe("AI fields", () => {
|
||||||
|
let table: Table
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
mocks.licenses.useBudibaseAI()
|
||||||
|
mocks.licenses.useAICustomConfigs()
|
||||||
|
table = await config.api.table.save(
|
||||||
|
saveTableRequest({
|
||||||
|
schema: {
|
||||||
|
ai: {
|
||||||
|
name: "ai",
|
||||||
|
type: FieldType.AI,
|
||||||
|
operation: AIOperationEnum.PROMPT,
|
||||||
|
prompt: "Convert the following to German: '{{ product }}'",
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
name: "product",
|
||||||
|
type: FieldType.STRING,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
await config.api.row.save(table._id!, {
|
||||||
|
product: generator.word(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
jest.unmock("@budibase/pro")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to save a row with an AI column", async () => {
|
||||||
|
const { rows } = await config.api.row.search(table._id!)
|
||||||
|
expect(rows.length).toBe(1)
|
||||||
|
expect(rows[0].ai).toEqual("Mock LLM Response")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to update a row with an AI column", async () => {
|
||||||
|
const { rows } = await config.api.row.search(table._id!)
|
||||||
|
expect(rows.length).toBe(1)
|
||||||
|
await config.api.row.save(table._id!, {
|
||||||
|
product: generator.word(),
|
||||||
|
...rows[0],
|
||||||
|
})
|
||||||
|
expect(rows.length).toBe(1)
|
||||||
|
expect(rows[0].ai).toEqual("Mock LLM Response")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("Formula fields", () => {
|
describe("Formula fields", () => {
|
||||||
let table: Table
|
let table: Table
|
||||||
let otherTable: Table
|
let otherTable: Table
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
|
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
import {
|
import {
|
||||||
|
AIOperationEnum,
|
||||||
AutoFieldSubType,
|
AutoFieldSubType,
|
||||||
BBReferenceFieldSubType,
|
BBReferenceFieldSubType,
|
||||||
Datasource,
|
Datasource,
|
||||||
|
@ -41,11 +42,23 @@ import tk from "timekeeper"
|
||||||
import { encodeJSBinding } from "@budibase/string-templates"
|
import { encodeJSBinding } from "@budibase/string-templates"
|
||||||
import { dataFilters } from "@budibase/shared-core"
|
import { dataFilters } from "@budibase/shared-core"
|
||||||
import { Knex } from "knex"
|
import { Knex } from "knex"
|
||||||
import { generator, structures } from "@budibase/backend-core/tests"
|
import { generator, structures, mocks } from "@budibase/backend-core/tests"
|
||||||
import { DEFAULT_EMPLOYEE_TABLE_SCHEMA } from "../../../db/defaultData/datasource_bb_default"
|
import { DEFAULT_EMPLOYEE_TABLE_SCHEMA } from "../../../db/defaultData/datasource_bb_default"
|
||||||
import { generateRowIdField } from "../../../integrations/utils"
|
import { generateRowIdField } from "../../../integrations/utils"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
||||||
|
jest.mock("@budibase/pro", () => ({
|
||||||
|
...jest.requireActual("@budibase/pro"),
|
||||||
|
ai: {
|
||||||
|
LargeLanguageModel: {
|
||||||
|
forCurrentTenant: async () => ({
|
||||||
|
run: jest.fn(() => `Mock LLM Response`),
|
||||||
|
buildPromptFromAIOperation: jest.fn(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
describe.each([
|
describe.each([
|
||||||
["in-memory", undefined],
|
["in-memory", undefined],
|
||||||
["lucene", undefined],
|
["lucene", undefined],
|
||||||
|
@ -1606,6 +1619,79 @@ describe.each([
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
isSqs &&
|
||||||
|
describe("AI Column", () => {
|
||||||
|
const UNEXISTING_AI_COLUMN = "Real LLM Response"
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
mocks.licenses.useBudibaseAI()
|
||||||
|
mocks.licenses.useAICustomConfigs()
|
||||||
|
|
||||||
|
tableOrViewId = await createTableOrView({
|
||||||
|
product: { name: "product", type: FieldType.STRING },
|
||||||
|
ai: {
|
||||||
|
name: "AI",
|
||||||
|
type: FieldType.AI,
|
||||||
|
operation: AIOperationEnum.PROMPT,
|
||||||
|
prompt: "Translate '{{ product }}' into German",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await createRows([{ product: "Big Mac" }, { product: "McCrispy" }])
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("equal", () => {
|
||||||
|
it("successfully finds rows based on AI column", async () => {
|
||||||
|
await expectQuery({
|
||||||
|
equal: { ai: "Mock LLM Response" },
|
||||||
|
}).toContainExactly([
|
||||||
|
{ product: "Big Mac" },
|
||||||
|
{ product: "McCrispy" },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("fails to find nonexistent row", async () => {
|
||||||
|
await expectQuery({
|
||||||
|
equal: { ai: UNEXISTING_AI_COLUMN },
|
||||||
|
}).toFindNothing()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("notEqual", () => {
|
||||||
|
it("Returns nothing when searching notEqual on the mock AI response", async () => {
|
||||||
|
await expectQuery({
|
||||||
|
notEqual: { ai: "Mock LLM Response" },
|
||||||
|
}).toContainExactly([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("return all when requesting non-existing response", async () => {
|
||||||
|
await expectQuery({
|
||||||
|
notEqual: { ai: "Real LLM Response" },
|
||||||
|
}).toContainExactly([
|
||||||
|
{ product: "Big Mac" },
|
||||||
|
{ product: "McCrispy" },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("oneOf", () => {
|
||||||
|
it("successfully finds a row", async () => {
|
||||||
|
await expectQuery({
|
||||||
|
oneOf: { ai: ["Mock LLM Response", "Other LLM Response"] },
|
||||||
|
}).toContainExactly([
|
||||||
|
{ product: "Big Mac" },
|
||||||
|
{ product: "McCrispy" },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("fails to find nonexistent row", async () => {
|
||||||
|
await expectQuery({
|
||||||
|
oneOf: { ai: ["Whopper"] },
|
||||||
|
}).toFindNothing()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe.each([FieldType.ARRAY, FieldType.OPTIONS])("%s", () => {
|
describe.each([FieldType.ARRAY, FieldType.OPTIONS])("%s", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
tableOrViewId = await createTableOrView({
|
tableOrViewId = await createTableOrView({
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
TRIGGER_DEFINITIONS,
|
TRIGGER_DEFINITIONS,
|
||||||
} from "../../automations"
|
} from "../../automations"
|
||||||
import {
|
import {
|
||||||
|
AIOperationEnum,
|
||||||
Automation,
|
Automation,
|
||||||
AutomationActionStepId,
|
AutomationActionStepId,
|
||||||
AutomationResults,
|
AutomationResults,
|
||||||
|
@ -666,6 +667,12 @@ export function fullSchemaWithoutLinks({
|
||||||
presence: allRequired,
|
presence: allRequired,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
[FieldType.AI]: {
|
||||||
|
name: "ai",
|
||||||
|
type: FieldType.AI,
|
||||||
|
operation: AIOperationEnum.PROMPT,
|
||||||
|
prompt: "Translate this into German :'{{ product }}'",
|
||||||
|
},
|
||||||
[FieldType.BARCODEQR]: {
|
[FieldType.BARCODEQR]: {
|
||||||
name: "barcodeqr",
|
name: "barcodeqr",
|
||||||
type: FieldType.BARCODEQR,
|
type: FieldType.BARCODEQR,
|
||||||
|
|
Loading…
Reference in New Issue