mirror of
https://github.com/langgenius/dify.git
synced 2026-07-16 01:48:40 +08:00
test(e2e): cover build draft supported config
This commit is contained in:
@ -45,6 +45,32 @@ Feature: Agent v2 build draft
|
||||
Then I should see the updated E2E prompt in the Agent v2 prompt editor
|
||||
And the Agent v2 Build draft should no longer be active
|
||||
|
||||
Scenario: Applying a Build draft updates supported configuration sections
|
||||
Given I am signed in as the default E2E admin
|
||||
And the Agent Builder stable chat model is available
|
||||
And a runnable Agent v2 test agent has been created via API
|
||||
And an Agent v2 Build draft adds the supported E2E files, skills, and env
|
||||
When I open the Agent v2 configure page
|
||||
Then I should see the Agent v2 Build draft pending changes
|
||||
And I should see the updated E2E prompt in the Agent v2 prompt editor
|
||||
And I should see the small Agent v2 file in the Files section
|
||||
And I should see the e2e-summary-skill Skill in the Skills section
|
||||
And I should see the supported E2E environment variable in Advanced Settings
|
||||
And the normal Agent v2 draft should still use the normal E2E prompt
|
||||
When I apply the Agent v2 Build draft
|
||||
Then I should see the updated E2E prompt in the Agent v2 prompt editor
|
||||
And I should see the small Agent v2 file in the Files section
|
||||
And I should see the e2e-summary-skill Skill in the Skills section
|
||||
And I should see the supported E2E environment variable in Advanced Settings
|
||||
And the Agent v2 draft should include the supported Build draft config
|
||||
And the Agent v2 Build draft should no longer be active
|
||||
When I refresh the current page
|
||||
Then I should see the updated E2E prompt in the Agent v2 prompt editor
|
||||
And I should see the small Agent v2 file in the Files section
|
||||
And I should see the e2e-summary-skill Skill in the Skills section
|
||||
And I should see the supported E2E environment variable in Advanced Settings
|
||||
And the Agent v2 Build draft should no longer be active
|
||||
|
||||
@build-tool-writeback @feature-gated
|
||||
Scenario: Applying a Build draft can add Dify Tools to the Agent configuration
|
||||
Given I am signed in as the default E2E admin
|
||||
|
||||
@ -18,6 +18,8 @@ import {
|
||||
saveAgentComposerDraft,
|
||||
updatedAgentPrompt,
|
||||
updatedAgentSoulConfig,
|
||||
uploadAgentConfigFileToDraft,
|
||||
uploadAgentConfigSkillToDraft,
|
||||
uploadAgentDriveSkill,
|
||||
} from '../../../support/agent'
|
||||
import {
|
||||
@ -133,6 +135,45 @@ const expectAgentConfigFileSaved = async (
|
||||
.toContain(fileName)
|
||||
}
|
||||
|
||||
const openAgentAdvancedSettings = async (page: ReturnType<DifyWorld['getPage']>) => {
|
||||
const advancedSettings = page.getByRole('region', { name: 'Advanced Settings' })
|
||||
const envEditorHeading = advancedSettings.getByRole('heading', { name: 'Env Editor' })
|
||||
|
||||
if (!await envEditorHeading.isVisible().catch(() => false))
|
||||
await page.getByRole('button', { name: 'Advanced Settings' }).first().click()
|
||||
|
||||
await expect(envEditorHeading).toBeVisible()
|
||||
|
||||
return advancedSettings
|
||||
}
|
||||
|
||||
const expectAgentEnvVariableVisible = async (
|
||||
world: DifyWorld,
|
||||
key: string,
|
||||
value: string,
|
||||
) => {
|
||||
const advancedSettings = await openAgentAdvancedSettings(world.getPage())
|
||||
|
||||
await expect.poll(
|
||||
async () => {
|
||||
const text = await advancedSettings.textContent()
|
||||
const inputValues = await advancedSettings.getByRole('textbox').evaluateAll(inputs =>
|
||||
inputs.map(input => (input as HTMLInputElement).value),
|
||||
)
|
||||
|
||||
return {
|
||||
hasKey: inputValues.includes(key) || !!text?.includes(key),
|
||||
hasValue: inputValues.includes(value) || !!text?.includes(value),
|
||||
}
|
||||
},
|
||||
{ timeout: 30_000 },
|
||||
).toEqual({
|
||||
hasKey: true,
|
||||
hasValue: true,
|
||||
})
|
||||
await expect(advancedSettings.getByText('Plain', { exact: true })).toBeVisible()
|
||||
}
|
||||
|
||||
const expectNormalAgentPromptDraft = async (world: DifyWorld) => {
|
||||
await expect.poll(
|
||||
async () => (await getAgentComposerDraft(getCurrentAgentId(world))).agent_soul?.prompt,
|
||||
@ -215,6 +256,55 @@ Then('the Agent v2 test agent should include drive skill {string}', async functi
|
||||
expect(skills.map(skill => skill.name)).toContain(skillName)
|
||||
})
|
||||
|
||||
Given(
|
||||
'an Agent v2 Build draft adds the supported E2E files, skills, and env',
|
||||
async function (this: DifyWorld) {
|
||||
const agentId = getCurrentAgentId(this)
|
||||
const configFile = await uploadAgentConfigFileToDraft({
|
||||
agentId,
|
||||
fileName: agentBuilderTestMaterials.smallFile,
|
||||
filePath: getAgentBuilderTestMaterialPath('smallFile'),
|
||||
})
|
||||
const skill = await uploadAgentConfigSkillToDraft({
|
||||
agentId,
|
||||
fileName: agentBuilderTestMaterials.summarySkill,
|
||||
filePath: getAgentBuilderTestMaterialPath('summarySkill'),
|
||||
})
|
||||
|
||||
if (!configFile.file_id)
|
||||
throw new Error('Agent v2 build draft config file fixture did not return a file_id.')
|
||||
if (!skill.file_id)
|
||||
throw new Error('Agent v2 build draft Skill fixture did not return a file_id.')
|
||||
|
||||
const normalConfig = this.agentBuilderStableChatModel
|
||||
? createAgentSoulConfigWithModel(normalAgentSoulConfig, this.agentBuilderStableChatModel)
|
||||
: normalAgentSoulConfig
|
||||
const updatedConfig = this.agentBuilderStableChatModel
|
||||
? createAgentSoulConfigWithModel(updatedAgentSoulConfig, this.agentBuilderStableChatModel)
|
||||
: updatedAgentSoulConfig
|
||||
|
||||
await saveAgentComposerDraft(agentId, normalConfig)
|
||||
await saveAgentBuildDraft(agentId, {
|
||||
...updatedConfig,
|
||||
config_files: [configFile],
|
||||
config_skills: [{
|
||||
...skill,
|
||||
file_kind: skill.file_kind ?? 'tool_file',
|
||||
}],
|
||||
env: {
|
||||
secret_refs: [],
|
||||
variables: [{
|
||||
id: agentBuilderFixedInputs.envPlainKey,
|
||||
key: agentBuilderFixedInputs.envPlainKey,
|
||||
name: agentBuilderFixedInputs.envPlainKey,
|
||||
value: agentBuilderFixedInputs.envPlainValue,
|
||||
variable: agentBuilderFixedInputs.envPlainKey,
|
||||
}],
|
||||
},
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
Given('an Agent v2 Build draft uses the updated E2E prompt', async function (this: DifyWorld) {
|
||||
await saveAgentBuildDraft(getCurrentAgentId(this), updatedAgentSoulConfig)
|
||||
})
|
||||
@ -553,6 +643,15 @@ Then('I should see the special-name Agent v2 file in the Files section', async f
|
||||
await expectAgentConfigFileVisible(this, 'specialFilename')
|
||||
})
|
||||
|
||||
Then('I should see the e2e-summary-skill Skill in the Skills section', async function (this: DifyWorld) {
|
||||
const skillsSection = this.getPage().getByRole('region', { name: 'Skills' })
|
||||
|
||||
await expect(skillsSection.getByRole('button', {
|
||||
exact: true,
|
||||
name: agentBuilderPreseededResources.summarySkill,
|
||||
})).toBeVisible({ timeout: 30_000 })
|
||||
})
|
||||
|
||||
Then(
|
||||
'the small Agent v2 file should be saved in the Agent v2 draft',
|
||||
async function (this: DifyWorld) {
|
||||
@ -819,10 +918,8 @@ Then(
|
||||
'I should see the plain Agent v2 environment variable in Advanced Settings',
|
||||
async function (this: DifyWorld) {
|
||||
const page = this.getPage()
|
||||
const advancedSettings = page.getByRole('region', { name: 'Advanced Settings' })
|
||||
const advancedSettings = await openAgentAdvancedSettings(page)
|
||||
|
||||
await page.getByRole('button', { name: 'Advanced Settings' }).first().click()
|
||||
await expect(advancedSettings.getByRole('heading', { name: 'Env Editor' })).toBeVisible()
|
||||
await expect(advancedSettings.getByRole('textbox', { name: 'Key' }))
|
||||
.toHaveValue(agentBuilderFixedInputs.envPlainKey)
|
||||
await expect(advancedSettings.getByRole('textbox', { name: 'Value' }))
|
||||
@ -832,6 +929,17 @@ Then(
|
||||
},
|
||||
)
|
||||
|
||||
Then(
|
||||
'I should see the supported E2E environment variable in Advanced Settings',
|
||||
async function (this: DifyWorld) {
|
||||
await expectAgentEnvVariableVisible(
|
||||
this,
|
||||
agentBuilderFixedInputs.envPlainKey,
|
||||
agentBuilderFixedInputs.envPlainValue,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
Then(
|
||||
'I should see the Agent v2 environment variables from the invalid import in Advanced Settings',
|
||||
async function (this: DifyWorld) {
|
||||
@ -896,6 +1004,31 @@ Then(
|
||||
},
|
||||
)
|
||||
|
||||
Then(
|
||||
'the Agent v2 draft should include the supported Build draft config',
|
||||
async function (this: DifyWorld) {
|
||||
await expect.poll(
|
||||
async () => {
|
||||
const agentSoul = (await getAgentComposerDraft(getCurrentAgentId(this))).agent_soul
|
||||
const variables = agentSoul?.env?.variables ?? []
|
||||
|
||||
return {
|
||||
envValue: getAgentEnvVariableValue(variables, agentBuilderFixedInputs.envPlainKey),
|
||||
fileNames: agentSoul?.config_files?.map(file => file.name) ?? [],
|
||||
prompt: agentSoul?.prompt,
|
||||
skillNames: agentSoul?.config_skills?.map(skill => skill.name) ?? [],
|
||||
}
|
||||
},
|
||||
{ timeout: 30_000 },
|
||||
).toEqual({
|
||||
envValue: agentBuilderFixedInputs.envPlainValue,
|
||||
fileNames: expect.arrayContaining([agentBuilderTestMaterials.smallFile]),
|
||||
prompt: { system_prompt: updatedAgentPrompt },
|
||||
skillNames: expect.arrayContaining([agentBuilderPreseededResources.summarySkill]),
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
Then('the Agent v2 Build draft should no longer be active', async function (this: DifyWorld) {
|
||||
const page = this.getPage()
|
||||
|
||||
|
||||
@ -28,6 +28,15 @@ export type AgentComposerConfigFile = {
|
||||
name: string
|
||||
size?: number | null
|
||||
}
|
||||
export type AgentComposerConfigSkill = {
|
||||
description?: string | null
|
||||
file_id?: string | null
|
||||
file_kind?: string | null
|
||||
hash?: string | null
|
||||
mime_type?: string | null
|
||||
name: string
|
||||
size?: number | null
|
||||
}
|
||||
export type AgentComposerEnvVariable = {
|
||||
id?: string | null
|
||||
key?: string | null
|
||||
@ -38,6 +47,7 @@ export type AgentComposerEnvVariable = {
|
||||
|
||||
export type AgentSoulConfig = Record<string, unknown> & {
|
||||
config_files?: AgentComposerConfigFile[]
|
||||
config_skills?: AgentComposerConfigSkill[]
|
||||
env?: {
|
||||
secret_refs?: unknown[]
|
||||
variables?: AgentComposerEnvVariable[]
|
||||
@ -93,6 +103,21 @@ export type AgentDriveSkillUpload = {
|
||||
}
|
||||
}
|
||||
|
||||
export type AgentConfigSkillUpload = {
|
||||
skill: AgentComposerConfigSkill
|
||||
}
|
||||
|
||||
export type UploadedConsoleFile = {
|
||||
id: string
|
||||
mime_type?: string | null
|
||||
name: string
|
||||
size?: number | null
|
||||
}
|
||||
|
||||
export type AgentConfigFileUpload = {
|
||||
file: AgentComposerConfigFile
|
||||
}
|
||||
|
||||
export type AgentDriveSkill = {
|
||||
description?: string | null
|
||||
name: string
|
||||
@ -389,6 +414,83 @@ export async function uploadAgentDriveSkill({
|
||||
}
|
||||
}
|
||||
|
||||
export async function uploadAgentConfigFileToDraft({
|
||||
agentId,
|
||||
fileName,
|
||||
filePath,
|
||||
}: {
|
||||
agentId: string
|
||||
fileName: string
|
||||
filePath: string
|
||||
}): Promise<AgentComposerConfigFile> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const uploadResponse = await ctx.post('/console/api/files/upload', {
|
||||
multipart: {
|
||||
file: {
|
||||
buffer: await readFile(filePath),
|
||||
mimeType: 'text/plain',
|
||||
name: fileName,
|
||||
},
|
||||
},
|
||||
})
|
||||
await expectApiResponseOK(uploadResponse, `Upload Agent v2 config source file ${fileName}`)
|
||||
const uploadedFile = (await uploadResponse.json()) as UploadedConsoleFile
|
||||
|
||||
const commitResponse = await ctx.post(`/console/api/agent/${agentId}/config/files`, {
|
||||
data: {
|
||||
upload_file_id: uploadedFile.id,
|
||||
},
|
||||
})
|
||||
await expectApiResponseOK(commitResponse, `Commit Agent v2 config file ${fileName} for ${agentId}`)
|
||||
const body = (await commitResponse.json()) as AgentConfigFileUpload
|
||||
const { id: _id, ...file } = body.file as AgentComposerConfigFile & { id?: string }
|
||||
|
||||
return {
|
||||
...file,
|
||||
file_kind: file.file_kind ?? 'upload_file',
|
||||
}
|
||||
}
|
||||
finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function uploadAgentConfigSkillToDraft({
|
||||
agentId,
|
||||
fileName,
|
||||
filePath,
|
||||
}: {
|
||||
agentId: string
|
||||
fileName: string
|
||||
filePath: string
|
||||
}): Promise<AgentComposerConfigSkill> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const upload = await toSkillArchiveUpload({ fileName, filePath })
|
||||
const response = await ctx.post(`/console/api/agent/${agentId}/config/skills/upload`, {
|
||||
multipart: {
|
||||
file: {
|
||||
buffer: upload.buffer,
|
||||
mimeType: 'application/zip',
|
||||
name: upload.name,
|
||||
},
|
||||
},
|
||||
})
|
||||
await expectApiResponseOK(response, `Upload Agent v2 config skill ${fileName} for ${agentId}`)
|
||||
const body = (await response.json()) as AgentConfigSkillUpload
|
||||
const { id: _id, ...skill } = body.skill as AgentComposerConfigSkill & { id?: string }
|
||||
|
||||
return {
|
||||
...skill,
|
||||
file_kind: skill.file_kind ?? 'tool_file',
|
||||
}
|
||||
}
|
||||
finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAgentDriveSkills(agentId: string): Promise<AgentDriveSkill[]> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user