217 lines
6.5 KiB
TypeScript
217 lines
6.5 KiB
TypeScript
import { describe, it, expect, afterAll, beforeEach, afterEach, vi } from 'vitest'
|
|
import { BotpressDocumentation, getClient, getZai, metadata } from './utils'
|
|
import { TableAdapter } from '../src/adapters/botpress-table'
|
|
import { getCachedCognitiveClient, getCognitiveClient } from './client'
|
|
|
|
describe('zai.check', { timeout: 60_000 }, () => {
|
|
const zai = getZai()
|
|
|
|
it('basic check on a string', async () => {
|
|
const value = await zai.check('This text is very clearly written in English.', 'is an english sentence')
|
|
expect(value).toBe(true)
|
|
})
|
|
|
|
it('basic check on a string (full)', async () => {
|
|
const { output, usage } = await zai
|
|
.check('This text is very clearly written in English.', 'is an english sentence')
|
|
.result()
|
|
|
|
expect(output.value).toBe(true)
|
|
expect(output.explanation).toBeTypeOf('string')
|
|
expect(output.explanation.length).toBeGreaterThan(5)
|
|
expect(usage.requests.requests).toBeGreaterThanOrEqual(1)
|
|
expect(usage.requests.responses).toBeGreaterThanOrEqual(1)
|
|
})
|
|
|
|
it('can abort', async () => {
|
|
// no caching
|
|
const request = getZai(getCognitiveClient()).check(
|
|
'This text is very clearly written in English.',
|
|
'is an english sentence'
|
|
)
|
|
|
|
request.abort('CANCEL')
|
|
await expect(request).rejects.toThrow('CANCEL')
|
|
})
|
|
|
|
it('can abort via external signal', async () => {
|
|
// no caching
|
|
const controller = new AbortController()
|
|
const request = getZai(getCognitiveClient())
|
|
.check('This text is very clearly written in English.', 'is an english sentence')
|
|
.bindSignal(controller.signal)
|
|
|
|
controller.abort('CANCEL2')
|
|
await expect(request).rejects.toThrow('CANCEL2')
|
|
})
|
|
|
|
it('text that is too long gets truncated', async () => {
|
|
const isBotpressDocumentation = await zai.check(
|
|
BotpressDocumentation,
|
|
'is about botpress and looks like documentation'
|
|
)
|
|
|
|
const isAboutBirds = await zai.check(BotpressDocumentation, 'is a book about birds and their species')
|
|
|
|
expect(isBotpressDocumentation).toBe(true)
|
|
expect(isAboutBirds).toBe(false)
|
|
})
|
|
|
|
it('works with any input type', async () => {
|
|
const sly = { name: 'Sylvain Perron', age: 30, job: 'CEO', company: 'Botpress', location: 'Quebec' }
|
|
const american = await zai.check(sly, 'person lives in north america')
|
|
const european = await zai.check(sly, 'person lives in europe')
|
|
|
|
expect(american).toBe(true)
|
|
expect(european).toBe(false)
|
|
})
|
|
|
|
it('retries on generation failure', async () => {
|
|
const cognitive = getCachedCognitiveClient()
|
|
const mocked = getCachedCognitiveClient()
|
|
|
|
let callCount = 0
|
|
|
|
const mock = vi.fn().mockImplementation(async (input) => {
|
|
const output = await cognitive.generateContent(input)
|
|
|
|
if (callCount++ < 1) {
|
|
output.output.choices[0].content = '...'
|
|
}
|
|
|
|
return output
|
|
})
|
|
|
|
mocked.clone = vi.fn().mockReturnValue(mocked)
|
|
mocked.generateContent = mock
|
|
|
|
const isEnglish = await getZai(mocked).check(
|
|
'This text is very clearly written in English',
|
|
'is an english sentence'
|
|
)
|
|
|
|
expect(isEnglish).toBe(true)
|
|
expect(mock).toHaveBeenCalledTimes(2)
|
|
})
|
|
|
|
it('check with examples', async () => {
|
|
const examples = [
|
|
{
|
|
input: 'Rasa (framework)',
|
|
check: true,
|
|
reason: 'Rasa is a chatbot framework, so it competes with us (Botpress).',
|
|
},
|
|
{
|
|
input: 'Rasa (coffee company)',
|
|
check: false,
|
|
reason:
|
|
'Rasa (coffee company) is not in the chatbot or AI agent industry, therefore it does not compete with us (Botpress).',
|
|
},
|
|
{
|
|
input: 'Dialogflow',
|
|
check: true,
|
|
reason: 'Dialogflow is a chatbot development product, so it competes with us (Botpress).',
|
|
},
|
|
]
|
|
|
|
const moveworks = await zai.check('Moveworks (AI chatbot)', 'competes with us', { examples })
|
|
const ada = await zai.check('Ada.cx', 'competes with us', { examples })
|
|
const voiceflow = await zai.check('Voiceflow', 'competes with us', { examples })
|
|
const nike = await zai.check('Nike', 'competes with us', { examples })
|
|
const adidas = await zai.check('Adidas', 'competes with us', { examples })
|
|
|
|
expect(moveworks).toBe(true)
|
|
expect(ada).toBe(true)
|
|
expect(voiceflow).toBe(true)
|
|
|
|
expect(nike).toBe(false)
|
|
expect(adidas).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('zai.learn.check', { timeout: 60_000, sequential: true }, () => {
|
|
const client = getClient()
|
|
let tableName = 'ZaiTestCheckInternalTable'
|
|
let taskId = 'check'
|
|
let zai = getZai()
|
|
|
|
beforeEach(async () => {
|
|
zai = getZai().with({
|
|
activeLearning: {
|
|
enable: true,
|
|
taskId,
|
|
tableName,
|
|
},
|
|
})
|
|
})
|
|
|
|
afterEach(async () => {
|
|
try {
|
|
await client.deleteTableRows({ table: tableName, deleteAllRows: true })
|
|
} catch (err) {}
|
|
})
|
|
|
|
afterAll(async () => {
|
|
try {
|
|
await client.deleteTable({ table: tableName })
|
|
} catch (err) {}
|
|
})
|
|
|
|
it('learns a contradiction from examples', async () => {
|
|
const adapter = new TableAdapter({
|
|
client,
|
|
tableName,
|
|
})
|
|
|
|
const value = await zai.learn(taskId).check(`What's up`, 'is a greeting')
|
|
expect(value).toBe(true)
|
|
|
|
let rows = await client.findTableRows({ table: tableName })
|
|
expect(rows.rows.length).toBe(1)
|
|
expect(rows.rows[0].output.value).toEqual(value)
|
|
|
|
await adapter.saveExample({
|
|
key: 't1',
|
|
taskId: `zai/${taskId}`,
|
|
taskType: 'zai.check',
|
|
instructions: 'is a greeting',
|
|
input: 'what is up',
|
|
output: false,
|
|
explanation: `"What's up" in our business scenario is NOT considered an official greeting.`,
|
|
metadata,
|
|
status: 'approved',
|
|
})
|
|
|
|
await adapter.saveExample({
|
|
key: 't2',
|
|
taskId: `zai/${taskId}`,
|
|
taskType: 'zai.check',
|
|
instructions: 'is a greeting',
|
|
input: 'hello! how are you?',
|
|
output: true,
|
|
explanation: `"hello!" is a common greeting in English.`,
|
|
metadata,
|
|
status: 'approved',
|
|
})
|
|
|
|
await adapter.saveExample({
|
|
key: 't3',
|
|
taskId: `zai/${taskId}`,
|
|
taskType: 'zai.check',
|
|
instructions: 'is a greeting',
|
|
input: 'wassup',
|
|
output: false,
|
|
explanation: `"wassup" is a slang term and not considered a formal greeting. It is therefore NOT considered a greeting.`,
|
|
metadata,
|
|
status: 'approved',
|
|
})
|
|
|
|
const second = await zai.learn(taskId).check(`What's up`, 'is a greeting')
|
|
expect(second).toBe(false)
|
|
rows = await client.findTableRows({ table: tableName })
|
|
|
|
expect(rows.rows.length).toBe(4)
|
|
expect(rows.rows[0].output.value).toEqual(second)
|
|
})
|
|
})
|