Compare commits

..

1 Commits

Author SHA1 Message Date
eux
bf46b82303 fix(web): clarify unpublished explore app handling (#38260) 2026-07-01 11:04:01 +00:00
25 changed files with 871 additions and 1651 deletions

View File

@ -1,10 +1,9 @@
import logging
from datetime import datetime
from typing import Any, Literal, cast
from flask import request
from flask_restx import Resource, fields, marshal, marshal_with
from pydantic import AliasChoices, BaseModel, Field, field_validator
from pydantic import BaseModel, Field
from sqlalchemy import select
from werkzeug.exceptions import Forbidden, InternalServerError, NotFound
@ -64,7 +63,6 @@ from fields.app_fields import (
site_fields,
tag_fields,
)
from fields.base import ResponseModel
from fields.dataset_fields import dataset_fields
from fields.member_fields import simple_account_fields
from fields.message_fields import SuggestedQuestionsResponse
@ -77,7 +75,7 @@ from fields.workflow_fields import (
from graphon.graph_engine.manager import GraphEngineManager
from graphon.model_runtime.errors.invoke import InvokeError
from libs import helper
from libs.helper import to_timestamp, uuid_value
from libs.helper import uuid_value
from models import Account
from models.account import TenantStatus
from models.model import AppMode, Site
@ -182,266 +180,6 @@ class TrialDatasetListQuery(BaseModel):
ids: list[str] = Field(default_factory=list, description="Dataset IDs")
type TrialAppMode = Literal["chat", "agent-chat", "advanced-chat", "workflow", "completion"]
type TrialIconType = Literal["emoji", "image", "link"]
type JsonObject = dict[str, Any]
class TrialAppModel(ResponseModel):
provider: str
name: str
mode: str | None = None
completion_params: JsonObject = Field(default_factory=dict)
class TrialAppAgentMode(ResponseModel):
enabled: bool | None = None
strategy: str | None = None
tools: list[JsonObject] = Field(default_factory=list)
class TrialAppModelConfigResponse(ResponseModel):
opening_statement: str | None = None
suggested_questions: list[str] = Field(
default_factory=list,
validation_alias=AliasChoices("suggested_questions_list", "suggested_questions"),
)
suggested_questions_after_answer: JsonObject | None = Field(
default=None,
validation_alias=AliasChoices("suggested_questions_after_answer_dict", "suggested_questions_after_answer"),
)
speech_to_text: JsonObject | None = Field(
default=None, validation_alias=AliasChoices("speech_to_text_dict", "speech_to_text")
)
text_to_speech: JsonObject | None = Field(
default=None, validation_alias=AliasChoices("text_to_speech_dict", "text_to_speech")
)
retriever_resource: JsonObject | None = Field(
default=None, validation_alias=AliasChoices("retriever_resource_dict", "retriever_resource")
)
annotation_reply: JsonObject | None = Field(
default=None, validation_alias=AliasChoices("annotation_reply_dict", "annotation_reply")
)
more_like_this: JsonObject | None = Field(
default=None, validation_alias=AliasChoices("more_like_this_dict", "more_like_this")
)
sensitive_word_avoidance: JsonObject | None = Field(
default=None, validation_alias=AliasChoices("sensitive_word_avoidance_dict", "sensitive_word_avoidance")
)
external_data_tools: list[JsonObject] = Field(
default_factory=list, validation_alias=AliasChoices("external_data_tools_list", "external_data_tools")
)
model: TrialAppModel | None = Field(default=None, validation_alias=AliasChoices("model_dict", "model"))
user_input_form: list[JsonObject] = Field(
default_factory=list, validation_alias=AliasChoices("user_input_form_list", "user_input_form")
)
dataset_query_variable: str | None = None
pre_prompt: str | None = None
agent_mode: TrialAppAgentMode | None = Field(
default=None,
validation_alias=AliasChoices("agent_mode_dict", "agent_mode"),
)
prompt_type: str | None = None
chat_prompt_config: JsonObject | None = Field(
default=None, validation_alias=AliasChoices("chat_prompt_config_dict", "chat_prompt_config")
)
completion_prompt_config: JsonObject | None = Field(
default=None, validation_alias=AliasChoices("completion_prompt_config_dict", "completion_prompt_config")
)
dataset_configs: JsonObject | None = Field(
default=None,
validation_alias=AliasChoices("dataset_configs_dict", "dataset_configs"),
)
file_upload: JsonObject | None = Field(
default=None,
validation_alias=AliasChoices("file_upload_dict", "file_upload"),
)
created_by: str | None = None
created_at: int | None = None
updated_by: str | None = None
updated_at: int | None = None
@field_validator("created_at", "updated_at", mode="before")
@classmethod
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
return to_timestamp(value)
class TrialDeletedToolResponse(ResponseModel):
type: str
tool_name: str
provider_id: str
class TrialTagResponse(ResponseModel):
id: str
name: str
type: str
class TrialSiteResponse(ResponseModel):
access_token: str | None = Field(default=None, validation_alias="code")
code: str | None = None
title: str
icon_type: TrialIconType | None = None
icon: str | None = None
icon_background: str | None = None
description: str | None = None
default_language: str
chat_color_theme: str | None = None
chat_color_theme_inverted: bool | None = None
customize_domain: str | None = None
copyright: str | None = None
privacy_policy: str | None = None
input_placeholder: str | None = None
custom_disclaimer: str | None = None
customize_token_strategy: str | None = None
prompt_public: bool | None = None
app_base_url: str | None = None
show_workflow_steps: bool | None = None
use_icon_as_answer_icon: bool | None = None
created_by: str | None = None
created_at: int | None = None
updated_by: str | None = None
updated_at: int | None = None
icon_url: str | None = None
@field_validator("icon_type", mode="before")
@classmethod
def _normalize_icon_type(cls, value: Any) -> str | None:
if hasattr(value, "value"):
return value.value
return value
@field_validator("created_at", "updated_at", mode="before")
@classmethod
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
return to_timestamp(value)
class TrialWorkflowPartialResponse(ResponseModel):
id: str
created_by: str | None = None
created_at: int | None = None
updated_by: str | None = None
updated_at: int | None = None
@field_validator("created_at", "updated_at", mode="before")
@classmethod
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
return to_timestamp(value)
class TrialAppDetailResponse(ResponseModel):
id: str
name: str
description: str | None = None
mode: TrialAppMode = Field(validation_alias="mode_compatible_with_agent")
icon_type: TrialIconType | None = None
icon: str | None = None
icon_background: str | None = None
icon_url: str | None = None
enable_site: bool
enable_api: bool
model_config_: TrialAppModelConfigResponse | None = Field(
default=None,
validation_alias=AliasChoices("app_model_config", "model_config"),
alias="model_config",
)
workflow: TrialWorkflowPartialResponse | None = None
api_base_url: str | None = None
use_icon_as_answer_icon: bool | None = None
max_active_requests: int | None = None
created_by: str | None = None
created_at: int | None = None
updated_by: str | None = None
updated_at: int | None = None
deleted_tools: list[TrialDeletedToolResponse] = Field(default_factory=list)
access_mode: str | None = None
tags: list[TrialTagResponse] = Field(default_factory=list)
permission_keys: list[str] = Field(default_factory=list)
site: TrialSiteResponse
@field_validator("icon_type", mode="before")
@classmethod
def _normalize_icon_type(cls, value: Any) -> str | None:
if hasattr(value, "value"):
return value.value
return value
@field_validator("created_at", "updated_at", mode="before")
@classmethod
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
return to_timestamp(value)
class TrialDatasetResponse(ResponseModel):
id: str
name: str
description: str | None = None
permission: str | None = None
data_source_type: str | None = None
indexing_technique: str | None = None
created_by: str | None = None
created_at: int | None = None
permission_keys: list[str] = Field(default_factory=list)
class TrialDatasetListResponse(ResponseModel):
data: list[TrialDatasetResponse]
has_more: bool
limit: int
total: int
page: int
class TrialWorkflowViewport(ResponseModel):
x: float
y: float
zoom: float
class TrialWorkflowGraph(ResponseModel):
nodes: list[JsonObject]
edges: list[JsonObject]
viewport: TrialWorkflowViewport
class TrialWorkflowAccount(ResponseModel):
id: str
name: str | None = None
email: str | None = None
class TrialWorkflowResponse(ResponseModel):
id: str
graph: TrialWorkflowGraph = Field(validation_alias=AliasChoices("graph_dict", "graph"))
features: JsonObject = Field(default_factory=dict, validation_alias=AliasChoices("features_dict", "features"))
hash: str | None = Field(default=None, validation_alias=AliasChoices("unique_hash", "hash"))
version: str | None = None
marked_name: str | None = None
marked_comment: str | None = None
created_by: TrialWorkflowAccount | None = Field(
default=None,
validation_alias=AliasChoices("created_by_account", "created_by"),
)
created_at: int | None = None
updated_by: TrialWorkflowAccount | None = Field(
default=None,
validation_alias=AliasChoices("updated_by_account", "updated_by"),
)
updated_at: int | None = None
tool_published: bool | None = None
environment_variables: list[JsonObject] = Field(default_factory=list)
conversation_variables: list[JsonObject] = Field(default_factory=list)
rag_pipeline_variables: list[JsonObject] = Field(default_factory=list)
@field_validator("created_at", "updated_at", mode="before")
@classmethod
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
return to_timestamp(value)
register_schema_models(
console_ns,
WorkflowRunRequest,
@ -459,9 +197,6 @@ register_response_schema_models(
SimpleResultResponse,
SiteResponse,
SuggestedQuestionsResponse,
TrialAppDetailResponse,
TrialDatasetListResponse,
TrialWorkflowResponse,
)
@ -831,7 +566,7 @@ class TrialAppParameterApi(Resource):
class AppApi(Resource):
@console_ns.response(200, "Success", console_ns.models[TrialAppDetailResponse.__name__])
@console_ns.response(200, "Success", app_detail_with_site_model)
@get_app_model_with_trial(None)
@marshal_with(app_detail_with_site_model)
def get(self, app_model):
@ -844,7 +579,7 @@ class AppApi(Resource):
class AppWorkflowApi(Resource):
@console_ns.response(200, "Success", console_ns.models[TrialWorkflowResponse.__name__])
@console_ns.response(200, "Success", workflow_model)
@get_app_model_with_trial(None)
@marshal_with(workflow_model)
def get(self, app_model):
@ -858,7 +593,7 @@ class AppWorkflowApi(Resource):
class DatasetListApi(Resource):
@console_ns.doc(params=query_params_from_model(TrialDatasetListQuery))
@console_ns.response(200, "Success", console_ns.models[TrialDatasetListResponse.__name__])
@console_ns.response(200, "Success", dataset_list_model)
@get_app_model_with_trial(None)
def get(self, app_model):
page = request.args.get("page", default=1, type=int)

View File

@ -9385,7 +9385,7 @@ Bedrock retrieval test (internal use only)
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [TrialAppDetailResponse](#trialappdetailresponse)<br> |
| 200 | Success | **application/json**: [TrialAppDetailWithSite](#trialappdetailwithsite)<br> |
### [POST] /trial-apps/{app_id}/audio-to-text
#### Parameters
@ -9452,7 +9452,7 @@ Bedrock retrieval test (internal use only)
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [TrialDatasetListResponse](#trialdatasetlistresponse)<br> |
| 200 | Success | **application/json**: [TrialDatasetList](#trialdatasetlist)<br> |
### [GET] /trial-apps/{app_id}/messages/{message_id}/suggested-questions
#### Parameters
@ -9532,7 +9532,7 @@ Returns the site configuration for the application including theme, icons, and t
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Success | **application/json**: [TrialWorkflowResponse](#trialworkflowresponse)<br> |
| 200 | Success | **application/json**: [TrialWorkflow](#trialworkflow)<br> |
### [POST] /trial-apps/{app_id}/workflows/run
**Run workflow**
@ -18048,12 +18048,6 @@ Input field definition for snippet parameters.
| ---- | ---- | ----------- | -------- |
| JSONValueType | | | |
#### JsonObject
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| JsonObject | object | | |
#### JsonValue
| Name | Type | Description | Required |
@ -21283,43 +21277,6 @@ Enum class for tool provider
| ---- | ---- | ----------- | -------- |
| tracing_provider | string | Tracing provider name | Yes |
#### TrialAppAgentMode
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| enabled | boolean | | No |
| strategy | string | | No |
| tools | [ [JsonObject](#jsonobject) ] | | No |
#### TrialAppDetailResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| access_mode | string | | No |
| api_base_url | string | | No |
| created_at | integer | | No |
| created_by | string | | No |
| deleted_tools | [ [TrialDeletedToolResponse](#trialdeletedtoolresponse) ] | | No |
| description | string | | No |
| enable_api | boolean | | Yes |
| enable_site | boolean | | Yes |
| icon | string | | No |
| icon_background | string | | No |
| icon_type | [TrialIconType](#trialicontype) | | No |
| icon_url | string | | No |
| id | string | | Yes |
| max_active_requests | integer | | No |
| mode | [TrialAppMode](#trialappmode) | | Yes |
| model_config | [TrialAppModelConfigResponse](#trialappmodelconfigresponse) | | No |
| name | string | | Yes |
| permission_keys | [ string ] | | No |
| site | [TrialSiteResponse](#trialsiteresponse) | | Yes |
| tags | [ [TrialTagResponse](#trialtagresponse) ] | | No |
| updated_at | integer | | No |
| updated_by | string | | No |
| use_icon_as_answer_icon | boolean | | No |
| workflow | [TrialWorkflowPartialResponse](#trialworkflowpartialresponse) | | No |
#### TrialAppDetailWithSite
| Name | Type | Description | Required |
@ -21349,21 +21306,6 @@ Enum class for tool provider
| use_icon_as_answer_icon | boolean | | No |
| workflow | [TrialWorkflowPartial](#trialworkflowpartial) | | No |
#### TrialAppMode
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| TrialAppMode | string | | |
#### TrialAppModel
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| completion_params | [JsonObject](#jsonobject) | | No |
| mode | string | | No |
| name | string | | Yes |
| provider | string | | Yes |
#### TrialAppModelConfig
| Name | Type | Description | Required |
@ -21393,35 +21335,6 @@ Enum class for tool provider
| updated_by | string | | No |
| user_input_form | [ object ] | | No |
#### TrialAppModelConfigResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| agent_mode | [TrialAppAgentMode](#trialappagentmode) | | No |
| annotation_reply | [JsonObject](#jsonobject) | | No |
| chat_prompt_config | [JsonObject](#jsonobject) | | No |
| completion_prompt_config | [JsonObject](#jsonobject) | | No |
| created_at | integer | | No |
| created_by | string | | No |
| dataset_configs | [JsonObject](#jsonobject) | | No |
| dataset_query_variable | string | | No |
| external_data_tools | [ [JsonObject](#jsonobject) ] | | No |
| file_upload | [JsonObject](#jsonobject) | | No |
| model | [TrialAppModel](#trialappmodel) | | No |
| more_like_this | [JsonObject](#jsonobject) | | No |
| opening_statement | string | | No |
| pre_prompt | string | | No |
| prompt_type | string | | No |
| retriever_resource | [JsonObject](#jsonobject) | | No |
| sensitive_word_avoidance | [JsonObject](#jsonobject) | | No |
| speech_to_text | [JsonObject](#jsonobject) | | No |
| suggested_questions | [ string ] | | No |
| suggested_questions_after_answer | [JsonObject](#jsonobject) | | No |
| text_to_speech | [JsonObject](#jsonobject) | | No |
| updated_at | integer | | No |
| updated_by | string | | No |
| user_input_form | [ [JsonObject](#jsonobject) ] | | No |
#### TrialConversationVariable
| Name | Type | Description | Required |
@ -21464,30 +21377,6 @@ Enum class for tool provider
| limit | integer, <br>**Default:** 20 | Number of items per page | No |
| page | integer, <br>**Default:** 1 | Page number | No |
#### TrialDatasetListResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| data | [ [TrialDatasetResponse](#trialdatasetresponse) ] | | Yes |
| has_more | boolean | | Yes |
| limit | integer | | Yes |
| page | integer | | Yes |
| total | integer | | Yes |
#### TrialDatasetResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| created_at | integer | | No |
| created_by | string | | No |
| data_source_type | string | | No |
| description | string | | No |
| id | string | | Yes |
| indexing_technique | string | | No |
| name | string | | Yes |
| permission | string | | No |
| permission_keys | [ string ] | | No |
#### TrialDeletedTool
| Name | Type | Description | Required |
@ -21496,20 +21385,6 @@ Enum class for tool provider
| tool_name | string | | No |
| type | string | | No |
#### TrialDeletedToolResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| provider_id | string | | Yes |
| tool_name | string | | Yes |
| type | string | | Yes |
#### TrialIconType
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| TrialIconType | string | | |
#### TrialModelsResponse
| Name | Type | Description | Required |
@ -21572,36 +21447,6 @@ Enum class for tool provider
| updated_by | string | | No |
| use_icon_as_answer_icon | boolean | | No |
#### TrialSiteResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| access_token | string | | No |
| app_base_url | string | | No |
| chat_color_theme | string | | No |
| chat_color_theme_inverted | boolean | | No |
| code | string | | No |
| copyright | string | | No |
| created_at | integer | | No |
| created_by | string | | No |
| custom_disclaimer | string | | No |
| customize_domain | string | | No |
| customize_token_strategy | string | | No |
| default_language | string | | Yes |
| description | string | | No |
| icon | string | | No |
| icon_background | string | | No |
| icon_type | [TrialIconType](#trialicontype) | | No |
| icon_url | string | | No |
| input_placeholder | string | | No |
| privacy_policy | string | | No |
| prompt_public | boolean | | No |
| show_workflow_steps | boolean | | No |
| title | string | | Yes |
| updated_at | integer | | No |
| updated_by | string | | No |
| use_icon_as_answer_icon | boolean | | No |
#### TrialTag
| Name | Type | Description | Required |
@ -21610,14 +21455,6 @@ Enum class for tool provider
| name | string | | No |
| type | string | | No |
#### TrialTagResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| id | string | | Yes |
| name | string | | Yes |
| type | string | | Yes |
#### TrialWorkflow
| Name | Type | Description | Required |
@ -21638,22 +21475,6 @@ Enum class for tool provider
| updated_by | [TrialSimpleAccount](#trialsimpleaccount) | | No |
| version | string | | No |
#### TrialWorkflowAccount
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| email | string | | No |
| id | string | | Yes |
| name | string | | No |
#### TrialWorkflowGraph
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| edges | [ [JsonObject](#jsonobject) ] | | Yes |
| nodes | [ [JsonObject](#jsonobject) ] | | Yes |
| viewport | [TrialWorkflowViewport](#trialworkflowviewport) | | Yes |
#### TrialWorkflowPartial
| Name | Type | Description | Required |
@ -21664,44 +21485,6 @@ Enum class for tool provider
| updated_at | long | | No |
| updated_by | string | | No |
#### TrialWorkflowPartialResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| created_at | integer | | No |
| created_by | string | | No |
| id | string | | Yes |
| updated_at | integer | | No |
| updated_by | string | | No |
#### TrialWorkflowResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| conversation_variables | [ [JsonObject](#jsonobject) ] | | No |
| created_at | integer | | No |
| created_by | [TrialWorkflowAccount](#trialworkflowaccount) | | No |
| environment_variables | [ [JsonObject](#jsonobject) ] | | No |
| features | [JsonObject](#jsonobject) | | No |
| graph | [TrialWorkflowGraph](#trialworkflowgraph) | | Yes |
| hash | string | | No |
| id | string | | Yes |
| marked_comment | string | | No |
| marked_name | string | | No |
| rag_pipeline_variables | [ [JsonObject](#jsonobject) ] | | No |
| tool_published | boolean | | No |
| updated_at | integer | | No |
| updated_by | [TrialWorkflowAccount](#trialworkflowaccount) | | No |
| version | string | | No |
#### TrialWorkflowViewport
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| x | number | | Yes |
| y | number | | Yes |
| zoom | number | | Yes |
#### TriggerCreationMethod
| Name | Type | Description | Required |

View File

@ -7274,6 +7274,19 @@
"count": 1
}
},
"web/service/try-app.spec.ts": {
"no-restricted-imports": {
"count": 1
}
},
"web/service/try-app.ts": {
"no-barrel-files/no-barrel-files": {
"count": 2
},
"no-restricted-imports": {
"count": 1
}
},
"web/service/use-apps.ts": {
"no-restricted-imports": {
"count": 1
@ -7393,6 +7406,11 @@
"count": 1
}
},
"web/types/app.ts": {
"ts/no-explicit-any": {
"count": 1
}
},
"web/types/assets.d.ts": {
"ts/no-explicit-any": {
"count": 5
@ -7464,6 +7482,16 @@
"count": 1
}
},
"web/utils/model-config.spec.ts": {
"ts/no-explicit-any": {
"count": 13
}
},
"web/utils/model-config.ts": {
"ts/no-explicit-any": {
"count": 6
}
},
"web/utils/tool-call.spec.ts": {
"ts/no-explicit-any": {
"count": 1

View File

@ -4,31 +4,31 @@ export type ClientOptions = {
baseUrl: `${string}://${string}/console/api` | (string & {})
}
export type TrialAppDetailResponse = {
access_mode?: string | null
api_base_url?: string | null
created_at?: number | null
created_by?: string | null
deleted_tools?: Array<TrialDeletedToolResponse>
description?: string | null
enable_api: boolean
enable_site: boolean
icon?: string | null
icon_background?: string | null
icon_type?: TrialIconType | null
icon_url?: string | null
id: string
max_active_requests?: number | null
mode: TrialAppMode
model_config?: TrialAppModelConfigResponse | null
name: string
export type TrialAppDetailWithSite = {
access_mode?: string
api_base_url?: string
created_at?: number
created_by?: string
deleted_tools?: Array<TrialDeletedTool>
description?: string
enable_api?: boolean
enable_site?: boolean
icon?: string
icon_background?: string
icon_type?: string
icon_url?: string
id?: string
max_active_requests?: number
mode?: string
model_config?: TrialAppModelConfig
name?: string
permission_keys?: Array<string>
site: TrialSiteResponse
tags?: Array<TrialTagResponse>
updated_at?: number | null
updated_by?: string | null
use_icon_as_answer_icon?: boolean | null
workflow?: TrialWorkflowPartialResponse | null
site?: TrialSite
tags?: Array<TrialTag>
updated_at?: number
updated_by?: string
use_icon_as_answer_icon?: boolean
workflow?: TrialWorkflowPartial
}
export type AudioTranscriptResponse = {
@ -58,12 +58,12 @@ export type CompletionRequest = {
retriever_from?: string
}
export type TrialDatasetListResponse = {
data: Array<TrialDatasetResponse>
has_more: boolean
limit: number
page: number
total: number
export type TrialDatasetList = {
data?: Array<TrialDataset>
has_more?: boolean
limit?: number
page?: number
total?: number
}
export type SuggestedQuestionsResponse = {
@ -112,22 +112,28 @@ export type TextToSpeechRequest = {
export type AudioBinaryResponse = Blob | File
export type TrialWorkflowResponse = {
conversation_variables?: Array<JsonObject2>
created_at?: number | null
created_by?: TrialWorkflowAccount | null
environment_variables?: Array<JsonObject2>
features?: JsonObject2
graph: TrialWorkflowGraph
hash?: string | null
id: string
marked_comment?: string | null
marked_name?: string | null
rag_pipeline_variables?: Array<JsonObject2>
tool_published?: boolean | null
updated_at?: number | null
updated_by?: TrialWorkflowAccount | null
version?: string | null
export type TrialWorkflow = {
conversation_variables?: Array<TrialConversationVariable>
created_at?: number
created_by?: TrialSimpleAccount
environment_variables?: Array<{
[key: string]: unknown
}>
features?: {
[key: string]: unknown
}
graph?: {
[key: string]: unknown
}
hash?: string
id?: string
marked_comment?: string
marked_name?: string
rag_pipeline_variables?: Array<TrialPipelineVariable>
tool_published?: boolean
updated_at?: number
updated_by?: TrialSimpleAccount
version?: string
}
export type WorkflowRunRequest = {
@ -141,83 +147,108 @@ export type SimpleResultResponse = {
result: string
}
export type TrialDeletedToolResponse = {
provider_id: string
tool_name: string
type: string
export type TrialDeletedTool = {
provider_id?: string
tool_name?: string
type?: string
}
export type TrialIconType = 'emoji' | 'image' | 'link'
export type TrialAppMode = 'advanced-chat' | 'agent-chat' | 'chat' | 'completion' | 'workflow'
export type TrialAppModelConfigResponse = {
agent_mode?: TrialAppAgentMode | null
annotation_reply?: JsonObject2 | null
chat_prompt_config?: JsonObject2 | null
completion_prompt_config?: JsonObject2 | null
created_at?: number | null
created_by?: string | null
dataset_configs?: JsonObject2 | null
dataset_query_variable?: string | null
external_data_tools?: Array<JsonObject2>
file_upload?: JsonObject2 | null
model?: TrialAppModel | null
more_like_this?: JsonObject2 | null
opening_statement?: string | null
pre_prompt?: string | null
prompt_type?: string | null
retriever_resource?: JsonObject2 | null
sensitive_word_avoidance?: JsonObject2 | null
speech_to_text?: JsonObject2 | null
export type TrialAppModelConfig = {
agent_mode?: {
[key: string]: unknown
}
annotation_reply?: {
[key: string]: unknown
}
chat_prompt_config?: {
[key: string]: unknown
}
completion_prompt_config?: {
[key: string]: unknown
}
created_at?: number
created_by?: string
dataset_configs?: {
[key: string]: unknown
}
dataset_query_variable?: string
external_data_tools?: Array<{
[key: string]: unknown
}>
file_upload?: {
[key: string]: unknown
}
model?: {
[key: string]: unknown
}
more_like_this?: {
[key: string]: unknown
}
opening_statement?: string
pre_prompt?: string
prompt_type?: string
retriever_resource?: {
[key: string]: unknown
}
sensitive_word_avoidance?: {
[key: string]: unknown
}
speech_to_text?: {
[key: string]: unknown
}
suggested_questions?: Array<string>
suggested_questions_after_answer?: JsonObject2 | null
text_to_speech?: JsonObject2 | null
updated_at?: number | null
updated_by?: string | null
user_input_form?: Array<JsonObject2>
suggested_questions_after_answer?: {
[key: string]: unknown
}
text_to_speech?: {
[key: string]: unknown
}
updated_at?: number
updated_by?: string
user_input_form?: Array<{
[key: string]: unknown
}>
}
export type TrialSiteResponse = {
access_token?: string | null
app_base_url?: string | null
chat_color_theme?: string | null
chat_color_theme_inverted?: boolean | null
code?: string | null
copyright?: string | null
created_at?: number | null
created_by?: string | null
custom_disclaimer?: string | null
customize_domain?: string | null
customize_token_strategy?: string | null
default_language: string
description?: string | null
icon?: string | null
icon_background?: string | null
icon_type?: TrialIconType | null
icon_url?: string | null
input_placeholder?: string | null
privacy_policy?: string | null
prompt_public?: boolean | null
show_workflow_steps?: boolean | null
title: string
updated_at?: number | null
updated_by?: string | null
use_icon_as_answer_icon?: boolean | null
export type TrialSite = {
access_token?: string
app_base_url?: string
chat_color_theme?: string
chat_color_theme_inverted?: boolean
code?: string
copyright?: string
created_at?: number
created_by?: string
custom_disclaimer?: string
customize_domain?: string
customize_token_strategy?: string
default_language?: string
description?: string
icon?: string
icon_background?: string
icon_type?: string
icon_url?: string
privacy_policy?: string
prompt_public?: boolean
show_workflow_steps?: boolean
title?: string
updated_at?: number
updated_by?: string
use_icon_as_answer_icon?: boolean
}
export type TrialTagResponse = {
id: string
name: string
type: string
export type TrialTag = {
id?: string
name?: string
type?: string
}
export type TrialWorkflowPartialResponse = {
created_at?: number | null
created_by?: string | null
id: string
updated_at?: number | null
updated_by?: string | null
export type TrialWorkflowPartial = {
created_at?: number
created_by?: string
id?: string
updated_at?: number
updated_by?: string
}
export type JsonValue
@ -231,15 +262,15 @@ export type JsonValue
| Array<unknown>
| null
export type TrialDatasetResponse = {
created_at?: number | null
created_by?: string | null
data_source_type?: string | null
description?: string | null
id: string
indexing_technique?: string | null
name: string
permission?: string | null
export type TrialDataset = {
created_at?: number
created_by?: string
data_source_type?: string
description?: string
id?: string
indexing_technique?: string
name?: string
permission?: string
permission_keys?: Array<string>
}
@ -255,39 +286,53 @@ export type SystemParameters = {
workflow_file_upload_limit: number
}
export type JsonObject2 = {
[key: string]: unknown
export type TrialConversationVariable = {
description?: string
id?: string
name?: string
value?:
| string
| number
| number
| boolean
| {
[key: string]: unknown
}
| Array<unknown>
| null
value_type?: string
}
export type TrialWorkflowAccount = {
email?: string | null
id: string
name?: string | null
export type TrialSimpleAccount = {
email?: string
id?: string
name?: string
}
export type TrialWorkflowGraph = {
edges: Array<JsonObject2>
nodes: Array<JsonObject2>
viewport: TrialWorkflowViewport
}
export type TrialAppAgentMode = {
enabled?: boolean | null
strategy?: string | null
tools?: Array<JsonObject2>
}
export type TrialAppModel = {
completion_params?: JsonObject2
mode?: string | null
name: string
provider: string
}
export type TrialWorkflowViewport = {
x: number
y: number
zoom: number
export type TrialPipelineVariable = {
allow_file_extension?: Array<string>
allow_file_upload_methods?: Array<string>
allowed_file_types?: Array<string>
belong_to_node_id?: string
default_value?:
| string
| number
| number
| boolean
| {
[key: string]: unknown
}
| Array<unknown>
| null
label?: string
max_length?: number
options?: Array<string>
placeholder?: string
required?: boolean
tooltips?: string
type?: string
unit?: string
variable?: string
}
export type GeneratedAppResponseWritable = JsonValue
@ -319,7 +364,7 @@ export type GetTrialAppsByAppIdData = {
}
export type GetTrialAppsByAppIdResponses = {
200: TrialAppDetailResponse
200: TrialAppDetailWithSite
}
export type GetTrialAppsByAppIdResponse
@ -387,7 +432,7 @@ export type GetTrialAppsByAppIdDatasetsData = {
}
export type GetTrialAppsByAppIdDatasetsResponses = {
200: TrialDatasetListResponse
200: TrialDatasetList
}
export type GetTrialAppsByAppIdDatasetsResponse
@ -468,7 +513,7 @@ export type GetTrialAppsByAppIdWorkflowsData = {
}
export type GetTrialAppsByAppIdWorkflowsResponses = {
200: TrialWorkflowResponse
200: TrialWorkflow
}
export type GetTrialAppsByAppIdWorkflowsResponse

View File

@ -90,74 +90,169 @@ export const zSimpleResultResponse = z.object({
result: z.string(),
})
/**
* TrialDeletedToolResponse
*/
export const zTrialDeletedToolResponse = z.object({
provider_id: z.string(),
tool_name: z.string(),
type: z.string(),
export const zTrialDeletedTool = z.object({
provider_id: z.string().optional(),
tool_name: z.string().optional(),
type: z.string().optional(),
})
export const zTrialIconType = z.enum(['emoji', 'image', 'link'])
export const zTrialAppMode = z.enum([
'advanced-chat',
'agent-chat',
'chat',
'completion',
'workflow',
])
/**
* TrialSiteResponse
*/
export const zTrialSiteResponse = z.object({
access_token: z.string().nullish(),
app_base_url: z.string().nullish(),
chat_color_theme: z.string().nullish(),
chat_color_theme_inverted: z.boolean().nullish(),
code: z.string().nullish(),
copyright: z.string().nullish(),
created_at: z.int().nullish(),
created_by: z.string().nullish(),
custom_disclaimer: z.string().nullish(),
customize_domain: z.string().nullish(),
customize_token_strategy: z.string().nullish(),
default_language: z.string(),
description: z.string().nullish(),
icon: z.string().nullish(),
icon_background: z.string().nullish(),
icon_type: zTrialIconType.nullish(),
icon_url: z.string().nullish(),
input_placeholder: z.string().nullish(),
privacy_policy: z.string().nullish(),
prompt_public: z.boolean().nullish(),
show_workflow_steps: z.boolean().nullish(),
title: z.string(),
updated_at: z.int().nullish(),
updated_by: z.string().nullish(),
use_icon_as_answer_icon: z.boolean().nullish(),
export const zTrialAppModelConfig = z.object({
agent_mode: z.record(z.string(), z.unknown()).optional(),
annotation_reply: z.record(z.string(), z.unknown()).optional(),
chat_prompt_config: z.record(z.string(), z.unknown()).optional(),
completion_prompt_config: z.record(z.string(), z.unknown()).optional(),
created_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
created_by: z.string().optional(),
dataset_configs: z.record(z.string(), z.unknown()).optional(),
dataset_query_variable: z.string().optional(),
external_data_tools: z.array(z.record(z.string(), z.unknown())).optional(),
file_upload: z.record(z.string(), z.unknown()).optional(),
model: z.record(z.string(), z.unknown()).optional(),
more_like_this: z.record(z.string(), z.unknown()).optional(),
opening_statement: z.string().optional(),
pre_prompt: z.string().optional(),
prompt_type: z.string().optional(),
retriever_resource: z.record(z.string(), z.unknown()).optional(),
sensitive_word_avoidance: z.record(z.string(), z.unknown()).optional(),
speech_to_text: z.record(z.string(), z.unknown()).optional(),
suggested_questions: z.array(z.string()).optional(),
suggested_questions_after_answer: z.record(z.string(), z.unknown()).optional(),
text_to_speech: z.record(z.string(), z.unknown()).optional(),
updated_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
updated_by: z.string().optional(),
user_input_form: z.array(z.record(z.string(), z.unknown())).optional(),
})
/**
* TrialTagResponse
*/
export const zTrialTagResponse = z.object({
id: z.string(),
name: z.string(),
type: z.string(),
export const zTrialSite = z.object({
access_token: z.string().optional(),
app_base_url: z.string().optional(),
chat_color_theme: z.string().optional(),
chat_color_theme_inverted: z.boolean().optional(),
code: z.string().optional(),
copyright: z.string().optional(),
created_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
created_by: z.string().optional(),
custom_disclaimer: z.string().optional(),
customize_domain: z.string().optional(),
customize_token_strategy: z.string().optional(),
default_language: z.string().optional(),
description: z.string().optional(),
icon: z.string().optional(),
icon_background: z.string().optional(),
icon_type: z.string().optional(),
icon_url: z.string().optional(),
privacy_policy: z.string().optional(),
prompt_public: z.boolean().optional(),
show_workflow_steps: z.boolean().optional(),
title: z.string().optional(),
updated_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
updated_by: z.string().optional(),
use_icon_as_answer_icon: z.boolean().optional(),
})
/**
* TrialWorkflowPartialResponse
*/
export const zTrialWorkflowPartialResponse = z.object({
created_at: z.int().nullish(),
created_by: z.string().nullish(),
id: z.string(),
updated_at: z.int().nullish(),
updated_by: z.string().nullish(),
export const zTrialTag = z.object({
id: z.string().optional(),
name: z.string().optional(),
type: z.string().optional(),
})
export const zTrialWorkflowPartial = z.object({
created_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
created_by: z.string().optional(),
id: z.string().optional(),
updated_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
updated_by: z.string().optional(),
})
export const zTrialAppDetailWithSite = z.object({
access_mode: z.string().optional(),
api_base_url: z.string().optional(),
created_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
created_by: z.string().optional(),
deleted_tools: z.array(zTrialDeletedTool).optional(),
description: z.string().optional(),
enable_api: z.boolean().optional(),
enable_site: z.boolean().optional(),
icon: z.string().optional(),
icon_background: z.string().optional(),
icon_type: z.string().optional(),
icon_url: z.string().optional(),
id: z.string().optional(),
max_active_requests: z.int().optional(),
mode: z.string().optional(),
model_config: zTrialAppModelConfig.optional(),
name: z.string().optional(),
permission_keys: z.array(z.string()).optional(),
site: zTrialSite.optional(),
tags: z.array(zTrialTag).optional(),
updated_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
updated_by: z.string().optional(),
use_icon_as_answer_icon: z.boolean().optional(),
workflow: zTrialWorkflowPartial.optional(),
})
export const zJsonValue = z
@ -176,30 +271,32 @@ export const zJsonValue = z
*/
export const zGeneratedAppResponse = zJsonValue
/**
* TrialDatasetResponse
*/
export const zTrialDatasetResponse = z.object({
created_at: z.int().nullish(),
created_by: z.string().nullish(),
data_source_type: z.string().nullish(),
description: z.string().nullish(),
id: z.string(),
indexing_technique: z.string().nullish(),
name: z.string(),
permission: z.string().nullish(),
export const zTrialDataset = z.object({
created_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
created_by: z.string().optional(),
data_source_type: z.string().optional(),
description: z.string().optional(),
id: z.string().optional(),
indexing_technique: z.string().optional(),
name: z.string().optional(),
permission: z.string().optional(),
permission_keys: z.array(z.string()).optional(),
})
/**
* TrialDatasetListResponse
*/
export const zTrialDatasetListResponse = z.object({
data: z.array(zTrialDatasetResponse),
has_more: z.boolean(),
limit: z.int(),
page: z.int(),
total: z.int(),
export const zTrialDatasetList = z.object({
data: z.array(zTrialDataset).optional(),
has_more: z.boolean().optional(),
limit: z.int().optional(),
page: z.int().optional(),
total: z.int().optional(),
})
export const zJsonObject = z.record(z.string(), z.unknown())
@ -233,133 +330,87 @@ export const zParameters = z.object({
user_input_form: z.array(zJsonObject),
})
export const zJsonObject2 = z.record(z.string(), z.unknown())
/**
* TrialWorkflowAccount
*/
export const zTrialWorkflowAccount = z.object({
email: z.string().nullish(),
id: z.string(),
name: z.string().nullish(),
export const zTrialConversationVariable = z.object({
description: z.string().optional(),
id: z.string().optional(),
name: z.string().optional(),
value: z
.union([
z.string(),
z.int(),
z.number(),
z.boolean(),
z.record(z.string(), z.unknown()),
z.array(z.unknown()),
])
.nullish(),
value_type: z.string().optional(),
})
/**
* TrialAppAgentMode
*/
export const zTrialAppAgentMode = z.object({
enabled: z.boolean().nullish(),
strategy: z.string().nullish(),
tools: z.array(zJsonObject2).optional(),
export const zTrialSimpleAccount = z.object({
email: z.string().optional(),
id: z.string().optional(),
name: z.string().optional(),
})
/**
* TrialAppModel
*/
export const zTrialAppModel = z.object({
completion_params: zJsonObject2.optional(),
mode: z.string().nullish(),
name: z.string(),
provider: z.string(),
export const zTrialPipelineVariable = z.object({
allow_file_extension: z.array(z.string()).optional(),
allow_file_upload_methods: z.array(z.string()).optional(),
allowed_file_types: z.array(z.string()).optional(),
belong_to_node_id: z.string().optional(),
default_value: z
.union([
z.string(),
z.int(),
z.number(),
z.boolean(),
z.record(z.string(), z.unknown()),
z.array(z.unknown()),
])
.nullish(),
label: z.string().optional(),
max_length: z.int().optional(),
options: z.array(z.string()).optional(),
placeholder: z.string().optional(),
required: z.boolean().optional(),
tooltips: z.string().optional(),
type: z.string().optional(),
unit: z.string().optional(),
variable: z.string().optional(),
})
/**
* TrialAppModelConfigResponse
*/
export const zTrialAppModelConfigResponse = z.object({
agent_mode: zTrialAppAgentMode.nullish(),
annotation_reply: zJsonObject2.nullish(),
chat_prompt_config: zJsonObject2.nullish(),
completion_prompt_config: zJsonObject2.nullish(),
created_at: z.int().nullish(),
created_by: z.string().nullish(),
dataset_configs: zJsonObject2.nullish(),
dataset_query_variable: z.string().nullish(),
external_data_tools: z.array(zJsonObject2).optional(),
file_upload: zJsonObject2.nullish(),
model: zTrialAppModel.nullish(),
more_like_this: zJsonObject2.nullish(),
opening_statement: z.string().nullish(),
pre_prompt: z.string().nullish(),
prompt_type: z.string().nullish(),
retriever_resource: zJsonObject2.nullish(),
sensitive_word_avoidance: zJsonObject2.nullish(),
speech_to_text: zJsonObject2.nullish(),
suggested_questions: z.array(z.string()).optional(),
suggested_questions_after_answer: zJsonObject2.nullish(),
text_to_speech: zJsonObject2.nullish(),
updated_at: z.int().nullish(),
updated_by: z.string().nullish(),
user_input_form: z.array(zJsonObject2).optional(),
})
/**
* TrialAppDetailResponse
*/
export const zTrialAppDetailResponse = z.object({
access_mode: z.string().nullish(),
api_base_url: z.string().nullish(),
created_at: z.int().nullish(),
created_by: z.string().nullish(),
deleted_tools: z.array(zTrialDeletedToolResponse).optional(),
description: z.string().nullish(),
enable_api: z.boolean(),
enable_site: z.boolean(),
icon: z.string().nullish(),
icon_background: z.string().nullish(),
icon_type: zTrialIconType.nullish(),
icon_url: z.string().nullish(),
id: z.string(),
max_active_requests: z.int().nullish(),
mode: zTrialAppMode,
model_config: zTrialAppModelConfigResponse.nullish(),
name: z.string(),
permission_keys: z.array(z.string()).optional(),
site: zTrialSiteResponse,
tags: z.array(zTrialTagResponse).optional(),
updated_at: z.int().nullish(),
updated_by: z.string().nullish(),
use_icon_as_answer_icon: z.boolean().nullish(),
workflow: zTrialWorkflowPartialResponse.nullish(),
})
/**
* TrialWorkflowViewport
*/
export const zTrialWorkflowViewport = z.object({
x: z.number(),
y: z.number(),
zoom: z.number(),
})
/**
* TrialWorkflowGraph
*/
export const zTrialWorkflowGraph = z.object({
edges: z.array(zJsonObject2),
nodes: z.array(zJsonObject2),
viewport: zTrialWorkflowViewport,
})
/**
* TrialWorkflowResponse
*/
export const zTrialWorkflowResponse = z.object({
conversation_variables: z.array(zJsonObject2).optional(),
created_at: z.int().nullish(),
created_by: zTrialWorkflowAccount.nullish(),
environment_variables: z.array(zJsonObject2).optional(),
features: zJsonObject2.optional(),
graph: zTrialWorkflowGraph,
hash: z.string().nullish(),
id: z.string(),
marked_comment: z.string().nullish(),
marked_name: z.string().nullish(),
rag_pipeline_variables: z.array(zJsonObject2).optional(),
tool_published: z.boolean().nullish(),
updated_at: z.int().nullish(),
updated_by: zTrialWorkflowAccount.nullish(),
version: z.string().nullish(),
export const zTrialWorkflow = z.object({
conversation_variables: z.array(zTrialConversationVariable).optional(),
created_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
created_by: zTrialSimpleAccount.optional(),
environment_variables: z.array(z.record(z.string(), z.unknown())).optional(),
features: z.record(z.string(), z.unknown()).optional(),
graph: z.record(z.string(), z.unknown()).optional(),
hash: z.string().optional(),
id: z.string().optional(),
marked_comment: z.string().optional(),
marked_name: z.string().optional(),
rag_pipeline_variables: z.array(zTrialPipelineVariable).optional(),
tool_published: z.boolean().optional(),
updated_at: z.coerce
.bigint()
.min(BigInt('-9223372036854775808'), {
error: 'Invalid value: Expected int64 to be >= -9223372036854775808',
})
.max(BigInt('9223372036854775807'), {
error: 'Invalid value: Expected int64 to be <= 9223372036854775807',
})
.optional(),
updated_by: zTrialSimpleAccount.optional(),
version: z.string().optional(),
})
/**
@ -394,7 +445,7 @@ export const zGetTrialAppsByAppIdPath = z.object({
/**
* Success
*/
export const zGetTrialAppsByAppIdResponse = zTrialAppDetailResponse
export const zGetTrialAppsByAppIdResponse = zTrialAppDetailWithSite
export const zPostTrialAppsByAppIdAudioToTextPath = z.object({
app_id: z.uuid(),
@ -440,7 +491,7 @@ export const zGetTrialAppsByAppIdDatasetsQuery = z.object({
/**
* Success
*/
export const zGetTrialAppsByAppIdDatasetsResponse = zTrialDatasetListResponse
export const zGetTrialAppsByAppIdDatasetsResponse = zTrialDatasetList
export const zGetTrialAppsByAppIdMessagesByMessageIdSuggestedQuestionsPath = z.object({
app_id: z.uuid(),
@ -489,7 +540,7 @@ export const zGetTrialAppsByAppIdWorkflowsPath = z.object({
/**
* Success
*/
export const zGetTrialAppsByAppIdWorkflowsResponse = zTrialWorkflowResponse
export const zGetTrialAppsByAppIdWorkflowsResponse = zTrialWorkflow
export const zPostTrialAppsByAppIdWorkflowsRunBody = zWorkflowRunRequest

View File

@ -215,7 +215,7 @@ describe('App Publisher Flow', () => {
fireEvent.click(screen.getByText('common.openInExplore'))
await waitFor(() => {
expect(mockToastError).toHaveBeenCalledWith('No app found in Explore')
expect(mockToastError).toHaveBeenCalledWith('notPublishedYet')
})
})
})

View File

@ -562,7 +562,7 @@ describe('AppPublisher', () => {
fireEvent.click(screen.getByText('publisher-open-in-explore'))
await waitFor(() => {
expect(mockToastError).toHaveBeenCalledWith('No app found in Explore')
expect(mockToastError).toHaveBeenCalledWith('notPublishedYet')
})
})

View File

@ -231,7 +231,7 @@ export function AppPublisher({
const { installed_apps } = await fetchInstalledAppList(appDetail.id)
if (installed_apps?.length > 0)
return `${basePath}${buildInstalledAppPath(installed_apps[0]!.id)}`
throw new Error('No app found in Explore')
throw new Error(t('notPublishedYet', { ns: 'app' }))
}, {
onError: (err) => {
toast.error(`${err.message || err}`)

View File

@ -14,6 +14,10 @@ import { StarredAppCard } from '../starred-app-card'
let mockWebappAuthEnabled = false
let mockRbacEnabled = true
const mockUserCanAccessApp = vi.hoisted(() => ({
result: true as boolean | undefined,
isLoading: false,
}))
const render = (ui: React.ReactElement) => renderWithSystemFeatures(ui, {
systemFeatures: {
@ -118,15 +122,15 @@ vi.mock('@/service/explore', () => ({
vi.mock('@/service/access-control', () => ({
useGetUserCanAccessApp: () => ({
data: { result: true },
isLoading: false,
data: mockUserCanAccessApp.result === undefined ? undefined : { result: mockUserCanAccessApp.result },
isLoading: mockUserCanAccessApp.isLoading,
}),
}))
vi.mock('@/service/access-control/use-app-access-control', () => ({
useGetUserCanAccessApp: () => ({
data: { result: true },
isLoading: false,
data: mockUserCanAccessApp.result === undefined ? undefined : { result: mockUserCanAccessApp.result },
isLoading: mockUserCanAccessApp.isLoading,
}),
}))
@ -380,6 +384,8 @@ describe('AppCard', () => {
mockOpenAsyncWindow.mockReset()
mockWebappAuthEnabled = false
mockRbacEnabled = true
mockUserCanAccessApp.result = true
mockUserCanAccessApp.isLoading = false
mockDeleteMutationPending = false
mockToggleStarMutationPending = false
mockAppContext.isCurrentWorkspaceEditor = true
@ -1733,6 +1739,28 @@ describe('AppCard', () => {
})
describe('Open in Explore - No App Found', () => {
it('should tell workflow users to publish before opening in explore', async () => {
const workflowApp = createMockApp({
mode: AppModeEnum.WORKFLOW,
workflow: undefined,
})
render(<AppCard app={workflowApp} />)
fireEvent.click(screen.getByTestId('dropdown-menu-trigger'))
await waitFor(() => {
expect(screen.getByText('app.openInExplore')).toBeInTheDocument()
})
fireEvent.click(screen.getByText('app.openInExplore'))
expect(mockOpenAsyncWindow).not.toHaveBeenCalled()
expect(exploreService.fetchInstalledAppList).not.toHaveBeenCalled()
expect(toastMocks.record).toHaveBeenCalledWith({
type: 'error',
message: 'app.notPublishedYet',
})
})
it('should handle case when installed_apps is empty array', async () => {
(exploreService.fetchInstalledAppList as Mock).mockResolvedValueOnce({ installed_apps: [] })
@ -1756,6 +1784,10 @@ describe('AppCard', () => {
await waitFor(() => {
expect(exploreService.fetchInstalledAppList).toHaveBeenCalled()
expect(toastMocks.record).toHaveBeenCalledWith({
type: 'error',
message: 'app.notPublishedYet',
})
})
})
@ -1944,6 +1976,31 @@ describe('AppCard', () => {
})
})
it('should keep open in explore visible for unpublished workflow apps while access check is pending', async () => {
mockUserCanAccessApp.result = false
mockUserCanAccessApp.isLoading = true
const workflowApp = createMockApp({
mode: AppModeEnum.WORKFLOW,
workflow: undefined,
})
render(<AppCard app={workflowApp} />)
fireEvent.click(screen.getByTestId('dropdown-menu-trigger'))
await waitFor(() => {
expect(screen.getByText('app.openInExplore')).toBeInTheDocument()
})
fireEvent.click(screen.getByText('app.openInExplore'))
expect(mockOpenAsyncWindow).not.toHaveBeenCalled()
expect(exploreService.fetchInstalledAppList).not.toHaveBeenCalled()
expect(toastMocks.record).toHaveBeenCalledWith({
type: 'error',
message: 'app.notPublishedYet',
})
})
it('should close access control modal when onClose is called', async () => {
render(<AppCard app={mockApp} />)

View File

@ -90,6 +90,11 @@ const ACCESS_MODE_LABEL_KEYS = {
[AccessMode.EXTERNAL_MEMBERS]: 'accessItemsDescription.external',
} as const
const APP_MODES_REQUIRING_PUBLISHED_WORKFLOW_IN_EXPLORE = new Set<AppModeEnum>([
AppModeEnum.ADVANCED_CHAT,
AppModeEnum.WORKFLOW,
])
type AppCardProps = {
app: App
onlineUsers?: WorkflowOnlineUser[]
@ -103,6 +108,10 @@ type AppAccessModeIconProps = {
const getAppResourceMaintainer = (app: App) => app.maintainer
function requiresPublishedWorkflowInExplore(app: App) {
return APP_MODES_REQUIRING_PUBLISHED_WORKFLOW_IN_EXPLORE.has(app.mode)
}
function AppAccessModeIcon({ accessMode }: AppAccessModeIconProps) {
const { t } = useTranslation()
@ -182,12 +191,17 @@ function AppCardOperationsMenu({
async function handleOpenInstalledApp(e: MouseEvent<HTMLElement>) {
e.stopPropagation()
e.preventDefault()
if (requiresPublishedWorkflowInExplore(app) && !app.workflow?.id) {
toast.error(t('notPublishedYet', { ns: 'app' }))
return
}
try {
await openAsyncWindow(async () => {
const { installed_apps } = await fetchInstalledAppList(app.id)
if (installed_apps?.length > 0)
return `${basePath}${buildInstalledAppPath(installed_apps[0]!.id)}`
throw new Error('No app found in Explore')
throw new Error(t('notPublishedYet', { ns: 'app' }))
}, {
onError: (err) => {
toast.error(`${err.message || err}`)
@ -272,10 +286,12 @@ function AppCardOperationsMenuContent(props: AppCardOperationsMenuContentProps)
appId: props.app.id,
enabled: systemFeatures.webapp_auth.enabled,
})
const needsPublishBeforeExplore = requiresPublishedWorkflowInExplore(props.app) && !props.app.workflow?.id
const shouldShowOpenInExploreOption = !props.app.has_draft_trigger
&& (
!systemFeatures.webapp_auth.enabled
needsPublishBeforeExplore
|| !systemFeatures.webapp_auth.enabled
|| (!isGettingUserCanAccessApp && Boolean(userCanAccessApp?.result))
)

View File

@ -68,9 +68,9 @@ const AppInfo: FC<Props> = ({
<AppIcon
size="large"
iconType={appDetail.site.icon_type}
icon={appDetail.site.icon ?? undefined}
background={appDetail.site.icon_background ?? undefined}
imageUrl={appDetail.site.icon_url ?? undefined}
icon={appDetail.site.icon}
background={appDetail.site.icon_background}
imageUrl={appDetail.site.icon_url}
/>
<AppTypeIcon
wrapperClassName="absolute -bottom-0.5 -right-0.5 w-4 h-4 shadow-sm"

View File

@ -1,3 +1,5 @@
import type { LLMNodeType } from '@/app/components/workflow/nodes/llm/types'
import type { ToolNodeType } from '@/app/components/workflow/nodes/tool/types'
import type { TryAppInfo } from '@/service/try-app'
import type { AgentTool } from '@/types/app'
import { uniqBy } from 'es-toolkit/compat'
@ -66,33 +68,6 @@ const getIconUrl = (providerId: string, type: ProviderType) => {
return `${MARKETPLACE_API_PREFIX}/plugins/${organization}/${pluginName}/icon`
}
const isRecord = (value: unknown): value is Record<string, unknown> => {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
const isAgentTool = (value: unknown): value is AgentTool => {
if (!isRecord(value))
return false
return typeof value.provider_id === 'string'
&& typeof value.tool_label === 'string'
&& value.enabled === true
}
const hasLLMRequirementData = (value: unknown): value is { model: { name: string, provider: string } } => {
if (!isRecord(value) || !isRecord(value.model))
return false
return typeof value.model.name === 'string' && typeof value.model.provider === 'string'
}
const hasToolRequirementData = (value: unknown): value is { provider_id: string, tool_label: string } => {
if (!isRecord(value))
return false
return typeof value.provider_id === 'string' && typeof value.tool_label === 'string'
}
const useGetRequirements = ({ appDetail, appId }: Params) => {
const isBasic = ['chat', 'completion', 'agent-chat'].includes(appDetail.mode)
const isAgent = appDetail.mode === 'agent-chat'
@ -100,44 +75,41 @@ const useGetRequirements = ({ appDetail, appId }: Params) => {
const { data: flowData } = useGetTryAppFlowPreview(appId, isBasic)
const requirements: RequirementItem[] = []
const modelConfig = appDetail.model_config
const model = modelConfig?.model
if (isBasic && model) {
const modelProvider = model.provider
const name = model.provider.split('/').pop() || ''
if (isBasic) {
const modelProvider = appDetail.model_config.model.provider
const name = appDetail.model_config.model.provider.split('/').pop() || ''
requirements.push({
name,
iconUrl: getIconUrl(modelProvider, 'model'),
})
}
if (isAgent && modelConfig?.agent_mode?.tools) {
requirements.push(...modelConfig.agent_mode.tools.filter(isAgentTool).map(tool => ({
name: tool.tool_label,
iconUrl: getIconUrl(tool.provider_id, 'tool'),
})))
if (isAgent) {
requirements.push(...appDetail.model_config.agent_mode.tools.filter(data => (data as AgentTool).enabled).map((data) => {
const tool = data as AgentTool
return {
name: tool.tool_label,
iconUrl: getIconUrl(tool.provider_id, 'tool'),
}
}))
}
if (isAdvanced && flowData && flowData?.graph?.nodes?.length > 0) {
const nodes = flowData.graph.nodes
requirements.push(...nodes.flatMap((node) => {
const data = isRecord(node.data) ? node.data : null
if (data?.type !== BlockEnum.LLM || !hasLLMRequirementData(data))
return []
return [{
const llmNodes = nodes.filter(node => node.data.type === BlockEnum.LLM)
requirements.push(...llmNodes.map((node) => {
const data = node.data as LLMNodeType
return {
name: data.model.name,
iconUrl: getIconUrl(data.model.provider, 'model'),
}]
}
}))
requirements.push(...nodes.flatMap((node) => {
const data = isRecord(node.data) ? node.data : null
if (data?.type !== BlockEnum.Tool || !hasToolRequirementData(data))
return []
return [{
const toolNodes = nodes.filter(node => node.data.type === BlockEnum.Tool)
requirements.push(...toolNodes.map((node) => {
const data = node.data as ToolNodeType
return {
name: data.tool_label,
iconUrl: getIconUrl(data.provider_id, 'tool'),
}]
}
}))
}

View File

@ -70,9 +70,9 @@ const TryApp: FC<Props> = ({
<AppIcon
size="large"
iconType={appDetail.site.icon_type}
icon={appDetail.site.icon ?? undefined}
background={appDetail.site.icon_background ?? undefined}
imageUrl={appDetail.site.icon_url ?? undefined}
icon={appDetail.site.icon}
background={appDetail.site.icon_background}
imageUrl={appDetail.site.icon_url}
/>
<div className="grow truncate system-md-semibold text-text-primary" title={appDetail.name}>{appDetail.name}</div>
</div>

View File

@ -4,8 +4,7 @@ import type { FC } from 'react'
import type { Features as FeaturesData, FileUpload } from '@/app/components/base/features/types'
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { ModelConfig } from '@/models/debug'
import type { TryAppInfo } from '@/service/try-app'
import type { PromptVariable } from '@/types/app'
import type { ModelConfig as BackendModelConfig, PromptVariable } from '@/types/app'
import { noop } from 'es-toolkit/function'
import { clone } from 'es-toolkit/object'
import * as React from 'react'
@ -54,62 +53,6 @@ const defaultModelConfig = {
dataSets: [],
agentConfig: DEFAULT_AGENT_SETTING,
}
const isRecord = (value: unknown): value is Record<string, unknown> => {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
const getString = (value: unknown) => {
return typeof value === 'string' ? value : ''
}
const getBoolean = (value: unknown) => {
return typeof value === 'boolean' ? value : false
}
const getAgentTools = (agentMode: unknown) => {
if (!isRecord(agentMode) || !Array.isArray(agentMode.tools))
return []
return agentMode.tools.filter(isRecord)
}
const isEnabledDatasetTool = (tool: Record<string, unknown>) => {
return isRecord(tool.dataset) && tool.dataset.enabled === true
}
const getDatasetConfigItems = (datasetConfigs: unknown) => {
if (!isRecord(datasetConfigs) || !isRecord(datasetConfigs.datasets) || !Array.isArray(datasetConfigs.datasets.datasets))
return []
return datasetConfigs.datasets.datasets.filter(isRecord)
}
const getDatasetId = (value: Record<string, unknown>) => {
if (typeof value.id === 'string')
return value.id
if (isRecord(value.dataset) && typeof value.dataset.id === 'string')
return value.dataset.id
return null
}
const normalizeExternalDataToolFormItem = (item: Record<string, unknown>) => {
return {
external_data_tool: {
variable: getString(item.variable),
label: getString(item.label),
enabled: getBoolean(item.enabled),
type: getString(item.type),
config: isRecord(item.config) ? item.config : undefined,
required: true,
icon: getString(item.icon),
icon_background: getString(item.icon_background),
},
}
}
const BasicAppPreview: FC<Props> = ({
appId,
}) => {
@ -130,13 +73,16 @@ const BasicAppPreview: FC<Props> = ({
const modelConfig = appDetail?.model_config
if (!modelConfig)
return []
let datasets: any = null
const agentDatasetTools = getAgentTools(modelConfig.agent_mode).filter(isEnabledDatasetTool)
const datasetConfigItems = getDatasetConfigItems(modelConfig.dataset_configs)
const datasets = agentDatasetTools.length > 0 ? agentDatasetTools : datasetConfigItems
if (modelConfig.agent_mode?.tools?.find(({ dataset }: any) => dataset?.enabled))
datasets = modelConfig.agent_mode?.tools.filter(({ dataset }: any) => dataset?.enabled)
// new dataset struct
else if (modelConfig.dataset_configs.datasets?.datasets?.length > 0)
datasets = modelConfig.dataset_configs?.datasets?.datasets
if (datasets?.length && datasets?.length > 0)
return datasets.map(getDatasetId).filter((id): id is string => !!id)
return datasets.map(({ dataset }: any) => dataset.id)
return []
})()
@ -144,29 +90,41 @@ const BasicAppPreview: FC<Props> = ({
const dataSets = dataSetData?.data || []
const isLoading = isLoadingAppDetail || isLoadingDatasets || isLoadingToolProviders
const modelConfig: ModelConfig = ((modelConfig?: TryAppInfo['model_config']) => {
if (isLoading || !modelConfig?.model)
const modelConfig: ModelConfig = ((modelConfig?: BackendModelConfig) => {
if (isLoading || !modelConfig)
return defaultModelConfig
const model = modelConfig.model
const mode = model.mode === ModelModeType.chat || model.mode === ModelModeType.completion ? model.mode : ModelModeType.unset
const newModelConfig = {
provider: correctModelProvider(model.provider),
model_id: model.name,
mode,
mode: model.mode,
configs: {
prompt_template: modelConfig.pre_prompt || '',
prompt_variables: userInputsFormToPromptVariables(
[
...(modelConfig.user_input_form || []),
...(modelConfig.user_input_form as any),
...(
modelConfig.external_data_tools?.length
? modelConfig.external_data_tools.map(normalizeExternalDataToolFormItem)
? modelConfig.external_data_tools.map((item) => {
return {
external_data_tool: {
variable: item.variable as string,
label: item.label as string,
enabled: item.enabled,
type: item.type as string,
config: item.config,
required: true,
icon: item.icon,
icon_background: item.icon_background,
},
}
})
: []
),
],
modelConfig.dataset_query_variable ?? undefined,
modelConfig.dataset_query_variable,
),
},
more_like_this: modelConfig.more_like_this,
@ -185,25 +143,21 @@ const BasicAppPreview: FC<Props> = ({
// eslint-disable-next-line style/multiline-ternary
? ({
max_iteration: DEFAULT_AGENT_SETTING.max_iteration,
...(isRecord(modelConfig.agent_mode) ? modelConfig.agent_mode : {}),
...modelConfig.agent_mode,
// remove dataset
enabled: true, // modelConfig.agent_mode?.enabled is not correct. old app: the value of app with dataset's is always true
tools: getAgentTools(modelConfig.agent_mode).filter((tool) => {
tools: modelConfig.agent_mode?.tools.filter((tool: any) => {
return !tool.dataset
}).map((tool) => {
const providerId = getString(tool.provider_id)
const providerName = getString(tool.provider_name)
const providerType = getString(tool.provider_type)
const toolName = getString(tool.tool_name)
const toolInCollectionList = collectionList?.find(c => providerId === c.id)
}).map((tool: any) => {
const toolInCollectionList = collectionList?.find(c => tool.provider_id === c.id)
return {
...tool,
isDeleted: appDetail?.deleted_tools?.some(deletedTool => deletedTool.provider_id === providerId && deletedTool.tool_name === toolName),
isDeleted: appDetail?.deleted_tools?.some((deletedTool: any) => deletedTool.id === tool.id && deletedTool.tool_name === tool.tool_name),
notAuthor: toolInCollectionList?.is_team_authorization === false,
...(providerType === 'builtin'
...(tool.provider_type === 'builtin'
? {
provider_id: correctToolProvider(providerName, !!toolInCollectionList),
provider_name: correctToolProvider(providerName, !!toolInCollectionList),
provider_id: correctToolProvider(tool.provider_name, !!toolInCollectionList),
provider_name: correctToolProvider(tool.provider_name, !!toolInCollectionList),
}
: {}),
}

View File

@ -1,11 +1,8 @@
'use client'
import type { TrialWorkflowGraph } from '@dify/contracts/api/console/trial-apps/types.gen'
import type { FC } from 'react'
import type { Edge, Node } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
import * as React from 'react'
import Loading from '@/app/components/base/loading'
import { BlockEnum } from '@/app/components/workflow/types'
import WorkflowPreview from '@/app/components/workflow/workflow-preview'
import { useGetTryAppFlowPreview } from '@/service/use-try-app'
@ -14,119 +11,6 @@ type Props = {
readonly className?: string
}
const blockTypeMap: Record<string, BlockEnum> = {
'agent': BlockEnum.Agent,
'agent-v2': BlockEnum.AgentV2,
'answer': BlockEnum.Answer,
'assigner': BlockEnum.Assigner,
'code': BlockEnum.Code,
'datasource': BlockEnum.DataSource,
'datasource-empty': BlockEnum.DataSourceEmpty,
'document-extractor': BlockEnum.DocExtractor,
'end': BlockEnum.End,
'http-request': BlockEnum.HttpRequest,
'human-input': BlockEnum.HumanInput,
'if-else': BlockEnum.IfElse,
'iteration': BlockEnum.Iteration,
'iteration-start': BlockEnum.IterationStart,
'knowledge-index': BlockEnum.KnowledgeBase,
'knowledge-retrieval': BlockEnum.KnowledgeRetrieval,
'list-operator': BlockEnum.ListFilter,
'llm': BlockEnum.LLM,
'loop': BlockEnum.Loop,
'loop-end': BlockEnum.LoopEnd,
'loop-start': BlockEnum.LoopStart,
'parameter-extractor': BlockEnum.ParameterExtractor,
'question-classifier': BlockEnum.QuestionClassifier,
'start': BlockEnum.Start,
'start-placeholder': BlockEnum.StartPlaceholder,
'template-transform': BlockEnum.TemplateTransform,
'tool': BlockEnum.Tool,
'trigger-plugin': BlockEnum.TriggerPlugin,
'trigger-schedule': BlockEnum.TriggerSchedule,
'trigger-webhook': BlockEnum.TriggerWebhook,
'variable-aggregator': BlockEnum.VariableAggregator,
'variable-assigner': BlockEnum.VariableAssigner,
}
const isRecord = (value: unknown): value is Record<string, unknown> => {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
const getString = (value: unknown) => {
return typeof value === 'string' ? value : undefined
}
const getPosition = (value: unknown) => {
if (!isRecord(value) || typeof value.x !== 'number' || typeof value.y !== 'number')
return { x: 0, y: 0 }
return {
x: value.x,
y: value.y,
}
}
const getBlockType = (value: unknown) => {
if (typeof value !== 'string')
return null
return blockTypeMap[value] || null
}
const normalizeWorkflowPreviewGraph = (graph: TrialWorkflowGraph) => {
const nodes: Node[] = graph.nodes.flatMap((node) => {
const id = getString(node.id)
if (!id)
return []
const data = isRecord(node.data) ? node.data : {}
const type = getBlockType(data.type) || BlockEnum.Start
const title = getString(data.title) || ''
return [{
id,
position: getPosition(node.position),
...(getString(node.type) ? { type: getString(node.type) } : {}),
data: {
...data,
desc: getString(data.desc) || '',
title,
type,
},
}]
})
const edges: Edge[] = graph.edges.flatMap((edge) => {
const id = getString(edge.id)
if (!id)
return []
const data = isRecord(edge.data) ? edge.data : {}
const sourceType = getBlockType(data.sourceType) || BlockEnum.Start
const targetType = getBlockType(data.targetType) || BlockEnum.Start
return [{
id,
source: getString(edge.source) || '',
target: getString(edge.target) || '',
...(getString(edge.type) ? { type: getString(edge.type) } : {}),
...(getString(edge.sourceHandle) ? { sourceHandle: getString(edge.sourceHandle) } : {}),
...(getString(edge.targetHandle) ? { targetHandle: getString(edge.targetHandle) } : {}),
data: {
...data,
sourceType,
targetType,
},
}]
})
return {
nodes,
edges,
viewport: graph.viewport,
}
}
const FlowAppPreview: FC<Props> = ({
appId,
className,
@ -142,11 +26,10 @@ const FlowAppPreview: FC<Props> = ({
}
if (!data)
return null
const previewGraph = normalizeWorkflowPreviewGraph(data.graph)
return (
<div className="size-full">
<WorkflowPreview
{...previewGraph}
{...data.graph}
className={cn(className)}
miniMapToRight
/>

View File

@ -0,0 +1,65 @@
import type { ChatConfig } from '@/app/components/base/chat/types'
import type { DataSetListResponse } from '@/models/datasets'
import type { TryAppFlowPreview, TryAppInfo } from '@/models/try-app'
import { trialApps } from '@dify/contracts/api/console/trial-apps/orpc.gen'
import { type } from '@orpc/contract'
import { base } from '../base'
export const trialAppInfoContract = base
.route({
path: '/trial-apps/{appId}',
method: 'GET',
})
.input(type<{
params: {
appId: string
}
}>())
.output(type<TryAppInfo>())
export const trialAppDatasetsContract = base
.route({
path: '/trial-apps/{appId}/datasets',
method: 'GET',
})
.input(type<{
params: {
appId: string
}
query: {
ids: string[]
}
}>())
.output(type<DataSetListResponse>())
export const trialAppWorkflowsContract = base
.route({
path: '/trial-apps/{appId}/workflows',
method: 'GET',
})
.input(type<{
params: {
appId: string
}
}>())
.output(type<TryAppFlowPreview>())
export const trialAppParametersContract = base
.route({
path: '/trial-apps/{appId}/parameters',
method: 'GET',
})
.input(type<{
params: {
appId: string
}
}>())
.output(type<ChatConfig>())
export const trialAppsRouterContract = {
...trialApps,
info: trialAppInfoContract,
datasets: trialAppDatasetsContract,
parameters: trialAppParametersContract,
workflows: trialAppWorkflowsContract,
}

View File

@ -39,7 +39,6 @@ import { systemFeatures } from '@dify/contracts/api/console/system-features/orpc
import { tagBindings } from '@dify/contracts/api/console/tag-bindings/orpc.gen'
import { tags } from '@dify/contracts/api/console/tags/orpc.gen'
import { test } from '@dify/contracts/api/console/test/orpc.gen'
import { trialApps } from '@dify/contracts/api/console/trial-apps/orpc.gen'
import { trialModels } from '@dify/contracts/api/console/trial-models/orpc.gen'
import { website } from '@dify/contracts/api/console/website/orpc.gen'
import { workflowGenerate } from '@dify/contracts/api/console/workflow-generate/orpc.gen'
@ -52,6 +51,7 @@ import { modelProvidersRouterContract } from './console/model-providers'
import { pluginsRouterContract } from './console/plugins'
import { snippetsRouterContract } from './console/snippets'
import { triggersRouterContract } from './console/trigger'
import { trialAppsRouterContract } from './console/try-app'
const communityContract = {
account,
@ -95,7 +95,6 @@ const communityContract = {
tagBindings,
tags,
test,
trialApps,
trialModels,
website,
workflow,
@ -112,4 +111,5 @@ export const consoleRouterContract = {
rbacAccessConfig: rbacAccessConfigContract,
snippets: snippetsRouterContract,
triggers: triggersRouterContract,
trialApps: trialAppsRouterContract,
}

21
web/models/try-app.ts Normal file
View File

@ -0,0 +1,21 @@
import type { Viewport } from 'reactflow'
import type { Edge, Node } from '@/app/components/workflow/types'
import type { SiteInfo } from '@/models/share'
import type { AppModeEnum, ModelConfig } from '@/types/app'
export type TryAppInfo = {
name: string
description: string
mode: AppModeEnum
site: SiteInfo
model_config: ModelConfig
deleted_tools: { id: string, tool_name: string }[]
}
export type TryAppFlowPreview = {
graph: {
nodes: Node[]
edges: Edge[]
viewport: Viewport
}
}

View File

@ -21,6 +21,7 @@ const customConsoleContractLoaders: Record<string, () => Promise<AnyContractRout
import('@/contract/console/access-control').then(({ rbacAccessConfigContract }) => wrapConsoleContract('rbacAccessConfig', rbacAccessConfigContract)),
snippets: () => import('@/contract/console/snippets').then(({ snippetsRouterContract }) => wrapConsoleContract('snippets', snippetsRouterContract)),
triggers: () => import('@/contract/console/trigger').then(({ triggersRouterContract }) => wrapConsoleContract('triggers', triggersRouterContract)),
trialApps: () => import('@/contract/console/try-app').then(({ trialAppsRouterContract }) => wrapConsoleContract('trialApps', trialAppsRouterContract)),
}
export async function loadConsoleContractForSegment(segment: string) {

View File

@ -1,33 +1,26 @@
import { consoleClient } from '@/service/client'
import { get } from './base'
import { fetchTryAppDatasets } from './try-app'
vi.mock('./base', () => ({
get: vi.fn(),
}))
vi.mock('@/service/client', () => ({
consoleClient: {
trialApps: {
byAppId: {
datasets: {
get: vi.fn(),
},
},
info: vi.fn(),
workflows: vi.fn(),
parameters: vi.fn(),
},
},
}))
describe('fetchTryAppDatasets', () => {
it('serializes ids as repeated query params', async () => {
vi.mocked(consoleClient.trialApps.byAppId.datasets.get).mockResolvedValue({
data: [],
has_more: false,
limit: 20,
page: 1,
total: 0,
})
vi.mocked(get).mockResolvedValue({ data: [] })
await fetchTryAppDatasets('app-1', ['id-1', 'id-2'])
expect(consoleClient.trialApps.byAppId.datasets.get).toHaveBeenCalledWith({
params: { app_id: 'app-1' },
query: { ids: ['id-1', 'id-2'] },
})
expect(get).toHaveBeenCalledWith('/trial-apps/app-1/datasets?ids=id-1&ids=id-2')
})
})

View File

@ -1,292 +1,28 @@
import type { ChatConfig } from '@/app/components/base/chat/types'
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
import { ANNOTATION_DEFAULT, DEFAULT_AGENT_SETTING } from '@/config'
import { PromptMode } from '@/models/debug'
import type { DataSetListResponse } from '@/models/datasets'
import type { TryAppFlowPreview, TryAppInfo } from '@/models/try-app'
import qs from 'qs'
import { consoleClient } from '@/service/client'
import { Resolution, RETRIEVE_TYPE, TransferMethod, TtsAutoPlay } from '@/types/app'
import { get } from './base'
type TryAppParameters = import('@dify/contracts/api/console/trial-apps/types.gen').Parameters
const transferMethodValues = new Set<string>(Object.values(TransferMethod))
const supportUploadFileTypeValues = new Set<string>(Object.values(SupportUploadFileTypes))
const isRecord = (value: unknown): value is Record<string, unknown> => {
return typeof value === 'object' && value !== null && !Array.isArray(value)
export const fetchTryAppInfo = (appId: string): Promise<TryAppInfo> => {
return consoleClient.trialApps.info({ params: { appId } })
}
const getString = (value: unknown, fallback = '') => {
return typeof value === 'string' ? value : fallback
export const fetchTryAppDatasets = (appId: string, ids: string[]): Promise<DataSetListResponse> => {
const queryString = qs.stringify({ ids }, { indices: false })
const url = `/trial-apps/${encodeURIComponent(appId)}/datasets${queryString ? `?${queryString}` : ''}`
return get<DataSetListResponse>(url)
}
const getOptionalString = (value: unknown) => {
return typeof value === 'string' ? value : undefined
export const fetchTryAppFlowPreview = (appId: string): Promise<TryAppFlowPreview> => {
return consoleClient.trialApps.workflows({ params: { appId } })
.then(res => res as TryAppFlowPreview)
}
const getBoolean = (value: unknown, fallback = false) => {
return typeof value === 'boolean' ? value : fallback
export const fetchTryAppParams = (appId: string): Promise<ChatConfig> => {
return consoleClient.trialApps.parameters({ params: { appId } })
}
const getNumber = (value: unknown, fallback: number) => {
return typeof value === 'number' ? value : fallback
}
const getStringArray = (value: unknown) => {
return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : []
}
const getStringRecord = (value: unknown) => {
if (!isRecord(value))
return undefined
const record: Record<string, string | undefined> = {}
Object.entries(value).forEach(([key, item]) => {
if (typeof item === 'string')
record[key] = item
})
return record
}
const normalizeEnabledConfig = (value: Record<string, unknown>, fallback = false) => {
return {
...value,
enabled: getBoolean(value.enabled, fallback),
}
}
const normalizeTextToSpeechConfig = (value: Record<string, unknown>): ChatConfig['text_to_speech'] => {
const autoPlay = getString(value.autoPlay)
const config = { ...value }
delete config.autoPlay
return {
...normalizeEnabledConfig(config),
voice: getOptionalString(value.voice),
language: getOptionalString(value.language),
...(autoPlay === TtsAutoPlay.enabled || autoPlay === TtsAutoPlay.disabled ? { autoPlay } : {}),
}
}
const normalizeAnnotationReplyConfig = (value: Record<string, unknown>): ChatConfig['annotation_reply'] => {
const embeddingModel = isRecord(value.embedding_model) ? value.embedding_model : {}
return {
id: getString(value.id),
enabled: getBoolean(value.enabled),
score_threshold: getNumber(value.score_threshold, ANNOTATION_DEFAULT.score_threshold),
embedding_model: {
embedding_provider_name: getString(embeddingModel.embedding_provider_name),
embedding_model_name: getString(embeddingModel.embedding_model_name),
},
}
}
const getTransferMethods = (value: unknown, fallback: TransferMethod[]) => {
if (!Array.isArray(value))
return fallback
const methods = value.filter((item): item is TransferMethod => {
return typeof item === 'string' && transferMethodValues.has(item)
})
return methods.length > 0 ? methods : fallback
}
const getSupportUploadFileTypes = (value: unknown): SupportUploadFileTypes[] => {
if (!Array.isArray(value))
return []
return value.filter((item): item is SupportUploadFileTypes => {
return typeof item === 'string' && supportUploadFileTypeValues.has(item)
})
}
const normalizeVisionSettings = (value: unknown): NonNullable<ChatConfig['file_upload']>['image'] => {
const image = isRecord(value) ? value : {}
return {
enabled: getBoolean(image.enabled),
number_limits: getNumber(image.number_limits, 3),
detail: getString(image.detail) === Resolution.low ? Resolution.low : Resolution.high,
transfer_methods: getTransferMethods(image.transfer_methods, [TransferMethod.local_file, TransferMethod.remote_url]),
}
}
const normalizeFileUploadConfig = (value: Record<string, unknown>): ChatConfig['file_upload'] => {
const allowedUploadMethods = getTransferMethods(value.allowed_upload_methods, [TransferMethod.local_file, TransferMethod.remote_url])
const allowedFileUploadMethods = getTransferMethods(value.allowed_file_upload_methods, allowedUploadMethods)
return {
image: normalizeVisionSettings(value.image),
allowed_file_upload_methods: allowedFileUploadMethods,
allowed_upload_methods: allowedUploadMethods,
allowed_file_types: getSupportUploadFileTypes(value.allowed_file_types),
allowed_file_extensions: getStringArray(value.allowed_file_extensions),
max_length: getNumber(value.max_length, 1),
number_limits: getNumber(value.number_limits, 1),
}
}
const defaultDatasetConfigs: ChatConfig['dataset_configs'] = {
retrieval_model: RETRIEVE_TYPE.oneWay,
reranking_model: {
reranking_provider_name: '',
reranking_model_name: '',
},
top_k: 4,
score_threshold_enabled: false,
score_threshold: null,
datasets: {
datasets: [],
},
}
const normalizeBaseInputForm = (value: Record<string, unknown>) => {
return {
default: getString(value.default),
label: getString(value.label),
variable: getString(value.variable),
required: getBoolean(value.required, true),
hide: getBoolean(value.hide),
}
}
const normalizeTextInputForm = (value: Record<string, unknown>) => {
return {
...normalizeBaseInputForm(value),
max_length: getNumber(value.max_length, 0),
}
}
const normalizeFileInputForm = (value: Record<string, unknown>) => {
return {
...normalizeBaseInputForm(value),
max_length: getNumber(value.max_length, 1),
allowed_file_upload_methods: getTransferMethods(value.allowed_file_upload_methods, [
TransferMethod.local_file,
TransferMethod.remote_url,
]),
allowed_upload_methods: getTransferMethods(value.allowed_upload_methods, [
TransferMethod.local_file,
TransferMethod.remote_url,
]),
allowed_file_types: getSupportUploadFileTypes(value.allowed_file_types),
allowed_file_extensions: getStringArray(value.allowed_file_extensions),
}
}
const normalizeUserInputFormItem = (item: Record<string, unknown>): ChatConfig['user_input_form'][number] | null => {
if (isRecord(item['text-input']))
return { 'text-input': normalizeTextInputForm(item['text-input']) }
if (isRecord(item.paragraph))
return { paragraph: normalizeTextInputForm(item.paragraph) }
if (isRecord(item.select)) {
return {
select: {
...normalizeBaseInputForm(item.select),
options: getStringArray(item.select.options),
},
}
}
if (isRecord(item.number)) {
return {
number: {
...normalizeBaseInputForm(item.number),
max_length: getNumber(item.number.max_length, 0),
},
}
}
if (isRecord(item.checkbox)) {
return {
checkbox: {
...normalizeBaseInputForm(item.checkbox),
default: getBoolean(item.checkbox.default),
},
}
}
if (isRecord(item.file))
return { file: normalizeFileInputForm(item.file) }
if (isRecord(item['file-list']))
return { 'file-list': normalizeFileInputForm(item['file-list']) }
if (isRecord(item.external_data_tool)) {
return {
external_data_tool: {
label: getString(item.external_data_tool.label),
variable: getString(item.external_data_tool.variable),
required: getBoolean(item.external_data_tool.required, true),
hide: getBoolean(item.external_data_tool.hide),
type: getOptionalString(item.external_data_tool.type),
enabled: getBoolean(item.external_data_tool.enabled),
icon: getOptionalString(item.external_data_tool.icon),
icon_background: getOptionalString(item.external_data_tool.icon_background),
config: getStringRecord(item.external_data_tool.config),
},
}
}
if (isRecord(item.json_object)) {
const jsonSchema = item.json_object.json_schema
return {
json_object: {
...normalizeBaseInputForm(item.json_object),
json_schema: typeof jsonSchema === 'string' || isRecord(jsonSchema) ? jsonSchema : undefined,
},
}
}
return null
}
const normalizeUserInputForm = (items: TryAppParameters['user_input_form']): ChatConfig['user_input_form'] => {
return items.reduce<ChatConfig['user_input_form']>((result, item) => {
const normalized = normalizeUserInputFormItem(item)
if (normalized)
result.push(normalized)
return result
}, [])
}
const normalizeTryAppParams = (params: TryAppParameters): ChatConfig => {
return {
opening_statement: params.opening_statement ?? '',
suggested_questions: params.suggested_questions,
suggested_questions_after_answer: normalizeEnabledConfig(params.suggested_questions_after_answer),
speech_to_text: normalizeEnabledConfig(params.speech_to_text),
text_to_speech: normalizeTextToSpeechConfig(params.text_to_speech),
retriever_resource: normalizeEnabledConfig(params.retriever_resource),
annotation_reply: normalizeAnnotationReplyConfig(params.annotation_reply),
more_like_this: normalizeEnabledConfig(params.more_like_this),
sensitive_word_avoidance: normalizeEnabledConfig(params.sensitive_word_avoidance),
file_upload: normalizeFileUploadConfig(params.file_upload),
user_input_form: normalizeUserInputForm(params.user_input_form),
system_parameters: params.system_parameters,
pre_prompt: '',
prompt_type: PromptMode.simple,
agent_mode: DEFAULT_AGENT_SETTING,
dataset_configs: defaultDatasetConfigs,
}
}
export const fetchTryAppInfo = (appId: string) => {
return consoleClient.trialApps.byAppId.get({ params: { app_id: appId } })
}
export const fetchTryAppDatasets = (appId: string, ids: string[]) => {
return consoleClient.trialApps.byAppId.datasets.get({
params: { app_id: appId },
query: { ids },
})
}
export const fetchTryAppFlowPreview = (appId: string) => {
return consoleClient.trialApps.byAppId.workflows.get({ params: { app_id: appId } })
}
export const fetchTryAppParams = (appId: string) => {
return consoleClient.trialApps.byAppId.parameters.get({ params: { app_id: appId } })
.then(normalizeTryAppParams)
}
export type TryAppInfo = Awaited<ReturnType<typeof fetchTryAppInfo>>
export type { TryAppInfo } from '@/models/try-app'

View File

@ -1,10 +1,11 @@
import type { DataSetListResponse } from '@/models/datasets'
import { useQuery } from '@tanstack/react-query'
import { consoleQuery } from '@/service/client'
import { fetchTryAppDatasets, fetchTryAppFlowPreview, fetchTryAppInfo, fetchTryAppParams } from './try-app'
export const useGetTryAppInfo = (appId: string) => {
return useQuery({
queryKey: consoleQuery.trialApps.byAppId.get.queryKey({ input: { params: { app_id: appId } } }),
queryKey: consoleQuery.trialApps.info.queryKey({ input: { params: { appId } } }),
queryFn: () => {
return fetchTryAppInfo(appId)
},
@ -14,7 +15,7 @@ export const useGetTryAppInfo = (appId: string) => {
export const useGetTryAppParams = (appId: string) => {
return useQuery({
queryKey: consoleQuery.trialApps.byAppId.parameters.get.queryKey({ input: { params: { app_id: appId } } }),
queryKey: consoleQuery.trialApps.parameters.queryKey({ input: { params: { appId } } }),
queryFn: () => {
return fetchTryAppParams(appId)
},
@ -23,8 +24,8 @@ export const useGetTryAppParams = (appId: string) => {
}
export const useGetTryAppDataSets = (appId: string, ids: string[]) => {
return useQuery({
queryKey: consoleQuery.trialApps.byAppId.datasets.get.queryKey({ input: { params: { app_id: appId }, query: { ids } } }),
return useQuery<DataSetListResponse>({
queryKey: consoleQuery.trialApps.datasets.queryKey({ input: { params: { appId }, query: { ids } } }),
queryFn: () => {
return fetchTryAppDatasets(appId, ids)
},
@ -34,7 +35,7 @@ export const useGetTryAppDataSets = (appId: string, ids: string[]) => {
export const useGetTryAppFlowPreview = (appId: string, disabled?: boolean) => {
return useQuery({
queryKey: consoleQuery.trialApps.byAppId.workflows.get.queryKey({ input: { params: { app_id: appId } } }),
queryKey: consoleQuery.trialApps.workflows.queryKey({ input: { params: { appId } } }),
enabled: !disabled,
queryFn: () => {
return fetchTryAppFlowPreview(appId)

View File

@ -85,47 +85,22 @@ export type PromptVariable = {
}
type TextTypeFormItem = {
default?: string
default: string
label: string
variable: string
required: boolean
max_length?: number
hide?: boolean
max_length: number
hide: boolean
}
type SelectTypeFormItem = {
default?: string
default: string
label: string
variable: string
required: boolean
options?: string[]
hide?: boolean
options: string[]
hide: boolean
}
type NumberTypeFormItem = Omit<TextTypeFormItem, 'default' | 'max_length'> & {
default?: string | number
max_length?: number
}
type CheckboxTypeFormItem = Omit<TextTypeFormItem, 'default' | 'max_length'> & {
default?: string | boolean
}
type FileTypeFormItem = Omit<TextTypeFormItem, 'max_length'> & Partial<UploadFileSetting> & {
max_length?: number
}
type ExternalDataToolFormItem = ExternalDataTool & {
label: string
variable: string
required?: boolean
hide?: boolean
}
type JsonObjectFormItem = Omit<TextTypeFormItem, 'max_length'> & {
json_schema?: string | Record<string, unknown>
}
/**
* User Input Form Item
*/
@ -135,18 +110,6 @@ export type UserInputFormItem = {
select: SelectTypeFormItem
} | {
paragraph: TextTypeFormItem
} | {
number: NumberTypeFormItem
} | {
checkbox: CheckboxTypeFormItem
} | {
file: FileTypeFormItem
} | {
'file-list': FileTypeFormItem
} | {
external_data_tool: ExternalDataToolFormItem
} | {
json_object: JsonObjectFormItem
}
export type AgentTool = {
@ -155,7 +118,7 @@ export type AgentTool = {
provider_name: string
tool_name: string
tool_label: string
tool_parameters: Record<string, unknown>
tool_parameters: Record<string, any>
enabled: boolean
isDeleted?: boolean
notAuthor?: boolean

View File

@ -1,6 +1,5 @@
import type { PromptVariable } from '@/models/debug'
import type { UserInputFormItem } from '@/types/app'
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
/**
* Test suite for model configuration transformation utilities
*
@ -19,20 +18,6 @@ import {
userInputsFormToPromptVariables,
} from './model-config'
const getTextInput = (item: UserInputFormItem | undefined) => {
if (!item || !('text-input' in item))
throw new Error('Expected text-input user input form item')
return item['text-input']
}
const getSelectInput = (item: UserInputFormItem | undefined) => {
if (!item || !('select' in item))
throw new Error('Expected select user input form item')
return item.select
}
describe('Model Config Utilities', () => {
describe('userInputsFormToPromptVariables', () => {
/**
@ -125,7 +110,7 @@ describe('Model Config Utilities', () => {
default: '',
hide: false,
},
},
} as any,
]
const result = userInputsFormToPromptVariables(userInputs)
@ -155,7 +140,7 @@ describe('Model Config Utilities', () => {
default: '',
hide: false,
},
},
} as any,
]
const result = userInputsFormToPromptVariables(userInputs)
@ -214,13 +199,13 @@ describe('Model Config Utilities', () => {
label: 'Profile Picture',
variable: 'profile_pic',
required: false,
allowed_file_types: [SupportUploadFileTypes.image],
allowed_file_types: ['image'],
allowed_file_extensions: ['.jpg', '.png'],
allowed_file_upload_methods: ['local_file', 'remote_url'],
default: '',
hide: false,
},
},
} as any,
]
const result = userInputsFormToPromptVariables(userInputs)
@ -252,14 +237,14 @@ describe('Model Config Utilities', () => {
label: 'Documents',
variable: 'documents',
required: true,
allowed_file_types: [SupportUploadFileTypes.document],
allowed_file_types: ['document'],
allowed_file_extensions: ['.pdf', '.docx'],
allowed_file_upload_methods: ['local_file'],
max_length: 5,
default: '',
hide: false,
},
},
} as any,
]
const result = userInputsFormToPromptVariables(userInputs)
@ -298,7 +283,7 @@ describe('Model Config Utilities', () => {
icon_background: '#FF5733',
hide: false,
},
},
} as any,
]
const result = userInputsFormToPromptVariables(userInputs)
@ -364,7 +349,7 @@ describe('Model Config Utilities', () => {
default: '',
hide: false,
},
},
} as any,
{
select: {
label: 'Gender',
@ -568,7 +553,7 @@ describe('Model Config Utilities', () => {
const result = promptVariablesToUserInputsForm(promptVariables)
expect(result).toHaveLength(1)
expect(getTextInput(result[0]).variable).toBe('valid_key')
expect((result[0] as any)['text-input']?.variable).toBe('valid_key')
})
/**
@ -628,8 +613,8 @@ describe('Model Config Utilities', () => {
const result = promptVariablesToUserInputsForm(promptVariables)
expect(getTextInput(result[0]).required).toBe(true)
expect(getTextInput(result[1]).required).toBe(false)
expect((result[0] as any)['text-input']?.required).toBe(true)
expect((result[1] as any)['text-input']?.required).toBe(false)
})
})
@ -758,7 +743,7 @@ describe('Model Config Utilities', () => {
bool1: 1,
bool2: 0,
bool3: 'yes',
bool4: null,
bool4: null as any,
}
const result = formatBooleanInputs(useInputs, inputs)
@ -826,9 +811,9 @@ describe('Model Config Utilities', () => {
const backToUserInputs = promptVariablesToUserInputsForm(promptVars)
expect(backToUserInputs).toHaveLength(2)
expect(getTextInput(backToUserInputs[0]).variable).toBe('name')
expect(getSelectInput(backToUserInputs[1]).variable).toBe('type')
expect(getSelectInput(backToUserInputs[1]).options).toEqual(['A', 'B', 'C'])
expect((backToUserInputs[0] as any)['text-input']?.variable).toBe('name')
expect((backToUserInputs[1] as any).select?.variable).toBe('type')
expect((backToUserInputs[1] as any).select?.options).toEqual(['A', 'B', 'C'])
})
})
})

View File

@ -1,139 +1,94 @@
import type { PromptVariable } from '@/models/debug'
import type { UserInputFormItem } from '@/types/app'
const isRecord = (value: unknown): value is Record<string, unknown> => {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
const getString = (value: unknown) => {
return typeof value === 'string' ? value : ''
}
const getOptionalString = (value: unknown) => {
return typeof value === 'string' ? value : undefined
}
const getBoolean = (value: unknown, fallback = false) => {
return typeof value === 'boolean' ? value : fallback
}
const getNumber = (value: unknown) => {
return typeof value === 'number' ? value : undefined
}
const getDefaultValue = (value: unknown) => {
return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' ? value : undefined
}
const getStringArray = (value: unknown) => {
return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : []
}
const getStringRecord = (value: unknown) => {
if (!isRecord(value))
return undefined
const record: Record<string, string | undefined> = {}
Object.entries(value).forEach(([key, item]) => {
if (typeof item === 'string')
record[key] = item
})
return record
}
const getRecord = (value: unknown) => {
return isRecord(value) ? value : {}
}
const getInputFormContent = (item: Record<string, unknown>) => {
if (isRecord(item.paragraph))
return { type: 'paragraph', content: item.paragraph }
if (isRecord(item['text-input']))
return { type: 'string', content: item['text-input'] }
if (isRecord(item.number))
return { type: 'number', content: item.number }
if (isRecord(item.checkbox))
return { type: 'boolean', content: item.checkbox }
if (isRecord(item.file))
return { type: 'file', content: item.file }
if (isRecord(item['file-list']))
return { type: 'file-list', content: item['file-list'] }
if (isRecord(item.external_data_tool))
return { type: getString(item.external_data_tool.type), content: item.external_data_tool }
if (isRecord(item.json_object))
return { type: 'json_object', content: item.json_object }
return { type: 'select', content: getRecord(item.select) }
}
export const userInputsFormToPromptVariables = (useInputs: Record<string, unknown>[] | null, dataset_query_variable?: string) => {
export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | null, dataset_query_variable?: string) => {
if (!useInputs)
return []
const promptVariables: PromptVariable[] = []
useInputs.forEach((item) => {
const { type, content } = getInputFormContent(item)
const variable = getString(content.variable)
const is_context_var = dataset_query_variable === variable
useInputs.forEach((item: any) => {
const isParagraph = !!item.paragraph
const [type, content] = (() => {
if (isParagraph)
return ['paragraph', item.paragraph]
if (item['text-input'])
return ['string', item['text-input']]
if (item.number)
return ['number', item.number]
if (item.checkbox)
return ['boolean', item.checkbox]
if (item.file)
return ['file', item.file]
if (item['file-list'])
return ['file-list', item['file-list']]
if (item.external_data_tool)
return [item.external_data_tool.type, item.external_data_tool]
if (item.json_object)
return ['json_object', item.json_object]
return ['select', item.select || {}]
})()
const is_context_var = dataset_query_variable === content?.variable
if (type === 'string' || type === 'paragraph') {
promptVariables.push({
key: variable,
name: getString(content.label),
required: getBoolean(content.required, true),
key: content.variable,
name: content.label,
required: content.required,
type,
max_length: getNumber(content.max_length),
max_length: content.max_length,
options: [],
is_context_var,
hide: getBoolean(content.hide),
default: getDefaultValue(content.default),
hide: content.hide,
default: content.default,
})
}
else if (type === 'number') {
promptVariables.push({
key: variable,
name: getString(content.label),
required: getBoolean(content.required, true),
key: content.variable,
name: content.label,
required: content.required,
type,
options: [],
hide: getBoolean(content.hide),
default: getDefaultValue(content.default),
hide: content.hide,
default: content.default,
})
}
else if (type === 'boolean') {
promptVariables.push({
key: variable,
name: getString(content.label),
required: getBoolean(content.required, true),
key: content.variable,
name: content.label,
required: content.required,
type: 'checkbox',
options: [],
hide: getBoolean(content.hide),
default: getDefaultValue(content.default),
hide: content.hide,
default: content.default,
})
}
else if (type === 'select') {
promptVariables.push({
key: variable,
name: getString(content.label),
required: getBoolean(content.required, true),
key: content.variable,
name: content.label,
required: content.required,
type: 'select',
options: getStringArray(content.options),
options: content.options,
is_context_var,
hide: getBoolean(content.hide),
default: getDefaultValue(content.default),
hide: content.hide,
default: content.default,
})
}
else if (type === 'file') {
promptVariables.push({
key: variable,
name: getString(content.label),
required: getBoolean(content.required, true),
key: content.variable,
name: content.label,
required: content.required,
type,
config: {
allowed_file_types: content.allowed_file_types,
@ -141,38 +96,38 @@ export const userInputsFormToPromptVariables = (useInputs: Record<string, unknow
allowed_file_upload_methods: content.allowed_file_upload_methods,
number_limits: 1,
},
hide: getBoolean(content.hide),
default: getDefaultValue(content.default),
hide: content.hide,
default: content.default,
})
}
else if (type === 'file-list') {
promptVariables.push({
key: variable,
name: getString(content.label),
required: getBoolean(content.required, true),
key: content.variable,
name: content.label,
required: content.required,
type,
config: {
allowed_file_types: content.allowed_file_types,
allowed_file_extensions: content.allowed_file_extensions,
allowed_file_upload_methods: content.allowed_file_upload_methods,
number_limits: getNumber(content.max_length),
number_limits: content.max_length,
},
hide: getBoolean(content.hide),
default: getDefaultValue(content.default),
hide: content.hide,
default: content.default,
})
}
else {
promptVariables.push({
key: variable,
name: getString(content.label),
required: getBoolean(content.required, true),
type: getString(content.type || type),
enabled: getBoolean(content.enabled),
config: getRecord(content.config),
icon: getOptionalString(content.icon),
icon_background: getOptionalString(content.icon_background),
key: content.variable,
name: content.label,
required: content.required,
type: content.type,
enabled: content.enabled,
config: content.config,
icon: content.icon,
icon_background: content.icon_background,
is_context_var,
hide: getBoolean(content.hide),
hide: content.hide,
})
}
})
@ -183,10 +138,10 @@ export const promptVariablesToUserInputsForm = (promptVariables: PromptVariable[
const userInputs: UserInputFormItem[] = []
promptVariables.filter(({ key, name }) => {
return key && key.trim() && name && name.trim()
}).forEach((item) => {
if (item.type === 'string') {
}).forEach((item: any) => {
if (item.type === 'string' || item.type === 'paragraph') {
userInputs.push({
'text-input': {
[item.type === 'string' ? 'text-input' : 'paragraph']: {
label: item.name,
variable: item.key,
required: item.required !== false, // default true
@ -194,43 +149,19 @@ export const promptVariablesToUserInputsForm = (promptVariables: PromptVariable[
default: '',
hide: item.hide,
},
})
} as any)
return
}
if (item.type === 'paragraph') {
if (item.type === 'number' || item.type === 'checkbox') {
userInputs.push({
paragraph: {
label: item.name,
variable: item.key,
required: item.required !== false, // default true
max_length: item.max_length,
default: '',
hide: item.hide,
},
})
return
}
if (item.type === 'number') {
userInputs.push({
number: {
[item.type]: {
label: item.name,
variable: item.key,
required: item.required !== false, // default true
default: '',
hide: item.hide,
},
})
}
else if (item.type === 'checkbox') {
userInputs.push({
checkbox: {
label: item.name,
variable: item.key,
required: item.required !== false, // default true
default: '',
hide: item.hide,
},
})
} as any)
}
else if (item.type === 'select') {
userInputs.push({
@ -239,10 +170,10 @@ export const promptVariablesToUserInputsForm = (promptVariables: PromptVariable[
variable: item.key,
required: item.required !== false, // default true
options: item.options,
default: getString(item.default),
default: item.default ?? '',
hide: item.hide,
},
})
} as any)
}
else {
userInputs.push({
@ -251,20 +182,20 @@ export const promptVariablesToUserInputsForm = (promptVariables: PromptVariable[
variable: item.key,
enabled: item.enabled,
type: item.type,
config: getStringRecord(item.config),
config: item.config,
required: item.required,
icon: item.icon,
icon_background: item.icon_background,
hide: item.hide,
},
})
} as any)
}
})
return userInputs
}
export const formatBooleanInputs = (useInputs?: PromptVariable[] | null, inputs?: Record<string, string | number | object | boolean | null> | null) => {
export const formatBooleanInputs = (useInputs?: PromptVariable[] | null, inputs?: Record<string, string | number | object | boolean> | null) => {
if (!useInputs)
return inputs
const res = { ...inputs }