mirror of
https://github.com/langgenius/dify.git
synced 2026-07-30 08:49:31 +08:00
fix(e2e): align fixtures with generated contracts (#39394)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
c5d471dbdd
commit
05a25943e7
@ -246,7 +246,7 @@ class ModelConfigPartial(ResponseModel):
|
||||
return to_timestamp(value)
|
||||
|
||||
|
||||
class ModelConfig(ResponseModel):
|
||||
class AppModelConfigResponse(ResponseModel):
|
||||
opening_statement: str | None = None
|
||||
suggested_questions: Any | None = Field(
|
||||
default=None, validation_alias=AliasChoices("suggested_questions_list", "suggested_questions")
|
||||
@ -419,7 +419,7 @@ class AppDetail(AppResponseModel):
|
||||
icon_background: str | None = None
|
||||
enable_site: bool
|
||||
enable_api: bool
|
||||
model_config_: ModelConfig | None = Field(
|
||||
model_config_: AppModelConfigResponse | None = Field(
|
||||
default=None,
|
||||
validation_alias=AliasChoices("app_model_config", "model_config"),
|
||||
alias="model_config",
|
||||
@ -525,7 +525,13 @@ def _enrich_app_list_items(session: Session, *, apps: Sequence[App], tenant_id:
|
||||
|
||||
register_enum_models(console_ns, RetrievalMethod, WorkflowExecutionStatus, DatasetPermissionEnum)
|
||||
register_response_schema_models(
|
||||
console_ns, RedirectUrlResponse, SimpleResultResponse, AppImportResponse, AppTraceResponse
|
||||
console_ns,
|
||||
RedirectUrlResponse,
|
||||
SimpleResultResponse,
|
||||
AppImportResponse,
|
||||
AppTraceResponse,
|
||||
AppModelConfigResponse,
|
||||
AppDetail,
|
||||
)
|
||||
|
||||
register_schema_models(
|
||||
@ -544,10 +550,8 @@ register_schema_models(
|
||||
Tag,
|
||||
WorkflowPartial,
|
||||
ModelConfigPartial,
|
||||
ModelConfig,
|
||||
AppDetailSiteResponse,
|
||||
DeletedTool,
|
||||
AppDetail,
|
||||
AppExportResponse,
|
||||
Segmentation,
|
||||
PreProcessingRule,
|
||||
|
||||
@ -359,6 +359,12 @@ class WorkflowPublishResponse(ResponseModel):
|
||||
created_at: int
|
||||
|
||||
|
||||
class SyncDraftWorkflowResponse(ResponseModel):
|
||||
result: str
|
||||
hash: str
|
||||
updated_at: int
|
||||
|
||||
|
||||
class WorkflowRestoreResponse(ResponseModel):
|
||||
result: str
|
||||
hash: str
|
||||
@ -441,6 +447,7 @@ register_response_schema_models(
|
||||
WorkflowOnlineUsersByApp,
|
||||
WorkflowOnlineUsersResponse,
|
||||
WorkflowPublishResponse,
|
||||
SyncDraftWorkflowResponse,
|
||||
WorkflowRestoreResponse,
|
||||
DefaultBlockConfigsResponse,
|
||||
DefaultBlockConfigResponse,
|
||||
@ -556,14 +563,7 @@ class DraftWorkflowApi(Resource):
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Draft workflow synced successfully",
|
||||
console_ns.model(
|
||||
"SyncDraftWorkflowResponse",
|
||||
{
|
||||
"result": fields.String,
|
||||
"hash": fields.String,
|
||||
"updated_at": fields.String,
|
||||
},
|
||||
),
|
||||
console_ns.models[SyncDraftWorkflowResponse.__name__],
|
||||
)
|
||||
@console_ns.response(400, "Invalid workflow configuration")
|
||||
@console_ns.response(403, "Permission denied")
|
||||
@ -618,11 +618,14 @@ class DraftWorkflowApi(Resource):
|
||||
except VariableError as e:
|
||||
raise InvalidArgumentError(description=str(e))
|
||||
|
||||
return {
|
||||
"result": "success",
|
||||
"hash": workflow.unique_hash,
|
||||
"updated_at": TimestampField().format(workflow.updated_at or workflow.created_at),
|
||||
}
|
||||
return dump_response(
|
||||
SyncDraftWorkflowResponse,
|
||||
{
|
||||
"result": "success",
|
||||
"hash": workflow.unique_hash,
|
||||
"updated_at": TimestampField().format(workflow.updated_at or workflow.created_at),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/advanced-chat/workflows/draft/run")
|
||||
|
||||
@ -13278,7 +13278,7 @@ Model class for AI model.
|
||||
| maintainer | string | | No |
|
||||
| max_active_requests | integer | | No |
|
||||
| mode | string | | Yes |
|
||||
| model_config | [ModelConfig](#modelconfig) | | No |
|
||||
| model_config | [AppModelConfigResponse](#appmodelconfigresponse) | | No |
|
||||
| name | string | | Yes |
|
||||
| permission_keys | [ string ] | | No |
|
||||
| role | string | | No |
|
||||
@ -15348,7 +15348,6 @@ This class is used to store the schema information of an api based tool.
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| access_mode | string | | No |
|
||||
| app_model_config | [ModelConfig](#modelconfig) | | No |
|
||||
| created_at | integer | | No |
|
||||
| created_by | string | | No |
|
||||
| description | string | | No |
|
||||
@ -15358,7 +15357,8 @@ This class is used to store the schema information of an api based tool.
|
||||
| icon_background | string | | No |
|
||||
| id | string | | Yes |
|
||||
| maintainer | string | | No |
|
||||
| mode_compatible_with_agent | string | | Yes |
|
||||
| mode | string | | Yes |
|
||||
| model_config | [AppModelConfigResponse](#appmodelconfigresponse) | | No |
|
||||
| name | string | | Yes |
|
||||
| permission_keys | [ string ] | | No |
|
||||
| tags | [ [Tag](#tag) ] | | No |
|
||||
@ -15420,7 +15420,7 @@ This class is used to store the schema information of an api based tool.
|
||||
| maintainer | string | | No |
|
||||
| max_active_requests | integer | | No |
|
||||
| mode | string | | Yes |
|
||||
| model_config | [ModelConfig](#modelconfig) | | No |
|
||||
| model_config | [AppModelConfigResponse](#appmodelconfigresponse) | | No |
|
||||
| name | string | | Yes |
|
||||
| permission_keys | [ string ] | | No |
|
||||
| site | [AppDetailSiteResponse](#appdetailsiteresponse) | | No |
|
||||
@ -15519,6 +15519,35 @@ AppMCPServer Status Enum
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| AppMCPServerStatus | string | AppMCPServer Status Enum | |
|
||||
|
||||
#### AppModelConfigResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| agent_mode | | | No |
|
||||
| annotation_reply | | | No |
|
||||
| chat_prompt_config | | | No |
|
||||
| completion_prompt_config | | | No |
|
||||
| created_at | integer | | No |
|
||||
| created_by | string | | No |
|
||||
| dataset_configs | | | No |
|
||||
| dataset_query_variable | string | | No |
|
||||
| external_data_tools | | | No |
|
||||
| file_upload | | | No |
|
||||
| model | | | No |
|
||||
| more_like_this | | | No |
|
||||
| opening_statement | string | | No |
|
||||
| pre_prompt | string | | No |
|
||||
| prompt_type | string | | No |
|
||||
| retriever_resource | | | No |
|
||||
| sensitive_word_avoidance | | | No |
|
||||
| speech_to_text | | | No |
|
||||
| suggested_questions | | | No |
|
||||
| suggested_questions_after_answer | | | No |
|
||||
| text_to_speech | | | No |
|
||||
| updated_at | integer | | No |
|
||||
| updated_by | string | | No |
|
||||
| user_input_form | | | No |
|
||||
|
||||
#### AppNamePayload
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
@ -21954,9 +21983,9 @@ The subscription constructor of the trigger provider
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| hash | string | | No |
|
||||
| result | string | | No |
|
||||
| updated_at | string | | No |
|
||||
| hash | string | | Yes |
|
||||
| result | string | | Yes |
|
||||
| updated_at | integer | | Yes |
|
||||
|
||||
#### SystemConfigurationResponse
|
||||
|
||||
|
||||
@ -212,6 +212,17 @@ def test_generate_specs_include_console_contract_shapes_for_schema_migration(tmp
|
||||
assert {"type": "null"} in app_detail_nullable_schema["anyOf"]
|
||||
assert schemas["RecommendedAppInfoResponse"]["properties"]["icon_url"]["readOnly"] is True
|
||||
assert schemas["InstalledAppInfoResponse"]["properties"]["icon_url"]["readOnly"] is True
|
||||
assert _response_schema(paths["/apps/{app_id}"]["get"])["$ref"] == "#/components/schemas/AppDetailWithSite"
|
||||
app_model_config = schemas["AppDetailWithSite"]["properties"]["model_config"]
|
||||
assert {"$ref": "#/components/schemas/AppModelConfigResponse"} in app_model_config["anyOf"]
|
||||
app_detail = schemas["AppDetail"]
|
||||
assert "mode" in app_detail["properties"]
|
||||
assert "mode_compatible_with_agent" not in app_detail["properties"]
|
||||
sync_draft_workflow = schemas["SyncDraftWorkflowResponse"]
|
||||
assert _response_schema(paths["/apps/{app_id}/workflows/draft"]["post"])["$ref"] == (
|
||||
"#/components/schemas/SyncDraftWorkflowResponse"
|
||||
)
|
||||
assert sync_draft_workflow["properties"]["updated_at"]["type"] == "integer"
|
||||
tool_icon_schema = schemas["ExploreAppMetaResponse"]["properties"]["tool_icons"]["additionalProperties"]
|
||||
assert {"type": "string"} in tool_icon_schema["anyOf"]
|
||||
assert {"additionalProperties": True, "type": "object"} in tool_icon_schema["anyOf"]
|
||||
|
||||
@ -1,12 +1,18 @@
|
||||
import type {
|
||||
AgentApiAccessResponse,
|
||||
AgentApiStatusPayload,
|
||||
ApiKeyItem,
|
||||
} from '@dify/contracts/api/console/agent/types.gen'
|
||||
import type {
|
||||
ChatRequestPayloadWithUser,
|
||||
PostChatMessagesResponse,
|
||||
} from '@dify/contracts/api/service/types.gen'
|
||||
import { createApiContext, expectApiResponseOK, setAppSiteEnabled } from '../../../support/api'
|
||||
import {
|
||||
zPostAgentByAgentIdApiEnableResponse,
|
||||
zPostAgentByAgentIdApiKeysResponse,
|
||||
} from '@dify/contracts/api/console/agent/zod.gen'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from '../../../support/api/console-context'
|
||||
import { setAppSiteEnabled } from '../../../support/api/web-apps'
|
||||
import { getTestAgent } from './agent'
|
||||
import { consumeServiceApiSse, SERVICE_API_STREAM_TIMEOUT_MS } from './service-api-sse'
|
||||
|
||||
@ -38,47 +44,40 @@ async function parseServiceApiChatResponse(response: Response) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function setAgentSiteAccessAndGetURL(
|
||||
agentId: string,
|
||||
enabled: boolean,
|
||||
): Promise<string> {
|
||||
export async function setAgentSiteAccess(agentId: string, enabled: boolean): Promise<void> {
|
||||
const agent = await getTestAgent(agentId)
|
||||
const appId = agent.app_id ?? agent.backing_app_id
|
||||
if (!appId) throw new Error(`Agent v2 ${agentId} does not expose a backing app ID.`)
|
||||
|
||||
const appDetail = await setAppSiteEnabled(appId, enabled)
|
||||
const token = agent.site?.access_token ?? agent.site?.code ?? appDetail.site?.access_token
|
||||
const baseURL = agent.site?.app_base_url ?? appDetail.site?.app_base_url
|
||||
if (!token || !baseURL) throw new Error(`Agent v2 ${agentId} does not expose a Web App URL.`)
|
||||
|
||||
return `${baseURL.replace(/\/$/, '')}/agent/${token}`
|
||||
await setAppSiteEnabled(appId, enabled)
|
||||
}
|
||||
|
||||
export async function setAgentApiAccess(
|
||||
agentId: string,
|
||||
enabled: boolean,
|
||||
): Promise<AgentApiAccessResponse> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = { enable_api: enabled } satisfies AgentApiStatusPayload
|
||||
const response = await ctx.post(`/console/api/agent/${agentId}/api-enable`, {
|
||||
data: { enable_api: enabled },
|
||||
data,
|
||||
})
|
||||
await expectApiResponseOK(
|
||||
response,
|
||||
`${enabled ? 'Enable' : 'Disable'} Agent v2 API access for ${agentId}`,
|
||||
)
|
||||
return (await response.json()) as AgentApiAccessResponse
|
||||
return zPostAgentByAgentIdApiEnableResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function createAgentApiKey(agentId: string): Promise<ApiKeyItem> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.post(`/console/api/agent/${agentId}/api-keys`)
|
||||
await expectApiResponseOK(response, `Create Agent v2 API key for ${agentId}`)
|
||||
return (await response.json()) as ApiKeyItem
|
||||
return zPostAgentByAgentIdApiKeysResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
|
||||
@ -2,10 +2,10 @@ import type {
|
||||
AgentBuildDraftResponse,
|
||||
AgentSoulConfig,
|
||||
} from '@dify/contracts/api/console/agent/types.gen'
|
||||
import { createApiContext, expectApiResponseOK } from '../../../support/api'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from '../../../support/api/console-context'
|
||||
|
||||
export async function checkoutAgentBuildDraft(agentId: string): Promise<AgentBuildDraftResponse> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.post(`/console/api/agent/${agentId}/build-draft/checkout`, {
|
||||
data: { force: true },
|
||||
@ -21,7 +21,7 @@ export async function saveAgentBuildDraft(
|
||||
agentId: string,
|
||||
agentSoul: AgentSoulConfig,
|
||||
): Promise<AgentBuildDraftResponse> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.put(`/console/api/agent/${agentId}/build-draft`, {
|
||||
data: {
|
||||
@ -38,7 +38,7 @@ export async function saveAgentBuildDraft(
|
||||
}
|
||||
|
||||
export async function agentBuildDraftExists(agentId: string): Promise<boolean> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/agent/${agentId}/build-draft`)
|
||||
if (response.status() === 404) return false
|
||||
@ -51,7 +51,7 @@ export async function agentBuildDraftExists(agentId: string): Promise<boolean> {
|
||||
}
|
||||
|
||||
export async function getAgentBuildDraft(agentId: string): Promise<AgentBuildDraftResponse> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/agent/${agentId}/build-draft`)
|
||||
await expectApiResponseOK(response, `Get Agent v2 build draft for ${agentId}`)
|
||||
@ -62,7 +62,7 @@ export async function getAgentBuildDraft(agentId: string): Promise<AgentBuildDra
|
||||
}
|
||||
|
||||
export async function discardAgentBuildDraft(agentId: string): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.delete(`/console/api/agent/${agentId}/build-draft`)
|
||||
await expectApiResponseOK(response, `Discard Agent v2 build draft for ${agentId}`)
|
||||
|
||||
@ -10,7 +10,7 @@ import type {
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { createApiContext, expectApiResponseOK } from '../../../support/api'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from '../../../support/api/console-context'
|
||||
|
||||
export type UploadedConsoleFile = {
|
||||
id: string
|
||||
@ -126,7 +126,7 @@ export async function uploadAgentDriveSkill({
|
||||
fileName: string
|
||||
filePath: string
|
||||
}): Promise<AgentSkillUploadResponse> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const upload = await toSkillArchiveUpload({ fileName, filePath })
|
||||
const response = await ctx.post(`/console/api/agent/${agentId}/skills/upload`, {
|
||||
@ -154,7 +154,7 @@ export async function uploadAgentConfigFileToDraft({
|
||||
fileName: string
|
||||
filePath: string
|
||||
}): Promise<AgentConfigFileRefConfig> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const uploadResponse = await ctx.post('/console/api/files/upload', {
|
||||
multipart: {
|
||||
@ -203,7 +203,7 @@ export async function uploadAgentConfigSkillToDraft({
|
||||
fileName: string
|
||||
filePath: string
|
||||
}): Promise<AgentConfigSkillRefConfig> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const upload = await toSkillArchiveUpload({ fileName, filePath })
|
||||
const response = await ctx.post(`/console/api/agent/${agentId}/config/skills/upload`, {
|
||||
@ -236,7 +236,7 @@ export async function uploadAgentConfigSkillToDraft({
|
||||
}
|
||||
|
||||
export async function getAgentDriveSkills(agentId: string): Promise<AgentDriveSkillItemResponse[]> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/agent/${agentId}/drive/skills`)
|
||||
await expectApiResponseOK(response, `Get Agent v2 drive skills for ${agentId}`)
|
||||
@ -248,7 +248,7 @@ export async function getAgentDriveSkills(agentId: string): Promise<AgentDriveSk
|
||||
}
|
||||
|
||||
export async function deleteAgentConfigFile(agentId: string, name: string): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.delete(
|
||||
`/console/api/agent/${agentId}/config/files/${encodeURIComponent(name)}`,
|
||||
@ -260,7 +260,7 @@ export async function deleteAgentConfigFile(agentId: string, name: string): Prom
|
||||
}
|
||||
|
||||
export async function deleteAgentConfigSkill(agentId: string, name: string): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.delete(
|
||||
`/console/api/agent/${agentId}/config/skills/${encodeURIComponent(name)}`,
|
||||
@ -272,7 +272,7 @@ export async function deleteAgentConfigSkill(agentId: string, name: string): Pro
|
||||
}
|
||||
|
||||
export async function deleteAgentDriveFile(agentId: string, key: string): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const searchParams = new URLSearchParams({ key })
|
||||
const response = await ctx.delete(`/console/api/agent/${agentId}/files?${searchParams}`)
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
import type {
|
||||
AgentAppComposerResponse,
|
||||
AgentAppCreatePayload,
|
||||
AgentAppDetailWithSite,
|
||||
AgentReferencingWorkflowResponse,
|
||||
AgentReferencingWorkflowsResponse,
|
||||
AgentSoulConfig,
|
||||
} from '@dify/contracts/api/console/agent/types.gen'
|
||||
import { createApiContext, expectApiResponseOK } from '../../../support/api'
|
||||
import {
|
||||
zGetAgentByAgentIdResponse,
|
||||
zPostAgentResponse,
|
||||
} from '@dify/contracts/api/console/agent/zod.gen'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from '../../../support/api/console-context'
|
||||
import { assertE2EResourceName, createE2EResourceName } from '../../../support/naming'
|
||||
import {
|
||||
createPublishableAgentSoulConfig,
|
||||
@ -13,21 +18,6 @@ import {
|
||||
normalAgentSoulConfig,
|
||||
} from './agent-soul'
|
||||
|
||||
export type AgentSeed = Pick<
|
||||
AgentAppDetailWithSite,
|
||||
| 'active_config_is_published'
|
||||
| 'app_id'
|
||||
| 'backing_app_id'
|
||||
| 'description'
|
||||
| 'enable_site'
|
||||
| 'id'
|
||||
| 'name'
|
||||
| 'role'
|
||||
| 'site'
|
||||
> & {
|
||||
active_config_snapshot_id?: string | null
|
||||
}
|
||||
|
||||
export type CreateTestAgentOptions = {
|
||||
description?: string
|
||||
name?: string
|
||||
@ -41,22 +31,23 @@ export async function createTestAgent({
|
||||
description = 'Created by Dify E2E.',
|
||||
name = createE2EResourceName('Agent'),
|
||||
role = 'E2E test assistant',
|
||||
}: CreateTestAgentOptions = {}): Promise<AgentSeed> {
|
||||
}: CreateTestAgentOptions = {}): Promise<AgentAppDetailWithSite> {
|
||||
assertE2EResourceName(name, 'Agent')
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = {
|
||||
description,
|
||||
icon: '🤖',
|
||||
icon_background: '#FFEAD5',
|
||||
icon_type: 'emoji',
|
||||
name,
|
||||
role,
|
||||
} satisfies AgentAppCreatePayload
|
||||
const response = await ctx.post('/console/api/agent', {
|
||||
data: {
|
||||
description,
|
||||
icon: '🤖',
|
||||
icon_background: '#FFEAD5',
|
||||
icon_type: 'emoji',
|
||||
name,
|
||||
role,
|
||||
},
|
||||
data,
|
||||
})
|
||||
await expectApiResponseOK(response, 'Create Agent v2 test agent')
|
||||
return (await response.json()) as AgentSeed
|
||||
return zPostAgentResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
@ -68,25 +59,25 @@ export async function createConfiguredTestAgent({
|
||||
}: {
|
||||
agentSoul?: AgentSoulConfig
|
||||
seed?: CreateTestAgentOptions
|
||||
} = {}): Promise<AgentSeed> {
|
||||
} = {}): Promise<AgentAppDetailWithSite> {
|
||||
const agent = await createTestAgent(seed)
|
||||
await saveAgentComposerDraft(agent.id, agentSoul)
|
||||
return agent
|
||||
}
|
||||
|
||||
export async function getTestAgent(agentId: string): Promise<AgentSeed> {
|
||||
const ctx = await createApiContext()
|
||||
export async function getTestAgent(agentId: string): Promise<AgentAppDetailWithSite> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/agent/${agentId}`)
|
||||
await expectApiResponseOK(response, `Get Agent v2 test agent ${agentId}`)
|
||||
return (await response.json()) as AgentSeed
|
||||
return zGetAgentByAgentIdResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteTestAgent(agentId: string): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.delete(`/console/api/agent/${agentId}`)
|
||||
await expectApiResponseOK(response, `Delete Agent v2 test agent ${agentId}`)
|
||||
@ -99,7 +90,7 @@ export async function saveAgentComposerDraft(
|
||||
agentId: string,
|
||||
agentSoul: AgentSoulConfig = defaultAgentSoulConfig,
|
||||
): Promise<AgentAppComposerResponse> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.put(`/console/api/agent/${agentId}/composer`, {
|
||||
data: {
|
||||
@ -118,7 +109,7 @@ export async function saveAgentComposerDraft(
|
||||
export async function getAgentReferencingWorkflows(
|
||||
agentId: string,
|
||||
): Promise<AgentReferencingWorkflowResponse[]> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/agent/${agentId}/referencing-workflows`)
|
||||
await expectApiResponseOK(response, `Get Agent v2 referencing workflows for ${agentId}`)
|
||||
@ -130,7 +121,7 @@ export async function getAgentReferencingWorkflows(
|
||||
}
|
||||
|
||||
export async function getAgentComposerDraft(agentId: string): Promise<AgentAppComposerResponse> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/agent/${agentId}/composer`)
|
||||
await expectApiResponseOK(response, `Get Agent v2 composer draft for ${agentId}`)
|
||||
@ -150,7 +141,7 @@ export async function ensureAgentComposerDraftIsPublishable(agentId: string): Pr
|
||||
}
|
||||
|
||||
export async function publishAgent(agentId: string, versionNote = 'E2E publish'): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.post(`/console/api/agent/${agentId}/publish`, {
|
||||
data: { version_note: versionNote },
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import type { AgentReferencingWorkflowsResponse } from '@dify/contracts/api/console/agent/types.gen'
|
||||
import type { DifyWorld } from '../../../support/world'
|
||||
import type { PreseededResource } from './common'
|
||||
import { createApiContext, expectApiResponseOK } from '../../../../support/api'
|
||||
import {
|
||||
createConsoleApiContext,
|
||||
expectApiResponseOK,
|
||||
} from '../../../../support/api/console-context'
|
||||
import { requirePreseededAgent, requirePreseededWorkflow } from './agents'
|
||||
import { failFixturePrerequisite } from './common'
|
||||
|
||||
@ -14,7 +17,7 @@ export async function requirePreseededAgentWorkflowReference(
|
||||
|
||||
const workflow = await requirePreseededWorkflow(world, workflowName)
|
||||
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/agent/${agent.id}/referencing-workflows`)
|
||||
await expectApiResponseOK(response, `Check preseeded Agent workflow reference ${agentName}`)
|
||||
|
||||
@ -4,7 +4,10 @@ import type {
|
||||
} from '@dify/contracts/api/console/agent/types.gen'
|
||||
import type { DifyWorld } from '../../../support/world'
|
||||
import type { PreseededResource } from './common'
|
||||
import { createApiContext, expectApiResponseOK } from '../../../../support/api'
|
||||
import {
|
||||
createConsoleApiContext,
|
||||
expectApiResponseOK,
|
||||
} from '../../../../support/api/console-context'
|
||||
import {
|
||||
agentBuilderExpectedTokens,
|
||||
agentBuilderFixedInputs,
|
||||
@ -124,7 +127,7 @@ export async function requirePreseededAgentDriveSkill(
|
||||
): Promise<PreseededResource> {
|
||||
const agent = await requirePreseededAgent(world, agentName)
|
||||
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/agent/${agent.id}/drive/skills`)
|
||||
await expectApiResponseOK(response, `Check preseeded Agent skill ${skillName}`)
|
||||
@ -169,7 +172,7 @@ export async function requirePreseededFullConfigAgentCoreConfiguration(
|
||||
agentBuilderPreseededResources.agentKnowledgeBase,
|
||||
)
|
||||
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/agent/${agent.id}/composer`)
|
||||
await expectApiResponseOK(response, `Check preseeded Agent core configuration ${agentName}`)
|
||||
@ -246,7 +249,7 @@ export async function requirePreseededToolStatesAgentConfiguration(
|
||||
agentBuilderPreseededResources.tavilySearchTool,
|
||||
)
|
||||
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/agent/${agent.id}/composer`)
|
||||
await expectApiResponseOK(response, `Check preseeded Agent tool states ${agentName}`)
|
||||
@ -320,7 +323,7 @@ export async function requirePreseededDualRetrievalAgentConfiguration(
|
||||
agentBuilderPreseededResources.agentKnowledgeBase,
|
||||
)
|
||||
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/agent/${agent.id}/composer`)
|
||||
await expectApiResponseOK(response, `Check preseeded Agent dual retrieval ${agentName}`)
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import type { DifyWorld } from '../../../support/world'
|
||||
import { createApiContext, expectApiResponseOK } from '../../../../support/api'
|
||||
import {
|
||||
createConsoleApiContext,
|
||||
expectApiResponseOK,
|
||||
} from '../../../../support/api/console-context'
|
||||
|
||||
export type PreseededResource = NonNullable<
|
||||
DifyWorld['agentBuilder']['fixtures']['preseededResources'][string]
|
||||
@ -45,7 +48,7 @@ export const findConsoleResourceByName = async <T extends NamedResource = NamedR
|
||||
path: string
|
||||
resourceName: string
|
||||
}) => {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(path)
|
||||
await expectApiResponseOK(response, action)
|
||||
|
||||
@ -6,7 +6,10 @@ import type {
|
||||
} from '@dify/contracts/api/console/datasets/types.gen'
|
||||
import type { DifyWorld } from '../../../support/world'
|
||||
import type { PreseededResource } from './common'
|
||||
import { createApiContext, expectApiResponseOK } from '../../../../support/api'
|
||||
import {
|
||||
createConsoleApiContext,
|
||||
expectApiResponseOK,
|
||||
} from '../../../../support/api/console-context'
|
||||
import {
|
||||
agentBuilderExpectedTokens,
|
||||
agentBuilderFixedInputs,
|
||||
@ -34,7 +37,7 @@ export const getPreseededDataset = async (resourceName: string) => {
|
||||
}
|
||||
|
||||
const getDatasetIndexingStatuses = async (datasetId: string, resourceName: string) => {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/datasets/${datasetId}/indexing-status`)
|
||||
await expectApiResponseOK(response, `Check preseeded dataset indexing status ${resourceName}`)
|
||||
@ -48,7 +51,7 @@ const getDatasetIndexingStatuses = async (datasetId: string, resourceName: strin
|
||||
|
||||
const getDatasetDocuments = async (datasetId: string, resourceName: string) => {
|
||||
const documents: DocumentWithSegmentsListResponse['data'] = []
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
let page = 1
|
||||
let hasMore = true
|
||||
@ -76,7 +79,7 @@ const datasetHasEnabledSegmentContainingTokens = async (
|
||||
expectedTokens: string[],
|
||||
) => {
|
||||
const documents = await getDatasetDocuments(datasetId, resourceName)
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
for (const document of documents) {
|
||||
const query = buildQuery({
|
||||
|
||||
@ -3,7 +3,10 @@ import type {
|
||||
ProviderWithModelsResponse,
|
||||
} from '@dify/contracts/api/console/workspaces/types.gen'
|
||||
import type { DifyWorld } from '../../../support/world'
|
||||
import { createApiContext, expectApiResponseOK } from '../../../../support/api'
|
||||
import {
|
||||
createConsoleApiContext,
|
||||
expectApiResponseOK,
|
||||
} from '../../../../support/api/console-context'
|
||||
import { agentBuilderPreseededResources } from '../agent-builder-resources'
|
||||
import { failFixturePrerequisite } from './common'
|
||||
|
||||
@ -82,7 +85,7 @@ async function requireAgentBuilderModel(
|
||||
): Promise<NonNullable<DifyWorld['agentBuilder']['fixtures']['stableModel']>> {
|
||||
if (!config.ok) return failFixturePrerequisite(world, config.reason)
|
||||
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(
|
||||
`/console/api/workspaces/current/models/model-types/${config.type}`,
|
||||
@ -132,7 +135,7 @@ export async function requireAgentBuilderStableChatModel(
|
||||
export async function requireAgentBuilderSpeechToTextModel(
|
||||
world: DifyWorld,
|
||||
): Promise<NonNullable<DifyWorld['agentBuilder']['fixtures']['speechToTextModel']>> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
let defaultModel: NonNullable<DefaultModelDataResponse['data']>
|
||||
|
||||
try {
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import type { DifyWorld } from '../../../support/world'
|
||||
import type { LocalizedLabel, PreseededResource } from './common'
|
||||
import { createApiContext, expectApiResponseOK } from '../../../../support/api'
|
||||
import {
|
||||
createConsoleApiContext,
|
||||
expectApiResponseOK,
|
||||
} from '../../../../support/api/console-context'
|
||||
import { asRecord, asString, failFixturePrerequisite, matchesNameOrLabel } from './common'
|
||||
|
||||
type BuiltinToolProvider = {
|
||||
@ -93,7 +96,7 @@ export async function requirePreseededTool(
|
||||
const parsed = splitToolDisplayName(resourceName)
|
||||
if (!parsed.ok) return failFixturePrerequisite(world, parsed.reason)
|
||||
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get('/console/api/workspaces/current/tools/builtin')
|
||||
await expectApiResponseOK(response, `Check preseeded tool ${resourceName}`)
|
||||
|
||||
@ -18,13 +18,9 @@ import type {
|
||||
import type { SeedContext, SeedResource, SeedTask } from '../../../support/seed'
|
||||
import type { UploadedConsoleFile } from './agent-drive'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import {
|
||||
createApiContext,
|
||||
createTestApp,
|
||||
expectApiResponseOK,
|
||||
publishWorkflowApp,
|
||||
syncAgentV2WorkflowDraft,
|
||||
} from '../../../support/api'
|
||||
import { createTestApp } from '../../../support/api/apps'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from '../../../support/api/console-context'
|
||||
import { publishWorkflowApp } from '../../../support/api/workflows'
|
||||
import { bootstrapMarketplacePlugins } from '../../../support/marketplace-plugins'
|
||||
import { sleep } from '../../../support/process'
|
||||
import { blocked, created, skipped, updated, verified } from '../../../support/seed'
|
||||
@ -53,6 +49,7 @@ import {
|
||||
} from './fixtures/common'
|
||||
import { splitToolDisplayName } from './fixtures/tools'
|
||||
import { agentBuilderTestMaterials, getAgentBuilderTestMaterialPath } from './test-materials'
|
||||
import { syncAgentV2WorkflowDraft } from './workflow'
|
||||
|
||||
type StableModel = {
|
||||
name: string
|
||||
@ -127,7 +124,7 @@ const parseJsonEnv = (envName: string) => {
|
||||
}
|
||||
|
||||
const findModel = async (config: StableModel, title: string) => {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(
|
||||
`/console/api/workspaces/current/models/model-types/${config.type}`,
|
||||
@ -156,7 +153,7 @@ const findModel = async (config: StableModel, title: string) => {
|
||||
}
|
||||
|
||||
const resolveProvider = async (config: StableModel) => {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(
|
||||
`/console/api/workspaces/current/model-providers?${buildQuery({ model_type: config.type })}`,
|
||||
@ -178,7 +175,7 @@ const resolveProvider = async (config: StableModel) => {
|
||||
}
|
||||
|
||||
const selectCustomProviderCredential = async (provider: string, credentialId?: string) => {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
if (credentialId) {
|
||||
const switchResponse = await ctx.post(
|
||||
@ -210,7 +207,7 @@ const upsertStableProviderCredential = async (
|
||||
credentials: Record<string, unknown>,
|
||||
credentialId?: string,
|
||||
) => {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
if (credentialId) {
|
||||
const updateResponse = await ctx.put(
|
||||
@ -336,7 +333,7 @@ const seedAgentDecisionModel = async (context: SeedContext) =>
|
||||
})
|
||||
|
||||
const getDefaultModel = async (modelType: string) => {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(
|
||||
`/console/api/workspaces/current/default-model?${buildQuery({ model_type: modelType })}`,
|
||||
@ -350,7 +347,7 @@ const getDefaultModel = async (modelType: string) => {
|
||||
}
|
||||
|
||||
const setDefaultModel = async (model: StableModel) => {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.post('/console/api/workspaces/current/default-model', {
|
||||
data: {
|
||||
@ -431,7 +428,7 @@ const findBuiltinTool = async (displayName: string) => {
|
||||
const parsed = splitToolDisplayName(displayName)
|
||||
if (!parsed.ok) return { ok: false as const, reason: parsed.reason }
|
||||
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get('/console/api/workspaces/current/tools/builtin')
|
||||
await expectApiResponseOK(response, `Check built-in tool ${displayName}`)
|
||||
@ -476,7 +473,7 @@ const uploadConsoleFile = async (
|
||||
fileName: string,
|
||||
filePath: string,
|
||||
): Promise<UploadedConsoleFile> => {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.post('/console/api/files/upload', {
|
||||
multipart: {
|
||||
@ -504,7 +501,7 @@ const findDataset = (name: string) => {
|
||||
}
|
||||
|
||||
const getDatasetDocuments = async (datasetId: string) => {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(
|
||||
`/console/api/datasets/${datasetId}/documents?${buildQuery({ limit: '100', page: '1' })}`,
|
||||
@ -525,7 +522,7 @@ const requiredKnowledgeSegmentTokens = [
|
||||
|
||||
const datasetHasKnowledgeSegment = async (datasetId: string) => {
|
||||
const documents = await getDatasetDocuments(datasetId)
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
for (const document of documents) {
|
||||
const response = await ctx.get(
|
||||
@ -563,7 +560,7 @@ const waitForDatasetCompleted = async (datasetId: string) => {
|
||||
let status = 'missing'
|
||||
|
||||
while (Date.now() < deadline) {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/datasets/${datasetId}/indexing-status`)
|
||||
await expectApiResponseOK(response, `Check dataset indexing ${datasetId}`)
|
||||
@ -614,7 +611,7 @@ const addKnowledgeDocument = async (datasetId: string) => {
|
||||
},
|
||||
} satisfies KnowledgeConfig
|
||||
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.post(`/console/api/datasets/${datasetId}/documents`, { data: body })
|
||||
await expectApiResponseOK(response, `Seed knowledge document ${datasetId}`)
|
||||
@ -624,7 +621,7 @@ const addKnowledgeDocument = async (datasetId: string) => {
|
||||
}
|
||||
|
||||
const createDataset = async (name: string) => {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.post('/console/api/datasets', {
|
||||
data: {
|
||||
|
||||
72
e2e/features/agent-v2/support/workflow.ts
Normal file
72
e2e/features/agent-v2/support/workflow.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import type { SyncDraftWorkflowPayload } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import { zPostAppsByAppIdWorkflowsDraftResponse } from '@dify/contracts/api/console/apps/zod.gen'
|
||||
import * as z from 'zod'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from '../../../support/api/console-context'
|
||||
import { getWorkflowDraft } from '../../../support/api/workflows'
|
||||
|
||||
const agentV2WorkflowNodeId = 'agent-v2'
|
||||
const zWorkflowGraph = z.object({
|
||||
nodes: z.array(
|
||||
z.object({
|
||||
data: z.record(z.string(), z.unknown()).optional(),
|
||||
id: z.string(),
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
export async function getAgentV2WorkflowNodeData(appId: string) {
|
||||
const draft = await getWorkflowDraft(appId)
|
||||
const graph = zWorkflowGraph.parse(draft.graph)
|
||||
const agentNode = graph.nodes.find((node) => node.id === agentV2WorkflowNodeId)
|
||||
if (!agentNode)
|
||||
throw new Error(
|
||||
`Workflow draft ${appId} does not include Agent v2 node ${agentV2WorkflowNodeId}.`,
|
||||
)
|
||||
|
||||
return agentNode.data ?? {}
|
||||
}
|
||||
|
||||
export async function syncAgentV2WorkflowDraft(appId: string, agentId: string): Promise<void> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = {
|
||||
graph: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'start',
|
||||
type: 'custom',
|
||||
position: { x: 80, y: 282 },
|
||||
data: { id: 'start', type: 'start', title: 'Start', variables: [] },
|
||||
},
|
||||
{
|
||||
id: 'agent-v2',
|
||||
type: 'custom',
|
||||
position: { x: 420, y: 282 },
|
||||
data: {
|
||||
id: 'agent-v2',
|
||||
type: 'agent',
|
||||
title: 'Agent',
|
||||
desc: '',
|
||||
agent_binding: {
|
||||
binding_type: 'roster_agent',
|
||||
agent_id: agentId,
|
||||
},
|
||||
agent_node_kind: 'dify_agent',
|
||||
version: '2',
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
viewport: { x: 0, y: 0, zoom: 1 },
|
||||
},
|
||||
features: {},
|
||||
environment_variables: [],
|
||||
conversation_variables: [],
|
||||
} satisfies SyncDraftWorkflowPayload
|
||||
const response = await ctx.post(`/console/api/apps/${appId}/workflows/draft`, { data })
|
||||
await expectApiResponseOK(response, `Sync Agent v2 workflow draft for ${appId}`)
|
||||
zPostAppsByAppIdWorkflowsDraftResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
@ -28,11 +28,11 @@ Feature: Agent v2 tools
|
||||
Then the Agent v2 Backend service API response should include the JSON Replace E2E marker
|
||||
|
||||
@core
|
||||
Scenario: Tool selector shows an empty state for a missing tool search
|
||||
Scenario: Tool selector recovers after an unavailable installed-tool search
|
||||
Given I am signed in as the default E2E admin
|
||||
And a basic configured Agent v2 test agent has been created via API
|
||||
When I open the Agent v2 configure page
|
||||
And I search for the missing Agent v2 tool from the Tools selector
|
||||
Then I should see the Agent v2 tool selector empty state
|
||||
Then I should see the unavailable Agent v2 installed-tool search applied
|
||||
When I clear the Agent v2 tool selector search
|
||||
Then I should see the Agent v2 tool selector ready for another search
|
||||
|
||||
@ -2,7 +2,7 @@ import type { DifyWorld } from '../../support/world'
|
||||
import type { AccessSurfaceName } from './access-point-helpers'
|
||||
import { Given, Then, When } from '@cucumber/cucumber'
|
||||
import { expect } from '@playwright/test'
|
||||
import { setAgentApiAccess, setAgentSiteAccessAndGetURL } from '../../agent-v2/support/access-point'
|
||||
import { setAgentApiAccess, setAgentSiteAccess } from '../../agent-v2/support/access-point'
|
||||
import { publishAgentWithPublishableDraft } from '../../agent-v2/support/agent'
|
||||
import {
|
||||
getAccessRegion,
|
||||
@ -19,10 +19,7 @@ Given(
|
||||
/^Agent v2 (Web app|Backend service API) access has been enabled via API$/,
|
||||
async function (this: DifyWorld, surface: AccessSurfaceName) {
|
||||
if (surface === 'Web app') {
|
||||
this.agentBuilder.accessPoint.webAppURL = await setAgentSiteAccessAndGetURL(
|
||||
getCurrentAgentId(this),
|
||||
true,
|
||||
)
|
||||
await setAgentSiteAccess(getCurrentAgentId(this), true)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -2,10 +2,11 @@ import type { DataTable } from '@cucumber/cucumber'
|
||||
import type { DeclaredOutputConfig } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { AgentV2WorkflowOutputVariable, DifyWorld } from '../../support/world'
|
||||
import { Then, When } from '@cucumber/cucumber'
|
||||
import { zDeclaredOutputConfig } from '@dify/contracts/api/console/apps/zod.gen'
|
||||
import { expect } from '@playwright/test'
|
||||
import { getWorkflowDraft } from '../../../support/api'
|
||||
import * as z from 'zod'
|
||||
import { getAgentV2WorkflowNodeData } from '../../agent-v2/support/workflow'
|
||||
|
||||
const agentV2WorkflowNodeId = 'agent-v2'
|
||||
const taskOutputName = 'e2e_report'
|
||||
const renamedTaskOutputName = 'e2e_final_report'
|
||||
|
||||
@ -18,23 +19,12 @@ const getCurrentAppId = (world: DifyWorld) => {
|
||||
return appId
|
||||
}
|
||||
|
||||
const getAgentV2WorkflowNodeData = async (appId: string) => {
|
||||
const draft = await getWorkflowDraft(appId)
|
||||
const agentNode = draft.graph.nodes.find((node) => node.id === agentV2WorkflowNodeId)
|
||||
if (!agentNode)
|
||||
throw new Error(
|
||||
`Workflow draft ${appId} does not include Agent v2 node ${agentV2WorkflowNodeId}.`,
|
||||
)
|
||||
|
||||
return agentNode.data ?? {}
|
||||
}
|
||||
const parseDeclaredOutputs = (value: unknown): DeclaredOutputConfig[] =>
|
||||
z.array(zDeclaredOutputConfig).optional().default([]).parse(value)
|
||||
|
||||
const getDeclaredOutputsFromDraft = async (appId: string): Promise<DeclaredOutputConfig[]> => {
|
||||
const data = await getAgentV2WorkflowNodeData(appId)
|
||||
const outputs = data.agent_declared_outputs
|
||||
if (!Array.isArray(outputs)) return []
|
||||
|
||||
return outputs as DeclaredOutputConfig[]
|
||||
return parseDeclaredOutputs(data.agent_declared_outputs)
|
||||
}
|
||||
|
||||
const getOutputVariablesFromDraft = async (appId: string) => getDeclaredOutputsFromDraft(appId)
|
||||
@ -310,9 +300,7 @@ async function expectAgentTaskOutputReference(
|
||||
.poll(
|
||||
async () => {
|
||||
const data = await getAgentV2WorkflowNodeData(appId)
|
||||
const outputs = Array.isArray(data.agent_declared_outputs)
|
||||
? (data.agent_declared_outputs as DeclaredOutputConfig[])
|
||||
: []
|
||||
const outputs = parseDeclaredOutputs(data.agent_declared_outputs)
|
||||
const expectedOutput = outputs.find((output) => output.name === expectedName)
|
||||
|
||||
return {
|
||||
|
||||
@ -249,15 +249,16 @@ Then(
|
||||
},
|
||||
)
|
||||
|
||||
Then('I should see the Agent v2 tool selector empty state', async function (this: DifyWorld) {
|
||||
const page = this.getPage()
|
||||
Then(
|
||||
'I should see the unavailable Agent v2 installed-tool search applied',
|
||||
async function (this: DifyWorld) {
|
||||
const page = this.getPage()
|
||||
const search = getToolSelectorSearch(this)
|
||||
|
||||
await expect(page.getByText('No integrations were found')).toBeVisible({ timeout: 30_000 })
|
||||
await expect(page.getByRole('link', { name: 'Requests to the community' })).toBeVisible()
|
||||
await expect(
|
||||
page.getByText(agentBuilderFixedInputs.missingToolSearchWithSuffix),
|
||||
).not.toBeVisible()
|
||||
})
|
||||
await expect(search).toHaveValue(agentBuilderFixedInputs.missingToolSearchWithSuffix)
|
||||
await expect(page.getByText('All tools', { exact: true })).not.toBeVisible()
|
||||
},
|
||||
)
|
||||
|
||||
Then(
|
||||
'I should see the Agent v2 tool selector ready for another search',
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { DifyWorld } from '../../support/world'
|
||||
import { Given, Then, When } from '@cucumber/cucumber'
|
||||
import { expect } from '@playwright/test'
|
||||
import { createTestApp, syncAgentV2WorkflowDraft } from '../../../support/api'
|
||||
import { createTestApp } from '../../../support/api/apps'
|
||||
import { createE2EResourceName } from '../../../support/naming'
|
||||
import { createConfiguredTestAgent, publishAgent } from '../../agent-v2/support/agent'
|
||||
import {
|
||||
@ -9,6 +9,7 @@ import {
|
||||
normalAgentPrompt,
|
||||
normalAgentSoulConfig,
|
||||
} from '../../agent-v2/support/agent-soul'
|
||||
import { syncAgentV2WorkflowDraft } from '../../agent-v2/support/workflow'
|
||||
|
||||
Given(
|
||||
'a workflow app with an Agent v2 node has been created via API',
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { DifyWorld } from '../../support/world'
|
||||
import { Given, When } from '@cucumber/cucumber'
|
||||
import { expect } from '@playwright/test'
|
||||
import { createTestApp } from '../../../support/api'
|
||||
import { createTestApp } from '../../../support/api/apps'
|
||||
import { createE2EResourceName } from '../../../support/naming'
|
||||
|
||||
Given('there is an existing E2E app available for testing', async function (this: DifyWorld) {
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
import type { DifyWorld } from '../../support/world'
|
||||
import { Given, Then, When } from '@cucumber/cucumber'
|
||||
import { expect } from '@playwright/test'
|
||||
import {
|
||||
createTestApp,
|
||||
enableAppSiteAndGetURL,
|
||||
publishWorkflowApp,
|
||||
syncRunnableWorkflowDraft,
|
||||
} from '../../../support/api'
|
||||
import { createTestApp } from '../../../support/api/apps'
|
||||
import { enableAppSiteAndGetURL } from '../../../support/api/web-apps'
|
||||
import { publishWorkflowApp, syncRunnableWorkflowDraft } from '../../../support/api/workflows'
|
||||
import { createE2EResourceName } from '../../../support/naming'
|
||||
|
||||
Given('a workflow app has been published and shared via API', async function (this: DifyWorld) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { DifyWorld } from '../../support/world'
|
||||
import { Given, Then, When } from '@cucumber/cucumber'
|
||||
import { expect } from '@playwright/test'
|
||||
import { createTestApp } from '../../../support/api'
|
||||
import { createTestApp } from '../../../support/api/apps'
|
||||
import { createE2EResourceName } from '../../../support/naming'
|
||||
|
||||
Given(
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import type { DifyWorld } from '../../support/world'
|
||||
import { Given, Then, When } from '@cucumber/cucumber'
|
||||
import { expect } from '@playwright/test'
|
||||
import {
|
||||
createTestApp,
|
||||
getAppSiteDetail,
|
||||
getAppSiteURL,
|
||||
publishWorkflowApp,
|
||||
syncRunnableWorkflowDraft,
|
||||
} from '../../../support/api'
|
||||
import { createTestApp } from '../../../support/api/apps'
|
||||
import { getAppSiteDetail, getAppSiteURL } from '../../../support/api/web-apps'
|
||||
import { publishWorkflowApp, syncRunnableWorkflowDraft } from '../../../support/api/workflows'
|
||||
import { createE2EResourceName } from '../../../support/naming'
|
||||
import { baseURL, defaultLocale } from '../../../test-env'
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { DifyWorld } from '../../support/world'
|
||||
import { Given, Then, When } from '@cucumber/cucumber'
|
||||
import { expect } from '@playwright/test'
|
||||
import { syncRunnableWorkflowDraft } from '../../../support/api'
|
||||
import { syncRunnableWorkflowDraft } from '../../../support/api/workflows'
|
||||
|
||||
Given('a minimal runnable workflow draft has been synced', async function (this: DifyWorld) {
|
||||
const appId = this.createdAppIds.at(-1)
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import type { DifyWorld } from '../../support/world'
|
||||
import { Given, When } from '@cucumber/cucumber'
|
||||
import { zCreateAppPayload } from '@dify/contracts/api/console/apps/zod.gen'
|
||||
import { expect } from '@playwright/test'
|
||||
import { createTestApp, syncMinimalWorkflowDraft } from '../../../support/api'
|
||||
import { createTestApp } from '../../../support/api/apps'
|
||||
import { syncMinimalWorkflowDraft } from '../../../support/api/workflows'
|
||||
import { waitForAppsConsole } from '../../../support/apps'
|
||||
import { createE2EResourceName } from '../../../support/naming'
|
||||
|
||||
Given('a {string} app has been created via API', async function (this: DifyWorld, mode: string) {
|
||||
const app = await createTestApp(createE2EResourceName('App', mode), mode)
|
||||
const appMode = zCreateAppPayload.shape.mode.parse(mode)
|
||||
const app = await createTestApp(createE2EResourceName('App', appMode), appMode)
|
||||
this.createdAppIds.push(app.id)
|
||||
this.lastCreatedAppName = app.name
|
||||
})
|
||||
|
||||
@ -8,11 +8,11 @@ import { fileURLToPath } from 'node:url'
|
||||
import { After, AfterAll, Before, setDefaultTimeout, Status } from '@cucumber/cucumber'
|
||||
import { chromium, webkit } from '@playwright/test'
|
||||
import { AUTH_BOOTSTRAP_TIMEOUT_MS, ensureAuthenticatedState } from '../../fixtures/auth'
|
||||
import { deleteTestApp } from '../../support/api'
|
||||
import { deleteTestApp } from '../../support/api/apps'
|
||||
import { deleteTestDataset } from '../../support/api/datasets'
|
||||
import { deleteBuiltinToolCredential } from '../../support/api/tools'
|
||||
import { runCleanupTasks, shouldFailForCleanupErrors } from '../../support/cleanup'
|
||||
import { deleteTestDataset } from '../../support/datasets'
|
||||
import { getVoiceInputTestMaterialPath } from '../../support/test-materials'
|
||||
import { deleteBuiltinToolCredential } from '../../support/tools'
|
||||
import { baseURL, cucumberHeadless, cucumberSlowMo, e2eBrowser } from '../../test-env'
|
||||
import { deleteTestAgent } from '../agent-v2/support/agent'
|
||||
import {
|
||||
|
||||
@ -1,270 +0,0 @@
|
||||
import type { AppDetailWithSite } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { APIResponse } from '@playwright/test'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { zAppDetailWithSite } from '@dify/contracts/api/console/apps/zod.gen'
|
||||
import { request } from '@playwright/test'
|
||||
import { authStatePath } from '../fixtures/auth'
|
||||
import { apiURL } from '../test-env'
|
||||
import { assertE2EResourceName, createE2EResourceName } from './naming'
|
||||
|
||||
type StorageState = {
|
||||
cookies: Array<{ name: string; value: string }>
|
||||
}
|
||||
|
||||
export async function createApiContext() {
|
||||
const state = JSON.parse(await readFile(authStatePath, 'utf8')) as StorageState
|
||||
const csrfToken = state.cookies.find((c) => c.name.endsWith('csrf_token'))?.value ?? ''
|
||||
|
||||
return request.newContext({
|
||||
baseURL: apiURL,
|
||||
extraHTTPHeaders: { 'X-CSRF-Token': csrfToken },
|
||||
storageState: authStatePath,
|
||||
})
|
||||
}
|
||||
|
||||
export async function expectApiResponseOK(response: APIResponse, action: string): Promise<void> {
|
||||
if (response.ok()) return
|
||||
|
||||
const body = await response.text().catch(() => '')
|
||||
throw new Error(`${action} failed with ${response.status()} ${response.statusText()}: ${body}`)
|
||||
}
|
||||
|
||||
export type AppSeed = {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export type WorkflowDraft = {
|
||||
graph: {
|
||||
edges: Array<Record<string, unknown>>
|
||||
nodes: Array<{
|
||||
data?: Record<string, unknown>
|
||||
id: string
|
||||
type: string
|
||||
}>
|
||||
viewport?: Record<string, unknown>
|
||||
}
|
||||
}
|
||||
|
||||
export async function createTestApp(
|
||||
name = createE2EResourceName('App'),
|
||||
mode = 'workflow',
|
||||
): Promise<AppSeed> {
|
||||
assertE2EResourceName(name, 'App')
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.post('/console/api/apps', {
|
||||
data: {
|
||||
name,
|
||||
mode,
|
||||
icon_type: 'emoji',
|
||||
icon: '🤖',
|
||||
icon_background: '#FFEAD5',
|
||||
},
|
||||
})
|
||||
await expectApiResponseOK(response, `Create ${mode} app ${name}`)
|
||||
const body = (await response.json()) as AppSeed
|
||||
return body
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function getWorkflowDraft(appId: string): Promise<WorkflowDraft> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/apps/${appId}/workflows/draft`)
|
||||
await expectApiResponseOK(response, `Get workflow draft for ${appId}`)
|
||||
return (await response.json()) as WorkflowDraft
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function syncMinimalWorkflowDraft(appId: string): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.post(`/console/api/apps/${appId}/workflows/draft`, {
|
||||
data: {
|
||||
graph: {
|
||||
nodes: [
|
||||
{
|
||||
id: '1',
|
||||
type: 'custom',
|
||||
position: { x: 80, y: 282 },
|
||||
data: { id: '1', type: 'start', title: 'Start', variables: [] },
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
viewport: { x: 0, y: 0, zoom: 1 },
|
||||
},
|
||||
features: {},
|
||||
environment_variables: [],
|
||||
conversation_variables: [],
|
||||
},
|
||||
})
|
||||
await expectApiResponseOK(response, `Sync minimal workflow draft for ${appId}`)
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function syncAgentV2WorkflowDraft(appId: string, agentId: string): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.post(`/console/api/apps/${appId}/workflows/draft`, {
|
||||
data: {
|
||||
graph: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'start',
|
||||
type: 'custom',
|
||||
position: { x: 80, y: 282 },
|
||||
data: { id: 'start', type: 'start', title: 'Start', variables: [] },
|
||||
},
|
||||
{
|
||||
id: 'agent-v2',
|
||||
type: 'custom',
|
||||
position: { x: 420, y: 282 },
|
||||
data: {
|
||||
id: 'agent-v2',
|
||||
type: 'agent',
|
||||
title: 'Agent',
|
||||
desc: '',
|
||||
agent_binding: {
|
||||
binding_type: 'roster_agent',
|
||||
agent_id: agentId,
|
||||
},
|
||||
agent_node_kind: 'dify_agent',
|
||||
version: '2',
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
viewport: { x: 0, y: 0, zoom: 1 },
|
||||
},
|
||||
features: {},
|
||||
environment_variables: [],
|
||||
conversation_variables: [],
|
||||
},
|
||||
})
|
||||
await expectApiResponseOK(response, `Sync Agent v2 workflow draft for ${appId}`)
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteTestApp(id: string): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.delete(`/console/api/apps/${id}`)
|
||||
await expectApiResponseOK(response, `Delete app ${id}`)
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function syncRunnableWorkflowDraft(appId: string): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.post(`/console/api/apps/${appId}/workflows/draft`, {
|
||||
data: {
|
||||
graph: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'start',
|
||||
type: 'custom',
|
||||
position: { x: 80, y: 282 },
|
||||
data: { id: 'start', type: 'start', title: 'Start', variables: [] },
|
||||
},
|
||||
{
|
||||
id: 'end',
|
||||
type: 'custom',
|
||||
position: { x: 480, y: 282 },
|
||||
data: {
|
||||
id: 'end',
|
||||
type: 'end',
|
||||
title: 'End',
|
||||
outputs: [{ variable: 'result', value_selector: ['sys', 'workflow_run_id'] }],
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'start-end',
|
||||
type: 'custom',
|
||||
source: 'start',
|
||||
target: 'end',
|
||||
sourceHandle: 'source',
|
||||
targetHandle: 'target',
|
||||
},
|
||||
],
|
||||
viewport: { x: 0, y: 0, zoom: 1 },
|
||||
},
|
||||
features: {},
|
||||
environment_variables: [],
|
||||
conversation_variables: [],
|
||||
},
|
||||
})
|
||||
await expectApiResponseOK(response, `Sync runnable workflow draft for ${appId}`)
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function publishWorkflowApp(appId: string): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.post(`/console/api/apps/${appId}/workflows/publish`, {
|
||||
data: { marked_name: '', marked_comment: '' },
|
||||
})
|
||||
await expectApiResponseOK(response, `Publish workflow app ${appId}`)
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export function getAppSiteURL({ mode, site }: AppDetailWithSite): string {
|
||||
if (!site?.app_base_url || !site.access_token)
|
||||
throw new Error('App detail does not include a Web App URL.')
|
||||
|
||||
const webAppMode = (() => {
|
||||
if (mode === 'completion' || mode === 'workflow') return mode
|
||||
if (mode === 'advanced-chat' || mode === 'agent-chat' || mode === 'chat') return 'chat'
|
||||
throw new Error(`Unsupported Web App mode: ${mode}`)
|
||||
})()
|
||||
|
||||
return `${site.app_base_url}/${webAppMode}/${site.access_token}`
|
||||
}
|
||||
|
||||
export async function getAppSiteDetail(appId: string): Promise<AppDetailWithSite> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/apps/${appId}`)
|
||||
await expectApiResponseOK(response, `Get app site detail for ${appId}`)
|
||||
return zAppDetailWithSite.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function enableAppSiteAndGetURL(appId: string): Promise<string> {
|
||||
return getAppSiteURL(await setAppSiteEnabled(appId, true))
|
||||
}
|
||||
|
||||
export async function setAppSiteEnabled(
|
||||
appId: string,
|
||||
enabled: boolean,
|
||||
): Promise<AppDetailWithSite> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const enableResponse = await ctx.post(`/console/api/apps/${appId}/site-enable`, {
|
||||
data: { enable_site: enabled },
|
||||
})
|
||||
await expectApiResponseOK(enableResponse, `${enabled ? 'Enable' : 'Disable'} app site ${appId}`)
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
|
||||
return getAppSiteDetail(appId)
|
||||
}
|
||||
36
e2e/support/api/apps.ts
Normal file
36
e2e/support/api/apps.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import type { CreateAppPayload, PostAppsResponse } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import { zPostAppsResponse } from '@dify/contracts/api/console/apps/zod.gen'
|
||||
import { assertE2EResourceName, createE2EResourceName } from '../naming'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from './console-context'
|
||||
|
||||
export async function createTestApp(
|
||||
name = createE2EResourceName('App'),
|
||||
mode: CreateAppPayload['mode'] = 'workflow',
|
||||
): Promise<PostAppsResponse> {
|
||||
assertE2EResourceName(name, 'App')
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = {
|
||||
name,
|
||||
mode,
|
||||
icon_type: 'emoji',
|
||||
icon: '🤖',
|
||||
icon_background: '#FFEAD5',
|
||||
} satisfies CreateAppPayload
|
||||
const response = await ctx.post('/console/api/apps', { data })
|
||||
await expectApiResponseOK(response, `Create ${mode} app ${name}`)
|
||||
return zPostAppsResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteTestApp(id: string): Promise<void> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.delete(`/console/api/apps/${id}`)
|
||||
await expectApiResponseOK(response, `Delete app ${id}`)
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
34
e2e/support/api/console-context.ts
Normal file
34
e2e/support/api/console-context.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import type { APIResponse } from '@playwright/test'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { request } from '@playwright/test'
|
||||
import * as z from 'zod'
|
||||
import { authStatePath } from '../../fixtures/auth'
|
||||
import { apiURL } from '../../test-env'
|
||||
|
||||
const zStorageState = z.object({
|
||||
cookies: z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
value: z.string(),
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
export async function createConsoleApiContext() {
|
||||
const state = zStorageState.parse(JSON.parse(await readFile(authStatePath, 'utf8')))
|
||||
const csrfToken = state.cookies.find((cookie) => cookie.name.endsWith('csrf_token'))?.value
|
||||
if (!csrfToken) throw new Error(`No CSRF token found in E2E auth state: ${authStatePath}`)
|
||||
|
||||
return request.newContext({
|
||||
baseURL: apiURL,
|
||||
extraHTTPHeaders: { 'X-CSRF-Token': csrfToken },
|
||||
storageState: authStatePath,
|
||||
})
|
||||
}
|
||||
|
||||
export async function expectApiResponseOK(response: APIResponse, action: string): Promise<void> {
|
||||
if (response.ok()) return
|
||||
|
||||
const body = await response.text().catch(() => '')
|
||||
throw new Error(`${action} failed with ${response.status()} ${response.statusText()}: ${body}`)
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { createApiContext, expectApiResponseOK } from './api'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from './console-context'
|
||||
|
||||
export async function deleteTestDataset(datasetId: string): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.delete(`/console/api/datasets/${datasetId}`)
|
||||
await expectApiResponseOK(response, `Delete dataset ${datasetId}`)
|
||||
122
e2e/support/api/marketplace-plugins.ts
Normal file
122
e2e/support/api/marketplace-plugins.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import type {
|
||||
GetWorkspacesCurrentPluginTasksByTaskIdResponse,
|
||||
ParserLatest,
|
||||
ParserPluginIdentifiers,
|
||||
PostWorkspacesCurrentPluginInstallMarketplaceResponse,
|
||||
PostWorkspacesCurrentPluginInstallPkgResponse,
|
||||
PostWorkspacesCurrentPluginListInstallationsIdsResponse,
|
||||
PostWorkspacesCurrentPluginListLatestVersionsResponse,
|
||||
PostWorkspacesCurrentPluginUploadPkgResponse,
|
||||
} from '@dify/contracts/api/console/workspaces/types.gen'
|
||||
import type { Buffer } from 'node:buffer'
|
||||
import {
|
||||
zGetWorkspacesCurrentPluginTasksByTaskIdResponse,
|
||||
zPostWorkspacesCurrentPluginInstallMarketplaceResponse,
|
||||
zPostWorkspacesCurrentPluginInstallPkgResponse,
|
||||
zPostWorkspacesCurrentPluginListInstallationsIdsResponse,
|
||||
zPostWorkspacesCurrentPluginListLatestVersionsResponse,
|
||||
zPostWorkspacesCurrentPluginUploadPkgResponse,
|
||||
} from '@dify/contracts/api/console/workspaces/zod.gen'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from './console-context'
|
||||
|
||||
export async function getLatestMarketplacePluginVersions(
|
||||
pluginIds: string[],
|
||||
): Promise<PostWorkspacesCurrentPluginListLatestVersionsResponse> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = { plugin_ids: pluginIds } satisfies ParserLatest
|
||||
const response = await ctx.post('/console/api/workspaces/current/plugin/list/latest-versions', {
|
||||
data,
|
||||
})
|
||||
await expectApiResponseOK(response, 'Resolve latest marketplace plugin versions')
|
||||
return zPostWorkspacesCurrentPluginListLatestVersionsResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function getInstalledMarketplacePlugins(
|
||||
pluginIds: string[],
|
||||
): Promise<PostWorkspacesCurrentPluginListInstallationsIdsResponse> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = { plugin_ids: pluginIds } satisfies ParserLatest
|
||||
const response = await ctx.post(
|
||||
'/console/api/workspaces/current/plugin/list/installations/ids',
|
||||
{ data },
|
||||
)
|
||||
await expectApiResponseOK(response, 'List installed marketplace plugins')
|
||||
return zPostWorkspacesCurrentPluginListInstallationsIdsResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function getMarketplacePluginInstallTask(
|
||||
taskId: string,
|
||||
): Promise<GetWorkspacesCurrentPluginTasksByTaskIdResponse> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/workspaces/current/plugin/tasks/${taskId}`)
|
||||
await expectApiResponseOK(response, `Fetch marketplace plugin install task ${taskId}`)
|
||||
return zGetWorkspacesCurrentPluginTasksByTaskIdResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function startMarketplacePluginInstall(
|
||||
pluginUniqueIdentifiers: string[],
|
||||
): Promise<PostWorkspacesCurrentPluginInstallMarketplaceResponse> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = {
|
||||
plugin_unique_identifiers: pluginUniqueIdentifiers,
|
||||
} satisfies ParserPluginIdentifiers
|
||||
const response = await ctx.post('/console/api/workspaces/current/plugin/install/marketplace', {
|
||||
data,
|
||||
})
|
||||
await expectApiResponseOK(response, 'Install marketplace plugins')
|
||||
return zPostWorkspacesCurrentPluginInstallMarketplaceResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function uploadMarketplacePluginPackageFile(
|
||||
pkg: Buffer,
|
||||
fileName: string,
|
||||
): Promise<PostWorkspacesCurrentPluginUploadPkgResponse> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.post('/console/api/workspaces/current/plugin/upload/pkg', {
|
||||
multipart: {
|
||||
pkg: {
|
||||
buffer: pkg,
|
||||
mimeType: 'application/octet-stream',
|
||||
name: fileName,
|
||||
},
|
||||
},
|
||||
})
|
||||
await expectApiResponseOK(response, `Upload marketplace package ${fileName}`)
|
||||
return zPostWorkspacesCurrentPluginUploadPkgResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function startUploadedPluginPackageInstall(
|
||||
pluginUniqueIdentifiers: string[],
|
||||
): Promise<PostWorkspacesCurrentPluginInstallPkgResponse> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = {
|
||||
plugin_unique_identifiers: pluginUniqueIdentifiers,
|
||||
} satisfies ParserPluginIdentifiers
|
||||
const response = await ctx.post('/console/api/workspaces/current/plugin/install/pkg', { data })
|
||||
await expectApiResponseOK(response, 'Install uploaded plugin packages')
|
||||
return zPostWorkspacesCurrentPluginInstallPkgResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
24
e2e/support/api/tools.ts
Normal file
24
e2e/support/api/tools.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import type { BuiltinToolCredentialDeletePayload } from '@dify/contracts/api/console/workspaces/types.gen'
|
||||
import { zPostWorkspacesCurrentToolProviderBuiltinByProviderDeleteResponse } from '@dify/contracts/api/console/workspaces/zod.gen'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from './console-context'
|
||||
|
||||
export async function deleteBuiltinToolCredential(
|
||||
provider: string,
|
||||
credentialId: string,
|
||||
): Promise<void> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = { credential_id: credentialId } satisfies BuiltinToolCredentialDeletePayload
|
||||
const response = await ctx.post(
|
||||
`/console/api/workspaces/current/tool-provider/builtin/${provider}/delete`,
|
||||
{ data },
|
||||
)
|
||||
await expectApiResponseOK(
|
||||
response,
|
||||
`Delete built-in tool credential ${credentialId} for ${provider}`,
|
||||
)
|
||||
zPostWorkspacesCurrentToolProviderBuiltinByProviderDeleteResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
47
e2e/support/api/web-apps.ts
Normal file
47
e2e/support/api/web-apps.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import type {
|
||||
AppDetailWithSite,
|
||||
AppSiteStatusPayload,
|
||||
GetAppsByAppIdResponse,
|
||||
} from '@dify/contracts/api/console/apps/types.gen'
|
||||
import { zGetAppsByAppIdResponse } from '@dify/contracts/api/console/apps/zod.gen'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from './console-context'
|
||||
|
||||
export function getAppSiteURL({ mode, site }: AppDetailWithSite): string {
|
||||
if (!site?.app_base_url || !site.access_token)
|
||||
throw new Error('App detail does not include a Web App URL.')
|
||||
|
||||
const webAppMode = (() => {
|
||||
if (mode === 'completion' || mode === 'workflow') return mode
|
||||
if (mode === 'advanced-chat' || mode === 'agent-chat' || mode === 'chat') return 'chat'
|
||||
throw new Error(`Unsupported Web App mode: ${mode}`)
|
||||
})()
|
||||
|
||||
return `${site.app_base_url}/${webAppMode}/${site.access_token}`
|
||||
}
|
||||
|
||||
export async function getAppSiteDetail(appId: string): Promise<GetAppsByAppIdResponse> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/apps/${appId}`)
|
||||
await expectApiResponseOK(response, `Get app site detail for ${appId}`)
|
||||
return zGetAppsByAppIdResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function enableAppSiteAndGetURL(appId: string): Promise<string> {
|
||||
await setAppSiteEnabled(appId, true)
|
||||
return getAppSiteURL(await getAppSiteDetail(appId))
|
||||
}
|
||||
|
||||
export async function setAppSiteEnabled(appId: string, enabled: boolean): Promise<void> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = { enable_site: enabled } satisfies AppSiteStatusPayload
|
||||
const enableResponse = await ctx.post(`/console/api/apps/${appId}/site-enable`, { data })
|
||||
await expectApiResponseOK(enableResponse, `${enabled ? 'Enable' : 'Disable'} app site ${appId}`)
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
115
e2e/support/api/workflows.ts
Normal file
115
e2e/support/api/workflows.ts
Normal file
@ -0,0 +1,115 @@
|
||||
import type {
|
||||
GetAppsByAppIdWorkflowsDraftResponse,
|
||||
PublishWorkflowPayload,
|
||||
SyncDraftWorkflowPayload,
|
||||
} from '@dify/contracts/api/console/apps/types.gen'
|
||||
import {
|
||||
zGetAppsByAppIdWorkflowsDraftResponse,
|
||||
zPostAppsByAppIdWorkflowsDraftResponse,
|
||||
zPostAppsByAppIdWorkflowsPublishResponse,
|
||||
} from '@dify/contracts/api/console/apps/zod.gen'
|
||||
import { createConsoleApiContext, expectApiResponseOK } from './console-context'
|
||||
|
||||
export async function getWorkflowDraft(
|
||||
appId: string,
|
||||
): Promise<GetAppsByAppIdWorkflowsDraftResponse> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/apps/${appId}/workflows/draft`)
|
||||
await expectApiResponseOK(response, `Get workflow draft for ${appId}`)
|
||||
return zGetAppsByAppIdWorkflowsDraftResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function syncMinimalWorkflowDraft(appId: string): Promise<void> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = {
|
||||
graph: {
|
||||
nodes: [
|
||||
{
|
||||
id: '1',
|
||||
type: 'custom',
|
||||
position: { x: 80, y: 282 },
|
||||
data: { id: '1', type: 'start', title: 'Start', variables: [] },
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
viewport: { x: 0, y: 0, zoom: 1 },
|
||||
},
|
||||
features: {},
|
||||
environment_variables: [],
|
||||
conversation_variables: [],
|
||||
} satisfies SyncDraftWorkflowPayload
|
||||
const response = await ctx.post(`/console/api/apps/${appId}/workflows/draft`, { data })
|
||||
await expectApiResponseOK(response, `Sync minimal workflow draft for ${appId}`)
|
||||
zPostAppsByAppIdWorkflowsDraftResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function syncRunnableWorkflowDraft(appId: string): Promise<void> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = {
|
||||
graph: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'start',
|
||||
type: 'custom',
|
||||
position: { x: 80, y: 282 },
|
||||
data: { id: 'start', type: 'start', title: 'Start', variables: [] },
|
||||
},
|
||||
{
|
||||
id: 'end',
|
||||
type: 'custom',
|
||||
position: { x: 480, y: 282 },
|
||||
data: {
|
||||
id: 'end',
|
||||
type: 'end',
|
||||
title: 'End',
|
||||
outputs: [{ variable: 'result', value_selector: ['sys', 'workflow_run_id'] }],
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'start-end',
|
||||
type: 'custom',
|
||||
source: 'start',
|
||||
target: 'end',
|
||||
sourceHandle: 'source',
|
||||
targetHandle: 'target',
|
||||
},
|
||||
],
|
||||
viewport: { x: 0, y: 0, zoom: 1 },
|
||||
},
|
||||
features: {},
|
||||
environment_variables: [],
|
||||
conversation_variables: [],
|
||||
} satisfies SyncDraftWorkflowPayload
|
||||
const response = await ctx.post(`/console/api/apps/${appId}/workflows/draft`, { data })
|
||||
await expectApiResponseOK(response, `Sync runnable workflow draft for ${appId}`)
|
||||
zPostAppsByAppIdWorkflowsDraftResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
export async function publishWorkflowApp(appId: string): Promise<void> {
|
||||
const ctx = await createConsoleApiContext()
|
||||
try {
|
||||
const data = {
|
||||
marked_name: '',
|
||||
marked_comment: '',
|
||||
} satisfies PublishWorkflowPayload
|
||||
const response = await ctx.post(`/console/api/apps/${appId}/workflows/publish`, { data })
|
||||
await expectApiResponseOK(response, `Publish workflow app ${appId}`)
|
||||
zPostAppsByAppIdWorkflowsPublishResponse.parse(await response.json())
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
@ -1,36 +1,17 @@
|
||||
import type { PluginInstallTask } from '@dify/contracts/api/console/workspaces/types.gen'
|
||||
import type { SeedContext, SeedResult } from './seed'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { createApiContext, expectApiResponseOK } from './api'
|
||||
import {
|
||||
getInstalledMarketplacePlugins,
|
||||
getLatestMarketplacePluginVersions,
|
||||
getMarketplacePluginInstallTask,
|
||||
startMarketplacePluginInstall,
|
||||
startUploadedPluginPackageInstall,
|
||||
uploadMarketplacePluginPackageFile,
|
||||
} from './api/marketplace-plugins'
|
||||
import { sleep } from './process'
|
||||
import { blocked, created, skipped, verified } from './seed'
|
||||
|
||||
type LatestPlugin = {
|
||||
unique_identifier?: string
|
||||
version?: string
|
||||
}
|
||||
|
||||
type PluginInstallation = {
|
||||
plugin_id: string
|
||||
plugin_unique_identifier: string
|
||||
}
|
||||
|
||||
type PluginInstallTask = {
|
||||
id?: string
|
||||
plugins?: Array<{
|
||||
message?: string
|
||||
plugin_id?: string
|
||||
plugin_unique_identifier?: string
|
||||
status?: string
|
||||
}>
|
||||
status?: string
|
||||
}
|
||||
|
||||
type PluginInstallStartResponse = {
|
||||
all_installed?: boolean
|
||||
task?: PluginInstallTask | null
|
||||
task_id?: string
|
||||
}
|
||||
|
||||
type MarketplacePluginBootstrapConfig = {
|
||||
defaultPluginIds: string[]
|
||||
pluginIdsEnv: string
|
||||
@ -56,45 +37,23 @@ const getPluginId = (pluginUniqueIdentifier: string) =>
|
||||
const resolveLatestPluginIdentifiers = async (pluginIds: string[]) => {
|
||||
if (pluginIds.length === 0) return { identifiers: [] as string[], missing: [] as string[] }
|
||||
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.post('/console/api/workspaces/current/plugin/list/latest-versions', {
|
||||
data: { plugin_ids: pluginIds },
|
||||
})
|
||||
await expectApiResponseOK(response, 'Resolve latest marketplace plugin versions')
|
||||
const body = (await response.json()) as { versions?: Record<string, LatestPlugin | null> }
|
||||
const identifiers: string[] = []
|
||||
const missing: string[] = []
|
||||
const body = await getLatestMarketplacePluginVersions(pluginIds)
|
||||
const identifiers: string[] = []
|
||||
const missing: string[] = []
|
||||
|
||||
for (const pluginId of pluginIds) {
|
||||
const latest = body.versions?.[pluginId]
|
||||
if (latest?.unique_identifier) identifiers.push(latest.unique_identifier)
|
||||
else missing.push(pluginId)
|
||||
}
|
||||
|
||||
return { identifiers, missing }
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
for (const pluginId of pluginIds) {
|
||||
const latest = body.versions[pluginId]
|
||||
if (latest?.unique_identifier) identifiers.push(latest.unique_identifier)
|
||||
else missing.push(pluginId)
|
||||
}
|
||||
|
||||
return { identifiers, missing }
|
||||
}
|
||||
|
||||
const listInstalledPlugins = async (pluginIds: string[]) => {
|
||||
if (pluginIds.length === 0) return [] as PluginInstallation[]
|
||||
if (pluginIds.length === 0) return []
|
||||
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.post(
|
||||
'/console/api/workspaces/current/plugin/list/installations/ids',
|
||||
{
|
||||
data: { plugin_ids: pluginIds },
|
||||
},
|
||||
)
|
||||
await expectApiResponseOK(response, 'List installed marketplace plugins')
|
||||
const body = (await response.json()) as { plugins?: PluginInstallation[] }
|
||||
return body.plugins ?? []
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
return (await getInstalledMarketplacePlugins(pluginIds)).plugins
|
||||
}
|
||||
|
||||
const waitForPluginInstallTask = async (taskId: string, timeoutMs = 300_000) => {
|
||||
@ -102,15 +61,7 @@ const waitForPluginInstallTask = async (taskId: string, timeoutMs = 300_000) =>
|
||||
let lastTask: PluginInstallTask | undefined
|
||||
|
||||
while (Date.now() < deadline) {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.get(`/console/api/workspaces/current/plugin/tasks/${taskId}`)
|
||||
await expectApiResponseOK(response, `Fetch marketplace plugin install task ${taskId}`)
|
||||
const body = (await response.json()) as { task?: PluginInstallTask }
|
||||
lastTask = body.task
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
lastTask = (await getMarketplacePluginInstallTask(taskId)).task
|
||||
|
||||
if (lastTask?.status === terminalSuccessTaskStatus) return { ok: true as const, task: lastTask }
|
||||
|
||||
@ -140,16 +91,7 @@ const waitForPluginInstallTask = async (taskId: string, timeoutMs = 300_000) =>
|
||||
}
|
||||
|
||||
const installMarketplacePlugins = async (pluginUniqueIdentifiers: string[]) => {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.post('/console/api/workspaces/current/plugin/install/marketplace', {
|
||||
data: { plugin_unique_identifiers: pluginUniqueIdentifiers },
|
||||
})
|
||||
await expectApiResponseOK(response, 'Install marketplace plugins')
|
||||
return (await response.json()) as PluginInstallStartResponse
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
return startMarketplacePluginInstall(pluginUniqueIdentifiers)
|
||||
}
|
||||
|
||||
const getMarketplaceDownloadUrl = (pluginUniqueIdentifier: string) => {
|
||||
@ -174,44 +116,12 @@ const downloadMarketplacePluginPackage = async (pluginUniqueIdentifier: string)
|
||||
|
||||
const uploadMarketplacePluginPackage = async (pluginUniqueIdentifier: string) => {
|
||||
const pkg = await downloadMarketplacePluginPackage(pluginUniqueIdentifier)
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.post('/console/api/workspaces/current/plugin/upload/pkg', {
|
||||
multipart: {
|
||||
pkg: {
|
||||
buffer: pkg,
|
||||
mimeType: 'application/octet-stream',
|
||||
name: `${getPluginId(pluginUniqueIdentifier).replaceAll('/', '-')}.difypkg`,
|
||||
},
|
||||
},
|
||||
})
|
||||
await expectApiResponseOK(
|
||||
response,
|
||||
`Upload marketplace package ${getPluginId(pluginUniqueIdentifier)}`,
|
||||
)
|
||||
const body = (await response.json()) as { unique_identifier?: string }
|
||||
if (!body.unique_identifier)
|
||||
throw new Error(
|
||||
`Upload marketplace package ${getPluginId(pluginUniqueIdentifier)} did not return a unique identifier.`,
|
||||
)
|
||||
|
||||
return body.unique_identifier
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
const fileName = `${getPluginId(pluginUniqueIdentifier).replaceAll('/', '-')}.difypkg`
|
||||
return (await uploadMarketplacePluginPackageFile(pkg, fileName)).unique_identifier
|
||||
}
|
||||
|
||||
const installLocalPluginPackages = async (pluginUniqueIdentifiers: string[]) => {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.post('/console/api/workspaces/current/plugin/install/pkg', {
|
||||
data: { plugin_unique_identifiers: pluginUniqueIdentifiers },
|
||||
})
|
||||
await expectApiResponseOK(response, 'Install uploaded plugin packages')
|
||||
return (await response.json()) as PluginInstallStartResponse
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
return startUploadedPluginPackageInstall(pluginUniqueIdentifiers)
|
||||
}
|
||||
|
||||
const shouldFallbackToLocalPackageInstall = (error: string) =>
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
import { createApiContext, expectApiResponseOK } from './api'
|
||||
|
||||
export async function deleteBuiltinToolCredential(
|
||||
provider: string,
|
||||
credentialId: string,
|
||||
): Promise<void> {
|
||||
const ctx = await createApiContext()
|
||||
try {
|
||||
const response = await ctx.post(
|
||||
`/console/api/workspaces/current/tool-provider/builtin/${provider}/delete`,
|
||||
{
|
||||
data: { credential_id: credentialId },
|
||||
},
|
||||
)
|
||||
await expectApiResponseOK(
|
||||
response,
|
||||
`Delete built-in tool credential ${credentialId} for ${provider}`,
|
||||
)
|
||||
} finally {
|
||||
await ctx.dispose()
|
||||
}
|
||||
}
|
||||
@ -46,7 +46,7 @@ export type AgentAppDetailWithSite = {
|
||||
maintainer?: string | null
|
||||
max_active_requests?: number | null
|
||||
mode: string
|
||||
model_config?: ModelConfig | null
|
||||
model_config?: AppModelConfigResponse | null
|
||||
name: string
|
||||
permission_keys?: Array<string>
|
||||
role?: string | null
|
||||
@ -536,13 +536,31 @@ export type DeletedTool = {
|
||||
type: string
|
||||
}
|
||||
|
||||
export type ModelConfig = {
|
||||
completion_params?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
mode: LlmMode
|
||||
name: string
|
||||
provider: string
|
||||
export type AppModelConfigResponse = {
|
||||
agent_mode?: unknown | null
|
||||
annotation_reply?: unknown | null
|
||||
chat_prompt_config?: unknown | null
|
||||
completion_prompt_config?: unknown | null
|
||||
created_at?: number | null
|
||||
created_by?: string | null
|
||||
dataset_configs?: unknown | null
|
||||
dataset_query_variable?: string | null
|
||||
external_data_tools?: unknown | null
|
||||
file_upload?: unknown | null
|
||||
model?: unknown | null
|
||||
more_like_this?: unknown | null
|
||||
opening_statement?: string | null
|
||||
pre_prompt?: string | null
|
||||
prompt_type?: string | null
|
||||
retriever_resource?: unknown | null
|
||||
sensitive_word_avoidance?: unknown | null
|
||||
speech_to_text?: unknown | null
|
||||
suggested_questions?: unknown | null
|
||||
suggested_questions_after_answer?: unknown | null
|
||||
text_to_speech?: unknown | null
|
||||
updated_at?: number | null
|
||||
updated_by?: string | null
|
||||
user_input_form?: unknown | null
|
||||
}
|
||||
|
||||
export type AppDetailSiteResponse = {
|
||||
@ -1095,8 +1113,6 @@ export type AgentAppPublishedReferenceResponse = {
|
||||
app_name: string
|
||||
}
|
||||
|
||||
export type LlmMode = 'chat' | 'completion'
|
||||
|
||||
export type AgentKind = 'dify_agent'
|
||||
|
||||
export type AgentPublishedReferenceResponse = {
|
||||
@ -1894,7 +1910,7 @@ export type AgentAppDetailWithSiteWritable = {
|
||||
maintainer?: string | null
|
||||
max_active_requests?: number | null
|
||||
mode: string
|
||||
model_config?: ModelConfig | null
|
||||
model_config?: AppModelConfigResponse | null
|
||||
name: string
|
||||
permission_keys?: Array<string>
|
||||
role?: string | null
|
||||
|
||||
@ -288,6 +288,36 @@ export const zDeletedTool = z.object({
|
||||
type: z.string(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AppModelConfigResponse
|
||||
*/
|
||||
export const zAppModelConfigResponse = z.object({
|
||||
agent_mode: z.unknown().nullish(),
|
||||
annotation_reply: z.unknown().nullish(),
|
||||
chat_prompt_config: z.unknown().nullish(),
|
||||
completion_prompt_config: z.unknown().nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
dataset_configs: z.unknown().nullish(),
|
||||
dataset_query_variable: z.string().nullish(),
|
||||
external_data_tools: z.unknown().nullish(),
|
||||
file_upload: z.unknown().nullish(),
|
||||
model: z.unknown().nullish(),
|
||||
more_like_this: z.unknown().nullish(),
|
||||
opening_statement: z.string().nullish(),
|
||||
pre_prompt: z.string().nullish(),
|
||||
prompt_type: z.string().nullish(),
|
||||
retriever_resource: z.unknown().nullish(),
|
||||
sensitive_word_avoidance: z.unknown().nullish(),
|
||||
speech_to_text: z.unknown().nullish(),
|
||||
suggested_questions: z.unknown().nullish(),
|
||||
suggested_questions_after_answer: z.unknown().nullish(),
|
||||
text_to_speech: z.unknown().nullish(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
user_input_form: z.unknown().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AppDetailSiteResponse
|
||||
*/
|
||||
@ -339,6 +369,47 @@ export const zWorkflowPartial = z.object({
|
||||
updated_by: z.string().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AgentAppDetailWithSite
|
||||
*/
|
||||
export const zAgentAppDetailWithSite = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
active_config_is_published: z.boolean().optional().default(false),
|
||||
api_base_url: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
backing_app_id: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
debug_conversation_has_messages: z.boolean().optional().default(false),
|
||||
debug_conversation_id: z.string().nullish(),
|
||||
debug_conversation_message_count: z.int().optional().default(0),
|
||||
deleted_tools: z.array(zDeletedTool).optional(),
|
||||
description: z.string().nullish(),
|
||||
enable_api: z.boolean(),
|
||||
enable_site: z.boolean(),
|
||||
hidden_app_backed: z.boolean().optional().default(false),
|
||||
icon: z.string().nullish(),
|
||||
icon_background: z.string().nullish(),
|
||||
icon_type: z.string().nullish(),
|
||||
icon_url: z.string().nullable(),
|
||||
id: z.string(),
|
||||
maintainer: z.string().nullish(),
|
||||
max_active_requests: z.int().nullish(),
|
||||
mode: z.string(),
|
||||
model_config: zAppModelConfigResponse.nullish(),
|
||||
name: z.string(),
|
||||
permission_keys: z.array(z.string()).optional(),
|
||||
role: z.string().nullish(),
|
||||
site: zAppDetailSiteResponse.nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
tracing: z.unknown().nullish(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
use_icon_as_answer_icon: z.boolean().nullish(),
|
||||
workflow: zWorkflowPartial.nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* ComposerBindingPayload
|
||||
*/
|
||||
@ -1005,64 +1076,6 @@ export const zAgentAppPagination = z.object({
|
||||
total: z.int(),
|
||||
})
|
||||
|
||||
/**
|
||||
* LLMMode
|
||||
*
|
||||
* Enum class for large language model mode.
|
||||
*/
|
||||
export const zLlmMode = z.enum(['chat', 'completion'])
|
||||
|
||||
/**
|
||||
* ModelConfig
|
||||
*/
|
||||
export const zModelConfig = z.object({
|
||||
completion_params: z.record(z.string(), z.unknown()).optional(),
|
||||
mode: zLlmMode,
|
||||
name: z.string(),
|
||||
provider: z.string(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AgentAppDetailWithSite
|
||||
*/
|
||||
export const zAgentAppDetailWithSite = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
active_config_is_published: z.boolean().optional().default(false),
|
||||
api_base_url: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
backing_app_id: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
debug_conversation_has_messages: z.boolean().optional().default(false),
|
||||
debug_conversation_id: z.string().nullish(),
|
||||
debug_conversation_message_count: z.int().optional().default(0),
|
||||
deleted_tools: z.array(zDeletedTool).optional(),
|
||||
description: z.string().nullish(),
|
||||
enable_api: z.boolean(),
|
||||
enable_site: z.boolean(),
|
||||
hidden_app_backed: z.boolean().optional().default(false),
|
||||
icon: z.string().nullish(),
|
||||
icon_background: z.string().nullish(),
|
||||
icon_type: z.string().nullish(),
|
||||
icon_url: z.string().nullable(),
|
||||
id: z.string(),
|
||||
maintainer: z.string().nullish(),
|
||||
max_active_requests: z.int().nullish(),
|
||||
mode: z.string(),
|
||||
model_config: zModelConfig.nullish(),
|
||||
name: z.string(),
|
||||
permission_keys: z.array(z.string()).optional(),
|
||||
role: z.string().nullish(),
|
||||
site: zAppDetailSiteResponse.nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
tracing: z.unknown().nullish(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
use_icon_as_answer_icon: z.boolean().nullish(),
|
||||
workflow: zWorkflowPartial.nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AgentKind
|
||||
*
|
||||
@ -2732,7 +2745,7 @@ export const zAgentAppDetailWithSiteWritable = z.object({
|
||||
maintainer: z.string().nullish(),
|
||||
max_active_requests: z.int().nullish(),
|
||||
mode: z.string(),
|
||||
model_config: zModelConfig.nullish(),
|
||||
model_config: zAppModelConfigResponse.nullish(),
|
||||
name: z.string(),
|
||||
permission_keys: z.array(z.string()).optional(),
|
||||
role: z.string().nullish(),
|
||||
|
||||
@ -40,7 +40,7 @@ export type AppDetailWithSite = {
|
||||
maintainer?: string | null
|
||||
max_active_requests?: number | null
|
||||
mode: string
|
||||
model_config?: ModelConfig | null
|
||||
model_config?: AppModelConfigResponse | null
|
||||
name: string
|
||||
permission_keys?: Array<string>
|
||||
site?: AppDetailSiteResponse | null
|
||||
@ -415,7 +415,6 @@ export type AppApiStatusPayload = {
|
||||
|
||||
export type AppDetail = {
|
||||
access_mode?: string | null
|
||||
app_model_config?: ModelConfig | null
|
||||
created_at?: number | null
|
||||
created_by?: string | null
|
||||
description?: string | null
|
||||
@ -425,7 +424,8 @@ export type AppDetail = {
|
||||
icon_background?: string | null
|
||||
id: string
|
||||
maintainer?: string | null
|
||||
mode_compatible_with_agent: string
|
||||
mode: string
|
||||
model_config?: AppModelConfigResponse | null
|
||||
name: string
|
||||
permission_keys?: Array<string>
|
||||
tags?: Array<Tag>
|
||||
@ -1020,9 +1020,9 @@ export type SyncDraftWorkflowPayload = {
|
||||
}
|
||||
|
||||
export type SyncDraftWorkflowResponse = {
|
||||
hash?: string
|
||||
result?: string
|
||||
updated_at?: string
|
||||
hash: string
|
||||
result: string
|
||||
updated_at: number
|
||||
}
|
||||
|
||||
export type WorkflowDraftVariableList = {
|
||||
@ -1322,13 +1322,31 @@ export type DeletedTool = {
|
||||
type: string
|
||||
}
|
||||
|
||||
export type ModelConfig = {
|
||||
completion_params?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
mode: LlmMode
|
||||
name: string
|
||||
provider: string
|
||||
export type AppModelConfigResponse = {
|
||||
agent_mode?: unknown | null
|
||||
annotation_reply?: unknown | null
|
||||
chat_prompt_config?: unknown | null
|
||||
completion_prompt_config?: unknown | null
|
||||
created_at?: number | null
|
||||
created_by?: string | null
|
||||
dataset_configs?: unknown | null
|
||||
dataset_query_variable?: string | null
|
||||
external_data_tools?: unknown | null
|
||||
file_upload?: unknown | null
|
||||
model?: unknown | null
|
||||
more_like_this?: unknown | null
|
||||
opening_statement?: string | null
|
||||
pre_prompt?: string | null
|
||||
prompt_type?: string | null
|
||||
retriever_resource?: unknown | null
|
||||
sensitive_word_avoidance?: unknown | null
|
||||
speech_to_text?: unknown | null
|
||||
suggested_questions?: unknown | null
|
||||
suggested_questions_after_answer?: unknown | null
|
||||
text_to_speech?: unknown | null
|
||||
updated_at?: number | null
|
||||
updated_by?: string | null
|
||||
user_input_form?: unknown | null
|
||||
}
|
||||
|
||||
export type AppDetailSiteResponse = {
|
||||
@ -1608,6 +1626,15 @@ export type FeedbackStat = {
|
||||
like: number
|
||||
}
|
||||
|
||||
export type ModelConfig = {
|
||||
completion_params?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
mode: LlmMode
|
||||
name: string
|
||||
provider: string
|
||||
}
|
||||
|
||||
export type Conversation = {
|
||||
admin_feedback_stats?: FeedbackStat | null
|
||||
annotation?: ConversationAnnotation | null
|
||||
@ -2196,8 +2223,6 @@ export type ModelConfigPartial = {
|
||||
updated_by?: string | null
|
||||
}
|
||||
|
||||
export type LlmMode = 'chat' | 'completion'
|
||||
|
||||
export type PluginDependencyType = 'github' | 'marketplace' | 'package'
|
||||
|
||||
export type Github = {
|
||||
@ -2259,6 +2284,8 @@ export type StatusCount = {
|
||||
success: number
|
||||
}
|
||||
|
||||
export type LlmMode = 'chat' | 'completion'
|
||||
|
||||
export type SimpleMessageDetail = {
|
||||
answer: string
|
||||
inputs: {
|
||||
@ -3075,7 +3102,7 @@ export type AppDetailWithSiteWritable = {
|
||||
maintainer?: string | null
|
||||
max_active_requests?: number | null
|
||||
mode: string
|
||||
model_config?: ModelConfig | null
|
||||
model_config?: AppModelConfigResponse | null
|
||||
name: string
|
||||
permission_keys?: Array<string>
|
||||
site?: AppDetailSiteResponseWritable | null
|
||||
|
||||
@ -663,10 +663,13 @@ export const zSyncDraftWorkflowPayload = z.object({
|
||||
hash: z.string().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* SyncDraftWorkflowResponse
|
||||
*/
|
||||
export const zSyncDraftWorkflowResponse = z.object({
|
||||
hash: z.string().optional(),
|
||||
result: z.string().optional(),
|
||||
updated_at: z.string().optional(),
|
||||
hash: z.string(),
|
||||
result: z.string(),
|
||||
updated_at: z.int(),
|
||||
})
|
||||
|
||||
/**
|
||||
@ -886,6 +889,36 @@ export const zDeletedTool = z.object({
|
||||
type: z.string(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AppModelConfigResponse
|
||||
*/
|
||||
export const zAppModelConfigResponse = z.object({
|
||||
agent_mode: z.unknown().nullish(),
|
||||
annotation_reply: z.unknown().nullish(),
|
||||
chat_prompt_config: z.unknown().nullish(),
|
||||
completion_prompt_config: z.unknown().nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
dataset_configs: z.unknown().nullish(),
|
||||
dataset_query_variable: z.string().nullish(),
|
||||
external_data_tools: z.unknown().nullish(),
|
||||
file_upload: z.unknown().nullish(),
|
||||
model: z.unknown().nullish(),
|
||||
more_like_this: z.unknown().nullish(),
|
||||
opening_statement: z.string().nullish(),
|
||||
pre_prompt: z.string().nullish(),
|
||||
prompt_type: z.string().nullish(),
|
||||
retriever_resource: z.unknown().nullish(),
|
||||
sensitive_word_avoidance: z.unknown().nullish(),
|
||||
speech_to_text: z.unknown().nullish(),
|
||||
suggested_questions: z.unknown().nullish(),
|
||||
suggested_questions_after_answer: z.unknown().nullish(),
|
||||
text_to_speech: z.unknown().nullish(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
user_input_form: z.unknown().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AppDetailSiteResponse
|
||||
*/
|
||||
@ -937,6 +970,66 @@ export const zWorkflowPartial = z.object({
|
||||
updated_by: z.string().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AppDetailWithSite
|
||||
*/
|
||||
export const zAppDetailWithSite = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
api_base_url: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
deleted_tools: z.array(zDeletedTool).optional(),
|
||||
description: z.string().nullish(),
|
||||
enable_api: z.boolean(),
|
||||
enable_site: z.boolean(),
|
||||
icon: z.string().nullish(),
|
||||
icon_background: z.string().nullish(),
|
||||
icon_type: z.string().nullish(),
|
||||
icon_url: z.string().nullable(),
|
||||
id: z.string(),
|
||||
maintainer: z.string().nullish(),
|
||||
max_active_requests: z.int().nullish(),
|
||||
mode: z.string(),
|
||||
model_config: zAppModelConfigResponse.nullish(),
|
||||
name: z.string(),
|
||||
permission_keys: z.array(z.string()).optional(),
|
||||
site: zAppDetailSiteResponse.nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
tracing: z.unknown().nullish(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
use_icon_as_answer_icon: z.boolean().nullish(),
|
||||
workflow: zWorkflowPartial.nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AppDetail
|
||||
*/
|
||||
export const zAppDetail = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
description: z.string().nullish(),
|
||||
enable_api: z.boolean(),
|
||||
enable_site: z.boolean(),
|
||||
icon: z.string().nullish(),
|
||||
icon_background: z.string().nullish(),
|
||||
id: z.string(),
|
||||
maintainer: z.string().nullish(),
|
||||
mode: z.string(),
|
||||
model_config: zAppModelConfigResponse.nullish(),
|
||||
name: z.string(),
|
||||
permission_keys: z.array(z.string()).optional(),
|
||||
tags: z.array(zTag).optional(),
|
||||
tracing: z.unknown().nullish(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
use_icon_as_answer_icon: z.boolean().nullish(),
|
||||
workflow: zWorkflowPartial.nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* ImportStatus
|
||||
*/
|
||||
@ -2251,102 +2344,6 @@ export const zAppPagination = z.object({
|
||||
total: z.int(),
|
||||
})
|
||||
|
||||
/**
|
||||
* LLMMode
|
||||
*
|
||||
* Enum class for large language model mode.
|
||||
*/
|
||||
export const zLlmMode = z.enum(['chat', 'completion'])
|
||||
|
||||
/**
|
||||
* ModelConfig
|
||||
*/
|
||||
export const zModelConfig = z.object({
|
||||
completion_params: z.record(z.string(), z.unknown()).optional(),
|
||||
mode: zLlmMode,
|
||||
name: z.string(),
|
||||
provider: z.string(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AppDetailWithSite
|
||||
*/
|
||||
export const zAppDetailWithSite = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
api_base_url: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
deleted_tools: z.array(zDeletedTool).optional(),
|
||||
description: z.string().nullish(),
|
||||
enable_api: z.boolean(),
|
||||
enable_site: z.boolean(),
|
||||
icon: z.string().nullish(),
|
||||
icon_background: z.string().nullish(),
|
||||
icon_type: z.string().nullish(),
|
||||
icon_url: z.string().nullable(),
|
||||
id: z.string(),
|
||||
maintainer: z.string().nullish(),
|
||||
max_active_requests: z.int().nullish(),
|
||||
mode: z.string(),
|
||||
model_config: zModelConfig.nullish(),
|
||||
name: z.string(),
|
||||
permission_keys: z.array(z.string()).optional(),
|
||||
site: zAppDetailSiteResponse.nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
tracing: z.unknown().nullish(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
use_icon_as_answer_icon: z.boolean().nullish(),
|
||||
workflow: zWorkflowPartial.nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AppDetail
|
||||
*/
|
||||
export const zAppDetail = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
app_model_config: zModelConfig.nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
description: z.string().nullish(),
|
||||
enable_api: z.boolean(),
|
||||
enable_site: z.boolean(),
|
||||
icon: z.string().nullish(),
|
||||
icon_background: z.string().nullish(),
|
||||
id: z.string(),
|
||||
maintainer: z.string().nullish(),
|
||||
mode_compatible_with_agent: z.string(),
|
||||
name: z.string(),
|
||||
permission_keys: z.array(z.string()).optional(),
|
||||
tags: z.array(zTag).optional(),
|
||||
tracing: z.unknown().nullish(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
use_icon_as_answer_icon: z.boolean().nullish(),
|
||||
workflow: zWorkflowPartial.nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* ConversationDetail
|
||||
*/
|
||||
export const zConversationDetail = z.object({
|
||||
admin_feedback_stats: zFeedbackStat.nullish(),
|
||||
annotated: z.boolean(),
|
||||
created_at: z.int().nullish(),
|
||||
from_account_id: z.string().nullish(),
|
||||
from_end_user_id: z.string().nullish(),
|
||||
from_source: z.string(),
|
||||
id: z.string(),
|
||||
introduction: z.string().nullish(),
|
||||
message_count: z.int(),
|
||||
model_config: zModelConfig.nullish(),
|
||||
status: z.string(),
|
||||
updated_at: z.int().nullish(),
|
||||
user_feedback_stats: zFeedbackStat.nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* PluginDependencyType
|
||||
*/
|
||||
@ -2537,6 +2534,42 @@ export const zConversationWithSummaryPagination = z.object({
|
||||
total: z.int(),
|
||||
})
|
||||
|
||||
/**
|
||||
* LLMMode
|
||||
*
|
||||
* Enum class for large language model mode.
|
||||
*/
|
||||
export const zLlmMode = z.enum(['chat', 'completion'])
|
||||
|
||||
/**
|
||||
* ModelConfig
|
||||
*/
|
||||
export const zModelConfig = z.object({
|
||||
completion_params: z.record(z.string(), z.unknown()).optional(),
|
||||
mode: zLlmMode,
|
||||
name: z.string(),
|
||||
provider: z.string(),
|
||||
})
|
||||
|
||||
/**
|
||||
* ConversationDetail
|
||||
*/
|
||||
export const zConversationDetail = z.object({
|
||||
admin_feedback_stats: zFeedbackStat.nullish(),
|
||||
annotated: z.boolean(),
|
||||
created_at: z.int().nullish(),
|
||||
from_account_id: z.string().nullish(),
|
||||
from_end_user_id: z.string().nullish(),
|
||||
from_source: z.string(),
|
||||
id: z.string(),
|
||||
introduction: z.string().nullish(),
|
||||
message_count: z.int(),
|
||||
model_config: zModelConfig.nullish(),
|
||||
status: z.string(),
|
||||
updated_at: z.int().nullish(),
|
||||
user_feedback_stats: zFeedbackStat.nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* SimpleMessageDetail
|
||||
*/
|
||||
@ -4234,7 +4267,7 @@ export const zAppDetailWithSiteWritable = z.object({
|
||||
maintainer: z.string().nullish(),
|
||||
max_active_requests: z.int().nullish(),
|
||||
mode: z.string(),
|
||||
model_config: zModelConfig.nullish(),
|
||||
model_config: zAppModelConfigResponse.nullish(),
|
||||
name: z.string(),
|
||||
permission_keys: z.array(z.string()).optional(),
|
||||
site: zAppDetailSiteResponseWritable.nullish(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user