Compare commits

..

5 Commits

3252 changed files with 32691 additions and 24652 deletions

5
.github/CODEOWNERS vendored
View File

@ -1,8 +1,5 @@
* @Tecvan-fe @hi-pender @fanlv
/frontend/ @Tecvan-fe
/common/ @Tecvan-fe
/frontend/apps/coze-studio/ @Tecvan-fe @evan-crash @duwenhan2byte
/frontend/packages/agent-ide/agent-publish/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei @catee
/frontend/packages/agent-ide/commons/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
@ -274,4 +271,4 @@
/backend/ @fanlv @junwen-lee @liuyunchao-1998 @lvxinyu-1117 @hi-pender @luohq-bytedance @shentongmartin @mrh997 @meguminnnnnnnnn @N3kox @zhuangjie1125
/docker/ @fanlv @junwen-lee @liuyunchao-1998 @lvxinyu-1117 @hi-pender @luohq-bytedance @shentongmartin @mrh997 @meguminnnnnnnnn @N3kox @zhuangjie1125
/helm/ @fanlv @junwen-lee @liuyunchao-1998 @lvxinyu-1117 @hi-pender @luohq-bytedance @shentongmartin @mrh997 @meguminnnnnnnnn @N3kox @zhuangjie1125
/helm/ @fanlv @junwen-lee @liuyunchao-1998 @lvxinyu-1117 @hi-pender @luohq-bytedance @shentongmartin @mrh997 @meguminnnnnnnnn @N3kox @zhuangjie1125

View File

@ -1,6 +1,6 @@
#!/bin/bash
# Set default value
# 设置默认值
TARGET_BRANCH=${targetBranch}
CI_MODE=${CI:-false}
@ -36,11 +36,11 @@ else
files=$(git diff --name-only --diff-filter=AM --cached $EXCLUDE_STRING)
fi
# The volume limit is 512KB.
# 体积限制为512KB
size_limit=$((512))
large_files_info=""
IFS=$'\n' # Handling the existence of spaces in the file name
IFS=$'\n' # 处理文件名存在空格情况
for file in $files; do
file_size=$(wc -c <"$file" 2>/dev/null)
if [ $? -ne 0 ]; then

View File

@ -37,10 +37,10 @@ jobs:
- name: Process changed files
id: process-files
run: |
# Get all change files
# 获取所有变更文件
all_files="${{ steps.changed-files.outputs.all_changed_files }}"
# Filter out files in the common/changes directory
# 过滤掉 common/changes 目录下的文件
filtered_files=""
for file in $all_files; do
if [[ ! "$file" =~ ^common/changes/.* ]]; then
@ -52,10 +52,10 @@ jobs:
fi
done
# Create cached files in JSON format
# 创建 JSON 格式的缓存文件
echo "[$( echo "$filtered_files" | sed 's/ /", "/g' | sed 's/^/"/' | sed 's/$/"/' )]" > changed-files-cache.json
# Output cache file path for subsequent steps
# 输出缓存文件路径供后续步骤使用
echo "cache_file=changed-files-cache.json" >> $GITHUB_OUTPUT
echo "过滤前文件数量: $(echo $all_files | wc -w)"
@ -163,13 +163,12 @@ jobs:
- name: Increment Test:cov
run: node common/scripts/install-run-rush.js increment --action test:cov -p "${{ needs.setup.outputs.cache_file }}"
# TODO: should resolve later
# - name: Upload coverage reports
# uses: codecov/codecov-action@v4
# with:
# token: ${{ secrets.CODECOV_TOKEN }}
# fail_ci_if_error: true
# verbose: true
- name: Upload coverage reports
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
verbose: true
lint:
needs: setup

8
.gitignore vendored
View File

@ -6,7 +6,6 @@
*.dylib
.env
.env.debug
# Test binary, built with `go test -c`
*.test
@ -33,7 +32,7 @@ output/*
*.swp
# Vscode files
.vscode/settings.json
.vscode
/patches
/oldimpl
@ -53,8 +52,3 @@ common/temp
backend/conf/model/*.yaml
values-dev.yaml
**/conf/model_ark_doubao-seed-1.6.yaml
*.tsbuildinfo

View File

@ -1,15 +0,0 @@
{
"recommendations": [
"editorconfig.editorconfig",
"esbenp.prettier-vscode",
"streetsidesoftware.code-spell-checker",
"codezombiech.gitignore",
"aaron-bond.better-comments",
"mikestead.dotenv",
"stylelint.vscode-stylelint"
],
"unwantedRecommendations": [
"nucllear.vscode-extension-auto-import",
"steoates.autoimport"
]
}

View File

@ -1,183 +0,0 @@
{
"eslint.nodePath": "frontend/config/eslint-config/node_modules/eslint",
"prettier.prettierPath": "frontend/config/eslint-config/node_modules/prettier",
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.formatOnSave": true,
"editor.formatOnType": false,
"editor.formatOnPaste": false,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll": "never",
"source.fixAll.eslint": "never",
"source.removeUnused": "never",
"source.organizeImports": "never"
// "source.sortImports": "always"
},
"editor.formatOnSaveMode": "modificationsIfAvailable",
"search.followSymlinks": false,
"search.exclude": {
"**/node_modules": true,
"**/.nyc_output": true,
"**/.rush": true,
"**/pnpm-lock.yaml": true,
"**/CHANGELOG.json": true,
"**/CHANGELOG.md": true,
"common/changes": true,
"**/output": true,
"**/lib": true,
"**/rush-logs": true,
"**/dist": true,
"**/coverage": true,
"common/temp": true
},
"eslint.workingDirectories": [
{
"mode": "auto"
}
],
"files.defaultLanguage": "plaintext",
"files.associations": {
".code-workspace": "jsonc",
".babelrc": "json",
".eslintrc": "jsonc",
".eslintrc*.json": "jsonc",
".stylelintrc": "javascript",
"stylelintrc": "jsonc",
"*.json": "jsonc",
"package.json": "json",
".htmlhintrc": "jsonc",
"htmlhintrc": "jsonc",
"Procfile*": "shellscript",
"README": "markdown",
"**/coverage/**/*.*": "plaintext",
"OWNERS": "yaml",
// Set the pnpm-lock file to plaintext to avoid parsing; otherwise, VSCode may freeze when opening the file
"**/pnpm-lock.yaml": "plaintext",
"**/dist/**": "plaintext",
"**/dist_*/**": "plaintext",
"*.map": "plaintext",
"*.log": "plaintext"
},
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/Thumbs.db": true,
"**/.rush": true,
"**/.swc": true
},
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/*/**": true
},
"search.useIgnoreFiles": true,
//
"editor.rulers": [80, 120],
"files.eol": "\n",
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"cSpell.diagnosticLevel": "Warning",
"eslint.probe": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"editor.semanticHighlighting.enabled": false,
"eslint.format.enable": false,
"eslint.enable": true,
"eslint.useFlatConfig": true,
"eslint.codeActionsOnSave.mode": "problems",
"eslint.lintTask.enable": false,
"javascript.validate.enable": false,
"typescript.tsdk": "frontend/config/ts-config/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.disableAutomaticTypeAcquisition": true,
"typescript.format.enable": false,
"typescript.referencesCodeLens.enabled": false,
"typescript.preferGoToSourceDefinition": true,
"typescript.updateImportsOnFileMove.enabled": "never",
// tsserver logs are not automatically deleted and accumulate over time, causing disk space issues, so disabled by default
"typescript.tsserver.log": "off",
"typescript.tsserver.experimental.enableProjectDiagnostics": false,
"typescript.workspaceSymbols.excludeLibrarySymbols": true,
"editor.minimap.enabled": true,
"typescript.preferences.includePackageJsonAutoImports": "off",
"typescript.suggest.autoImports": true,
"typescript.tsserver.maxTsServerMemory": 10240,
"typescript.tsserver.enableRegionDiagnostics": false,
"typescript.tsserver.watchOptions": {
"fallbackPolling": "dynamicPriorityPolling",
"synchronousWatchDirectory": true,
"watchDirectory": "useFsEvents",
"watchFile": "useFsEventsOnParentDirectory",
"excludeDirectories": ["/**/node_modules"],
"excludeLibrarySymbols": true,
"excludeFiles": ["/**/node_modules/**"]
},
"css.validate": false,
"scss.validate": false,
"less.validate": false,
"stylelint.enable": true,
"stylelint.validate": ["css", "scss", "less"],
"stylelint.stylelintPath": "frontend/config/stylelint-config/node_modules/stylelint",
"emmet.triggerExpansionOnTab": true,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[less]": {
"editor.defaultFormatter": "stylelint.vscode-stylelint"
},
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[ignore]": {
"editor.defaultFormatter": "foxundermoon.shell-format"
},
"[shellscript]": {
"editor.defaultFormatter": "foxundermoon.shell-format"
},
"[dotenv]": {
"editor.defaultFormatter": "foxundermoon.shell-format"
},
"[svg]": {
"editor.defaultFormatter": "jock.svg"
},
"svg.preview.background": "white",
"[xml]": {
"editor.defaultFormatter": "mblode.pretty-formatter"
},
"[sql]": {
"editor.defaultFormatter": "adpyke.vscode-sql-formatter"
},
"git.openRepositoryInParentFolders": "always",
"references.preferredLocation": "view"
}

View File

@ -8,10 +8,10 @@ SYNC_DB_SCRIPT := $(SCRIPTS_DIR)/setup/db_migrate_apply.sh
DUMP_DB_SCRIPT := $(SCRIPTS_DIR)/setup/db_migrate_dump.sh
SETUP_DOCKER_SCRIPT := $(SCRIPTS_DIR)/setup/docker.sh
SETUP_PYTHON_SCRIPT := $(SCRIPTS_DIR)/setup/python.sh
COMPOSE_FILE := docker/docker-compose-debug.yml
COMPOSE_FILE := docker/docker-compose.yml
MYSQL_SCHEMA := ./docker/volumes/mysql/schema.sql
MYSQL_INIT_SQL := ./docker/volumes/mysql/sql_init.sql
ENV_FILE := ./docker/.env.debug
ENV_FILE := ./docker/.env
STATIC_DIR := ./bin/resources/static
ES_INDEX_SCHEMA := ./docker/volumes/elasticsearch/es_index_schema
ES_SETUP_SCRIPT := ./docker/volumes/elasticsearch/setup_es.sh
@ -21,7 +21,7 @@ debug: env middleware python server
env:
@if [ ! -f "$(ENV_FILE)" ]; then \
echo "Env file '$(ENV_FILE)' not found, using example env..."; \
cp ./docker/.env.debug.example $(ENV_FILE); \
cp ./docker/.env.example $(ENV_FILE); \
fi
fe:
@ -34,7 +34,7 @@ server: env setup_es_index
$(MAKE) fe; \
fi
@echo "Building and run server..."
@APP_ENV=debug bash $(BUILD_SERVER_SCRIPT) -start
@bash $(BUILD_SERVER_SCRIPT) -start
build_server:
@echo "Building server..."
@ -59,9 +59,9 @@ middleware:
web:
@echo "Start web server in docker"
@docker compose -f docker/docker-compose.yml up -d
@docker compose -f $(COMPOSE_FILE) --env-file $(ENV_FILE) --profile '*' up -d --wait
down: env
down:
@echo "Stop all docker containers"
@docker compose -f $(COMPOSE_FILE) --profile '*' down
@ -77,10 +77,10 @@ dump_sql_schema:
@echo "Dumping mysql schema to $(MYSQL_SCHEMA)..."
@. $(ENV_FILE); \
{ echo "SET NAMES utf8mb4;\nCREATE DATABASE IF NOT EXISTS opencoze COLLATE utf8mb4_unicode_ci;"; atlas schema inspect -u $$ATLAS_URL --format "{{ sql . }}" --exclude "atlas_schema_revisions,table_*" | sed 's/CREATE TABLE/CREATE TABLE IF NOT EXISTS/g'; } > $(MYSQL_SCHEMA)
@sed -i.bak -E 's/(\))[[:space:]]+CHARSET utf8mb4/\1 ENGINE=InnoDB CHARSET utf8mb4/' $(MYSQL_SCHEMA) && rm -f $(MYSQL_SCHEMA).bak
@cat $(MYSQL_INIT_SQL) >> $(MYSQL_SCHEMA)
@sed -I '' -E 's/(\))[[:space:]]+CHARSET utf8mb4/\1 ENGINE=InnoDB CHARSET utf8mb4/' $(MYSQL_SCHEMA)
@echo "Dumping mysql schema to helm/charts/opencoze/files/mysql ..."
@cp $(MYSQL_SCHEMA) ./helm/charts/opencoze/files/mysql/
@cp $(MYSQL_INIT_SQL) ./helm/charts/opencoze/files/mysql/
atlas-hash:
@echo "Rehash atlas migration files..."

View File

@ -72,9 +72,9 @@ Deployment steps:
# Start the service
cd docker
cp .env.example .env
docker compose up -d
docker compose --profile "*" up -d
```
For common startup failure issues, **please refer to the [FAQ](https://github.com/coze-dev/coze-studio/wiki/9.-FAQ)**.
After the service starts, it is normal for the `coze-minio-setup` , `coze-mysql-setup-init-sql` , and `coze-mysql-setup-schema` containers to be in an exited state (exit 0). For common startup failure issues, **please refer to the [FAQ](https://github.com/coze-dev/coze-studio/wiki/9.-FAQ)**.
4. After starting the service, you can open Coze Studio by accessing `http://localhost:8888/` through your browser.

View File

@ -72,9 +72,9 @@ Coze Studio 的后端采用 Golang 开发,前端使用 React + TypeScript
# 启动服务
cd docker
cp .env.example .env
docker compose up -d
docker compose --profile "*" up -d
```
**启动失败常见问题可参考[常见问题](https://github.com/coze-dev/coze-studio/wiki/9.-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)**。
服务启动之后`coze-minio-setup`、`coze-mysql-setup-init-sql`、`coze-mysql-setup-schema` 这几个容器处于退出状态exit 0是正常现象。**启动失败常见问题可参考[常见问题](https://github.com/coze-dev/coze-studio/wiki/9.-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)**。
4. 启动服务后,通过浏览器访问 `http://localhost:8888/` 即可打开 Coze Studio。

View File

@ -10,6 +10,12 @@ RUN apk add --no-cache git gcc libc-dev
COPY backend/go.mod backend/go.sum ./
RUN go mod download
COPY docker/proxy ./proxy
# Build the proxy application
RUN go build -ldflags="-s -w" -o /app/proxy_app ./proxy/proxy.go
# Copy the entire backend source code
COPY backend/ ./
@ -44,6 +50,7 @@ RUN apk add --no-cache --virtual .python-build-deps build-base py3-pip git && \
# Copy the built Go binary from the builder stage
COPY --from=builder /app/opencoze /app/opencoze
COPY --from=builder /app/proxy_app /app/proxy
# Copy Python application scripts
COPY backend/infra/impl/document/parser/builtin/parse_pdf.py /app/parse_pdf.py
@ -67,6 +74,8 @@ ENV PATH="/app/.venv/bin:${PATH}"
RUN chmod +x /app/parse_pdf.py /app/parse_docx.py && \
find /app/.venv/bin -type f -exec chmod +x {} \;
# Ensure Go binaries are executable
RUN chmod +x /app/opencoze /app/proxy
EXPOSE 8888

View File

@ -555,7 +555,11 @@ func CreateProjectConversationDef(ctx context.Context, c *app.RequestContext) {
return
}
resp := new(workflow.CreateProjectConversationDefResponse)
resp, err := appworkflow.SVC.CreateApplicationConversationDef(ctx, &req)
if err != nil {
internalServerErrorResponse(ctx, c, err)
return
}
c.JSON(consts.StatusOK, resp)
}
@ -570,8 +574,11 @@ func UpdateProjectConversationDef(ctx context.Context, c *app.RequestContext) {
invalidParamRequestResponse(c, err.Error())
return
}
resp := new(workflow.UpdateProjectConversationDefResponse)
resp, err := appworkflow.SVC.UpdateApplicationConversationDef(ctx, &req)
if err != nil {
internalServerErrorResponse(ctx, c, err)
return
}
c.JSON(consts.StatusOK, resp)
}
@ -587,7 +594,11 @@ func DeleteProjectConversationDef(ctx context.Context, c *app.RequestContext) {
return
}
resp := new(workflow.DeleteProjectConversationDefResponse)
resp, err := appworkflow.SVC.DeleteApplicationConversationDef(ctx, &req)
if err != nil {
internalServerErrorResponse(ctx, c, err)
return
}
c.JSON(consts.StatusOK, resp)
}
@ -603,7 +614,11 @@ func ListProjectConversationDef(ctx context.Context, c *app.RequestContext) {
return
}
resp := new(workflow.ListProjectConversationResponse)
resp, err := appworkflow.SVC.ListApplicationConversationDef(ctx, &req)
if err != nil {
internalServerErrorResponse(ctx, c, err)
return
}
c.JSON(consts.StatusOK, resp)
}
@ -723,7 +738,11 @@ func GetChatFlowRole(ctx context.Context, c *app.RequestContext) {
return
}
resp := new(workflow.GetChatFlowRoleResponse)
resp, err := appworkflow.SVC.GetChatFlowRole(ctx, &req)
if err != nil {
internalServerErrorResponse(ctx, c, err)
return
}
c.JSON(consts.StatusOK, resp)
}
@ -738,8 +757,11 @@ func CreateChatFlowRole(ctx context.Context, c *app.RequestContext) {
invalidParamRequestResponse(c, err.Error())
return
}
resp := new(workflow.CreateChatFlowRoleResponse)
resp, err := appworkflow.SVC.CreateChatFlowRole(ctx, &req)
if err != nil {
internalServerErrorResponse(ctx, c, err)
return
}
c.JSON(consts.StatusOK, resp)
}
@ -755,7 +777,11 @@ func DeleteChatFlowRole(ctx context.Context, c *app.RequestContext) {
return
}
resp := new(workflow.DeleteChatFlowRoleResponse)
resp, err := appworkflow.SVC.DeleteChatFlowRole(ctx, &req)
if err != nil {
internalServerErrorResponse(ctx, c, err)
return
}
c.JSON(consts.StatusOK, resp)
}

View File

@ -32,6 +32,7 @@ import (
"github.com/alicebob/miniredis/v2"
"github.com/bytedance/mockey"
"github.com/cloudwego/eino/callbacks"
model2 "github.com/cloudwego/eino/components/model"
"github.com/cloudwego/eino/schema"
"github.com/cloudwego/hertz/pkg/app"
@ -102,6 +103,11 @@ import (
"github.com/coze-dev/coze-studio/backend/types/errno"
)
func TestMain(m *testing.M) {
callbacks.AppendGlobalHandlers(service.GetTokenCallbackHandler())
os.Exit(m.Run())
}
type wfTestRunner struct {
t *testing.T
h *server.Hertz
@ -239,7 +245,7 @@ func newWfTestRunner(t *testing.T) *wfTestRunner {
mockTos := storageMock.NewMockStorage(ctrl)
mockTos.EXPECT().GetObjectUrl(gomock.Any(), gomock.Any(), gomock.Any()).Return("", nil).AnyTimes()
workflowRepo := service.NewWorkflowRepository(mockIDGen, db, redisClient, mockTos, cpStore, nil)
workflowRepo := service.NewWorkflowRepository(mockIDGen, db, redisClient, mockTos, cpStore)
mockey.Mock(appworkflow.GetWorkflowDomainSVC).Return(service.NewWorkflowService(workflowRepo)).Build()
mockey.Mock(workflow2.GetRepository).Return(workflowRepo).Build()

View File

@ -26,6 +26,8 @@ const (
Scene_GenerateAgentInfo Scene = 8
//openapi
Scene_SceneOpenApi Scene = 9
// 工作流
Scene_SceneWorkflow Scene = 50
)
func (p Scene) String() string {

View File

@ -30,7 +30,6 @@ import (
"github.com/coze-dev/coze-studio/backend/pkg/lang/slices"
"github.com/coze-dev/coze-studio/backend/pkg/logs"
"github.com/coze-dev/coze-studio/backend/types/errno"
"github.com/cloudwego/eino/schema"
)
@ -66,6 +65,10 @@ func (ot Openapi3T) Validate(ctx context.Context) (err error) {
return errorx.New(errno.ErrPluginInvalidOpenapi3Doc, errorx.KVf(errno.PluginMsgKey,
"invalid server url '%s'", serverURL))
}
if urlSchema.Scheme != "https" {
return errorx.New(errno.ErrPluginInvalidOpenapi3Doc, errorx.KV(errno.PluginMsgKey,
"server url must start with 'https://'"))
}
if urlSchema.Host == "" {
return errorx.New(errno.ErrPluginInvalidOpenapi3Doc, errorx.KVf(errno.PluginMsgKey,
"invalid server url '%s'", serverURL))

View File

@ -473,7 +473,10 @@ const (
NodeType_Input NodeType = 30
NodeType_Batch NodeType = 28
NodeType_Continue NodeType = 29
NodeType_MessageList NodeType = 37
NodeType_AssignVariable NodeType = 40
NodeType_ConversationList NodeType = 53
NodeType_CreateMessage NodeType = 55
NodeType_JsonSerialization NodeType = 58
NodeType_JsonDeserialization NodeType = 59
NodeType_DatasetDelete NodeType = 60
@ -533,8 +536,14 @@ func (p NodeType) String() string {
return "Batch"
case NodeType_Continue:
return "Continue"
case NodeType_MessageList:
return "MessageList"
case NodeType_AssignVariable:
return "AssignVariable"
case NodeType_ConversationList:
return "ConversationList"
case NodeType_CreateMessage:
return "CreateMessage"
case NodeType_JsonSerialization:
return "JsonSerialization"
case NodeType_JsonDeserialization:
@ -599,8 +608,14 @@ func NodeTypeFromString(s string) (NodeType, error) {
return NodeType_Batch, nil
case "Continue":
return NodeType_Continue, nil
case "MessageList":
return NodeType_MessageList, nil
case "AssignVariable":
return NodeType_AssignVariable, nil
case "ConversationList":
return NodeType_ConversationList, nil
case "CreateMessage":
return NodeType_CreateMessage, nil
case "JsonSerialization":
return NodeType_JsonSerialization, nil
case "JsonDeserialization":
@ -657,11 +672,14 @@ const (
NodeTemplateType_Input NodeTemplateType = 30
NodeTemplateType_Batch NodeTemplateType = 28
NodeTemplateType_Continue NodeTemplateType = 29
NodeTemplateType_MessageList NodeTemplateType = 37
NodeTemplateType_AssignVariable NodeTemplateType = 40
NodeTemplateType_DatabaseInsert NodeTemplateType = 41
NodeTemplateType_DatabaseUpdate NodeTemplateType = 42
NodeTemplateType_DatabasesELECT NodeTemplateType = 43
NodeTemplateType_DatabaseDelete NodeTemplateType = 44
NodeTemplateType_ConversationList NodeTemplateType = 53
NodeTemplateType_CreateMessage NodeTemplateType = 55
NodeTemplateType_JsonSerialization NodeTemplateType = 58
NodeTemplateType_JsonDeserialization NodeTemplateType = 59
NodeTemplateType_DatasetDelete NodeTemplateType = 60
@ -723,6 +741,8 @@ func (p NodeTemplateType) String() string {
return "Batch"
case NodeTemplateType_Continue:
return "Continue"
case NodeTemplateType_MessageList:
return "MessageList"
case NodeTemplateType_AssignVariable:
return "AssignVariable"
case NodeTemplateType_DatabaseInsert:
@ -733,6 +753,10 @@ func (p NodeTemplateType) String() string {
return "DatabasesELECT"
case NodeTemplateType_DatabaseDelete:
return "DatabaseDelete"
case NodeTemplateType_ConversationList:
return "ConversationList"
case NodeTemplateType_CreateMessage:
return "CreateMessage"
case NodeTemplateType_JsonSerialization:
return "JsonSerialization"
case NodeTemplateType_JsonDeserialization:
@ -799,6 +823,8 @@ func NodeTemplateTypeFromString(s string) (NodeTemplateType, error) {
return NodeTemplateType_Batch, nil
case "Continue":
return NodeTemplateType_Continue, nil
case "MessageList":
return NodeTemplateType_MessageList, nil
case "AssignVariable":
return NodeTemplateType_AssignVariable, nil
case "DatabaseInsert":
@ -809,6 +835,10 @@ func NodeTemplateTypeFromString(s string) (NodeTemplateType, error) {
return NodeTemplateType_DatabasesELECT, nil
case "DatabaseDelete":
return NodeTemplateType_DatabaseDelete, nil
case "ConversationList":
return NodeTemplateType_ConversationList, nil
case "CreateMessage":
return NodeTemplateType_CreateMessage, nil
case "JsonSerialization":
return NodeTemplateType_JsonSerialization, nil
case "JsonDeserialization":
@ -64103,7 +64133,7 @@ func (p *ChatFlowRole) String() string {
}
type CreateChatFlowRoleRequest struct {
ChatFlowRole *ChatFlowRole `thrift:"ChatFlowRole,1" json:"chat_flow_role" form:"ChatFlowRole" query:"ChatFlowRole"`
ChatFlowRole *ChatFlowRole `thrift:"ChatFlowRole,1" json:"chat_flow_role" query:"chat_flow_role" `
Base *base.Base `thrift:"Base,255,optional" form:"Base" json:"Base,omitempty" query:"Base"`
}
@ -64302,7 +64332,9 @@ func (p *CreateChatFlowRoleRequest) String() string {
type CreateChatFlowRoleResponse struct {
// 数据库中ID
ID string `thrift:"ID,1" form:"ID" json:"ID" query:"ID"`
ID string `thrift:"ID,1" json:"id" query:"id" `
Code int64 `thrift:"code,253,required" form:"code,required" json:"code,required" query:"code,required"`
Msg string `thrift:"msg,254,required" form:"msg,required" json:"msg,required" query:"msg,required"`
BaseResp *base.BaseResp `thrift:"BaseResp,255,required" form:"BaseResp,required" json:"BaseResp,required" query:"BaseResp,required"`
}
@ -64317,6 +64349,14 @@ func (p *CreateChatFlowRoleResponse) GetID() (v string) {
return p.ID
}
func (p *CreateChatFlowRoleResponse) GetCode() (v int64) {
return p.Code
}
func (p *CreateChatFlowRoleResponse) GetMsg() (v string) {
return p.Msg
}
var CreateChatFlowRoleResponse_BaseResp_DEFAULT *base.BaseResp
func (p *CreateChatFlowRoleResponse) GetBaseResp() (v *base.BaseResp) {
@ -64328,6 +64368,8 @@ func (p *CreateChatFlowRoleResponse) GetBaseResp() (v *base.BaseResp) {
var fieldIDToName_CreateChatFlowRoleResponse = map[int16]string{
1: "ID",
253: "code",
254: "msg",
255: "BaseResp",
}
@ -64338,6 +64380,8 @@ func (p *CreateChatFlowRoleResponse) IsSetBaseResp() bool {
func (p *CreateChatFlowRoleResponse) Read(iprot thrift.TProtocol) (err error) {
var fieldTypeId thrift.TType
var fieldId int16
var issetCode bool = false
var issetMsg bool = false
var issetBaseResp bool = false
if _, err = iprot.ReadStructBegin(); err != nil {
@ -64362,6 +64406,24 @@ func (p *CreateChatFlowRoleResponse) Read(iprot thrift.TProtocol) (err error) {
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
case 253:
if fieldTypeId == thrift.I64 {
if err = p.ReadField253(iprot); err != nil {
goto ReadFieldError
}
issetCode = true
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
case 254:
if fieldTypeId == thrift.STRING {
if err = p.ReadField254(iprot); err != nil {
goto ReadFieldError
}
issetMsg = true
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
case 255:
if fieldTypeId == thrift.STRUCT {
if err = p.ReadField255(iprot); err != nil {
@ -64384,6 +64446,16 @@ func (p *CreateChatFlowRoleResponse) Read(iprot thrift.TProtocol) (err error) {
goto ReadStructEndError
}
if !issetCode {
fieldId = 253
goto RequiredFieldNotSetError
}
if !issetMsg {
fieldId = 254
goto RequiredFieldNotSetError
}
if !issetBaseResp {
fieldId = 255
goto RequiredFieldNotSetError
@ -64417,6 +64489,28 @@ func (p *CreateChatFlowRoleResponse) ReadField1(iprot thrift.TProtocol) error {
p.ID = _field
return nil
}
func (p *CreateChatFlowRoleResponse) ReadField253(iprot thrift.TProtocol) error {
var _field int64
if v, err := iprot.ReadI64(); err != nil {
return err
} else {
_field = v
}
p.Code = _field
return nil
}
func (p *CreateChatFlowRoleResponse) ReadField254(iprot thrift.TProtocol) error {
var _field string
if v, err := iprot.ReadString(); err != nil {
return err
} else {
_field = v
}
p.Msg = _field
return nil
}
func (p *CreateChatFlowRoleResponse) ReadField255(iprot thrift.TProtocol) error {
_field := base.NewBaseResp()
if err := _field.Read(iprot); err != nil {
@ -64436,6 +64530,14 @@ func (p *CreateChatFlowRoleResponse) Write(oprot thrift.TProtocol) (err error) {
fieldId = 1
goto WriteFieldError
}
if err = p.writeField253(oprot); err != nil {
fieldId = 253
goto WriteFieldError
}
if err = p.writeField254(oprot); err != nil {
fieldId = 254
goto WriteFieldError
}
if err = p.writeField255(oprot); err != nil {
fieldId = 255
goto WriteFieldError
@ -64474,6 +64576,38 @@ WriteFieldBeginError:
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 1 end error: ", p), err)
}
func (p *CreateChatFlowRoleResponse) writeField253(oprot thrift.TProtocol) (err error) {
if err = oprot.WriteFieldBegin("code", thrift.I64, 253); err != nil {
goto WriteFieldBeginError
}
if err := oprot.WriteI64(p.Code); err != nil {
return err
}
if err = oprot.WriteFieldEnd(); err != nil {
goto WriteFieldEndError
}
return nil
WriteFieldBeginError:
return thrift.PrependError(fmt.Sprintf("%T write field 253 begin error: ", p), err)
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 253 end error: ", p), err)
}
func (p *CreateChatFlowRoleResponse) writeField254(oprot thrift.TProtocol) (err error) {
if err = oprot.WriteFieldBegin("msg", thrift.STRING, 254); err != nil {
goto WriteFieldBeginError
}
if err := oprot.WriteString(p.Msg); err != nil {
return err
}
if err = oprot.WriteFieldEnd(); err != nil {
goto WriteFieldEndError
}
return nil
WriteFieldBeginError:
return thrift.PrependError(fmt.Sprintf("%T write field 254 begin error: ", p), err)
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 254 end error: ", p), err)
}
func (p *CreateChatFlowRoleResponse) writeField255(oprot thrift.TProtocol) (err error) {
if err = oprot.WriteFieldBegin("BaseResp", thrift.STRUCT, 255); err != nil {
goto WriteFieldBeginError
@ -64500,10 +64634,10 @@ func (p *CreateChatFlowRoleResponse) String() string {
}
type DeleteChatFlowRoleRequest struct {
WorkflowID string `thrift:"WorkflowID,1" form:"WorkflowID" json:"WorkflowID" query:"WorkflowID"`
ConnectorID string `thrift:"ConnectorID,2" form:"ConnectorID" json:"ConnectorID" query:"ConnectorID"`
WorkflowID string `thrift:"WorkflowID,1" json:"workflow_id" query:"workflow_id" `
ConnectorID string `thrift:"ConnectorID,2" json:"connector_id" query:"connector_id" `
// 数据库中ID
ID string `thrift:"ID,4" form:"ID" json:"ID" query:"ID"`
ID string `thrift:"ID,4" json:"id" query:"id" `
Base *base.Base `thrift:"Base,255,optional" form:"Base" json:"Base,omitempty" query:"Base"`
}
@ -64936,12 +65070,12 @@ func (p *DeleteChatFlowRoleResponse) String() string {
}
type GetChatFlowRoleRequest struct {
WorkflowID string `thrift:"WorkflowID,1" form:"WorkflowID" json:"WorkflowID" query:"WorkflowID"`
ConnectorID string `thrift:"ConnectorID,2" form:"ConnectorID" json:"ConnectorID" query:"ConnectorID"`
IsDebug bool `thrift:"IsDebug,3" form:"IsDebug" json:"IsDebug" query:"IsDebug"`
WorkflowID string `thrift:"WorkflowID,1" json:"workflow_id" query:"workflow_id" `
ConnectorID string `thrift:"ConnectorID,2" json:"connector_id" query:"connector_id" `
IsDebug bool `thrift:"IsDebug,3" json:"is_debug" query:"is_debug" `
// 4: optional string AppID (api.query = "app_id")
Ext map[string]string `thrift:"Ext,5,optional" json:"Ext,omitempty" query:"ext"`
Base *base.Base `thrift:"Base,255,optional" form:"Base" json:"Base,omitempty" query:"Base"`
Base *base.Base `thrift:"Base,255,optional" json:"base" query:"base" `
}
func NewGetChatFlowRoleRequest() *GetChatFlowRoleRequest {
@ -65304,8 +65438,10 @@ func (p *GetChatFlowRoleRequest) String() string {
}
type GetChatFlowRoleResponse struct {
Role *ChatFlowRole `thrift:"Role,1,optional" form:"Role" json:"Role,omitempty" query:"Role"`
BaseResp *base.BaseResp `thrift:"BaseResp,255,required" form:"BaseResp,required" json:"BaseResp,required" query:"BaseResp,required"`
Code int64 `thrift:"code,1,required" form:"code,required" json:"code,required" query:"code,required"`
Msg string `thrift:"msg,2,required" form:"msg,required" json:"msg,required" query:"msg,required"`
Role *ChatFlowRole `thrift:"Role,3,optional" json:"role" query:"role" `
BaseResp *base.BaseResp `thrift:"BaseResp,255,required" json:"base_resp" query:"base_resp,required" `
}
func NewGetChatFlowRoleResponse() *GetChatFlowRoleResponse {
@ -65315,6 +65451,14 @@ func NewGetChatFlowRoleResponse() *GetChatFlowRoleResponse {
func (p *GetChatFlowRoleResponse) InitDefault() {
}
func (p *GetChatFlowRoleResponse) GetCode() (v int64) {
return p.Code
}
func (p *GetChatFlowRoleResponse) GetMsg() (v string) {
return p.Msg
}
var GetChatFlowRoleResponse_Role_DEFAULT *ChatFlowRole
func (p *GetChatFlowRoleResponse) GetRole() (v *ChatFlowRole) {
@ -65334,7 +65478,9 @@ func (p *GetChatFlowRoleResponse) GetBaseResp() (v *base.BaseResp) {
}
var fieldIDToName_GetChatFlowRoleResponse = map[int16]string{
1: "Role",
1: "code",
2: "msg",
3: "Role",
255: "BaseResp",
}
@ -65349,6 +65495,8 @@ func (p *GetChatFlowRoleResponse) IsSetBaseResp() bool {
func (p *GetChatFlowRoleResponse) Read(iprot thrift.TProtocol) (err error) {
var fieldTypeId thrift.TType
var fieldId int16
var issetCode bool = false
var issetMsg bool = false
var issetBaseResp bool = false
if _, err = iprot.ReadStructBegin(); err != nil {
@ -65366,10 +65514,28 @@ func (p *GetChatFlowRoleResponse) Read(iprot thrift.TProtocol) (err error) {
switch fieldId {
case 1:
if fieldTypeId == thrift.STRUCT {
if fieldTypeId == thrift.I64 {
if err = p.ReadField1(iprot); err != nil {
goto ReadFieldError
}
issetCode = true
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
case 2:
if fieldTypeId == thrift.STRING {
if err = p.ReadField2(iprot); err != nil {
goto ReadFieldError
}
issetMsg = true
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
case 3:
if fieldTypeId == thrift.STRUCT {
if err = p.ReadField3(iprot); err != nil {
goto ReadFieldError
}
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
@ -65395,6 +65561,16 @@ func (p *GetChatFlowRoleResponse) Read(iprot thrift.TProtocol) (err error) {
goto ReadStructEndError
}
if !issetCode {
fieldId = 1
goto RequiredFieldNotSetError
}
if !issetMsg {
fieldId = 2
goto RequiredFieldNotSetError
}
if !issetBaseResp {
fieldId = 255
goto RequiredFieldNotSetError
@ -65418,6 +65594,28 @@ RequiredFieldNotSetError:
}
func (p *GetChatFlowRoleResponse) ReadField1(iprot thrift.TProtocol) error {
var _field int64
if v, err := iprot.ReadI64(); err != nil {
return err
} else {
_field = v
}
p.Code = _field
return nil
}
func (p *GetChatFlowRoleResponse) ReadField2(iprot thrift.TProtocol) error {
var _field string
if v, err := iprot.ReadString(); err != nil {
return err
} else {
_field = v
}
p.Msg = _field
return nil
}
func (p *GetChatFlowRoleResponse) ReadField3(iprot thrift.TProtocol) error {
_field := NewChatFlowRole()
if err := _field.Read(iprot); err != nil {
return err
@ -65444,6 +65642,14 @@ func (p *GetChatFlowRoleResponse) Write(oprot thrift.TProtocol) (err error) {
fieldId = 1
goto WriteFieldError
}
if err = p.writeField2(oprot); err != nil {
fieldId = 2
goto WriteFieldError
}
if err = p.writeField3(oprot); err != nil {
fieldId = 3
goto WriteFieldError
}
if err = p.writeField255(oprot); err != nil {
fieldId = 255
goto WriteFieldError
@ -65467,8 +65673,40 @@ WriteStructEndError:
}
func (p *GetChatFlowRoleResponse) writeField1(oprot thrift.TProtocol) (err error) {
if err = oprot.WriteFieldBegin("code", thrift.I64, 1); err != nil {
goto WriteFieldBeginError
}
if err := oprot.WriteI64(p.Code); err != nil {
return err
}
if err = oprot.WriteFieldEnd(); err != nil {
goto WriteFieldEndError
}
return nil
WriteFieldBeginError:
return thrift.PrependError(fmt.Sprintf("%T write field 1 begin error: ", p), err)
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 1 end error: ", p), err)
}
func (p *GetChatFlowRoleResponse) writeField2(oprot thrift.TProtocol) (err error) {
if err = oprot.WriteFieldBegin("msg", thrift.STRING, 2); err != nil {
goto WriteFieldBeginError
}
if err := oprot.WriteString(p.Msg); err != nil {
return err
}
if err = oprot.WriteFieldEnd(); err != nil {
goto WriteFieldEndError
}
return nil
WriteFieldBeginError:
return thrift.PrependError(fmt.Sprintf("%T write field 2 begin error: ", p), err)
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 2 end error: ", p), err)
}
func (p *GetChatFlowRoleResponse) writeField3(oprot thrift.TProtocol) (err error) {
if p.IsSetRole() {
if err = oprot.WriteFieldBegin("Role", thrift.STRUCT, 1); err != nil {
if err = oprot.WriteFieldBegin("Role", thrift.STRUCT, 3); err != nil {
goto WriteFieldBeginError
}
if err := p.Role.Write(oprot); err != nil {
@ -65480,9 +65718,9 @@ func (p *GetChatFlowRoleResponse) writeField1(oprot thrift.TProtocol) (err error
}
return nil
WriteFieldBeginError:
return thrift.PrependError(fmt.Sprintf("%T write field 1 begin error: ", p), err)
return thrift.PrependError(fmt.Sprintf("%T write field 3 begin error: ", p), err)
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 1 end error: ", p), err)
return thrift.PrependError(fmt.Sprintf("%T write field 3 end error: ", p), err)
}
func (p *GetChatFlowRoleResponse) writeField255(oprot thrift.TProtocol) (err error) {
if err = oprot.WriteFieldBegin("BaseResp", thrift.STRUCT, 255); err != nil {

View File

@ -190,11 +190,8 @@ func initPrimaryServices(ctx context.Context, basicServices *basicServices) (*pr
return nil, err
}
workflowDomainSVC, err := workflow.InitService(ctx,
workflowDomainSVC := workflow.InitService(
basicServices.toWorkflowServiceComponents(pluginSVC, memorySVC, knowledgeSVC))
if err != nil {
return nil, err
}
shortcutSVC := shortcutcmd.InitService(basicServices.infra.DB, basicServices.infra.IDGenSVC)

View File

@ -88,15 +88,6 @@ func APIParamsToOpenapiOperation(reqParams, respParams []*common.APIParameter) (
}
}
if reqParams != nil {
if !hasSetParams {
op.Parameters = []*openapi3.ParameterRef{}
}
if !hasSetReqBody {
op.RequestBody = entity.DefaultOpenapi3RequestBody()
}
}
hasSetRespBody := false
for _, apiParam := range respParams {
@ -136,7 +127,13 @@ func APIParamsToOpenapiOperation(reqParams, respParams []*common.APIParameter) (
}
}
if respParams != nil && !hasSetRespBody {
if op.Parameters == nil {
op.Parameters = []*openapi3.ParameterRef{}
}
if op.RequestBody == nil {
op.RequestBody = entity.DefaultOpenapi3RequestBody()
}
if op.Responses == nil {
op.Responses = entity.DefaultOpenapi3Responses()
}

View File

@ -455,7 +455,7 @@ func (c *ConversationApplicationService) parseMultiContent(ctx context.Context,
mc[index].File.FileURL = resourceUrl
multiContents = append(multiContents, &crossDomainMessage.InputMetaData{
Type: c.getType(item.File.FileType),
Type: crossDomainMessage.InputType(item.Type),
FileData: []*crossDomainMessage.FileData{
{
Url: resourceUrl,
@ -468,20 +468,10 @@ func (c *ConversationApplicationService) parseMultiContent(ctx context.Context,
return multiContents, mc
}
func (c *ConversationApplicationService) getType(fileType string) crossDomainMessage.InputType {
switch fileType {
case string(crossDomainMessage.InputTypeAudio):
return crossDomainMessage.InputTypeAudio
case string(crossDomainMessage.InputTypeVideo):
return crossDomainMessage.InputTypeVideo
default:
return crossDomainMessage.InputTypeFile
}
}
func (c *ConversationApplicationService) getUrlByUri(ctx context.Context, uri string) (string, error) {
func (s *ConversationApplicationService) getUrlByUri(ctx context.Context, uri string) (string, error) {
url, err := c.appContext.ImageX.GetResourceURL(ctx, uri)
url, err := s.appContext.ImageX.GetResourceURL(ctx, uri)
if err != nil {
return "", err
}

View File

@ -1,109 +0,0 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package internal
import (
"context"
"fmt"
"os"
"strconv"
ao "github.com/cloudwego/eino-ext/components/model/ark"
"github.com/cloudwego/eino-ext/components/model/deepseek"
"github.com/cloudwego/eino-ext/components/model/gemini"
"github.com/cloudwego/eino-ext/components/model/ollama"
mo "github.com/cloudwego/eino-ext/components/model/openai"
"github.com/cloudwego/eino-ext/components/model/qwen"
"google.golang.org/genai"
"github.com/coze-dev/coze-studio/backend/infra/contract/chatmodel"
)
func GetBuiltinChatModel(ctx context.Context, envPrefix string) (bcm chatmodel.BaseChatModel, configured bool, err error) {
getEnv := func(key string) string {
if val := os.Getenv(envPrefix + key); val != "" {
return val
}
return os.Getenv(key)
}
switch getEnv("BUILTIN_CM_TYPE") {
case "openai":
byAzure, _ := strconv.ParseBool(getEnv("BUILTIN_CM_OPENAI_BY_AZURE"))
bcm, err = mo.NewChatModel(ctx, &mo.ChatModelConfig{
APIKey: getEnv("BUILTIN_CM_OPENAI_API_KEY"),
ByAzure: byAzure,
BaseURL: getEnv("BUILTIN_CM_OPENAI_BASE_URL"),
Model: getEnv("BUILTIN_CM_OPENAI_MODEL"),
})
case "ark":
bcm, err = ao.NewChatModel(ctx, &ao.ChatModelConfig{
APIKey: getEnv("BUILTIN_CM_ARK_API_KEY"),
Model: getEnv("BUILTIN_CM_ARK_MODEL"),
BaseURL: getEnv("BUILTIN_CM_ARK_BASE_URL"),
})
case "deepseek":
bcm, err = deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
APIKey: getEnv("BUILTIN_CM_DEEPSEEK_API_KEY"),
BaseURL: getEnv("BUILTIN_CM_DEEPSEEK_BASE_URL"),
Model: getEnv("BUILTIN_CM_DEEPSEEK_MODEL"),
})
case "ollama":
bcm, err = ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
BaseURL: getEnv("BUILTIN_CM_OLLAMA_BASE_URL"),
Model: getEnv("BUILTIN_CM_OLLAMA_MODEL"),
})
case "qwen":
bcm, err = qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
APIKey: getEnv("BUILTIN_CM_QWEN_API_KEY"),
BaseURL: getEnv("BUILTIN_CM_QWEN_BASE_URL"),
Model: getEnv("BUILTIN_CM_QWEN_MODEL"),
})
case "gemini":
backend, convErr := strconv.ParseInt(getEnv("BUILTIN_CM_GEMINI_BACKEND"), 10, 64)
if convErr != nil {
return nil, false, convErr
}
c, clientErr := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: getEnv("BUILTIN_CM_GEMINI_API_KEY"),
Backend: genai.Backend(backend),
Project: getEnv("BUILTIN_CM_GEMINI_PROJECT"),
Location: getEnv("BUILTIN_CM_GEMINI_LOCATION"),
HTTPOptions: genai.HTTPOptions{
BaseURL: getEnv("BUILTIN_CM_GEMINI_BASE_URL"),
},
})
if clientErr != nil {
return nil, false, clientErr
}
bcm, err = gemini.NewChatModel(ctx, &gemini.Config{
Client: c,
Model: getEnv("BUILTIN_CM_GEMINI_MODEL"),
})
default:
// accept builtin chat model not configured
}
if err != nil {
return nil, false, fmt.Errorf("knowledge init openai chat mode failed, %w", err)
}
if bcm != nil {
configured = true
}
return
}

View File

@ -28,17 +28,25 @@ import (
"github.com/cloudwego/eino-ext/components/embedding/ark"
ollamaEmb "github.com/cloudwego/eino-ext/components/embedding/ollama"
"github.com/cloudwego/eino-ext/components/embedding/openai"
ao "github.com/cloudwego/eino-ext/components/model/ark"
"github.com/cloudwego/eino-ext/components/model/deepseek"
"github.com/cloudwego/eino-ext/components/model/gemini"
"github.com/cloudwego/eino-ext/components/model/ollama"
mo "github.com/cloudwego/eino-ext/components/model/openai"
"github.com/cloudwego/eino-ext/components/model/qwen"
"github.com/cloudwego/eino/components/prompt"
"github.com/cloudwego/eino/schema"
"github.com/coze-dev/coze-studio/backend/infra/impl/embedding/http"
"github.com/milvus-io/milvus/client/v2/milvusclient"
"github.com/volcengine/volc-sdk-golang/service/vikingdb"
"github.com/volcengine/volc-sdk-golang/service/visual"
"google.golang.org/genai"
"gorm.io/gorm"
"github.com/coze-dev/coze-studio/backend/application/internal"
"github.com/coze-dev/coze-studio/backend/application/search"
knowledgeImpl "github.com/coze-dev/coze-studio/backend/domain/knowledge/service"
"github.com/coze-dev/coze-studio/backend/infra/contract/cache"
"github.com/coze-dev/coze-studio/backend/infra/contract/chatmodel"
"github.com/coze-dev/coze-studio/backend/infra/contract/document/nl2sql"
"github.com/coze-dev/coze-studio/backend/infra/contract/document/ocr"
"github.com/coze-dev/coze-studio/backend/infra/contract/document/searchstore"
@ -58,7 +66,6 @@ import (
ssmilvus "github.com/coze-dev/coze-studio/backend/infra/impl/document/searchstore/milvus"
ssvikingdb "github.com/coze-dev/coze-studio/backend/infra/impl/document/searchstore/vikingdb"
arkemb "github.com/coze-dev/coze-studio/backend/infra/impl/embedding/ark"
"github.com/coze-dev/coze-studio/backend/infra/impl/embedding/http"
"github.com/coze-dev/coze-studio/backend/infra/impl/embedding/wrap"
"github.com/coze-dev/coze-studio/backend/infra/impl/eventbus"
builtinM2Q "github.com/coze-dev/coze-studio/backend/infra/impl/messages2query/builtin"
@ -124,7 +131,7 @@ func InitService(c *ServiceComponents) (*KnowledgeApplicationService, error) {
}
var rewriter messages2query.MessagesToQuery
if rewriterChatModel, _, err := internal.GetBuiltinChatModel(ctx, "M2Q_"); err != nil {
if rewriterChatModel, _, err := getBuiltinChatModel(ctx, "M2Q_"); err != nil {
return nil, err
} else {
filePath := filepath.Join(root, "resources/conf/prompt/messages_to_query_template_jinja2.json")
@ -139,7 +146,7 @@ func InitService(c *ServiceComponents) (*KnowledgeApplicationService, error) {
}
var n2s nl2sql.NL2SQL
if n2sChatModel, _, err := internal.GetBuiltinChatModel(ctx, "NL2SQL_"); err != nil {
if n2sChatModel, _, err := getBuiltinChatModel(ctx, "NL2SQL_"); err != nil {
return nil, err
} else {
filePath := filepath.Join(root, "resources/conf/prompt/nl2sql_template_jinja2.json")
@ -153,7 +160,7 @@ func InitService(c *ServiceComponents) (*KnowledgeApplicationService, error) {
}
}
imageAnnoChatModel, configured, err := internal.GetBuiltinChatModel(ctx, "IA_")
imageAnnoChatModel, configured, err := getBuiltinChatModel(ctx, "IA_")
if err != nil {
return nil, err
}
@ -280,14 +287,6 @@ func getVectorStore(ctx context.Context) (searchstore.Manager, error) {
}
func getEmbedding(ctx context.Context) (embedding.Embedder, error) {
var batchSize int
if bs, err := strconv.ParseInt(os.Getenv("EMBEDDING_MAX_BATCH_SIZE"), 10, 64); err != nil {
logs.CtxWarnf(ctx, "EMBEDDING_MAX_BATCH_SIZE not set / invalid, using default batchSize=100")
batchSize = 100
} else {
batchSize = int(bs)
}
var emb embedding.Embedder
switch os.Getenv("EMBEDDING_TYPE") {
@ -326,7 +325,7 @@ func getEmbedding(ctx context.Context) (embedding.Embedder, error) {
openAICfg.Dimensions = ptr.Of(int(reqDims))
}
emb, err = wrap.NewOpenAIEmbedder(ctx, openAICfg, dims, batchSize)
emb, err = wrap.NewOpenAIEmbedder(ctx, openAICfg, dims)
if err != nil {
return nil, fmt.Errorf("init openai embedding failed, err=%w", err)
}
@ -348,7 +347,7 @@ func getEmbedding(ctx context.Context) (embedding.Embedder, error) {
APIKey: arkEmbeddingAK,
Model: arkEmbeddingModel,
BaseURL: arkEmbeddingBaseURL,
}, dims, batchSize)
}, dims)
if err != nil {
return nil, fmt.Errorf("init ark embedding client failed, err=%w", err)
}
@ -368,11 +367,12 @@ func getEmbedding(ctx context.Context) (embedding.Embedder, error) {
emb, err = wrap.NewOllamaEmbedder(ctx, &ollamaEmb.EmbeddingConfig{
BaseURL: ollamaEmbeddingBaseURL,
Model: ollamaEmbeddingModel,
}, dims, batchSize)
}, dims)
if err != nil {
return nil, fmt.Errorf("init ollama embedding failed, err=%w", err)
}
case "http":
var (
httpEmbeddingBaseURL = os.Getenv("HTTP_EMBEDDING_ADDR")
@ -382,7 +382,7 @@ func getEmbedding(ctx context.Context) (embedding.Embedder, error) {
if err != nil {
return nil, fmt.Errorf("init http embedding dims failed, err=%w", err)
}
emb, err = http.NewEmbedding(httpEmbeddingBaseURL, dims, batchSize)
emb, err = http.NewEmbedding(httpEmbeddingBaseURL, dims)
if err != nil {
return nil, fmt.Errorf("init http embedding failed, err=%w", err)
}
@ -394,6 +394,81 @@ func getEmbedding(ctx context.Context) (embedding.Embedder, error) {
return emb, nil
}
func getBuiltinChatModel(ctx context.Context, envPrefix string) (bcm chatmodel.BaseChatModel, configured bool, err error) {
getEnv := func(key string) string {
if val := os.Getenv(envPrefix + key); val != "" {
return val
}
return os.Getenv(key)
}
switch getEnv("BUILTIN_CM_TYPE") {
case "openai":
byAzure, _ := strconv.ParseBool(getEnv("BUILTIN_CM_OPENAI_BY_AZURE"))
bcm, err = mo.NewChatModel(ctx, &mo.ChatModelConfig{
APIKey: getEnv("BUILTIN_CM_OPENAI_API_KEY"),
ByAzure: byAzure,
BaseURL: getEnv("BUILTIN_CM_OPENAI_BASE_URL"),
Model: getEnv("BUILTIN_CM_OPENAI_MODEL"),
})
case "ark":
bcm, err = ao.NewChatModel(ctx, &ao.ChatModelConfig{
APIKey: getEnv("BUILTIN_CM_ARK_API_KEY"),
Model: getEnv("BUILTIN_CM_ARK_MODEL"),
BaseURL: getEnv("BUILTIN_CM_ARK_BASE_URL"),
})
case "deepseek":
bcm, err = deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{
APIKey: getEnv("BUILTIN_CM_DEEPSEEK_API_KEY"),
BaseURL: getEnv("BUILTIN_CM_DEEPSEEK_BASE_URL"),
Model: getEnv("BUILTIN_CM_DEEPSEEK_MODEL"),
})
case "ollama":
bcm, err = ollama.NewChatModel(ctx, &ollama.ChatModelConfig{
BaseURL: getEnv("BUILTIN_CM_OLLAMA_BASE_URL"),
Model: getEnv("BUILTIN_CM_OLLAMA_MODEL"),
})
case "qwen":
bcm, err = qwen.NewChatModel(ctx, &qwen.ChatModelConfig{
APIKey: getEnv("BUILTIN_CM_QWEN_API_KEY"),
BaseURL: getEnv("BUILTIN_CM_QWEN_BASE_URL"),
Model: getEnv("BUILTIN_CM_QWEN_MODEL"),
})
case "gemini":
backend, convErr := strconv.ParseInt(getEnv("BUILTIN_CM_GEMINI_BACKEND"), 10, 64)
if convErr != nil {
return nil, false, convErr
}
c, clientErr := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: getEnv("BUILTIN_CM_GEMINI_API_KEY"),
Backend: genai.Backend(backend),
Project: getEnv("BUILTIN_CM_GEMINI_PROJECT"),
Location: getEnv("BUILTIN_CM_GEMINI_LOCATION"),
HTTPOptions: genai.HTTPOptions{
BaseURL: getEnv("BUILTIN_CM_GEMINI_BASE_URL"),
},
})
if clientErr != nil {
return nil, false, clientErr
}
bcm, err = gemini.NewChatModel(ctx, &gemini.Config{
Client: c,
Model: getEnv("BUILTIN_CM_GEMINI_MODEL"),
})
default:
// accept builtin chat model not configured
}
if err != nil {
return nil, false, fmt.Errorf("knowledge init openai chat mode failed, %w", err)
}
if bcm != nil {
configured = true
}
return
}
func readJinja2PromptTemplate(jsonFilePath string) (prompt.ChatTemplate, error) {
b, err := os.ReadFile(jsonFilePath)
if err != nil {

View File

@ -106,7 +106,7 @@ func modelDo2To(model *modelmgr.Model, locale i18n.Locale) (*developer_api.Model
PriceOut: 0,
SystemPromptLimit: nil,
},
ModelName: model.Name,
ModelName: mm.Name,
ModelClassName: mm.Protocol.TOModelClass().String(),
IsOffline: mm.Status != modelmgr.StatusInUse,
ModelParams: mps,

View File

@ -822,7 +822,7 @@ func (p *PluginApplicationService) UpdateAPI(ctx context.Context, req *pluginAPI
method = &m
}
updateReq := &service.UpdateDraftToolRequest{
updateReq := &service.UpdateToolDraftRequest{
PluginID: req.PluginID,
ToolID: req.APIID,
Name: req.Name,

View File

@ -259,7 +259,7 @@ func toModelDetail(m *modelmgr.Model) *playground.ModelDetail {
return &playground.ModelDetail{
Name: ptr.Of(m.Name),
ModelName: ptr.Of(m.Name),
ModelName: ptr.Of(m.Meta.Name),
ModelID: ptr.Of(m.ID),
ModelFamily: ptr.Of(int64(mm.Protocol.TOModelClass())),
ModelIconURL: ptr.Of(m.IconURL),

View File

@ -0,0 +1,264 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package workflow
import (
"context"
"fmt"
"github.com/coze-dev/coze-studio/backend/types/consts"
"runtime/debug"
"strconv"
"github.com/coze-dev/coze-studio/backend/api/model/ocean/cloud/workflow"
"github.com/coze-dev/coze-studio/backend/application/base/ctxutil"
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity"
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
"github.com/coze-dev/coze-studio/backend/pkg/lang/maps"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
"github.com/coze-dev/coze-studio/backend/pkg/lang/slices"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ternary"
"github.com/coze-dev/coze-studio/backend/pkg/safego"
"github.com/coze-dev/coze-studio/backend/types/errno"
)
func (w *ApplicationService) CreateApplicationConversationDef(ctx context.Context, req *workflow.CreateProjectConversationDefRequest) (resp *workflow.CreateProjectConversationDefResponse, err error) {
defer func() {
if panicErr := recover(); panicErr != nil {
err = safego.NewPanicErr(panicErr, debug.Stack())
}
if err != nil {
err = vo.WrapIfNeeded(errno.ErrConversationOfAppOperationFail, err, errorx.KV("cause", vo.UnwrapRootErr(err).Error()))
}
}()
var (
spaceID = mustParseInt64(req.GetSpaceID())
appID = mustParseInt64(req.GetProjectID())
userID = ctxutil.MustGetUIDFromCtx(ctx)
)
if err := checkUserSpace(ctx, userID, spaceID); err != nil {
return nil, err
}
uniqueID, err := GetWorkflowDomainSVC().CreateDraftConversationTemplate(ctx, &vo.CreateConversationTemplateMeta{
AppID: appID,
SpaceID: spaceID,
Name: req.GetConversationName(),
UserID: userID,
})
if err != nil {
return nil, err
}
return &workflow.CreateProjectConversationDefResponse{
UniqueID: strconv.FormatInt(uniqueID, 10),
SpaceID: req.GetSpaceID(),
}, err
}
func (w *ApplicationService) UpdateApplicationConversationDef(ctx context.Context, req *workflow.UpdateProjectConversationDefRequest) (resp *workflow.UpdateProjectConversationDefResponse, err error) {
defer func() {
if panicErr := recover(); panicErr != nil {
err = safego.NewPanicErr(panicErr, debug.Stack())
}
if err != nil {
err = vo.WrapIfNeeded(errno.ErrConversationOfAppOperationFail, err, errorx.KV("cause", vo.UnwrapRootErr(err).Error()))
}
}()
var (
spaceID = mustParseInt64(req.GetSpaceID())
templateID = mustParseInt64(req.GetUniqueID())
appID = mustParseInt64(req.GetProjectID())
userID = ctxutil.MustGetUIDFromCtx(ctx)
)
if err := checkUserSpace(ctx, userID, spaceID); err != nil {
return nil, err
}
err = GetWorkflowDomainSVC().UpdateDraftConversationTemplateName(ctx, appID, userID, templateID, req.GetConversationName())
if err != nil {
return nil, err
}
return &workflow.UpdateProjectConversationDefResponse{}, err
}
func (w *ApplicationService) DeleteApplicationConversationDef(ctx context.Context, req *workflow.DeleteProjectConversationDefRequest) (resp *workflow.DeleteProjectConversationDefResponse, err error) {
defer func() {
if panicErr := recover(); panicErr != nil {
err = safego.NewPanicErr(panicErr, debug.Stack())
}
if err != nil {
err = vo.WrapIfNeeded(errno.ErrConversationOfAppOperationFail, err, errorx.KV("cause", vo.UnwrapRootErr(err).Error()))
}
}()
var (
appID = mustParseInt64(req.GetProjectID())
templateID = mustParseInt64(req.GetUniqueID())
)
if err := checkUserSpace(ctx, ctxutil.MustGetUIDFromCtx(ctx), mustParseInt64(req.GetSpaceID())); err != nil {
return nil, err
}
if req.GetCheckOnly() {
wfs, err := GetWorkflowDomainSVC().CheckWorkflowsToReplace(ctx, appID, templateID)
if err != nil {
return nil, err
}
resp = &workflow.DeleteProjectConversationDefResponse{NeedReplace: make([]*workflow.Workflow, 0)}
for _, wf := range wfs {
resp.NeedReplace = append(resp.NeedReplace, &workflow.Workflow{
Name: wf.Name,
URL: wf.IconURL,
WorkflowID: strconv.FormatInt(wf.ID, 10),
})
}
return resp, nil
}
wfID2ConversationName, err := maps.TransformKeyWithErrorCheck(req.GetReplace(), func(k1 string) (int64, error) {
return strconv.ParseInt(k1, 10, 64)
})
rowsAffected, err := GetWorkflowDomainSVC().DeleteDraftConversationTemplate(ctx, templateID, wfID2ConversationName)
if err != nil {
return nil, err
}
if rowsAffected > 0 {
return &workflow.DeleteProjectConversationDefResponse{
Success: true,
}, err
}
rowsAffected, err = GetWorkflowDomainSVC().DeleteDynamicConversation(ctx, vo.Draft, templateID)
if err != nil {
return nil, err
}
if rowsAffected == 0 {
return nil, fmt.Errorf("delete conversation failed")
}
return &workflow.DeleteProjectConversationDefResponse{
Success: true,
}, nil
}
func (w *ApplicationService) ListApplicationConversationDef(ctx context.Context, req *workflow.ListProjectConversationRequest) (resp *workflow.ListProjectConversationResponse, err error) {
defer func() {
if panicErr := recover(); panicErr != nil {
err = safego.NewPanicErr(panicErr, debug.Stack())
}
if err != nil {
err = vo.WrapIfNeeded(errno.ErrConversationOfAppOperationFail, err, errorx.KV("cause", vo.UnwrapRootErr(err).Error()))
}
}()
var connectorID int64
if len(req.GetConnectorID()) != 0 {
connectorID = mustParseInt64(req.GetConnectorID())
} else {
connectorID = consts.CozeConnectorID
}
var (
page = mustParseInt64(ternary.IFElse(req.GetCursor() == "", "0", req.GetCursor()))
size = req.GetLimit()
userID = ctxutil.MustGetUIDFromCtx(ctx)
spaceID = mustParseInt64(req.GetSpaceID())
appID = mustParseInt64(req.GetProjectID())
version = req.ProjectVersion
listConversationMeta = vo.ListConversationMeta{
APPID: appID,
UserID: userID,
ConnectorID: connectorID,
}
)
if err := checkUserSpace(ctx, userID, spaceID); err != nil {
return nil, err
}
env := ternary.IFElse(req.GetCreateEnv() == workflow.CreateEnv_Draft, vo.Draft, vo.Online)
if req.GetCreateMethod() == workflow.CreateMethod_ManualCreate {
templates, err := GetWorkflowDomainSVC().ListConversationTemplate(ctx, env, &vo.ListConversationTemplatePolicy{
AppID: appID,
Page: &vo.Page{
Page: int32(page),
Size: int32(size),
},
NameLike: ternary.IFElse(len(req.GetNameLike()) == 0, nil, ptr.Of(req.GetNameLike())),
Version: version,
})
if err != nil {
return nil, err
}
stsConversations, err := GetWorkflowDomainSVC().MGetStaticConversation(ctx, env, userID, connectorID, slices.Transform(templates, func(a *entity.ConversationTemplate) int64 {
return a.TemplateID
}))
if err != nil {
return nil, err
}
stsConversationMap := slices.ToMap(stsConversations, func(e *entity.StaticConversation) (int64, *entity.StaticConversation) {
return e.TemplateID, e
})
resp = &workflow.ListProjectConversationResponse{Data: make([]*workflow.ProjectConversation, 0)}
for _, tmpl := range templates {
conversationID := ""
if c, ok := stsConversationMap[tmpl.TemplateID]; ok {
conversationID = strconv.FormatInt(c.ConversationID, 10)
}
resp.Data = append(resp.Data, &workflow.ProjectConversation{
UniqueID: strconv.FormatInt(tmpl.TemplateID, 10),
ConversationName: tmpl.Name,
ConversationID: conversationID,
})
}
}
if req.GetCreateMethod() == workflow.CreateMethod_NodeCreate {
dyConversations, err := GetWorkflowDomainSVC().ListDynamicConversation(ctx, env, &vo.ListConversationPolicy{
ListConversationMeta: listConversationMeta,
Page: &vo.Page{
Page: int32(page),
Size: int32(size),
},
NameLike: ternary.IFElse(len(req.GetNameLike()) == 0, nil, ptr.Of(req.GetNameLike())),
})
if err != nil {
return nil, err
}
resp = &workflow.ListProjectConversationResponse{Data: make([]*workflow.ProjectConversation, 0, len(dyConversations))}
resp.Data = append(resp.Data, slices.Transform(dyConversations, func(a *entity.DynamicConversation) *workflow.ProjectConversation {
return &workflow.ProjectConversation{
UniqueID: strconv.FormatInt(a.ID, 10),
ConversationName: a.Name,
ConversationID: strconv.FormatInt(a.ConversationID, 10),
}
})...)
}
return resp, nil
}

View File

@ -17,15 +17,12 @@
package workflow
import (
"context"
"github.com/cloudwego/eino/callbacks"
"github.com/cloudwego/eino/compose"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
"github.com/coze-dev/coze-studio/backend/application/internal"
"github.com/coze-dev/coze-studio/backend/pkg/logs"
wfconversation "github.com/coze-dev/coze-studio/backend/crossdomain/workflow/conversation"
wfdatabase "github.com/coze-dev/coze-studio/backend/crossdomain/workflow/database"
wfknowledge "github.com/coze-dev/coze-studio/backend/crossdomain/workflow/knowledge"
wfmodel "github.com/coze-dev/coze-studio/backend/crossdomain/workflow/model"
@ -39,6 +36,7 @@ import (
search "github.com/coze-dev/coze-studio/backend/domain/search/service"
"github.com/coze-dev/coze-studio/backend/domain/workflow"
crosscode "github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/code"
crossconversation "github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/conversation"
crossdatabase "github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/database"
crossknowledge "github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/knowledge"
crossmodel "github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/model"
@ -46,6 +44,7 @@ import (
crosssearch "github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/search"
crossvariable "github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/variable"
"github.com/coze-dev/coze-studio/backend/domain/workflow/service"
workflowservice "github.com/coze-dev/coze-studio/backend/domain/workflow/service"
"github.com/coze-dev/coze-studio/backend/infra/contract/coderunner"
"github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
"github.com/coze-dev/coze-studio/backend/infra/contract/imagex"
@ -69,16 +68,9 @@ type ServiceComponents struct {
CodeRunner coderunner.Runner
}
func InitService(ctx context.Context, components *ServiceComponents) (*ApplicationService, error) {
bcm, ok, err := internal.GetBuiltinChatModel(ctx, "WKR_")
if err != nil {
return nil, err
}
if !ok {
logs.CtxWarnf(ctx, "workflow builtin chat model for knowledge recall not configured")
}
func InitService(components *ServiceComponents) *ApplicationService {
workflowRepo := service.NewWorkflowRepository(components.IDGen, components.DB, components.Cache,
components.Tos, components.CPStore, bcm)
components.Tos, components.CPStore)
workflow.SetRepository(workflowRepo)
workflowDomainSVC := service.NewWorkflowService(workflowRepo)
@ -90,11 +82,13 @@ func InitService(ctx context.Context, components *ServiceComponents) (*Applicati
crossmodel.SetManager(wfmodel.NewModelManager(components.ModelManager, nil))
crosscode.SetCodeRunner(components.CodeRunner)
crosssearch.SetNotifier(wfsearch.NewNotify(components.DomainNotifier))
crossconversation.SetConversationManager(wfconversation.NewConversationRepository())
callbacks.AppendGlobalHandlers(workflowservice.GetTokenCallbackHandler())
SVC.DomainSVC = workflowDomainSVC
SVC.ImageX = components.ImageX
SVC.TosClient = components.Tos
SVC.IDGenerator = components.IDGen
return SVC, err
return SVC
}

View File

@ -93,7 +93,7 @@ func (w *ApplicationService) GetNodeTemplateList(ctx context.Context, req *workf
toQueryTypes := make(map[entity.NodeType]bool)
for _, t := range req.NodeTypes {
entityType, err := nodeType2EntityNodeType(t)
entityType, err := entity.BlockType2EntityNodeType(t)
if err != nil {
logs.Warnf("get node type %v failed, err:=%v", t, err)
continue
@ -116,7 +116,7 @@ func (w *ApplicationService) GetNodeTemplateList(ctx context.Context, req *workf
Name: category,
}
for _, nodeMeta := range nodeMetaList {
tplType, err := entityNodeTypeToAPINodeTemplateType(nodeMeta.Type)
tplType, err := entity.NodeTypeToAPINodeTemplateType(nodeMeta.Type)
if err != nil {
return nil, err
}
@ -169,6 +169,21 @@ func (w *ApplicationService) CreateWorkflow(ctx context.Context, req *workflow.C
if err := checkUserSpace(ctx, uID, spaceID); err != nil {
return nil, err
}
var createConversation bool
if req.ProjectID != nil && req.IsSetFlowMode() && req.GetFlowMode() == workflow.WorkflowMode_ChatFlow && req.IsSetCreateConversation() && req.GetCreateConversation() {
createConversation = true
_, err := GetWorkflowDomainSVC().CreateDraftConversationTemplate(ctx, &vo.CreateConversationTemplateMeta{
AppID: mustParseInt64(req.GetProjectID()),
UserID: uID,
SpaceID: spaceID,
Name: req.Name,
})
if err != nil {
return nil, err
}
}
wf := &vo.MetaCreate{
CreatorID: uID,
SpaceID: spaceID,
@ -180,6 +195,13 @@ func (w *ApplicationService) CreateWorkflow(ctx context.Context, req *workflow.C
Mode: ternary.IFElse(req.IsSetFlowMode(), req.GetFlowMode(), workflow.WorkflowMode_Workflow),
InitCanvasSchema: entity.GetDefaultInitCanvasJsonSchema(i18n.GetLocale(ctx)),
}
if req.IsSetFlowMode() && req.GetFlowMode() == workflow.WorkflowMode_ChatFlow {
conversationName := req.Name
if !req.IsSetProjectID() || mustParseInt64(req.GetProjectID()) == 0 || !createConversation {
conversationName = "Default"
}
wf.InitCanvasSchema = entity.GetDefaultInitCanvasJsonSchemaChat(i18n.GetLocale(ctx), conversationName)
}
id, err := GetWorkflowDomainSVC().Create(ctx, wf)
if err != nil {
@ -1034,6 +1056,18 @@ func (w *ApplicationService) CopyWorkflowFromLibraryToApp(ctx context.Context, w
wf, err := GetWorkflowDomainSVC().CopyWorkflow(ctx, workflowID, vo.CopyWorkflowPolicy{
TargetAppID: &appID,
})
if wf.Mode == workflow.WorkflowMode_ChatFlow {
err = GetWorkflowDomainSVC().CopyChatFlowRole(ctx, &vo.CopyRolePolicy{
SourceID: workflowID,
TargetID: wf.ID,
CreatorID: wf.CreatorID,
})
if err != nil {
return 0, err
}
}
if err != nil {
return 0, err
}
@ -1127,7 +1161,7 @@ func (w *ApplicationService) MoveWorkflowFromAppToLibrary(ctx context.Context, w
}
func convertNodeExecution(nodeExe *entity.NodeExecution) (*workflow.NodeResult, error) {
nType, err := entityNodeTypeToAPINodeTemplateType(nodeExe.NodeType)
nType, err := entity.NodeTypeToAPINodeTemplateType(nodeExe.NodeType)
if err != nil {
return nil, err
}
@ -1317,7 +1351,7 @@ func convertStreamRunEvent(workflowID int64) func(msg *entity.Message) (res *wor
}
var nodeType workflow.NodeTemplateType
nodeType, err = entityNodeTypeToAPINodeTemplateType(msg.NodeType)
nodeType, err = entity.NodeTypeToAPINodeTemplateType(msg.NodeType)
if err != nil {
logs.Errorf("convert node type %v failed, err:=%v", msg.NodeType, err)
nodeType = workflow.NodeTemplateType(0)
@ -2087,6 +2121,11 @@ func (w *ApplicationService) ListWorkflow(ctx context.Context, req *workflow.Get
},
}
ww.CheckResult, err = GetWorkflowDomainSVC().WorkflowSchemaCheck(ctx, w, req.Checker)
if err != nil {
return nil, err
}
if qType == vo.FromDraft {
ww.UpdateTime = w.DraftMeta.Timestamp.Unix()
} else if qType == vo.FromLatestVersion || qType == vo.FromSpecificVersion {
@ -3338,178 +3377,6 @@ func toWorkflowParameter(nType *vo.NamedTypeInfo) (*workflow.Parameter, error) {
return wp, nil
}
func nodeType2EntityNodeType(t string) (entity.NodeType, error) {
i, err := strconv.Atoi(t)
if err != nil {
return "", fmt.Errorf("invalid node type string '%s': %w", t, err)
}
switch i {
case 1:
return entity.NodeTypeEntry, nil
case 2:
return entity.NodeTypeExit, nil
case 3:
return entity.NodeTypeLLM, nil
case 4:
return entity.NodeTypePlugin, nil
case 5:
return entity.NodeTypeCodeRunner, nil
case 6:
return entity.NodeTypeKnowledgeRetriever, nil
case 8:
return entity.NodeTypeSelector, nil
case 9:
return entity.NodeTypeSubWorkflow, nil
case 12:
return entity.NodeTypeDatabaseCustomSQL, nil
case 13:
return entity.NodeTypeOutputEmitter, nil
case 15:
return entity.NodeTypeTextProcessor, nil
case 18:
return entity.NodeTypeQuestionAnswer, nil
case 19:
return entity.NodeTypeBreak, nil
case 20:
return entity.NodeTypeVariableAssignerWithinLoop, nil
case 21:
return entity.NodeTypeLoop, nil
case 22:
return entity.NodeTypeIntentDetector, nil
case 27:
return entity.NodeTypeKnowledgeIndexer, nil
case 28:
return entity.NodeTypeBatch, nil
case 29:
return entity.NodeTypeContinue, nil
case 30:
return entity.NodeTypeInputReceiver, nil
case 32:
return entity.NodeTypeVariableAggregator, nil
case 37:
return entity.NodeTypeMessageList, nil
case 38:
return entity.NodeTypeClearMessage, nil
case 39:
return entity.NodeTypeCreateConversation, nil
case 40:
return entity.NodeTypeVariableAssigner, nil
case 42:
return entity.NodeTypeDatabaseUpdate, nil
case 43:
return entity.NodeTypeDatabaseQuery, nil
case 44:
return entity.NodeTypeDatabaseDelete, nil
case 45:
return entity.NodeTypeHTTPRequester, nil
case 46:
return entity.NodeTypeDatabaseInsert, nil
case 58:
return entity.NodeTypeJsonSerialization, nil
case 59:
return entity.NodeTypeJsonDeserialization, nil
case 60:
return entity.NodeTypeKnowledgeDeleter, nil
default:
// Handle all unknown or unsupported types here
return "", fmt.Errorf("unsupported or unknown node type ID: %d", i)
}
}
// entityNodeTypeToAPINodeTemplateType converts an entity.NodeType to the corresponding workflow.NodeTemplateType.
func entityNodeTypeToAPINodeTemplateType(nodeType entity.NodeType) (workflow.NodeTemplateType, error) {
switch nodeType {
case entity.NodeTypeEntry:
return workflow.NodeTemplateType_Start, nil
case entity.NodeTypeExit:
return workflow.NodeTemplateType_End, nil
case entity.NodeTypeLLM:
return workflow.NodeTemplateType_LLM, nil
case entity.NodeTypePlugin:
// Maps to Api type in the API model
return workflow.NodeTemplateType_Api, nil
case entity.NodeTypeCodeRunner:
return workflow.NodeTemplateType_Code, nil
case entity.NodeTypeKnowledgeRetriever:
// Maps to Dataset type in the API model
return workflow.NodeTemplateType_Dataset, nil
case entity.NodeTypeSelector:
// Maps to If type in the API model
return workflow.NodeTemplateType_If, nil
case entity.NodeTypeSubWorkflow:
return workflow.NodeTemplateType_SubWorkflow, nil
case entity.NodeTypeDatabaseCustomSQL:
// Maps to the generic Database type in the API model
return workflow.NodeTemplateType_Database, nil
case entity.NodeTypeOutputEmitter:
// Maps to Message type in the API model
return workflow.NodeTemplateType_Message, nil
case entity.NodeTypeTextProcessor:
return workflow.NodeTemplateType_Text, nil
case entity.NodeTypeQuestionAnswer:
return workflow.NodeTemplateType_Question, nil
case entity.NodeTypeBreak:
return workflow.NodeTemplateType_Break, nil
case entity.NodeTypeVariableAssigner:
return workflow.NodeTemplateType_AssignVariable, nil
case entity.NodeTypeVariableAssignerWithinLoop:
return workflow.NodeTemplateType_LoopSetVariable, nil
case entity.NodeTypeLoop:
return workflow.NodeTemplateType_Loop, nil
case entity.NodeTypeIntentDetector:
return workflow.NodeTemplateType_Intent, nil
case entity.NodeTypeKnowledgeIndexer:
// Maps to DatasetWrite type in the API model
return workflow.NodeTemplateType_DatasetWrite, nil
case entity.NodeTypeBatch:
return workflow.NodeTemplateType_Batch, nil
case entity.NodeTypeContinue:
return workflow.NodeTemplateType_Continue, nil
case entity.NodeTypeInputReceiver:
return workflow.NodeTemplateType_Input, nil
case entity.NodeTypeMessageList:
return workflow.NodeTemplateType(37), nil
case entity.NodeTypeVariableAggregator:
return workflow.NodeTemplateType(32), nil
case entity.NodeTypeClearMessage:
return workflow.NodeTemplateType(38), nil
case entity.NodeTypeCreateConversation:
return workflow.NodeTemplateType(39), nil
// Note: entity.NodeTypeVariableAggregator (ID 32) has no direct mapping in NodeTemplateType
// Note: entity.NodeTypeMessageList (ID 37) has no direct mapping in NodeTemplateType
// Note: entity.NodeTypeClearMessage (ID 38) has no direct mapping in NodeTemplateType
// Note: entity.NodeTypeCreateConversation (ID 39) has no direct mapping in NodeTemplateType
case entity.NodeTypeDatabaseUpdate:
return workflow.NodeTemplateType_DatabaseUpdate, nil
case entity.NodeTypeDatabaseQuery:
// Maps to DatabasesELECT (ID 43) in the API model (note potential typo)
return workflow.NodeTemplateType_DatabasesELECT, nil
case entity.NodeTypeDatabaseDelete:
return workflow.NodeTemplateType_DatabaseDelete, nil
// Note: entity.NodeTypeHTTPRequester (ID 45) has no direct mapping in NodeTemplateType
case entity.NodeTypeHTTPRequester:
return workflow.NodeTemplateType(45), nil
case entity.NodeTypeDatabaseInsert:
// Maps to DatabaseInsert (ID 41) in the API model, despite entity ID being 46.
// return workflow.NodeTemplateType_DatabaseInsert, nil
return workflow.NodeTemplateType(46), nil
case entity.NodeTypeJsonSerialization:
return workflow.NodeTemplateType(58), nil
case entity.NodeTypeJsonDeserialization:
return workflow.NodeTemplateType_JsonDeserialization, nil
case entity.NodeTypeKnowledgeDeleter:
return workflow.NodeTemplateType_DatasetDelete, nil
case entity.NodeTypeLambda:
return 0, nil
default:
// Handle entity types that don't have a corresponding NodeTemplateType
return workflow.NodeTemplateType(0), fmt.Errorf("cannot map entity node type '%s' to a workflow.NodeTemplateType", nodeType)
}
}
func i64PtrToStringPtr(i *int64) *string {
if i == nil {
return nil
@ -3813,3 +3680,357 @@ func checkUserSpace(ctx context.Context, uid int64, spaceID int64) error {
return nil
}
func (w *ApplicationService) populateChatFlowRoleFields(role *workflow.ChatFlowRole, targetRole interface{}) error {
var avatarUri, audioStr, bgStr, obStr, srStr, uiStr string
var err error
if role.Avatar != nil {
avatarUri = role.Avatar.ImageUri
}
if role.AudioConfig != nil {
audioStr, err = sonic.MarshalString(*role.AudioConfig)
if err != nil {
return vo.WrapError(errno.ErrSerializationDeserializationFail, err)
}
}
if role.BackgroundImageInfo != nil {
bgStr, err = sonic.MarshalString(*role.BackgroundImageInfo)
if err != nil {
return vo.WrapError(errno.ErrSerializationDeserializationFail, err)
}
}
if role.OnboardingInfo != nil {
obStr, err = sonic.MarshalString(*role.OnboardingInfo)
if err != nil {
return vo.WrapError(errno.ErrSerializationDeserializationFail, err)
}
}
if role.SuggestReplyInfo != nil {
srStr, err = sonic.MarshalString(*role.SuggestReplyInfo)
if err != nil {
return vo.WrapError(errno.ErrSerializationDeserializationFail, err)
}
}
if role.UserInputConfig != nil {
uiStr, err = sonic.MarshalString(*role.UserInputConfig)
if err != nil {
return vo.WrapError(errno.ErrSerializationDeserializationFail, err)
}
}
switch r := targetRole.(type) {
case *vo.ChatFlowRoleCreate:
if role.Name != nil {
r.Name = *role.Name
}
if role.Description != nil {
r.Description = *role.Description
}
if avatarUri != "" {
r.AvatarUri = avatarUri
}
if audioStr != "" {
r.AudioConfig = audioStr
}
if bgStr != "" {
r.BackgroundImageInfo = bgStr
}
if obStr != "" {
r.OnboardingInfo = obStr
}
if srStr != "" {
r.SuggestReplyInfo = srStr
}
if uiStr != "" {
r.UserInputConfig = uiStr
}
case *vo.ChatFlowRoleUpdate:
r.Name = role.Name
r.Description = role.Description
if avatarUri != "" {
r.AvatarUri = ptr.Of(avatarUri)
}
if audioStr != "" {
r.AudioConfig = ptr.Of(audioStr)
}
if bgStr != "" {
r.BackgroundImageInfo = ptr.Of(bgStr)
}
if obStr != "" {
r.OnboardingInfo = ptr.Of(obStr)
}
if srStr != "" {
r.SuggestReplyInfo = ptr.Of(srStr)
}
if uiStr != "" {
r.UserInputConfig = ptr.Of(uiStr)
}
default:
return vo.WrapError(errno.ErrInvalidParameter, fmt.Errorf("invalid type for targetRole: %T", targetRole))
}
return nil
}
func IsChatFlow(wf *entity.Workflow) bool {
if wf == nil || wf.ID == 0 {
return false
}
return wf.Meta.Mode == workflow.WorkflowMode_ChatFlow
}
func (w *ApplicationService) CreateChatFlowRole(ctx context.Context, req *workflow.CreateChatFlowRoleRequest) (
_ *workflow.CreateChatFlowRoleResponse, err error) {
defer func() {
if panicErr := recover(); panicErr != nil {
err = safego.NewPanicErr(panicErr, debug.Stack())
}
if err != nil {
err = vo.WrapIfNeeded(errno.ErrChatFlowRoleOperationFail, err, errorx.KV("cause", vo.UnwrapRootErr(err).Error()))
}
}()
uID := ctxutil.MustGetUIDFromCtx(ctx)
wf, err := GetWorkflowDomainSVC().Get(ctx, &vo.GetPolicy{
ID: mustParseInt64(req.GetChatFlowRole().GetWorkflowID()),
MetaOnly: true,
})
if err != nil {
return nil, err
}
if err = checkUserSpace(ctx, uID, wf.Meta.SpaceID); err != nil {
return nil, err
}
role := req.GetChatFlowRole()
if !IsChatFlow(wf) {
logs.CtxWarnf(ctx, "CreateChatFlowRole not chat flow, workflowID: %d", wf.ID)
return nil, vo.WrapError(errno.ErrChatFlowRoleOperationFail, fmt.Errorf("workflow %d is not a chat flow", wf.ID))
}
oldRole, err := GetWorkflowDomainSVC().GetChatFlowRole(ctx, mustParseInt64(role.WorkflowID), "")
if err != nil {
return nil, err
}
var roleID int64
if oldRole != nil {
role.ID = strconv.FormatInt(oldRole.ID, 10)
roleID = oldRole.ID
}
if role.GetID() == "" || role.GetID() == "0" {
chatFlowRole := &vo.ChatFlowRoleCreate{
WorkflowID: mustParseInt64(role.WorkflowID),
CreatorID: uID,
}
if err = w.populateChatFlowRoleFields(role, chatFlowRole); err != nil {
return nil, err
}
roleID, err = GetWorkflowDomainSVC().CreateChatFlowRole(ctx, chatFlowRole)
if err != nil {
return nil, err
}
} else {
chatFlowRole := &vo.ChatFlowRoleUpdate{
WorkflowID: mustParseInt64(role.WorkflowID),
}
if err = w.populateChatFlowRoleFields(role, chatFlowRole); err != nil {
return nil, err
}
err = GetWorkflowDomainSVC().UpdateChatFlowRole(ctx, chatFlowRole.WorkflowID, chatFlowRole)
if err != nil {
return nil, err
}
}
return &workflow.CreateChatFlowRoleResponse{
ID: strconv.FormatInt(roleID, 10),
}, nil
}
func (w *ApplicationService) DeleteChatFlowRole(ctx context.Context, req *workflow.DeleteChatFlowRoleRequest) (
_ *workflow.DeleteChatFlowRoleResponse, err error) {
defer func() {
if panicErr := recover(); panicErr != nil {
err = safego.NewPanicErr(panicErr, debug.Stack())
}
if err != nil {
err = vo.WrapIfNeeded(errno.ErrChatFlowRoleOperationFail, err, errorx.KV("cause", vo.UnwrapRootErr(err).Error()))
}
}()
uID := ctxutil.MustGetUIDFromCtx(ctx)
wf, err := GetWorkflowDomainSVC().Get(ctx, &vo.GetPolicy{
ID: mustParseInt64(req.GetWorkflowID()),
MetaOnly: true,
})
if err != nil {
return nil, err
}
if err = checkUserSpace(ctx, uID, wf.Meta.SpaceID); err != nil {
return nil, err
}
err = GetWorkflowDomainSVC().DeleteChatFlowRole(ctx, mustParseInt64(req.ID), mustParseInt64(req.WorkflowID))
if err != nil {
return nil, err
}
return &workflow.DeleteChatFlowRoleResponse{}, nil
}
func (w *ApplicationService) GetChatFlowRole(ctx context.Context, req *workflow.GetChatFlowRoleRequest) (
_ *workflow.GetChatFlowRoleResponse, err error) {
defer func() {
if panicErr := recover(); panicErr != nil {
err = safego.NewPanicErr(panicErr, debug.Stack())
}
if err != nil {
err = vo.WrapIfNeeded(errno.ErrChatFlowRoleOperationFail, err, errorx.KV("cause", vo.UnwrapRootErr(err).Error()))
}
}()
uID := ctxutil.MustGetUIDFromCtx(ctx)
wf, err := GetWorkflowDomainSVC().Get(ctx, &vo.GetPolicy{
ID: mustParseInt64(req.GetWorkflowID()),
MetaOnly: true,
})
if err != nil {
return nil, err
}
if err = checkUserSpace(ctx, uID, wf.Meta.SpaceID); err != nil {
return nil, err
}
if !IsChatFlow(wf) {
logs.CtxWarnf(ctx, "GetChatFlowRole not chat flow, workflowID: %d", wf.ID)
return nil, vo.WrapError(errno.ErrChatFlowRoleOperationFail, fmt.Errorf("workflow %d is not a chat flow", wf.ID))
}
var version string
if wf.Meta.AppID != nil {
vl, err := GetWorkflowDomainSVC().GetWorkflowVersionsByConnector(ctx, mustParseInt64(req.GetConnectorID()), wf.ID, 1)
if err != nil {
return nil, err
}
if len(vl) > 0 {
version = vl[0]
}
}
role, err := GetWorkflowDomainSVC().GetChatFlowRole(ctx, mustParseInt64(req.WorkflowID), version)
if err != nil {
return nil, err
}
if role == nil {
logs.CtxWarnf(ctx, "GetChatFlowRole role nil, workflowID: %d", wf.ID)
// Return nil for the error to align with the production behavior,
// where the GET API may be called before the CREATE API during chatflow creation.
return &workflow.GetChatFlowRoleResponse{}, nil
}
wfRole, err := w.convertChatFlowRole(ctx, role)
if err != nil {
return nil, fmt.Errorf("failed to get chat flow role config, internal data processing error: %+v", err)
}
return &workflow.GetChatFlowRoleResponse{
Role: wfRole,
}, nil
}
func (w *ApplicationService) convertChatFlowRole(ctx context.Context, role *entity.ChatFlowRole) (*workflow.ChatFlowRole, error) {
var err error
res := &workflow.ChatFlowRole{
ID: strconv.FormatInt(role.ID, 10),
WorkflowID: strconv.FormatInt(role.WorkflowID, 10),
Name: ptr.Of(role.Name),
Description: ptr.Of(role.Description),
}
if role.AvatarUri != "" {
url, err := w.ImageX.GetResourceURL(ctx, role.AvatarUri)
if err != nil {
return nil, err
}
res.Avatar = &workflow.AvatarConfig{
ImageUri: role.AvatarUri,
ImageUrl: url.URL,
}
}
if role.AudioConfig != "" {
err = sonic.UnmarshalString(role.AudioConfig, &res.AudioConfig)
if err != nil {
logs.CtxErrorf(ctx, "GetChatFlowRole AudioConfig UnmarshalString err: %+v", err)
return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
}
}
if role.OnboardingInfo != "" {
err = sonic.UnmarshalString(role.OnboardingInfo, &res.OnboardingInfo)
if err != nil {
logs.CtxErrorf(ctx, "GetChatFlowRole OnboardingInfo UnmarshalString err: %+v", err)
return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
}
}
if role.SuggestReplyInfo != "" {
err = sonic.UnmarshalString(role.SuggestReplyInfo, &res.SuggestReplyInfo)
if err != nil {
logs.CtxErrorf(ctx, "GetChatFlowRole SuggestReplyInfo UnmarshalString err: %+v", err)
return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
}
}
if role.UserInputConfig != "" {
err = sonic.UnmarshalString(role.UserInputConfig, &res.UserInputConfig)
if err != nil {
logs.CtxErrorf(ctx, "GetChatFlowRole UserInputConfig UnmarshalString err: %+v", err)
return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
}
}
if role.BackgroundImageInfo != "" {
res.BackgroundImageInfo = &workflow.BackgroundImageInfo{}
err = sonic.UnmarshalString(role.BackgroundImageInfo, res.BackgroundImageInfo)
if err != nil {
logs.CtxErrorf(ctx, "GetChatFlowRole BackgroundImageInfo UnmarshalString err: %+v", err)
return nil, vo.WrapError(errno.ErrSerializationDeserializationFail, err)
}
if res.BackgroundImageInfo != nil {
if res.BackgroundImageInfo.WebBackgroundImage != nil && res.BackgroundImageInfo.WebBackgroundImage.OriginImageUri != nil {
url, err := w.ImageX.GetResourceURL(ctx, res.BackgroundImageInfo.WebBackgroundImage.GetOriginImageUri())
if err != nil {
logs.CtxErrorf(ctx, "get url by uri err, err:%s", err.Error())
return nil, err
}
res.BackgroundImageInfo.WebBackgroundImage.ImageUrl = &url.URL
}
if res.BackgroundImageInfo.MobileBackgroundImage != nil && res.BackgroundImageInfo.MobileBackgroundImage.OriginImageUri != nil {
url, err := w.ImageX.GetResourceURL(ctx, res.BackgroundImageInfo.MobileBackgroundImage.GetOriginImageUri())
if err != nil {
logs.CtxErrorf(ctx, "get url by uri err, err:%s", err.Error())
return nil, err
}
res.BackgroundImageInfo.MobileBackgroundImage.ImageUrl = &url.URL
}
}
}
return res, nil
}

View File

@ -90,6 +90,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: Doubao
protocol: ark
capability:
function_call: true
@ -106,10 +107,10 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -117,10 +118,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -66,6 +66,7 @@ default_parameters:
zh: 生成多样性
en: Generation diversity
meta:
name: doubao-1.5-lite
protocol: ark
capability:
function_call: true
@ -81,10 +82,10 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -92,10 +93,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -66,6 +66,7 @@ default_parameters:
zh: 生成多样性
en: Generation diversity
meta:
name: doubao-1.5-pro-256k
protocol: ark
capability:
function_call: true
@ -81,10 +82,10 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -92,11 +93,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -66,6 +66,7 @@ default_parameters:
zh: 生成多样性
en: Generation diversity
meta:
name: doubao-1.5-pro-32k
protocol: ark
capability:
function_call: true
@ -81,10 +82,10 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -92,11 +93,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -88,6 +88,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: doubao-1.5-thinking-pro
protocol: ark
capability:
function_call: true
@ -104,10 +105,10 @@ meta:
reasoning: true
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -115,11 +116,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -88,6 +88,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: doubao-1.5-thinking-vision-pro
protocol: ark
capability:
function_call: true
@ -105,10 +106,10 @@ meta:
reasoning: true
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -116,11 +117,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -88,6 +88,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: doubao-1.5-vision-lite
protocol: ark
capability:
function_call: false
@ -104,10 +105,10 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -115,11 +116,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -88,6 +88,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: doubao-1.5-vision-pro
protocol: ark
capability:
function_call: true
@ -105,10 +106,10 @@ meta:
reasoning: true
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -116,11 +117,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -88,6 +88,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: doubao-seed-1.6-flash
protocol: ark
capability:
function_call: true
@ -105,10 +106,10 @@ meta:
reasoning: true
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -116,11 +117,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -88,6 +88,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: doubao-seed-1.6-thinking
protocol: ark
capability:
function_call: true
@ -105,10 +106,10 @@ meta:
reasoning: true
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -116,11 +117,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -88,6 +88,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: doubao-seed-1.6
protocol: ark
capability:
function_call: true
@ -105,10 +106,10 @@ meta:
reasoning: true
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -116,11 +117,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -66,6 +66,7 @@ default_parameters:
zh: 生成多样性
en: Generation diversity
meta:
name: deepseek-r1-ve
protocol: ark
capability:
function_call: true
@ -81,10 +82,10 @@ meta:
reasoning: true
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -92,11 +93,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -66,6 +66,7 @@ default_parameters:
zh: 生成多样性
en: Generation diversity
meta:
name: deepseek-v3-ve
protocol: ark
capability:
function_call: true
@ -81,10 +82,10 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: "https://ark.cn-beijing.volces.com/api/v3/"
base_url: ""
api_key: ""
timeout: 0s
model: "" # model_id / endpoint_id
model: ""
temperature: 0.1
frequency_penalty: 0
presence_penalty: 0
@ -92,11 +93,16 @@ meta:
top_p: 0.7
top_k: 0
stop: []
openai: null
claude: null
ark:
region: ""
access_key: ""
secret_key: ""
retry_times: null
custom_header: {}
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -128,6 +128,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: test_model
protocol: test_protocol
capability:
function_call: true
@ -149,7 +150,7 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: https://localhost:1234/v1
base_url: https://localhost:1234/chat/completion
api_key: qweasdzxc
timeout: 10s
model: model_name
@ -163,7 +164,7 @@ meta:
- bye
enable_thinking: false
openai:
by_azure: false
by_azure: true
api_version: "2024-10-21"
response_format:
type: text

View File

@ -47,6 +47,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: Claude-3.5-Sonnet
protocol: claude
capability:
function_call: true
@ -63,7 +64,7 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: "https://api.anthropic.com/v1/"
base_url: ""
api_key: ""
timeout: 0s
model: ""
@ -74,11 +75,16 @@ meta:
top_p: 1
top_k: 0
stop: []
openai: null
claude:
by_bedrock: false
access_key: ""
secret_access_key: ""
session_token: ""
region: ""
ark: null
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -69,6 +69,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: DeepSeek-V3
protocol: deepseek
capability:
function_call: false
@ -84,7 +85,7 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: "https://api.deepseek.com"
base_url: ""
api_key: ""
timeout: 0s
model: ""
@ -95,7 +96,12 @@ meta:
top_p: 1
top_k: 0
stop: []
openai: null
claude: null
ark: null
deepseek:
response_format_type: text
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -88,6 +88,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: Gemini-2.5-Flash
protocol: gemini
capability:
function_call: true
@ -106,7 +107,7 @@ meta:
reasoning: true
prefill_response: true
conn_config:
base_url: "https://generativelanguage.googleapis.com/"
base_url: ""
api_key: ""
timeout: 0s
model: gemini-2.5-flash
@ -117,6 +118,11 @@ meta:
top_p: 1
top_k: 0
stop: []
openai: null
claude: null
ark: null
deepseek: null
qwen: null
gemini:
backend: 0
project: ""

View File

@ -47,6 +47,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: Gemma-3
protocol: ollama
capability:
function_call: true
@ -62,7 +63,7 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: "http://host.docker.internal:11434"
base_url: ""
api_key: ""
timeout: 0s
model: ""
@ -73,5 +74,11 @@ meta:
top_p: 0.95
top_k: 20
stop: []
openai: null
claude: null
ark: null
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -128,6 +128,7 @@ default_parameters:
zh: 输入及输出设置
en: Input and output settings
meta:
name: GPT-4o
protocol: openai
capability:
function_call: true
@ -144,7 +145,7 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: "https://api.openai.com/v1"
base_url: ""
api_key: ""
timeout: 0s
model: ""
@ -161,5 +162,10 @@ meta:
response_format:
type: text
jsonschema: null
claude: null
ark: null
deepseek: null
qwen: null
gemini: null
custom: {}
status: 0

View File

@ -66,6 +66,7 @@ default_parameters:
zh: 生成多样性
en: Generation diversity
meta:
name: Qwen3-32B
protocol: qwen
capability:
function_call: true
@ -81,7 +82,7 @@ meta:
reasoning: false
prefill_response: false
conn_config:
base_url: "https://dashscope.aliyuncs.com/compatible-mode/v1"
base_url: ""
api_key: ""
timeout: 0s
model: ""
@ -92,9 +93,14 @@ meta:
top_p: 1
top_k: 0
stop: []
openai: null
claude: null
ark: null
deepseek: null
qwen:
response_format:
type: text
jsonschema: null
gemini: null
custom: {}
status: 0

View File

@ -20,10 +20,13 @@ import (
"context"
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/conversation"
"github.com/coze-dev/coze-studio/backend/domain/conversation/conversation/entity"
)
type Conversation interface {
GetCurrentConversation(ctx context.Context, req *conversation.GetCurrent) (*conversation.Conversation, error)
Create(ctx context.Context, req *entity.CreateMeta) (*entity.Conversation, error)
NewConversationCtx(ctx context.Context, req *entity.NewConversationCtxRequest) (*entity.NewConversationCtxResponse, error)
}
var defaultSVC Conversation

View File

@ -20,13 +20,16 @@ import (
"context"
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/message"
"github.com/coze-dev/coze-studio/backend/domain/conversation/message/entity"
)
type Message interface {
GetByRunIDs(ctx context.Context, conversationID int64, runIDs []int64) ([]*message.Message, error)
PreCreate(ctx context.Context, msg *message.Message) (*message.Message, error)
Create(ctx context.Context, msg *message.Message) (*message.Message, error)
List(ctx context.Context, meta *entity.ListMeta) (*entity.ListResult, error)
Edit(ctx context.Context, msg *message.Message) (*message.Message, error)
Delete(ctx context.Context, req *entity.DeleteMeta) error
}
var defaultSVC Message

View File

@ -20,8 +20,10 @@ import (
"context"
einoCompose "github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/schema"
"github.com/coze-dev/coze-studio/backend/domain/workflow"
workflowEntity "github.com/coze-dev/coze-studio/backend/domain/workflow/entity"
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
)
@ -37,10 +39,13 @@ type Workflow interface {
GetWorkflowIDsByAppID(ctx context.Context, appID int64) ([]int64, error)
SyncExecuteWorkflow(ctx context.Context, config vo.ExecuteConfig, input map[string]any) (*workflowEntity.WorkflowExecution, vo.TerminatePlan, error)
WithExecuteConfig(cfg vo.ExecuteConfig) einoCompose.Option
StreamExecute(ctx context.Context, config vo.ExecuteConfig, input map[string]any) (*schema.StreamReader[*workflowEntity.Message], error)
InitApplicationDefaultConversationTemplate(ctx context.Context, spaceID int64, appID int64, userID int64) error
}
type ExecuteConfig = vo.ExecuteConfig
type ExecuteMode = vo.ExecuteMode
type WorkflowMessage = workflowEntity.Message
const (
ExecuteModeDebug ExecuteMode = "debug"

View File

@ -21,6 +21,7 @@ import (
model "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/conversation"
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossconversation"
"github.com/coze-dev/coze-studio/backend/domain/conversation/conversation/entity"
conversation "github.com/coze-dev/coze-studio/backend/domain/conversation/conversation/service"
)
@ -40,3 +41,11 @@ func InitDomainService(c conversation.Conversation) crossconversation.Conversati
func (s *impl) GetCurrentConversation(ctx context.Context, req *model.GetCurrent) (*model.Conversation, error) {
return s.DomainSVC.GetCurrentConversation(ctx, req)
}
func (s *impl) Create(ctx context.Context, req *entity.CreateMeta) (*entity.Conversation, error) {
return s.DomainSVC.Create(ctx, req)
}
func (s *impl) NewConversationCtx(ctx context.Context, req *entity.NewConversationCtxRequest) (*entity.NewConversationCtxResponse, error) {
return s.DomainSVC.NewConversationCtx(ctx, req)
}

View File

@ -21,6 +21,7 @@ import (
model "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/message"
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossmessage"
"github.com/coze-dev/coze-studio/backend/domain/conversation/message/entity"
message "github.com/coze-dev/coze-studio/backend/domain/conversation/message/service"
)
@ -53,3 +54,11 @@ func (c *impl) Edit(ctx context.Context, msg *model.Message) (*model.Message, er
func (c *impl) PreCreate(ctx context.Context, msg *model.Message) (*model.Message, error) {
return c.DomainSVC.PreCreate(ctx, msg)
}
func (c *impl) List(ctx context.Context, lm *entity.ListMeta) (*entity.ListResult, error) {
return c.DomainSVC.List(ctx, lm)
}
func (c *impl) Delete(ctx context.Context, req *entity.DeleteMeta) error {
return c.DomainSVC.Delete(ctx, req)
}

View File

@ -20,6 +20,7 @@ import (
"context"
einoCompose "github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/schema"
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossworkflow"
"github.com/coze-dev/coze-studio/backend/domain/workflow"
@ -72,6 +73,14 @@ func (i *impl) WithExecuteConfig(cfg vo.ExecuteConfig) einoCompose.Option {
return i.DomainSVC.WithExecuteConfig(cfg)
}
func (i *impl) StreamExecute(ctx context.Context, config vo.ExecuteConfig, input map[string]any) (*schema.StreamReader[*workflowEntity.Message], error) {
return i.DomainSVC.StreamExecute(ctx, config, input)
}
func (i *impl) InitApplicationDefaultConversationTemplate(ctx context.Context, spaceID int64, appID int64, userID int64) error {
return i.DomainSVC.InitApplicationDefaultConversationTemplate(ctx, spaceID, appID, userID)
}
func (i *impl) GetWorkflowIDsByAppID(ctx context.Context, appID int64) ([]int64, error) {
metas, _, err := i.DomainSVC.MGet(ctx, &vo.MGetPolicy{
MetaQuery: vo.MetaQuery{

View File

@ -0,0 +1,201 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package conversation
import (
"context"
"strconv"
"github.com/cloudwego/eino/schema"
"github.com/coze-dev/coze-studio/backend/api/model/conversation/common"
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/message"
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossconversation"
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossmessage"
"github.com/coze-dev/coze-studio/backend/domain/conversation/conversation/entity"
msgentity "github.com/coze-dev/coze-studio/backend/domain/conversation/message/entity"
"github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/conversation"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
)
type ConversationRepository struct {
}
func NewConversationRepository() *ConversationRepository {
return &ConversationRepository{}
}
func (c *ConversationRepository) CreateConversation(ctx context.Context, req *conversation.CreateConversationRequest) (int64, error) {
ret, err := crossconversation.DefaultSVC().Create(ctx, &entity.CreateMeta{
AgentID: req.AppID,
UserID: req.UserID,
ConnectorID: req.ConnectorID,
Scene: common.Scene_SceneWorkflow,
})
if err != nil {
return 0, err
}
return ret.ID, nil
}
func (c *ConversationRepository) CreateMessage(ctx context.Context, req *conversation.CreateMessageRequest) (int64, error) {
msg := &message.Message{
ConversationID: req.ConversationID,
Role: schema.RoleType(req.Role),
Content: req.Content,
ContentType: message.ContentType(req.ContentType),
UserID: strconv.FormatInt(req.UserID, 10),
AgentID: req.AppID,
RunID: req.RunID,
}
if msg.Role == "user" {
msg.MessageType = message.MessageTypeQuestion
} else {
msg.MessageType = message.MessageTypeAnswer
}
ret, err := crossmessage.DefaultSVC().Create(ctx, msg)
if err != nil {
return 0, err
}
return ret.ID, nil
}
func (c *ConversationRepository) MessageList(ctx context.Context, req *conversation.MessageListRequest) (*conversation.MessageListResponse, error) {
lm := &msgentity.ListMeta{
ConversationID: req.ConversationID,
Limit: int(req.Limit), // Since the value of limit is checked inside the node, the type cast here is safe
UserID: strconv.FormatInt(req.UserID, 10),
AgentID: req.AppID,
OrderBy: req.OrderBy,
}
if req.BeforeID != nil {
lm.Cursor, _ = strconv.ParseInt(*req.BeforeID, 10, 64)
lm.Direction = msgentity.ScrollPageDirectionPrev
}
if req.AfterID != nil {
lm.Cursor, _ = strconv.ParseInt(*req.AfterID, 10, 64)
lm.Direction = msgentity.ScrollPageDirectionNext
}
lm.Direction = msgentity.ScrollPageDirectionNext
lr, err := crossmessage.DefaultSVC().List(ctx, lm)
if err != nil {
return nil, err
}
response := &conversation.MessageListResponse{}
if lr.PrevCursor > 0 {
response.FirstID = strconv.FormatInt(lr.PrevCursor, 10)
}
if lr.NextCursor > 0 {
response.LastID = strconv.FormatInt(lr.NextCursor, 10)
}
if len(lr.Messages) == 0 {
return response, nil
}
messages, err := convertMessage(lr.Messages)
if err != nil {
return nil, err
}
response.Messages = messages
return response, nil
}
func (c *ConversationRepository) ClearConversationHistory(ctx context.Context, req *conversation.ClearConversationHistoryReq) error {
_, err := crossconversation.DefaultSVC().NewConversationCtx(ctx, &entity.NewConversationCtxRequest{
ID: req.ConversationID,
})
if err != nil {
return err
}
return nil
}
func (c *ConversationRepository) DeleteMessage(ctx context.Context, req *conversation.DeleteMessageRequest) error {
return crossmessage.DefaultSVC().Delete(ctx, &msgentity.DeleteMeta{
MessageIDs: []int64{req.MessageID},
})
}
func (c *ConversationRepository) EditMessage(ctx context.Context, req *conversation.EditMessageRequest) error {
_, err := crossmessage.DefaultSVC().Edit(ctx, &msgentity.Message{
ID: req.MessageID,
ConversationID: req.ConversationID,
Content: req.Content,
})
if err != nil {
return err
}
return nil
}
func (c *ConversationRepository) GetLatestRunIDs(ctx context.Context, req *conversation.GetLatestRunIDsRequest) ([]int64, error) {
return []int64{0}, nil
}
func (c *ConversationRepository) GetMessagesByRunIDs(ctx context.Context, req *conversation.GetMessagesByRunIDsRequest) (*conversation.GetMessagesByRunIDsResponse, error) {
messages, err := crossmessage.DefaultSVC().GetByRunIDs(ctx, req.ConversationID, req.RunIDs)
if err != nil {
return nil, err
}
msgs, err := convertMessage(messages)
if err != nil {
return nil, err
}
return &conversation.GetMessagesByRunIDsResponse{
Messages: msgs,
}, nil
}
func convertMessage(msgs []*msgentity.Message) ([]*conversation.Message, error) {
messages := make([]*conversation.Message, 0, len(msgs))
for _, m := range msgs {
msg := &conversation.Message{
ID: m.ID,
Role: string(m.Role),
ContentType: string(m.ContentType)}
if m.MultiContent != nil {
var mcs []*conversation.Content
for _, c := range m.MultiContent {
if c.FileData != nil {
for _, fd := range c.FileData {
mcs = append(mcs, &conversation.Content{
Type: string(c.Type),
Uri: ptr.Of(fd.Url),
})
}
} else {
mcs = append(mcs, &conversation.Content{
Type: string(c.Type),
Text: ptr.Of(c.Text),
})
}
}
msg.MultiContent = mcs
} else {
msg.Text = ptr.Of(m.Content)
}
messages = append(messages, msg)
}
return messages, nil
}

View File

@ -0,0 +1,380 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package conversation
import (
"testing"
apimessage "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/message"
"github.com/coze-dev/coze-studio/backend/domain/conversation/message/entity"
"github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/conversation"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
"github.com/stretchr/testify/assert"
)
func Test_convertMessage(t *testing.T) {
type args struct {
lr *entity.ListResult
}
tests := []struct {
name string
args args
want *conversation.MessageListResponse
wantErr bool
}{
{
name: "pure text",
args: args{
lr: &entity.ListResult{
Messages: []*entity.Message{
{
ID: 1,
Role: "user",
ContentType: "text",
MultiContent: []*apimessage.InputMetaData{
{
Type: "text",
Text: "hello",
},
},
},
},
},
},
want: &conversation.MessageListResponse{
Messages: []*conversation.Message{
{
ID: 1,
Role: "user",
ContentType: "text",
MultiContent: []*conversation.Content{
{Type: "text", Text: ptr.Of("hello")},
},
},
},
},
},
{
name: "pure file",
args: args{
lr: &entity.ListResult{
Messages: []*entity.Message{
{
ID: 2,
Role: "user",
ContentType: "file",
MultiContent: []*apimessage.InputMetaData{
{
Type: "file",
FileData: []*apimessage.FileData{
{
Url: "f_uri_1",
},
},
},
{
Type: "text",
Text: "",
},
},
},
},
},
},
want: &conversation.MessageListResponse{
Messages: []*conversation.Message{
{
ID: 2,
Role: "user",
ContentType: "file",
MultiContent: []*conversation.Content{
{Type: "file", Uri: ptr.Of("f_uri_1")},
{Type: "text", Text: ptr.Of("")},
},
},
},
},
},
{
name: "text and file",
args: args{
lr: &entity.ListResult{
Messages: []*entity.Message{
{
ID: 3,
Role: "user",
ContentType: "text_file",
MultiContent: []*apimessage.InputMetaData{
{
Type: "text",
Text: "hello",
},
{
Type: "file",
FileData: []*apimessage.FileData{
{
Url: "f_uri_2",
},
},
},
},
},
},
},
},
want: &conversation.MessageListResponse{
Messages: []*conversation.Message{
{
ID: 3,
Role: "user",
ContentType: "text_file",
MultiContent: []*conversation.Content{
{Type: "text", Text: ptr.Of("hello")},
{Type: "file", Uri: ptr.Of("f_uri_2")},
},
},
},
},
},
{
name: "multiple files",
args: args{
lr: &entity.ListResult{
Messages: []*entity.Message{
{
ID: 4,
Role: "user",
ContentType: "file",
MultiContent: []*apimessage.InputMetaData{
{
Type: "file",
FileData: []*apimessage.FileData{
{
Url: "f_uri_3",
},
{
Url: "f_uri_4",
},
},
},
{
Type: "text",
Text: "",
},
},
},
},
},
},
want: &conversation.MessageListResponse{
Messages: []*conversation.Message{
{
ID: 4,
Role: "user",
ContentType: "file",
MultiContent: []*conversation.Content{
{Type: "file", Uri: ptr.Of("f_uri_3")},
{Type: "file", Uri: ptr.Of("f_uri_4")},
{Type: "text", Text: ptr.Of("")},
},
},
},
},
},
{
name: "empty text",
args: args{
lr: &entity.ListResult{
Messages: []*entity.Message{
{
ID: 5,
Role: "user",
ContentType: "text",
MultiContent: []*apimessage.InputMetaData{
{
Type: "text",
Text: "",
},
},
},
},
},
},
want: &conversation.MessageListResponse{
Messages: []*conversation.Message{
{
ID: 5,
Role: "user",
ContentType: "text",
MultiContent: []*conversation.Content{
{Type: "text", Text: ptr.Of("")},
},
},
},
},
},
{
name: "pure image",
args: args{
lr: &entity.ListResult{
Messages: []*entity.Message{
{
ID: 6,
Role: "user",
ContentType: "image",
MultiContent: []*apimessage.InputMetaData{
{
Type: "image",
FileData: []*apimessage.FileData{
{
Url: "image_uri_5",
},
},
},
{
Type: "text",
Text: "",
},
},
},
},
},
},
want: &conversation.MessageListResponse{
Messages: []*conversation.Message{
{
ID: 6,
Role: "user",
ContentType: "image",
MultiContent: []*conversation.Content{
{Type: "image", Uri: ptr.Of("image_uri_5")},
{Type: "text", Text: ptr.Of("")},
},
},
},
},
},
{
name: "multiple images",
args: args{
lr: &entity.ListResult{
Messages: []*entity.Message{
{
ID: 7,
Role: "user",
ContentType: "image",
MultiContent: []*apimessage.InputMetaData{
{
Type: "image",
FileData: []*apimessage.FileData{
{
Url: "file_id_6",
},
{
Url: "file_id_7",
},
},
},
{
Type: "text",
Text: "",
},
},
},
},
},
},
want: &conversation.MessageListResponse{
Messages: []*conversation.Message{
{
ID: 7,
Role: "user",
ContentType: "image",
MultiContent: []*conversation.Content{
{Type: "image", Uri: ptr.Of("file_id_6")},
{Type: "image", Uri: ptr.Of("file_id_7")},
{Type: "text", Text: ptr.Of("")},
},
},
},
},
},
{
name: "mixed content",
args: args{
lr: &entity.ListResult{
Messages: []*entity.Message{
{
ID: 8,
Role: "user",
ContentType: "mix",
MultiContent: []*apimessage.InputMetaData{
{
Type: "text",
Text: "hello",
},
{
Type: "image",
FileData: []*apimessage.FileData{
{
Url: "file_id_8",
},
},
},
{
Type: "file",
FileData: []*apimessage.FileData{
{
Url: "file_id_9",
},
},
},
},
},
},
},
},
want: &conversation.MessageListResponse{
Messages: []*conversation.Message{
{
ID: 8,
Role: "user",
ContentType: "mix",
MultiContent: []*conversation.Content{
{Type: "text", Text: ptr.Of("hello")},
{Type: "image", Uri: ptr.Of("file_id_8")},
{Type: "file", Uri: ptr.Of("file_id_9")},
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
msgs, err := convertMessage(tt.args.lr.Messages)
if (err != nil) != tt.wantErr {
t.Errorf("convertMessage() error = %v, wantErr %v", err, tt.wantErr)
return
}
for i, msg := range msgs {
assert.Equal(t, msg.MultiContent, tt.want.Messages[i].MultiContent)
}
})
}
}

View File

@ -127,6 +127,7 @@ func (k *Knowledge) Retrieve(ctx context.Context, r *crossknowledge.RetrieveRequ
Query: r.Query,
KnowledgeIDs: r.KnowledgeIDs,
Strategy: rs,
ChatHistory: r.ChatHistory,
}
response, err := k.client.Retrieve(ctx, req)

View File

@ -163,7 +163,7 @@ func BuildAgent(ctx context.Context, conf *Config) (r *AgentRunner, err error) {
isReActAgent = true
requireCheckpoint = true
if modelInfo.Meta.Capability != nil && !modelInfo.Meta.Capability.FunctionCall {
return nil, fmt.Errorf("model %v does not support function call", modelInfo.Name)
return nil, fmt.Errorf("model %v does not support function call", modelInfo.Meta.Name)
}
}

View File

@ -21,10 +21,9 @@ import (
"fmt"
"math/rand"
"github.com/jinzhu/copier"
"github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/schema"
"github.com/jinzhu/copier"
"github.com/coze-dev/coze-studio/backend/api/model/ocean/cloud/bot_common"
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossplugin"

View File

@ -29,6 +29,7 @@ import (
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossdatabase"
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossknowledge"
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossplugin"
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossworkflow"
"github.com/coze-dev/coze-studio/backend/domain/app/entity"
"github.com/coze-dev/coze-studio/backend/domain/app/repository"
"github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
@ -67,6 +68,11 @@ func (a *appServiceImpl) CreateDraftAPP(ctx context.Context, req *CreateDraftAPP
return 0, errorx.Wrapf(err, "CreateDraftAPP failed, spaceID=%d", req.SpaceID)
}
err = crossworkflow.DefaultSVC().InitApplicationDefaultConversationTemplate(ctx, req.SpaceID, appID, req.OwnerID)
if err != nil {
return 0, err
}
return appID, nil
}

View File

@ -300,7 +300,7 @@ func (dao *MessageDAO) buildModelContent(msgDO *entity.Message) (string, error)
URL: contentData.FileData[0].Url,
}
case message.InputTypeAudio:
one.Type = schema.ChatMessagePartTypeAudioURL
one.Type = schema.ChatMessagePartTypeFileURL
one.AudioURL = &schema.ChatMessageAudioURL{
URL: contentData.FileData[0].Url,
URI: contentData.FileData[0].URI,
@ -309,12 +309,10 @@ func (dao *MessageDAO) buildModelContent(msgDO *entity.Message) (string, error)
multiContent = append(multiContent, one)
}
if len(multiContent) > 0 {
if len(msgDO.Content) > 0 {
multiContent = append(multiContent, schema.ChatMessagePart{
Type: schema.ChatMessagePartTypeText,
Text: msgDO.Content,
})
}
multiContent = append(multiContent, schema.ChatMessagePart{
Type: schema.ChatMessagePartTypeText,
Text: msgDO.Content,
})
} else {
modelContentObj.Content = msgDO.Content
}

View File

@ -145,7 +145,7 @@ func (suite *KnowledgeTestSuite) SetupSuite() {
panic(err)
}
emb, err := hembed.NewEmbedding(embEndpoint, 1024, 1)
emb, err := hembed.NewEmbedding(embEndpoint, 1024)
if err != nil {
panic(err)
}

View File

@ -24,7 +24,6 @@ import (
"strings"
"github.com/getkin/kin-openapi/openapi3"
"github.com/mohae/deepcopy"
"golang.org/x/mod/semver"
"gopkg.in/yaml.v3"
@ -60,26 +59,18 @@ var (
func GetToolProduct(toolID int64) (*ToolInfo, bool) {
ti, ok := toolProducts[toolID]
if !ok {
return nil, false
}
ti_ := deepcopy.Copy(ti).(*ToolInfo)
return ti_, true
return ti, ok
}
func MGetToolProducts(toolIDs []int64) []*ToolInfo {
tools := make([]*ToolInfo, 0, len(toolIDs))
for _, toolID := range toolIDs {
ti, ok := GetToolProduct(toolID)
ti, ok := toolProducts[toolID]
if !ok {
continue
}
tools = append(tools, ti)
}
return tools
}

View File

@ -20,9 +20,7 @@ import (
"context"
"errors"
"gorm.io/gen/field"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
"github.com/coze-dev/coze-studio/backend/domain/plugin/internal/dal/model"
@ -57,26 +55,6 @@ func (a agentToolDraftPO) ToDO() *entity.ToolInfo {
}
}
func (at *AgentToolDraftDAO) getSelected(opt *ToolSelectedOption) (selected []field.Expr) {
if opt == nil {
return selected
}
table := at.query.AgentToolDraft
if opt.ToolID {
selected = append(selected, table.ToolID)
}
if opt.ToolMethod {
selected = append(selected, table.Method)
}
if opt.ToolSubURL {
selected = append(selected, table.SubURL)
}
return selected
}
func (at *AgentToolDraftDAO) Get(ctx context.Context, agentID, toolID int64) (tool *entity.ToolInfo, exist bool, err error) {
table := at.query.AgentToolDraft
tl, err := table.WithContext(ctx).
@ -142,14 +120,13 @@ func (at *AgentToolDraftDAO) MGet(ctx context.Context, agentID int64, toolIDs []
return tools, nil
}
func (at *AgentToolDraftDAO) GetAll(ctx context.Context, agentID int64, opt *ToolSelectedOption) (tools []*entity.ToolInfo, err error) {
func (at *AgentToolDraftDAO) GetAll(ctx context.Context, agentID int64) (tools []*entity.ToolInfo, err error) {
const limit = 20
table := at.query.AgentToolDraft
cursor := int64(0)
for {
tls, err := table.WithContext(ctx).
Select(at.getSelected(opt)...).
Where(
table.AgentID.Eq(agentID),
table.ID.Gt(cursor),
@ -194,16 +171,6 @@ func (at *AgentToolDraftDAO) UpdateWithToolName(ctx context.Context, agentID int
}
func (at *AgentToolDraftDAO) BatchCreateWithTX(ctx context.Context, tx *query.QueryTx, agentID int64, tools []*entity.ToolInfo) (err error) {
return at.batchCreateWithTX(ctx, tx, agentID, tools, false)
}
func (at *AgentToolDraftDAO) BatchCreateIgnoreConflictWithTX(ctx context.Context, tx *query.QueryTx, agentID int64, tools []*entity.ToolInfo) (err error) {
return at.batchCreateWithTX(ctx, tx, agentID, tools, true)
}
func (at *AgentToolDraftDAO) batchCreateWithTX(ctx context.Context, tx *query.QueryTx, agentID int64,
tools []*entity.ToolInfo, ignoreConflict bool) (err error) {
tls := make([]*model.AgentToolDraft, 0, len(tools))
for _, tl := range tools {
id, err := at.idGen.GenID(ctx)
@ -225,13 +192,7 @@ func (at *AgentToolDraftDAO) batchCreateWithTX(ctx context.Context, tx *query.Qu
}
table := tx.AgentToolDraft
if ignoreConflict {
err = table.WithContext(ctx).Clauses(clause.OnConflict{DoNothing: true}).
CreateInBatches(tls, 20)
} else {
err = table.WithContext(ctx).CreateInBatches(tls, 20)
}
err = table.WithContext(ctx).CreateInBatches(tls, 20)
if err != nil {
return err
}
@ -239,6 +200,27 @@ func (at *AgentToolDraftDAO) batchCreateWithTX(ctx context.Context, tx *query.Qu
return nil
}
func (at *AgentToolDraftDAO) DeleteAll(ctx context.Context, agentID int64) (err error) {
const limit = 20
table := at.query.AgentToolDraft
for {
info, err := table.WithContext(ctx).
Where(table.AgentID.Eq(agentID)).
Limit(limit).
Delete()
if err != nil {
return err
}
if info.RowsAffected == 0 || info.RowsAffected < limit {
break
}
}
return nil
}
func (at *AgentToolDraftDAO) GetAllPluginIDs(ctx context.Context, agentID int64) (pluginIDs []int64, err error) {
const size = 100
table := at.query.AgentToolDraft
@ -272,22 +254,22 @@ func (at *AgentToolDraftDAO) GetAllPluginIDs(ctx context.Context, agentID int64)
return slices.Unique(pluginIDs), nil
}
func (at *AgentToolDraftDAO) DeleteWithTX(ctx context.Context, tx *query.QueryTx, agentID int64, toolIDs []int64) (err error) {
func (at *AgentToolDraftDAO) DeleteAllWithTX(ctx context.Context, tx *query.QueryTx, agentID int64) (err error) {
const limit = 20
table := tx.AgentToolDraft
chunks := slices.Chunks(toolIDs, limit)
for _, chunk := range chunks {
_, err = table.WithContext(ctx).
Where(
table.AgentID.Eq(agentID),
table.ToolID.In(chunk...),
).
for {
info, err := table.WithContext(ctx).
Where(table.AgentID.Eq(agentID)).
Limit(limit).
Delete()
if err != nil {
return err
}
if info.RowsAffected == 0 || info.RowsAffected < limit {
break
}
}
return nil

View File

@ -25,7 +25,7 @@ import (
"gorm.io/gen/field"
"gorm.io/gorm"
pluginModel "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
"github.com/coze-dev/coze-studio/backend/api/model/plugin_develop_common"
"github.com/coze-dev/coze-studio/backend/domain/plugin/conf"
"github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
@ -50,7 +50,7 @@ type PluginDraftDAO struct {
type pluginDraftPO model.PluginDraft
func (p pluginDraftPO) ToDO() *entity.PluginInfo {
return entity.NewPluginInfo(&pluginModel.PluginInfo{
return entity.NewPluginInfo(&plugin.PluginInfo{
ID: p.ID,
SpaceID: p.SpaceID,
DeveloperID: p.DeveloperID,
@ -257,12 +257,9 @@ func (p *PluginDraftDAO) List(ctx context.Context, spaceID, appID int64, pageInf
}
func (p *PluginDraftDAO) Update(ctx context.Context, plugin *entity.PluginInfo) (err error) {
var mf *pluginModel.PluginManifest
if plugin.Manifest != nil {
mf, err = plugin.Manifest.EncryptAuthPayload()
if err != nil {
return fmt.Errorf("EncryptAuthPayload failed, err=%w", err)
}
mf, err := plugin.Manifest.EncryptAuthPayload()
if err != nil {
return fmt.Errorf("EncryptAuthPayload failed, err=%w", err)
}
m := &model.PluginDraft{

View File

@ -114,6 +114,7 @@ func (t *ToolDraftDAO) Create(ctx context.Context, tool *entity.ToolInfo) (toolI
}
func (t *ToolDraftDAO) genToolID(ctx context.Context) (id int64, err error) {
retryTimes := 5
for i := 0; i < retryTimes; i++ {
@ -122,13 +123,11 @@ func (t *ToolDraftDAO) genToolID(ctx context.Context) (id int64, err error) {
return 0, err
}
_, ok := conf.GetToolProduct(id)
if !ok {
if _, ok := conf.GetToolProduct(id); !ok {
break
}
if i == retryTimes-1 {
return 0, fmt.Errorf("id %d is confilict with product tool id", id)
return 0, fmt.Errorf("id %d is confilict with product tool id.", id)
}
}

View File

@ -946,10 +946,14 @@ func fillManifestWithOpenapiDoc(mf *entity.PluginManifest, doc *model.Openapi3T)
}
func addHTTPProtocolHeadIfNeed(url string) string {
if strings.HasPrefix(url, "https://") || strings.HasPrefix(url, "http://") {
if strings.HasPrefix(url, "https://") {
return url
}
return "http://" + url
if strings.HasPrefix(url, "http://") {
url = strings.Replace(url, "http://", "https://", 1)
return url
}
return "https://" + url
}
func fillNecessaryInfoForOpenapi3Doc(doc *model.Openapi3T) {

View File

@ -853,11 +853,3 @@ func (p *pluginRepoImpl) MoveAPPPluginToLibrary(ctx context.Context, draftPlugin
return nil
}
func (p *pluginRepoImpl) UpdateDebugExample(ctx context.Context, pluginID int64, openapiDoc *model.Openapi3T) (err error) {
updatedPlugin := entity.NewPluginInfo(&model.PluginInfo{
ID: pluginID,
OpenapiDoc: openapiDoc,
})
return p.pluginDraftDAO.Update(ctx, updatedPlugin)
}

View File

@ -35,7 +35,6 @@ type PluginRepository interface {
UpdateDraftPluginWithCode(ctx context.Context, req *UpdatePluginDraftWithCode) (err error)
DeleteDraftPlugin(ctx context.Context, pluginID int64) (err error)
DeleteAPPAllPlugins(ctx context.Context, appID int64) (pluginIDs []int64, err error)
UpdateDebugExample(ctx context.Context, pluginID int64, openapiDoc *plugin.Openapi3T) (err error)
GetOnlinePlugin(ctx context.Context, pluginID int64, opts ...PluginSelectedOptions) (plugin *entity.PluginInfo, exist bool, err error)
MGetOnlinePlugins(ctx context.Context, pluginIDs []int64, opts ...PluginSelectedOptions) (plugins []*entity.PluginInfo, err error)

View File

@ -23,6 +23,7 @@ import (
"gorm.io/gorm"
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
pluginConf "github.com/coze-dev/coze-studio/backend/domain/plugin/conf"
"github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
"github.com/coze-dev/coze-studio/backend/domain/plugin/internal/dal"
@ -216,7 +217,6 @@ func (t *toolRepoImpl) GetOnlineTool(ctx context.Context, toolID int64) (tool *e
func (t *toolRepoImpl) MGetOnlineTools(ctx context.Context, toolIDs []int64, opts ...ToolSelectedOptions) (tools []*entity.ToolInfo, err error) {
toolProducts := pluginConf.MGetToolProducts(toolIDs)
tools = slices.Transform(toolProducts, func(tool *pluginConf.ToolInfo) *entity.ToolInfo {
return tool.Info
})
@ -270,38 +270,13 @@ func (t *toolRepoImpl) MGetVersionTools(ctx context.Context, versionTools []enti
}
func (t *toolRepoImpl) BindDraftAgentTools(ctx context.Context, agentID int64, toolIDs []int64) (err error) {
opt := &dal.ToolSelectedOption{
ToolID: true,
}
draftAgentTools, err := t.agentToolDraftDAO.GetAll(ctx, agentID, opt)
onlineTools, err := t.MGetOnlineTools(ctx, toolIDs)
if err != nil {
return err
}
draftAgentToolIDMap := slices.ToMap(draftAgentTools, func(tool *entity.ToolInfo) (int64, bool) {
return tool.ID, true
})
bindToolIDMap := slices.ToMap(toolIDs, func(toolID int64) (int64, bool) {
return toolID, true
})
newBindToolIDs := make([]int64, 0, len(toolIDs))
for _, toolID := range toolIDs {
_, ok := draftAgentToolIDMap[toolID]
if ok {
continue
}
newBindToolIDs = append(newBindToolIDs, toolID)
}
removeToolIDs := make([]int64, 0, len(draftAgentTools))
for toolID := range draftAgentToolIDMap {
_, ok := bindToolIDMap[toolID]
if ok {
continue
}
removeToolIDs = append(removeToolIDs, toolID)
if len(onlineTools) == 0 {
return t.agentToolDraftDAO.DeleteAll(ctx, agentID)
}
tx := t.query.Begin()
@ -324,29 +299,17 @@ func (t *toolRepoImpl) BindDraftAgentTools(ctx context.Context, agentID int64, t
}
}()
onlineTools, err := t.MGetOnlineTools(ctx, newBindToolIDs)
err = t.agentToolDraftDAO.DeleteAllWithTX(ctx, tx, agentID)
if err != nil {
return err
}
if len(onlineTools) > 0 {
err = t.agentToolDraftDAO.BatchCreateIgnoreConflictWithTX(ctx, tx, agentID, onlineTools)
if err != nil {
return err
}
}
err = t.agentToolDraftDAO.DeleteWithTX(ctx, tx, agentID, removeToolIDs)
err = t.agentToolDraftDAO.BatchCreateWithTX(ctx, tx, agentID, onlineTools)
if err != nil {
return err
}
err = tx.Commit()
if err != nil {
return err
}
return nil
return tx.Commit()
}
func (t *toolRepoImpl) GetAgentPluginIDs(ctx context.Context, agentID int64) (pluginIDs []int64, err error) {
@ -354,7 +317,7 @@ func (t *toolRepoImpl) GetAgentPluginIDs(ctx context.Context, agentID int64) (pl
}
func (t *toolRepoImpl) DuplicateDraftAgentTools(ctx context.Context, fromAgentID, toAgentID int64) (err error) {
tools, err := t.agentToolDraftDAO.GetAll(ctx, fromAgentID, nil)
tools, err := t.agentToolDraftDAO.GetAll(ctx, fromAgentID)
if err != nil {
return err
}
@ -408,7 +371,7 @@ func (t *toolRepoImpl) UpdateDraftAgentTool(ctx context.Context, req *UpdateDraf
}
func (t *toolRepoImpl) GetSpaceAllDraftAgentTools(ctx context.Context, agentID int64) (tools []*entity.ToolInfo, err error) {
return t.agentToolDraftDAO.GetAll(ctx, agentID, nil)
return t.agentToolDraftDAO.GetAll(ctx, agentID)
}
func (t *toolRepoImpl) GetVersionAgentTool(ctx context.Context, agentID int64, vAgentTool entity.VersionAgentTool) (tool *entity.ToolInfo, exist bool, err error) {
@ -426,3 +389,41 @@ func (t *toolRepoImpl) MGetVersionAgentTool(ctx context.Context, agentID int64,
func (t *toolRepoImpl) BatchCreateVersionAgentTools(ctx context.Context, agentID int64, agentVersion string, tools []*entity.ToolInfo) (err error) {
return t.agentToolVersionDAO.BatchCreate(ctx, agentID, agentVersion, tools)
}
func (t *toolRepoImpl) UpdateDraftToolAndDebugExample(ctx context.Context, pluginID int64, doc *plugin.Openapi3T, updatedTool *entity.ToolInfo) (err error) {
tx := t.query.Begin()
if tx.Error != nil {
return tx.Error
}
defer func() {
if r := recover(); r != nil {
if e := tx.Rollback(); e != nil {
logs.CtxErrorf(ctx, "rollback failed, err=%v", e)
}
err = fmt.Errorf("catch panic: %v\nstack=%s", r, string(debug.Stack()))
return
}
if err != nil {
if e := tx.Rollback(); e != nil {
logs.CtxErrorf(ctx, "rollback failed, err=%v", e)
}
}
}()
err = t.toolDraftDAO.UpdateWithTX(ctx, tx, updatedTool)
if err != nil {
return err
}
updatedPlugin := entity.NewPluginInfo(&plugin.PluginInfo{
ID: pluginID,
OpenapiDoc: doc,
})
err = t.pluginDraftDAO.UpdateWithTX(ctx, tx, updatedPlugin)
if err != nil {
return err
}
return tx.Commit()
}

View File

@ -19,6 +19,7 @@ package repository
import (
"context"
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
"github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
)
@ -53,6 +54,8 @@ type ToolRepository interface {
MGetVersionAgentTool(ctx context.Context, agentID int64, vAgentTools []entity.VersionAgentTool) (tools []*entity.ToolInfo, err error)
BatchCreateVersionAgentTools(ctx context.Context, agentID int64, agentVersion string, tools []*entity.ToolInfo) (err error)
UpdateDraftToolAndDebugExample(ctx context.Context, pluginID int64, doc *plugin.Openapi3T, updatedTool *entity.ToolInfo) (err error)
GetPluginAllDraftTools(ctx context.Context, pluginID int64, opts ...ToolSelectedOptions) (tools []*entity.ToolInfo, err error)
GetPluginAllOnlineTools(ctx context.Context, pluginID int64) (tools []*entity.ToolInfo, err error)
ListPluginDraftTools(ctx context.Context, pluginID int64, pageInfo entity.PageInfo) (tools []*entity.ToolInfo, total int64, err error)

View File

@ -596,7 +596,7 @@ func genRequestString(req *http.Request, body []byte) (string, error) {
return "", fmt.Errorf("[genRequestString] marshal failed, err=%s", err)
}
if len(body) > 0 {
if body != nil {
requestStr, err = sjson.SetRaw(requestStr, "body", string(body))
if err != nil {
return "", fmt.Errorf("[genRequestString] set body failed, err=%s", err)

View File

@ -578,7 +578,7 @@ func (p *pluginServiceImpl) MGetDraftTools(ctx context.Context, toolIDs []int64)
return tools, nil
}
func (p *pluginServiceImpl) UpdateDraftTool(ctx context.Context, req *UpdateDraftToolRequest) (err error) {
func (p *pluginServiceImpl) UpdateDraftTool(ctx context.Context, req *UpdateToolDraftRequest) (err error) {
draftPlugin, exist, err := p.pluginRepo.GetDraftPlugin(ctx, req.PluginID)
if err != nil {
return errorx.Wrapf(err, "GetDraftPlugin failed, pluginID=%d", req.PluginID)
@ -595,14 +595,6 @@ func (p *pluginServiceImpl) UpdateDraftTool(ctx context.Context, req *UpdateDraf
return errorx.New(errno.ErrPluginRecordNotFound)
}
if req.SaveExample != nil {
return p.updateDraftToolDebugExample(ctx, draftPlugin, draftTool, *req.SaveExample, req.DebugExample)
}
return p.updateDraftTool(ctx, req, draftTool)
}
func (p *pluginServiceImpl) updateDraftTool(ctx context.Context, req *UpdateDraftToolRequest, draftTool *entity.ToolInfo) (err error) {
if req.Method != nil && req.SubURL != nil {
api := entity.UniqueToolAPI{
SubURL: ptr.FromOrDefault(req.SubURL, ""),
@ -642,6 +634,9 @@ func (p *pluginServiceImpl) updateDraftTool(ctx context.Context, req *UpdateDraf
if req.Desc != nil {
op.Summary = *req.Desc
}
if req.Parameters != nil {
op.Parameters = req.Parameters
}
if req.APIExtend != nil {
if op.Extensions == nil {
op.Extensions = map[string]any{}
@ -652,12 +647,6 @@ func (p *pluginServiceImpl) updateDraftTool(ctx context.Context, req *UpdateDraf
}
}
// update request parameters
if req.Parameters != nil {
op.Parameters = req.Parameters
}
// update request body
if req.RequestBody == nil {
op.RequestBody = draftTool.Operation.RequestBody
} else {
@ -675,7 +664,6 @@ func (p *pluginServiceImpl) updateDraftTool(ctx context.Context, req *UpdateDraf
op.RequestBody.Value.Content[model.MediaTypeJson] = mType
}
// update responses
if req.Responses == nil {
op.Responses = draftTool.Operation.Responses
} else {
@ -719,24 +707,11 @@ func (p *pluginServiceImpl) updateDraftTool(ctx context.Context, req *UpdateDraf
Operation: op,
}
err = p.toolRepo.UpdateDraftTool(ctx, updatedTool)
if err != nil {
return errorx.Wrapf(err, "UpdateDraftTool failed, toolID=%d", req.ToolID)
}
return nil
}
func (p *pluginServiceImpl) updateDraftToolDebugExample(ctx context.Context, draftPlugin *entity.PluginInfo,
draftTool *entity.ToolInfo, save bool, example *common.DebugExample) (err error) {
components := draftPlugin.OpenapiDoc.Components
if !save && components != nil && components.Examples != nil {
if req.SaveExample != nil && !*req.SaveExample &&
components != nil && components.Examples != nil {
delete(components.Examples, draftTool.Operation.OperationID)
}
if save {
} else if req.DebugExample != nil {
if components == nil {
components = &openapi3.Components{}
}
@ -747,14 +722,14 @@ func (p *pluginServiceImpl) updateDraftToolDebugExample(ctx context.Context, dra
draftPlugin.OpenapiDoc.Components = components
reqExample, respExample := map[string]any{}, map[string]any{}
if example.ReqExample != "" {
err = sonic.UnmarshalString(example.ReqExample, &reqExample)
if req.DebugExample.ReqExample != "" {
err = sonic.UnmarshalString(req.DebugExample.ReqExample, &reqExample)
if err != nil {
return errorx.WrapByCode(err, errno.ErrPluginInvalidOpenapi3Doc, errorx.KV(errno.PluginMsgKey, "invalid request example"))
}
}
if example.RespExample != "" {
err = sonic.UnmarshalString(example.RespExample, &respExample)
if req.DebugExample.RespExample != "" {
err = sonic.UnmarshalString(req.DebugExample.RespExample, &respExample)
if err != nil {
return errorx.WrapByCode(err, errno.ErrPluginInvalidOpenapi3Doc, errorx.KV(errno.PluginMsgKey, "invalid response example"))
}
@ -770,9 +745,9 @@ func (p *pluginServiceImpl) updateDraftToolDebugExample(ctx context.Context, dra
}
}
err = p.pluginRepo.UpdateDebugExample(ctx, draftPlugin.ID, draftPlugin.OpenapiDoc)
err = p.toolRepo.UpdateDraftToolAndDebugExample(ctx, draftPlugin.ID, draftPlugin.OpenapiDoc, updatedTool)
if err != nil {
return errorx.Wrapf(err, "UpdateDebugExample failed, pluginID=%d", draftPlugin.ID)
return errorx.Wrapf(err, "UpdateDraftToolAndDebugExample failed, pluginID=%d, toolID=%d", draftPlugin.ID, req.ToolID)
}
return nil

View File

@ -57,7 +57,7 @@ type PluginService interface {
// Draft Tool
MGetDraftTools(ctx context.Context, toolIDs []int64) (tools []*entity.ToolInfo, err error)
UpdateDraftTool(ctx context.Context, req *UpdateDraftToolRequest) (err error)
UpdateDraftTool(ctx context.Context, req *UpdateToolDraftRequest) (err error)
ConvertToOpenapi3Doc(ctx context.Context, req *ConvertToOpenapi3DocRequest) (resp *ConvertToOpenapi3DocResponse)
CreateDraftToolsWithCode(ctx context.Context, req *CreateDraftToolsWithCodeRequest) (resp *CreateDraftToolsWithCodeResponse, err error)
CheckPluginToolsDebugStatus(ctx context.Context, pluginID int64) (err error)
@ -312,7 +312,7 @@ type PublishAPPPluginsResponse = model.PublishAPPPluginsResponse
type MGetPluginLatestVersionResponse = model.MGetPluginLatestVersionResponse
type UpdateDraftToolRequest struct {
type UpdateToolDraftRequest struct {
PluginID int64
ToolID int64
Name *string

View File

@ -53,6 +53,20 @@ type AsTool interface {
allInterruptEvents map[string]*entity.ToolInterruptEvent) compose.Option
}
type ConversationService interface {
CreateDraftConversationTemplate(ctx context.Context, template *vo.CreateConversationTemplateMeta) (int64, error)
UpdateDraftConversationTemplateName(ctx context.Context, appID int64, userID int64, templateID int64, name string) error
DeleteDraftConversationTemplate(ctx context.Context, templateID int64, wfID2ConversationName map[int64]string) (int64, error)
CheckWorkflowsToReplace(ctx context.Context, appID int64, templateID int64) ([]*entity.Workflow, error)
DeleteDynamicConversation(ctx context.Context, env vo.Env, templateID int64) (int64, error)
ListConversationTemplate(ctx context.Context, env vo.Env, policy *vo.ListConversationTemplatePolicy) ([]*entity.ConversationTemplate, error)
MGetStaticConversation(ctx context.Context, env vo.Env, userID, connectorID int64, templateIDs []int64) ([]*entity.StaticConversation, error)
ListDynamicConversation(ctx context.Context, env vo.Env, policy *vo.ListConversationPolicy) ([]*entity.DynamicConversation, error)
ReleaseConversationTemplate(ctx context.Context, appID int64, version string) error
InitApplicationDefaultConversationTemplate(ctx context.Context, spaceID int64, appID int64, userID int64) error
}
type InterruptEventStore interface {
SaveInterruptEvents(ctx context.Context, wfExeID int64, events []*entity.InterruptEvent) error
GetFirstInterruptEvent(ctx context.Context, wfExeID int64) (*entity.InterruptEvent, bool, error)
@ -90,3 +104,22 @@ type ToolFromWorkflow interface {
TerminatePlan() vo.TerminatePlan
GetWorkflow() *entity.Workflow
}
type ConversationIDGenerator func(ctx context.Context, appID int64, userID, connectorID int64) (int64, error)
type ConversationRepository interface {
CreateDraftConversationTemplate(ctx context.Context, template *vo.CreateConversationTemplateMeta) (int64, error)
UpdateDraftConversationTemplateName(ctx context.Context, templateID int64, name string) error
DeleteDraftConversationTemplate(ctx context.Context, templateID int64) (int64, error)
GetConversationTemplate(ctx context.Context, env vo.Env, policy vo.GetConversationTemplatePolicy) (*entity.ConversationTemplate, bool, error)
DeleteDynamicConversation(ctx context.Context, env vo.Env, id int64) (int64, error)
ListConversationTemplate(ctx context.Context, env vo.Env, policy *vo.ListConversationTemplatePolicy) ([]*entity.ConversationTemplate, error)
MGetStaticConversation(ctx context.Context, env vo.Env, userID, connectorID int64, templateIDs []int64) ([]*entity.StaticConversation, error)
GetOrCreateStaticConversation(ctx context.Context, env vo.Env, idGen ConversationIDGenerator, meta *vo.CreateStaticConversation) (int64, bool, error)
GetOrCreateDynamicConversation(ctx context.Context, env vo.Env, idGen ConversationIDGenerator, meta *vo.CreateDynamicConversation) (int64, bool, error)
GetDynamicConversationByName(ctx context.Context, env vo.Env, appID, connectorID, userID int64, name string) (*entity.DynamicConversation, bool, error)
GetStaticConversationByTemplateID(ctx context.Context, env vo.Env, userID, connectorID, templateID int64) (*entity.StaticConversation, bool, error)
ListDynamicConversation(ctx context.Context, env vo.Env, policy *vo.ListConversationPolicy) ([]*entity.DynamicConversation, error)
BatchCreateOnlineConversationTemplate(ctx context.Context, templates []*entity.ConversationTemplate, version string) error
UpdateDynamicConversationNameByID(ctx context.Context, env vo.Env, templateID int64, name string) error
}

View File

@ -16,46 +16,116 @@
package conversation
import "context"
import (
"context"
)
type ClearMessageRequest struct {
Name string
}
type ClearMessageResponse struct {
IsSuccess bool
}
type CreateConversationRequest struct {
Name string
AppID int64
UserID int64
ConnectorID int64
}
type CreateConversationResponse struct {
Result map[string]any
type CreateMessageRequest struct {
ConversationID int64
Role string
Content string
ContentType string
UserID int64
AppID int64
RunID int64
}
type ListMessageRequest struct {
ConversationName string
Limit *int
BeforeID *string
AfterID *string
}
type Message struct {
ID string `json:"id"`
Role string `json:"role"`
ContentType string `json:"contentType"`
Content string `json:"content"`
type MessageListRequest struct {
ConversationID int64
Limit int64
BeforeID *string
AfterID *string
UserID int64
AppID int64
OrderBy *string
}
type ListMessageResponse struct {
type MessageListResponse struct {
Messages []*Message
FirstID string
LastID string
HasMore bool
}
var ConversationManagerImpl ConversationManager
var conversationManagerImpl ConversationManager
type ConversationManager interface {
ClearMessage(context.Context, *ClearMessageRequest) (*ClearMessageResponse, error)
CreateConversation(ctx context.Context, c *CreateConversationRequest) (*CreateConversationResponse, error)
MessageList(ctx context.Context, req *ListMessageRequest) (*ListMessageResponse, error)
func GetConversationManager() ConversationManager {
return conversationManagerImpl
}
func SetConversationManager(c ConversationManager) {
conversationManagerImpl = c
}
type ConversationHistoryRequest struct {
ConversationID int64
AppID int64
UserID int64
Rounds int64
}
type Content struct {
Type string `json:"type"`
Text *string `json:"text,omitempty"`
Uri *string `json:"uri,omitempty"`
}
type Message struct {
ID int64
Role string `json:"role"` // user or assistant
MultiContent []*Content `json:"multi_content"`
Text *string `json:"text,omitempty"`
ContentType string `json:"content_type"`
}
type ConversationHistoryResponse struct {
Messages []*Message
}
type GetLatestRunIDsRequest struct {
ConversationID int64
UserID int64
AppID int64
Rounds int64
}
type ClearConversationHistoryReq struct {
ConversationID int64
}
type DeleteMessageRequest struct {
ConversationID int64
MessageID int64
}
type EditMessageRequest struct {
ConversationID int64
MessageID int64
Content string
}
type GetMessagesByRunIDsRequest struct {
ConversationID int64
RunIDs []int64
}
type GetMessagesByRunIDsResponse struct {
Messages []*Message
}
//go:generate mockgen -destination conversationmock/conversation_mock.go --package conversationmock -source conversation.go
type ConversationManager interface {
CreateConversation(ctx context.Context, req *CreateConversationRequest) (int64, error)
CreateMessage(ctx context.Context, req *CreateMessageRequest) (int64, error)
MessageList(ctx context.Context, req *MessageListRequest) (*MessageListResponse, error)
GetLatestRunIDs(ctx context.Context, req *GetLatestRunIDsRequest) ([]int64, error)
GetMessagesByRunIDs(ctx context.Context, req *GetMessagesByRunIDsRequest) (*GetMessagesByRunIDsResponse, error)
ClearConversationHistory(ctx context.Context, req *ClearConversationHistoryReq) error
DeleteMessage(ctx context.Context, req *DeleteMessageRequest) error
EditMessage(ctx context.Context, req *EditMessageRequest) error
}

View File

@ -0,0 +1,159 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: conversation.go
//
// Generated by this command:
//
// mockgen -destination conversationmock/conversation_mock.go --package conversationmock -source conversation.go
//
// Package conversationmock is a generated GoMock package.
package conversationmock
import (
context "context"
reflect "reflect"
conversation "github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/conversation"
gomock "go.uber.org/mock/gomock"
)
// MockConversationManager is a mock of ConversationManager interface.
type MockConversationManager struct {
ctrl *gomock.Controller
recorder *MockConversationManagerMockRecorder
isgomock struct{}
}
// MockConversationManagerMockRecorder is the mock recorder for MockConversationManager.
type MockConversationManagerMockRecorder struct {
mock *MockConversationManager
}
// NewMockConversationManager creates a new mock instance.
func NewMockConversationManager(ctrl *gomock.Controller) *MockConversationManager {
mock := &MockConversationManager{ctrl: ctrl}
mock.recorder = &MockConversationManagerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockConversationManager) EXPECT() *MockConversationManagerMockRecorder {
return m.recorder
}
// ClearConversationHistory mocks base method.
func (m *MockConversationManager) ClearConversationHistory(ctx context.Context, req *conversation.ClearConversationHistoryReq) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ClearConversationHistory", ctx, req)
ret0, _ := ret[0].(error)
return ret0
}
// ClearConversationHistory indicates an expected call of ClearConversationHistory.
func (mr *MockConversationManagerMockRecorder) ClearConversationHistory(ctx, req any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearConversationHistory", reflect.TypeOf((*MockConversationManager)(nil).ClearConversationHistory), ctx, req)
}
// CreateConversation mocks base method.
func (m *MockConversationManager) CreateConversation(ctx context.Context, req *conversation.CreateConversationRequest) (int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateConversation", ctx, req)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateConversation indicates an expected call of CreateConversation.
func (mr *MockConversationManagerMockRecorder) CreateConversation(ctx, req any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateConversation", reflect.TypeOf((*MockConversationManager)(nil).CreateConversation), ctx, req)
}
// CreateMessage mocks base method.
func (m *MockConversationManager) CreateMessage(ctx context.Context, req *conversation.CreateMessageRequest) (int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateMessage", ctx, req)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateMessage indicates an expected call of CreateMessage.
func (mr *MockConversationManagerMockRecorder) CreateMessage(ctx, req any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateMessage", reflect.TypeOf((*MockConversationManager)(nil).CreateMessage), ctx, req)
}
// DeleteMessage mocks base method.
func (m *MockConversationManager) DeleteMessage(ctx context.Context, req *conversation.DeleteMessageRequest) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteMessage", ctx, req)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteMessage indicates an expected call of DeleteMessage.
func (mr *MockConversationManagerMockRecorder) DeleteMessage(ctx, req any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMessage", reflect.TypeOf((*MockConversationManager)(nil).DeleteMessage), ctx, req)
}
// EditMessage mocks base method.
func (m *MockConversationManager) EditMessage(ctx context.Context, req *conversation.EditMessageRequest) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EditMessage", ctx, req)
ret0, _ := ret[0].(error)
return ret0
}
// EditMessage indicates an expected call of EditMessage.
func (mr *MockConversationManagerMockRecorder) EditMessage(ctx, req any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EditMessage", reflect.TypeOf((*MockConversationManager)(nil).EditMessage), ctx, req)
}
// GetLatestRunIDs mocks base method.
func (m *MockConversationManager) GetLatestRunIDs(ctx context.Context, req *conversation.GetLatestRunIDsRequest) ([]int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetLatestRunIDs", ctx, req)
ret0, _ := ret[0].([]int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetLatestRunIDs indicates an expected call of GetLatestRunIDs.
func (mr *MockConversationManagerMockRecorder) GetLatestRunIDs(ctx, req any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestRunIDs", reflect.TypeOf((*MockConversationManager)(nil).GetLatestRunIDs), ctx, req)
}
// GetMessagesByRunIDs mocks base method.
func (m *MockConversationManager) GetMessagesByRunIDs(ctx context.Context, req *conversation.GetMessagesByRunIDsRequest) (*conversation.GetMessagesByRunIDsResponse, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetMessagesByRunIDs", ctx, req)
ret0, _ := ret[0].(*conversation.GetMessagesByRunIDsResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetMessagesByRunIDs indicates an expected call of GetMessagesByRunIDs.
func (mr *MockConversationManagerMockRecorder) GetMessagesByRunIDs(ctx, req any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessagesByRunIDs", reflect.TypeOf((*MockConversationManager)(nil).GetMessagesByRunIDs), ctx, req)
}
// MessageList mocks base method.
func (m *MockConversationManager) MessageList(ctx context.Context, req *conversation.MessageListRequest) (*conversation.MessageListResponse, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "MessageList", ctx, req)
ret0, _ := ret[0].(*conversation.MessageListResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// MessageList indicates an expected call of MessageList.
func (mr *MockConversationManagerMockRecorder) MessageList(ctx, req any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessageList", reflect.TypeOf((*MockConversationManager)(nil).MessageList), ctx, req)
}

View File

@ -19,6 +19,7 @@ package knowledge
import (
"context"
"github.com/cloudwego/eino/schema"
"github.com/coze-dev/coze-studio/backend/infra/contract/document/parser"
)
@ -87,6 +88,7 @@ type RetrieveRequest struct {
Query string
KnowledgeIDs []int64
RetrievalStrategy *RetrievalStrategy
ChatHistory []*schema.Message
}
type Slice struct {

View File

@ -35,6 +35,7 @@ type LLMParams struct {
TopP *float64 `json:"topP"`
TopK *int `json:"topK"`
EnableChatHistory bool `json:"enableChatHistory"`
ChatHistoryRound int64 `json:"chatHistoryRound"`
SystemPrompt string `json:"systemPrompt"`
ResponseFormat ResponseFormat `json:"responseFormat"`
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package entity
import "time"
type ChatFlowRole struct {
ID int64
WorkflowID int64
ConnectorID int64
Name string
Description string
Version string
AvatarUri string
BackgroundImageInfo string
OnboardingInfo string
SuggestReplyInfo string
AudioConfig string
UserInputConfig string
CreatorID int64
CreatedAt time.Time
UpdatedAt time.Time
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package entity
type ConversationTemplate struct {
SpaceID int64
AppID int64
Name string
TemplateID int64
}
type StaticConversation struct {
UserID int64
ConnectorID int64
TemplateID int64
ConversationID int64
}
type DynamicConversation struct {
ID int64
UserID int64
ConnectorID int64
ConversationID int64
Name string
}

View File

@ -16,6 +16,15 @@
package entity
import (
"fmt"
"strconv"
"github.com/coze-dev/coze-studio/backend/api/model/ocean/cloud/workflow"
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
"github.com/coze-dev/coze-studio/backend/types/errno"
)
type NodeType string
type NodeTypeMeta struct {
@ -116,8 +125,11 @@ const (
NodeTypeCodeRunner NodeType = "CodeRunner"
NodeTypePlugin NodeType = "Plugin"
NodeTypeCreateConversation NodeType = "CreateConversation"
NodeTypeConversationList NodeType = "ConversationList"
NodeTypeMessageList NodeType = "MessageList"
NodeTypeClearMessage NodeType = "ClearMessage"
NodeTypeCreateMessage NodeType = "CreateMessage"
NodeTypeEditMessage NodeType = "EditMessage"
NodeTypeDeleteMessage NodeType = "DeleteMessage"
NodeTypeLambda NodeType = "Lambda"
NodeTypeLLM NodeType = "LLM"
NodeTypeSelector NodeType = "Selector"
@ -125,9 +137,93 @@ const (
NodeTypeSubWorkflow NodeType = "SubWorkflow"
NodeTypeJsonSerialization NodeType = "JsonSerialization"
NodeTypeJsonDeserialization NodeType = "JsonDeserialization"
NodeTypeConversationUpdate NodeType = "ConversationUpdate"
NodeTypeConversationDelete NodeType = "ConversationDelete"
NodeTypeClearConversationHistory NodeType = "ClearConversationHistory"
NodeTypeConversationHistory NodeType = "ConversationHistory"
NodeTypeComment NodeType = "Comment"
)
const (
EntryNodeKey = "100001"
ExitNodeKey = "900001"
)
var blockType2NodeType = map[vo.BlockType]NodeType{
vo.BlockTypeBotStart: NodeTypeEntry,
vo.BlockTypeBotEnd: NodeTypeExit,
vo.BlockTypeBotLLM: NodeTypeLLM,
vo.BlockTypeBotAPI: NodeTypePlugin,
vo.BlockTypeBotCode: NodeTypeCodeRunner,
vo.BlockTypeBotDataset: NodeTypeKnowledgeRetriever,
vo.BlockTypeCondition: NodeTypeSelector,
vo.BlockTypeBotSubWorkflow: NodeTypeSubWorkflow,
vo.BlockTypeDatabase: NodeTypeDatabaseCustomSQL,
vo.BlockTypeBotMessage: NodeTypeOutputEmitter,
vo.BlockTypeBotText: NodeTypeTextProcessor,
vo.BlockTypeQuestion: NodeTypeQuestionAnswer,
vo.BlockTypeBotBreak: NodeTypeBreak,
vo.BlockTypeBotLoopSetVariable: NodeTypeVariableAssignerWithinLoop,
vo.BlockTypeBotLoop: NodeTypeLoop,
vo.BlockTypeBotIntent: NodeTypeIntentDetector,
vo.BlockTypeBotDatasetWrite: NodeTypeKnowledgeIndexer,
vo.BlockTypeBotInput: NodeTypeInputReceiver,
vo.BlockTypeBotBatch: NodeTypeBatch,
vo.BlockTypeBotContinue: NodeTypeContinue,
vo.BlockTypeBotComment: NodeTypeComment,
vo.BlockTypeBotVariableMerge: NodeTypeVariableAggregator,
vo.BlockTypeCreateConversation: NodeTypeCreateConversation,
vo.BlockTypeBotAssignVariable: NodeTypeVariableAssigner,
vo.BlockTypeDatabaseUpdate: NodeTypeDatabaseUpdate,
vo.BlockTypeDatabaseSelect: NodeTypeDatabaseQuery,
vo.BlockTypeDatabaseDelete: NodeTypeDatabaseDelete,
vo.BlockTypeDatabaseInsert: NodeTypeDatabaseInsert,
vo.BlockTypeBotHttp: NodeTypeHTTPRequester,
vo.BlockTypeConversationUpdate: NodeTypeConversationUpdate,
vo.BlockTypeConversationDelete: NodeTypeConversationDelete,
vo.BlockTypeJsonSerialization: NodeTypeJsonSerialization,
vo.BlockTypeJsonDeserialization: NodeTypeJsonDeserialization,
vo.BlockTypeBotDatasetDelete: NodeTypeKnowledgeDeleter,
vo.BlockTypeConversationList: NodeTypeConversationList,
vo.BlockTypeClearConversationHistory: NodeTypeClearConversationHistory,
vo.BlockTypeConversationHistory: NodeTypeConversationHistory,
vo.BlockTypeBotMessageList: NodeTypeMessageList,
vo.BlockTypeCreateMessage: NodeTypeCreateMessage,
vo.BlockTypeEditeMessage: NodeTypeEditMessage,
vo.BlockTypeDeleteMessage: NodeTypeDeleteMessage,
}
var nodeType2BlockType = func() map[NodeType]vo.BlockType {
nodeType2BlockType := make(map[NodeType]vo.BlockType, len(blockType2NodeType))
for k, v := range blockType2NodeType {
nodeType2BlockType[v] = k
}
return nodeType2BlockType
}()
func BlockType2EntityNodeType(t string) (NodeType, error) {
blockType := vo.BlockType(t)
if nodeType, ok := blockType2NodeType[blockType]; ok {
return nodeType, nil
}
return "", fmt.Errorf("cannot map block type'%s' to a node type", t)
}
func NodeTypeToAPINodeTemplateType(nodeType NodeType) (workflow.NodeTemplateType, error) {
if blockType, ok := nodeType2BlockType[nodeType]; ok {
blockTypeInt, err := strconv.ParseInt(string(blockType), 10, 64)
if err != nil {
return 0, err
}
return workflow.NodeTemplateType(blockTypeInt), nil
}
return workflow.NodeTemplateType(0), fmt.Errorf("cannot map entity node type '%s' to a workflow.NodeTemplateType", nodeType)
}
func NodeTypeToBlockType(nodeType NodeType) (vo.BlockType, error) {
if t, ok := nodeType2BlockType[nodeType]; ok {
return t, nil
}
return "", vo.WrapError(errno.ErrSchemaConversionFail,
fmt.Errorf("cannot map entity node type '%s' to a block type", nodeType))
}

View File

@ -17,6 +17,8 @@
package entity
import (
"fmt"
"github.com/coze-dev/coze-studio/backend/pkg/i18n"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ternary"
)
@ -481,7 +483,6 @@ var NodeTypeMetas = []*NodeTypeMeta{
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Conversation-List.jpeg",
SupportBatch: false, // supportBatch: 1
Disabled: true,
ExecutableMeta: ExecutableMeta{
PreFillZero: true,
PostFillNil: true,
@ -492,14 +493,13 @@ var NodeTypeMetas = []*NodeTypeMeta{
},
{
ID: 38,
Name: "清除上下文",
Type: NodeTypeClearMessage,
Name: "清空会话历史",
Type: NodeTypeClearConversationHistory,
Category: "conversation_history", // Mapped from cate_list
Desc: "用于清空会话历史清空后LLM看到的会话历史为空",
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Conversation-Delete.jpeg",
SupportBatch: false, // supportBatch: 1
Disabled: true,
ExecutableMeta: ExecutableMeta{
PreFillZero: true,
PostFillNil: true,
@ -508,6 +508,25 @@ var NodeTypeMetas = []*NodeTypeMeta{
EnUSName: "Clear conversation history",
EnUSDescription: "Used to clear conversation history. After clearing, the conversation history visible to the LLM node will be empty.",
},
{
ID: 54,
Name: "查询会话历史",
Type: NodeTypeConversationHistory,
Category: "conversation_history", // Mapped from cate_list
Desc: "用于查询会话历史返回LLM可见的会话消息",
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-查询会话历史.jpg",
SupportBatch: false,
ExecutableMeta: ExecutableMeta{
PreFillZero: true,
PostFillNil: true,
StreamingParadigms: map[StreamingParadigm]bool{Invoke: true},
},
EnUSName: "Query Conversation History",
EnUSDescription: "Used to query conversation history, returns conversation messages visible to the LLM",
},
{
ID: 39,
Name: "创建会话",
@ -517,15 +536,51 @@ var NodeTypeMetas = []*NodeTypeMeta{
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Conversation-Create.jpeg",
SupportBatch: false, // supportBatch: 1
Disabled: true,
ExecutableMeta: ExecutableMeta{
PreFillZero: true,
PostFillNil: true,
StreamingParadigms: map[StreamingParadigm]bool{Invoke: true},
},
EnUSName: "Create conversation",
EnUSName: "Create Conversation",
EnUSDescription: "This node is used to create a conversation.",
},
{
ID: 51,
Name: "修改会话",
Type: NodeTypeConversationUpdate,
Category: "conversation_management", // Mapped from cate_list
Desc: "用于修改会话的名字",
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-编辑会话.jpg",
SupportBatch: false,
ExecutableMeta: ExecutableMeta{
PreFillZero: true,
PostFillNil: true,
StreamingParadigms: map[StreamingParadigm]bool{Invoke: true},
},
EnUSName: "Edit Conversation",
EnUSDescription: "Used to modify the name of a conversation.",
},
{
ID: 52,
Name: "删除会话",
Type: NodeTypeConversationDelete,
Category: "conversation_management", // Mapped from cate_list
Desc: "用于删除会话",
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-删除会话.jpg",
SupportBatch: false,
ExecutableMeta: ExecutableMeta{
PreFillZero: true,
PostFillNil: true,
StreamingParadigms: map[StreamingParadigm]bool{Invoke: true},
},
EnUSName: "Delete Conversation",
EnUSDescription: "Used to delete a conversation.",
},
{
ID: 40,
Name: "变量赋值",
@ -632,13 +687,83 @@ var NodeTypeMetas = []*NodeTypeMeta{
EnUSName: "Add Data",
EnUSDescription: "Add new data records to the table, and insert them into the database after the user enters the data content",
},
{
ID: 53,
Name: "查询会话列表",
Type: NodeTypeConversationList,
Category: "conversation_management",
Desc: "用于查询所有会话,包含静态会话、动态会话",
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-查询会话.jpg",
SupportBatch: false,
ExecutableMeta: ExecutableMeta{
PostFillNil: true,
StreamingParadigms: map[StreamingParadigm]bool{Invoke: true},
},
EnUSName: "Query Conversation List",
EnUSDescription: "Used to query all conversations, including static conversations and dynamic conversations",
},
{
ID: 55,
Name: "创建消息",
Type: NodeTypeCreateMessage,
Category: "message",
Desc: "用于创建消息",
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-创建消息.jpg",
SupportBatch: false, // supportBatch: 1
ExecutableMeta: ExecutableMeta{
PreFillZero: true,
PostFillNil: true,
StreamingParadigms: map[StreamingParadigm]bool{Invoke: true},
},
EnUSName: "Create message",
EnUSDescription: "Used to create messages",
},
{
ID: 56,
Name: "修改消息",
Type: NodeTypeEditMessage,
Category: "message",
Desc: "用于修改消息",
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-修改消息.jpg",
SupportBatch: false, // supportBatch: 1
ExecutableMeta: ExecutableMeta{
PreFillZero: true,
PostFillNil: true,
StreamingParadigms: map[StreamingParadigm]bool{Invoke: true},
},
EnUSName: "Edit message",
EnUSDescription: "Used to edit messages",
},
{
ID: 57,
Name: "删除消息",
Type: NodeTypeDeleteMessage,
Category: "message",
Desc: "用于删除消息",
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-删除消息.jpg",
SupportBatch: false, // supportBatch: 1
ExecutableMeta: ExecutableMeta{
PreFillZero: true,
PostFillNil: true,
StreamingParadigms: map[StreamingParadigm]bool{Invoke: true},
},
EnUSName: "Delete message",
EnUSDescription: "Used to delete messages",
},
{
ID: 58,
Name: "JSON 序列化",
Type: NodeTypeJsonSerialization,
Category: "utilities",
Desc: "用于把变量转化为JSON字符串",
Color: "F2B600",
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-to_json.png",
SupportBatch: false,
ExecutableMeta: ExecutableMeta{
@ -656,7 +781,7 @@ var NodeTypeMetas = []*NodeTypeMeta{
Type: NodeTypeJsonDeserialization,
Category: "utilities",
Desc: "用于将JSON字符串解析为变量",
Color: "F2B600",
Color: "#F2B600",
IconURL: "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-from_json.png",
SupportBatch: false,
ExecutableMeta: ExecutableMeta{
@ -862,6 +987,133 @@ const defaultEnUSInitCanvasJsonSchema = `{
}
}`
const defaultZhCNInitCanvasJsonSchemaChat = `{
"nodes": [{
"id": "100001",
"type": "1",
"meta": {
"position": {
"x": 0,
"y": 0
}
},
"data": {
"outputs": [{
"type": "string",
"name": "USER_INPUT",
"required": true
}, {
"type": "string",
"name": "CONVERSATION_NAME",
"required": false,
"description": "本次请求绑定的会话,会自动写入消息、会从该会话读对话历史。",
"defaultValue": "%s"
}],
"nodeMeta": {
"title": "开始",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start.png",
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
"subTitle": ""
}
}
}, {
"id": "900001",
"type": "2",
"meta": {
"position": {
"x": 1000,
"y": 0
}
},
"data": {
"nodeMeta": {
"title": "结束",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End.png",
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
"subTitle": ""
},
"inputs": {
"terminatePlan": "useAnswerContent",
"streamingOutput": true,
"inputParameters": [{
"name": "output",
"input": {
"type": "string",
"value": {
"type": "ref"
}
}
}]
}
}
}]
}`
const defaultEnUSInitCanvasJsonSchemaChat = `{
"nodes": [{
"id": "100001",
"type": "1",
"meta": {
"position": {
"x": 0,
"y": 0
}
},
"data": {
"outputs": [{
"type": "string",
"name": "USER_INPUT",
"required": true
}, {
"type": "string",
"name": "CONVERSATION_NAME",
"required": false,
"description": "The conversation bound to this request will automatically write messages and read conversation history from that conversation.",
"defaultValue": "%s"
}],
"nodeMeta": {
"title": "Start",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start.png",
"description": "The starting node of the workflow, used to set the information needed to initiate the workflow.",
"subTitle": ""
}
}
}, {
"id": "900001",
"type": "2",
"meta": {
"position": {
"x": 1000,
"y": 0
}
},
"data": {
"nodeMeta": {
"title": "End",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End.png",
"description": "The final node of the workflow, used to return the result information after the workflow runs.",
"subTitle": ""
},
"inputs": {
"terminatePlan": "useAnswerContent",
"streamingOutput": true,
"inputParameters": [{
"name": "output",
"input": {
"type": "string",
"value": {
"type": "ref"
}
}
}]
}
}
}]
}`
func GetDefaultInitCanvasJsonSchema(locale i18n.Locale) string {
return ternary.IFElse(locale == i18n.LocaleEN, defaultEnUSInitCanvasJsonSchema, defaultZhCNInitCanvasJsonSchema)
}
func GetDefaultInitCanvasJsonSchemaChat(locale i18n.Locale, name string) string {
return ternary.IFElse(locale == i18n.LocaleEN, fmt.Sprintf(defaultEnUSInitCanvasJsonSchemaChat, name), fmt.Sprintf(defaultZhCNInitCanvasJsonSchemaChat, name))
}

View File

@ -70,15 +70,16 @@ type Data struct {
}
type Inputs struct {
InputParameters []*Param `json:"inputParameters"`
Content *BlockInput `json:"content"`
TerminatePlan *TerminatePlan `json:"terminatePlan,omitempty"`
StreamingOutput bool `json:"streamingOutput,omitempty"`
CallTransferVoice bool `json:"callTransferVoice,omitempty"`
ChatHistoryWriting string `json:"chatHistoryWriting,omitempty"`
LLMParam any `json:"llmParam,omitempty"` // The LLMParam type may be one of the LLMParam or IntentDetectorLLMParam type or QALLMParam type
FCParam *FCParam `json:"fcParam,omitempty"`
SettingOnError *SettingOnError `json:"settingOnError,omitempty"`
InputParameters []*Param `json:"inputParameters"`
Content *BlockInput `json:"content"`
TerminatePlan *TerminatePlan `json:"terminatePlan,omitempty"`
StreamingOutput bool `json:"streamingOutput,omitempty"`
CallTransferVoice bool `json:"callTransferVoice,omitempty"`
ChatHistoryWriting string `json:"chatHistoryWriting,omitempty"`
ChatHistorySetting *ChatHistorySetting `json:"chatHistorySetting,omitempty"`
LLMParam any `json:"llmParam,omitempty"` // The LLMParam type may be one of the LLMParam or IntentDetectorLLMParam type or QALLMParam type
FCParam *FCParam `json:"fcParam,omitempty"`
SettingOnError *SettingOnError `json:"settingOnError,omitempty"`
LoopType LoopType `json:"loopType,omitempty"`
LoopCount *BlockInput `json:"loopCount,omitempty"`
@ -372,9 +373,8 @@ type DatabaseInfo struct {
}
type IntentDetector struct {
ChatHistorySetting *ChatHistorySetting `json:"chatHistorySetting,omitempty"`
Intents []*Intent `json:"intents,omitempty"`
Mode string `json:"mode,omitempty"`
Intents []*Intent `json:"intents,omitempty"`
Mode string `json:"mode,omitempty"`
}
type ChatHistorySetting struct {
EnableChatHistory bool `json:"enableChatHistory,omitempty"`
@ -445,37 +445,47 @@ func (b BlockType) String() string {
}
const (
BlockTypeBotStart BlockType = "1"
BlockTypeBotEnd BlockType = "2"
BlockTypeBotLLM BlockType = "3"
BlockTypeBotAPI BlockType = "4"
BlockTypeBotCode BlockType = "5"
BlockTypeBotDataset BlockType = "6"
BlockTypeCondition BlockType = "8"
BlockTypeBotSubWorkflow BlockType = "9"
BlockTypeDatabase BlockType = "12"
BlockTypeBotMessage BlockType = "13"
BlockTypeBotText BlockType = "15"
BlockTypeQuestion BlockType = "18"
BlockTypeBotBreak BlockType = "19"
BlockTypeBotLoopSetVariable BlockType = "20"
BlockTypeBotLoop BlockType = "21"
BlockTypeBotIntent BlockType = "22"
BlockTypeBotDatasetWrite BlockType = "27"
BlockTypeBotInput BlockType = "30"
BlockTypeBotBatch BlockType = "28"
BlockTypeBotContinue BlockType = "29"
BlockTypeBotComment BlockType = "31"
BlockTypeBotVariableMerge BlockType = "32"
BlockTypeBotAssignVariable BlockType = "40"
BlockTypeDatabaseUpdate BlockType = "42"
BlockTypeDatabaseSelect BlockType = "43"
BlockTypeDatabaseDelete BlockType = "44"
BlockTypeBotHttp BlockType = "45"
BlockTypeDatabaseInsert BlockType = "46"
BlockTypeJsonSerialization BlockType = "58"
BlockTypeJsonDeserialization BlockType = "59"
BlockTypeBotDatasetDelete BlockType = "60"
BlockTypeBotStart BlockType = "1"
BlockTypeBotEnd BlockType = "2"
BlockTypeBotLLM BlockType = "3"
BlockTypeBotAPI BlockType = "4"
BlockTypeBotCode BlockType = "5"
BlockTypeBotDataset BlockType = "6"
BlockTypeCondition BlockType = "8"
BlockTypeBotSubWorkflow BlockType = "9"
BlockTypeDatabase BlockType = "12"
BlockTypeBotMessage BlockType = "13"
BlockTypeBotText BlockType = "15"
BlockTypeQuestion BlockType = "18"
BlockTypeBotBreak BlockType = "19"
BlockTypeBotLoopSetVariable BlockType = "20"
BlockTypeBotLoop BlockType = "21"
BlockTypeBotIntent BlockType = "22"
BlockTypeBotDatasetWrite BlockType = "27"
BlockTypeBotInput BlockType = "30"
BlockTypeBotBatch BlockType = "28"
BlockTypeBotContinue BlockType = "29"
BlockTypeBotComment BlockType = "31"
BlockTypeBotVariableMerge BlockType = "32"
BlockTypeBotMessageList BlockType = "37"
BlockTypeClearConversationHistory BlockType = "38"
BlockTypeCreateConversation BlockType = "39"
BlockTypeBotAssignVariable BlockType = "40"
BlockTypeDatabaseUpdate BlockType = "42"
BlockTypeDatabaseSelect BlockType = "43"
BlockTypeDatabaseDelete BlockType = "44"
BlockTypeBotHttp BlockType = "45"
BlockTypeDatabaseInsert BlockType = "46"
BlockTypeConversationList BlockType = "53"
BlockTypeConversationUpdate BlockType = "51"
BlockTypeConversationDelete BlockType = "52"
BlockTypeConversationHistory BlockType = "54"
BlockTypeCreateMessage BlockType = "55"
BlockTypeEditeMessage BlockType = "56"
BlockTypeDeleteMessage BlockType = "57"
BlockTypeJsonSerialization BlockType = "58"
BlockTypeJsonDeserialization BlockType = "59"
BlockTypeBotDatasetDelete BlockType = "60"
)
type VariableType string

View File

@ -0,0 +1,54 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package vo
type ChatFlowRoleCreate struct {
WorkflowID int64
CreatorID int64
Name string
Description string
AvatarUri string
BackgroundImageInfo string
OnboardingInfo string
SuggestReplyInfo string
AudioConfig string
UserInputConfig string
}
type ChatFlowRoleUpdate struct {
WorkflowID int64
Name *string
Description *string
AvatarUri *string
BackgroundImageInfo *string
OnboardingInfo *string
SuggestReplyInfo *string
AudioConfig *string
UserInputConfig *string
}
type PublishRolePolicy struct {
WorkflowID int64
CreatorID int64
Version string
}
type CopyRolePolicy struct {
SourceID int64
TargetID int64
CreatorID int64
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package vo
type Env string
const (
Draft Env = "draft"
Online Env = "online"
)
type CreateConversationTemplateMeta struct {
UserID int64
AppID int64
SpaceID int64
Name string
}
type GetConversationTemplatePolicy struct {
AppID *int64
Name *string
Version *string
TemplateID *int64
}
type ListConversationTemplatePolicy struct {
AppID int64
Page *Page
NameLike *string
Version *string
}
type ListConversationMeta struct {
APPID int64
UserID int64
ConnectorID int64
}
type ListConversationPolicy struct {
ListConversationMeta
Page *Page
NameLike *string
Version *string
}
type CreateStaticConversation struct {
AppID int64
UserID int64
ConnectorID int64
TemplateID int64
}
type CreateDynamicConversation struct {
AppID int64
UserID int64
ConnectorID int64
Name string
}

View File

@ -16,22 +16,27 @@
package vo
import "github.com/coze-dev/coze-studio/backend/api/model/ocean/cloud/workflow"
type ExecuteConfig struct {
ID int64
From Locator
Version string
CommitID string
Operator int64
Mode ExecuteMode
AppID *int64
AgentID *int64
ConnectorID int64
ConnectorUID string
TaskType TaskType
SyncPattern SyncPattern
InputFailFast bool // whether to fail fast if input conversion has warnings
BizType BizType
Cancellable bool
ID int64
From Locator
Version string
CommitID string
Operator int64
Mode ExecuteMode
AppID *int64
AgentID *int64
ConnectorID int64
ConnectorUID string
TaskType TaskType
SyncPattern SyncPattern
InputFailFast bool // whether to fail fast if input conversion has warnings
BizType BizType
Cancellable bool
WorkflowMode WorkflowMode
RoundID *int64 // if workflow is chat flow, conversation round id is required
ConversationID *int64 // if workflow is chat flow, conversation id is required
}
type ExecuteMode string
@ -42,6 +47,8 @@ const (
ExecuteModeNodeDebug ExecuteMode = "node_debug"
)
type WorkflowMode = workflow.WorkflowMode
type TaskType string
const (

View File

@ -80,4 +80,5 @@ type MetaQuery struct {
LibOnly bool
NeedTotalNumber bool
DescByUpdate bool
Mode *workflow.WorkflowMode
}

View File

@ -19,13 +19,12 @@ package workflow
import (
"context"
"github.com/cloudwego/eino/components/model"
"github.com/cloudwego/eino/compose"
"github.com/coze-dev/coze-studio/backend/api/model/ocean/cloud/workflow"
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity"
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
"github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
"github.com/coze-dev/coze-studio/backend/infra/contract/storage"
)
//go:generate mockgen -destination ../../internal/mock/domain/workflow/interface.go --package mockWorkflow -source interface.go
@ -39,12 +38,21 @@ type Service interface {
Publish(ctx context.Context, policy *vo.PublishPolicy) (err error)
UpdateMeta(ctx context.Context, id int64, metaUpdate *vo.MetaUpdate) (err error)
CopyWorkflow(ctx context.Context, workflowID int64, policy vo.CopyWorkflowPolicy) (*entity.Workflow, error)
WorkflowSchemaCheck(ctx context.Context, wf *entity.Workflow, checks []workflow.CheckType) ([]*workflow.CheckResult, error)
QueryNodeProperties(ctx context.Context, id int64) (map[string]*vo.NodeProperty, error) // only draft
ValidateTree(ctx context.Context, id int64, validateConfig vo.ValidateTreeConfig) ([]*workflow.ValidateTreeInfo, error)
GetWorkflowReference(ctx context.Context, id int64) (map[int64]*vo.Meta, error)
CreateChatFlowRole(ctx context.Context, role *vo.ChatFlowRoleCreate) (int64, error)
UpdateChatFlowRole(ctx context.Context, workflowID int64, role *vo.ChatFlowRoleUpdate) error
GetChatFlowRole(ctx context.Context, workflowID int64, version string) (*entity.ChatFlowRole, error)
DeleteChatFlowRole(ctx context.Context, id int64, workflowID int64) error
PublishChatFlowRole(ctx context.Context, policy *vo.PublishRolePolicy) error
CopyChatFlowRole(ctx context.Context, policy *vo.CopyRolePolicy) error
GetWorkflowVersionsByConnector(ctx context.Context, connectorID, workflowID int64, limit int) ([]string, error)
Executable
AsTool
@ -53,17 +61,24 @@ type Service interface {
DuplicateWorkflowsByAppID(ctx context.Context, sourceAPPID, targetAppID int64, related vo.ExternalResourceRelated) error
GetWorkflowDependenceResource(ctx context.Context, workflowID int64) (*vo.DependenceResource, error)
SyncRelatedWorkflowResources(ctx context.Context, appID int64, relatedWorkflows map[int64]entity.IDVersionPair, related vo.ExternalResourceRelated) error
ConversationService
}
type Repository interface {
CreateMeta(ctx context.Context, meta *vo.Meta) (int64, error)
CreateVersion(ctx context.Context, id int64, info *vo.VersionInfo, newRefs map[entity.WorkflowReferenceKey]struct{}) (err error)
CreateOrUpdateDraft(ctx context.Context, id int64, draft *vo.DraftInfo) error
CreateChatFlowRoleConfig(ctx context.Context, chatFlowRole *entity.ChatFlowRole) (int64, error)
UpdateChatFlowRoleConfig(ctx context.Context, workflowID int64, chatFlowRole *vo.ChatFlowRoleUpdate) error
GetChatFlowRoleConfig(ctx context.Context, workflowID int64, version string) (*entity.ChatFlowRole, error, bool)
DeleteChatFlowRoleConfig(ctx context.Context, id int64, workflowID int64) error
Delete(ctx context.Context, id int64) error
MDelete(ctx context.Context, ids []int64) error
GetMeta(ctx context.Context, id int64) (*vo.Meta, error)
UpdateMeta(ctx context.Context, id int64, metaUpdate *vo.MetaUpdate) error
GetVersion(ctx context.Context, id int64, version string) (*vo.VersionInfo, error)
GetVersionListByConnectorAndWorkflowID(ctx context.Context, connectorID, workflowID int64, limit int) ([]string, error)
GetEntity(ctx context.Context, policy *vo.GetPolicy) (*entity.Workflow, error)
@ -95,10 +110,11 @@ type Repository interface {
IsApplicationConnectorWorkflowVersion(ctx context.Context, connectorID, workflowID int64, version string) (b bool, err error)
GetObjectUrl(ctx context.Context, objectKey string, opts ...storage.GetOptFn) (string, error)
compose.CheckPointStore
idgen.IDGenerator
GetKnowledgeRecallChatModel() model.BaseChatModel
ConversationRepository
}
var repositorySingleton Repository

View File

@ -210,33 +210,43 @@ func normalizePorts(connections []*compose.Connection, nodeMap map[string]*vo.No
}
var blockTypeToNodeSchema = map[vo.BlockType]func(*vo.Node, ...OptionFn) (*compose.NodeSchema, error){
vo.BlockTypeBotStart: toEntryNodeSchema,
vo.BlockTypeBotEnd: toExitNodeSchema,
vo.BlockTypeBotLLM: toLLMNodeSchema,
vo.BlockTypeBotLoopSetVariable: toLoopSetVariableNodeSchema,
vo.BlockTypeBotBreak: toBreakNodeSchema,
vo.BlockTypeBotContinue: toContinueNodeSchema,
vo.BlockTypeCondition: toSelectorNodeSchema,
vo.BlockTypeBotText: toTextProcessorNodeSchema,
vo.BlockTypeBotIntent: toIntentDetectorSchema,
vo.BlockTypeDatabase: toDatabaseCustomSQLSchema,
vo.BlockTypeDatabaseSelect: toDatabaseQuerySchema,
vo.BlockTypeDatabaseInsert: toDatabaseInsertSchema,
vo.BlockTypeDatabaseDelete: toDatabaseDeleteSchema,
vo.BlockTypeDatabaseUpdate: toDatabaseUpdateSchema,
vo.BlockTypeBotHttp: toHttpRequesterSchema,
vo.BlockTypeBotDatasetWrite: toKnowledgeIndexerSchema,
vo.BlockTypeBotDatasetDelete: toKnowledgeDeleterSchema,
vo.BlockTypeBotDataset: toKnowledgeRetrieverSchema,
vo.BlockTypeBotAssignVariable: toVariableAssignerSchema,
vo.BlockTypeBotCode: toCodeRunnerSchema,
vo.BlockTypeBotAPI: toPluginSchema,
vo.BlockTypeBotVariableMerge: toVariableAggregatorSchema,
vo.BlockTypeBotInput: toInputReceiverSchema,
vo.BlockTypeBotMessage: toOutputEmitterNodeSchema,
vo.BlockTypeQuestion: toQASchema,
vo.BlockTypeJsonSerialization: toJSONSerializeSchema,
vo.BlockTypeJsonDeserialization: toJSONDeserializeSchema,
vo.BlockTypeBotStart: toEntryNodeSchema,
vo.BlockTypeBotEnd: toExitNodeSchema,
vo.BlockTypeBotLLM: toLLMNodeSchema,
vo.BlockTypeBotLoopSetVariable: toLoopSetVariableNodeSchema,
vo.BlockTypeBotBreak: toBreakNodeSchema,
vo.BlockTypeBotContinue: toContinueNodeSchema,
vo.BlockTypeCondition: toSelectorNodeSchema,
vo.BlockTypeBotText: toTextProcessorNodeSchema,
vo.BlockTypeBotIntent: toIntentDetectorSchema,
vo.BlockTypeDatabase: toDatabaseCustomSQLSchema,
vo.BlockTypeDatabaseSelect: toDatabaseQuerySchema,
vo.BlockTypeDatabaseInsert: toDatabaseInsertSchema,
vo.BlockTypeDatabaseDelete: toDatabaseDeleteSchema,
vo.BlockTypeDatabaseUpdate: toDatabaseUpdateSchema,
vo.BlockTypeBotHttp: toHttpRequesterSchema,
vo.BlockTypeBotDatasetWrite: toKnowledgeIndexerSchema,
vo.BlockTypeBotDatasetDelete: toKnowledgeDeleterSchema,
vo.BlockTypeBotDataset: toKnowledgeRetrieverSchema,
vo.BlockTypeBotAssignVariable: toVariableAssignerSchema,
vo.BlockTypeBotCode: toCodeRunnerSchema,
vo.BlockTypeBotAPI: toPluginSchema,
vo.BlockTypeBotVariableMerge: toVariableAggregatorSchema,
vo.BlockTypeBotInput: toInputReceiverSchema,
vo.BlockTypeBotMessage: toOutputEmitterNodeSchema,
vo.BlockTypeQuestion: toQASchema,
vo.BlockTypeJsonSerialization: toJSONSerializeSchema,
vo.BlockTypeJsonDeserialization: toJSONDeserializeSchema,
vo.BlockTypeCreateConversation: toCreateConversationSchema,
vo.BlockTypeConversationUpdate: toConversationUpdateSchema,
vo.BlockTypeConversationDelete: toConversationDeleteSchema,
vo.BlockTypeConversationList: toConversationListSchema,
vo.BlockTypeClearConversationHistory: toClearConversationHistorySchema,
vo.BlockTypeConversationHistory: toConversationHistorySchema,
vo.BlockTypeCreateMessage: toCreateMessageSchema,
vo.BlockTypeBotMessageList: toMessageListSchema,
vo.BlockTypeDeleteMessage: toDeleteMessageSchema,
vo.BlockTypeEditeMessage: toEditMessageSchema,
}
var blockTypeToSkip = map[vo.BlockType]bool{
@ -1110,6 +1120,10 @@ func toIntentDetectorSchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, err
ns.SetConfigKV("IsFastMode", true)
}
if n.Data.Inputs.ChatHistorySetting != nil {
ns.SetConfigKV("ChatHistorySetting", n.Data.Inputs.ChatHistorySetting)
}
if err = SetInputsForNodeSchema(n, ns); err != nil {
return nil, err
}
@ -1394,11 +1408,10 @@ func toHttpRequesterSchema(n *vo.Node, opts ...OptionFn) (*compose.NodeSchema, e
formDataVars := make([]string, 0)
for i := range inputs.Body.BodyData.FormData.Data {
p := inputs.Body.BodyData.FormData.Data[i]
formDataVars = append(formDataVars, p.Name)
if p.Input.Type == vo.VariableTypeString && p.Input.AssistType > vo.AssistTypeNotSet && p.Input.AssistType < vo.AssistTypeTime {
bodyConfig.FormDataConfig.FileTypeMapping[p.Name] = true
formDataVars = append(formDataVars, p.Name)
}
}
md5FieldMapping.SetBodyFields(formDataVars...)
case httprequester.BodyTypeRawText:
@ -1508,6 +1521,10 @@ func toKnowledgeRetrieverSchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema,
}
ns.SetConfigKV("KnowledgeIDs", knowledgeIDs)
if n.Data.Inputs.ChatHistorySetting != nil {
ns.SetConfigKV("ChatHistorySetting", n.Data.Inputs.ChatHistorySetting)
}
retrievalStrategy := &knowledge.RetrievalStrategy{}
var getDesignatedParamContent = func(name string) (any, bool) {
@ -1944,6 +1961,180 @@ func toJSONDeserializeSchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, er
return ns, nil
}
func toCreateConversationSchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, error) {
ns := &compose.NodeSchema{
Key: vo.NodeKey(n.ID),
Type: entity.NodeTypeCreateConversation,
Name: n.Data.Meta.Title,
}
if err := SetInputsForNodeSchema(n, ns); err != nil {
return nil, err
}
if err := SetOutputTypesForNodeSchema(n, ns); err != nil {
return nil, err
}
return ns, nil
}
func toConversationUpdateSchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, error) {
ns := &compose.NodeSchema{
Key: vo.NodeKey(n.ID),
Type: entity.NodeTypeConversationUpdate,
Name: n.Data.Meta.Title,
}
if err := SetInputsForNodeSchema(n, ns); err != nil {
return nil, err
}
if err := SetOutputTypesForNodeSchema(n, ns); err != nil {
return nil, err
}
return ns, nil
}
func toConversationListSchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, error) {
ns := &compose.NodeSchema{
Key: vo.NodeKey(n.ID),
Type: entity.NodeTypeConversationList,
Name: n.Data.Meta.Title,
}
if err := SetInputsForNodeSchema(n, ns); err != nil {
return nil, err
}
if err := SetOutputTypesForNodeSchema(n, ns); err != nil {
return nil, err
}
return ns, nil
}
func toCreateMessageSchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, error) {
ns := &compose.NodeSchema{
Key: vo.NodeKey(n.ID),
Type: entity.NodeTypeCreateMessage,
Name: n.Data.Meta.Title,
}
if err := SetInputsForNodeSchema(n, ns); err != nil {
return nil, err
}
if err := SetOutputTypesForNodeSchema(n, ns); err != nil {
return nil, err
}
return ns, nil
}
func toMessageListSchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, error) {
ns := &compose.NodeSchema{
Key: vo.NodeKey(n.ID),
Type: entity.NodeTypeMessageList,
Name: n.Data.Meta.Title,
}
if err := SetInputsForNodeSchema(n, ns); err != nil {
return nil, err
}
if err := SetOutputTypesForNodeSchema(n, ns); err != nil {
return nil, err
}
return ns, nil
}
func toDeleteMessageSchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, error) {
ns := &compose.NodeSchema{
Key: vo.NodeKey(n.ID),
Type: entity.NodeTypeDeleteMessage,
Name: n.Data.Meta.Title,
}
if err := SetInputsForNodeSchema(n, ns); err != nil {
return nil, err
}
if err := SetOutputTypesForNodeSchema(n, ns); err != nil {
return nil, err
}
return ns, nil
}
func toEditMessageSchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, error) {
ns := &compose.NodeSchema{
Key: vo.NodeKey(n.ID),
Type: entity.NodeTypeEditMessage,
Name: n.Data.Meta.Title,
}
if err := SetInputsForNodeSchema(n, ns); err != nil {
return nil, err
}
if err := SetOutputTypesForNodeSchema(n, ns); err != nil {
return nil, err
}
return ns, nil
}
func toConversationHistorySchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, error) {
ns := &compose.NodeSchema{
Key: vo.NodeKey(n.ID),
Type: entity.NodeTypeConversationHistory,
Name: n.Data.Meta.Title,
}
if err := SetInputsForNodeSchema(n, ns); err != nil {
return nil, err
}
if err := SetOutputTypesForNodeSchema(n, ns); err != nil {
return nil, err
}
return ns, nil
}
func toClearConversationHistorySchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, error) {
ns := &compose.NodeSchema{
Key: vo.NodeKey(n.ID),
Type: entity.NodeTypeClearConversationHistory,
Name: n.Data.Meta.Title,
}
if err := SetInputsForNodeSchema(n, ns); err != nil {
return nil, err
}
if err := SetOutputTypesForNodeSchema(n, ns); err != nil {
return nil, err
}
return ns, nil
}
func toConversationDeleteSchema(n *vo.Node, _ ...OptionFn) (*compose.NodeSchema, error) {
ns := &compose.NodeSchema{
Key: vo.NodeKey(n.ID),
Type: entity.NodeTypeConversationDelete,
Name: n.Data.Meta.Title,
}
if err := SetInputsForNodeSchema(n, ns); err != nil {
return nil, err
}
if err := SetOutputTypesForNodeSchema(n, ns); err != nil {
return nil, err
}
return ns, nil
}
func buildClauseGroupFromCondition(condition *vo.DBCondition) (*database.ClauseGroup, error) {
clauseGroup := &database.ClauseGroup{}
if len(condition.ConditionList) == 1 {

View File

@ -495,7 +495,14 @@ func LLMParamsToLLMParam(params vo.LLMParam) (*model.LLMParams, error) {
case "systemPrompt":
strVal := param.Input.Value.Content.(string)
p.SystemPrompt = strVal
case "chatHistoryRound", "generationDiversity", "frequencyPenalty", "presencePenalty":
case "chatHistoryRound":
strVal := param.Input.Value.Content.(string)
int64Val, err := strconv.ParseInt(strVal, 10, 64)
if err != nil {
return nil, err
}
p.ChatHistoryRound = int64Val
case "generationDiversity", "frequencyPenalty", "presencePenalty":
// do nothing
case "topP":
strVal := param.Input.Value.Content.(string)

View File

@ -0,0 +1,93 @@
{
"nodes": [
{
"id": "100001",
"type": "1",
"meta": {
"position": {
"x": 13.818572856225469,
"y": -37.20384999753011
}
},
"data": {
"nodeMeta": {
"title": "开始",
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start-v2.jpg",
"subTitle": ""
},
"settings": null,
"version": "",
"outputs": [
{
"type": "string",
"name": "USER_INPUT",
"required": false
},
{
"type": "string",
"name": "CONVERSATION_NAME",
"required": false,
"description": "本次请求绑定的会话,会自动写入消息、会从该会话读对话历史。",
"defaultValue": "Default"
},
{
"type": "string",
"name": "input",
"required": false
}
],
"trigger_parameters": []
}
},
{
"id": "900001",
"type": "2",
"meta": {
"position": {
"x": 642.9671427865745,
"y": -37.20384999753011
}
},
"data": {
"nodeMeta": {
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End-v2.jpg",
"subTitle": "",
"title": "结束"
},
"inputs": {
"terminatePlan": "returnVariables",
"inputParameters": [
{
"name": "output",
"input": {
"type": "string",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "100001",
"name": "input"
},
"rawMeta": {
"type": 1
}
}
}
}
]
}
}
}
],
"edges": [
{
"sourceNodeID": "100001",
"targetNodeID": "900001"
}
],
"versions": {
"loop": "v2"
}
}

View File

@ -0,0 +1,89 @@
{
"nodes": [{
"blocks": [],
"data": {
"nodeMeta": {
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start-v2.jpg",
"subTitle": "",
"title": "开始"
},
"outputs": [{
"name": "USER_INPUT",
"required": false,
"type": "string"
}, {
"defaultValue": "Default",
"description": "本次请求绑定的会话,会自动写入消息、会从该会话读对话历史。",
"name": "CONVERSATION_NAME",
"required": false,
"type": "string"
}],
"trigger_parameters": []
},
"edges": null,
"id": "100001",
"meta": {
"position": {
"x": 0,
"y": 0
}
},
"type": "1"
}, {
"blocks": [],
"data": {
"inputs": {
"content": {
"type": "string",
"value": {
"content": "{{output}}",
"type": "literal"
}
},
"inputParameters": [{
"input": {
"type": "string",
"value": {
"content": {
"blockID": "100001",
"name": "USER_INPUT",
"source": "block-output"
},
"rawMeta": {
"type": 1
},
"type": "ref"
}
},
"name": "output"
}],
"streamingOutput": true,
"terminatePlan": "useAnswerContent"
},
"nodeMeta": {
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End-v2.jpg",
"subTitle": "",
"title": "结束"
}
},
"edges": null,
"id": "900001",
"meta": {
"position": {
"x": 1000,
"y": 0
}
},
"type": "2"
}],
"edges": [{
"sourceNodeID": "100001",
"targetNodeID": "900001",
"sourcePortID": ""
}],
"versions": {
"loop": "v2"
}
}

View File

@ -0,0 +1,193 @@
{
"nodes": [{
"blocks": [],
"data": {
"nodeMeta": {
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start-v2.jpg",
"subTitle": "",
"title": "开始"
},
"outputs": [{
"name": "USER_INPUT",
"required": false,
"type": "string"
}, {
"defaultValue": "Default",
"description": "本次请求绑定的会话,会自动写入消息、会从该会话读对话历史。",
"name": "CONVERSATION_NAME",
"required": false,
"type": "string"
}],
"trigger_parameters": []
},
"edges": null,
"id": "100001",
"meta": {
"position": {
"x": 0,
"y": 0
}
},
"type": "1"
}, {
"blocks": [],
"data": {
"inputs": {
"content": {
"type": "string",
"value": {
"content": "{{output}}",
"type": "literal"
}
},
"inputParameters": [{
"input": {
"schema": {
"schema": [{
"name": "conversationName",
"type": "string"
}, {
"name": "conversationId",
"type": "string"
}],
"type": "object"
},
"type": "list",
"value": {
"content": {
"blockID": "107363",
"name": "conversationList",
"source": "block-output"
},
"rawMeta": {
"type": 103
},
"type": "ref"
}
},
"name": "output"
}],
"streamingOutput": true,
"terminatePlan": "useAnswerContent"
},
"nodeMeta": {
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End-v2.jpg",
"subTitle": "",
"title": "结束"
}
},
"edges": null,
"id": "900001",
"meta": {
"position": {
"x": 1058,
"y": -13
}
},
"type": "2"
}, {
"blocks": [],
"data": {
"inputs": {
"inputParameters": []
},
"nodeMeta": {
"description": "用于查询所有会话,包含静态会话、动态会话",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-查询会话.jpg",
"mainColor": "#F2B600",
"subTitle": "查询会话列表",
"title": "查询会话列表"
},
"outputs": [{
"name": "conversationList",
"schema": {
"schema": [{
"name": "conversationName",
"type": "string"
}, {
"name": "conversationId",
"type": "string"
}],
"type": "object"
},
"type": "list"
}]
},
"edges": null,
"id": "107363",
"meta": {
"position": {
"x": 561,
"y": 186
}
},
"type": "53"
}, {
"blocks": [],
"data": {
"inputs": {
"inputParameters": [{
"input": {
"type": "string",
"value": {
"content": {
"blockID": "100001",
"name": "CONVERSATION_NAME",
"source": "block-output"
},
"rawMeta": {
"type": 1
},
"type": "ref"
}
},
"name": "conversationName"
}]
},
"nodeMeta": {
"description": "用于创建会话",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Conversation-Create.jpeg",
"mainColor": "#F2B600",
"subTitle": "创建会话",
"title": "创建会话"
},
"outputs": [{
"name": "isSuccess",
"type": "boolean"
}, {
"name": "isExisted",
"type": "boolean"
}, {
"name": "conversationId",
"type": "string"
}]
},
"edges": null,
"id": "110245",
"meta": {
"position": {
"x": 487,
"y": -196
}
},
"type": "39"
}],
"edges": [{
"sourceNodeID": "100001",
"targetNodeID": "110245",
"sourcePortID": ""
}, {
"sourceNodeID": "107363",
"targetNodeID": "900001",
"sourcePortID": ""
}, {
"sourceNodeID": "110245",
"targetNodeID": "107363",
"sourcePortID": ""
}],
"versions": {
"loop": "v2"
}
}

View File

@ -0,0 +1,137 @@
{
"nodes": [
{
"id": "100001",
"type": "1",
"meta": {
"position": {
"x": 180,
"y": 13.700000000000003
}
},
"data": {
"nodeMeta": {
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start-v2.jpg",
"subTitle": "",
"title": "开始"
},
"outputs": [
{
"type": "string",
"name": "input",
"required": false
}
],
"trigger_parameters": []
}
},
{
"id": "900001",
"type": "2",
"meta": {
"position": {
"x": 1100,
"y": 0.7000000000000028
}
},
"data": {
"nodeMeta": {
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End-v2.jpg",
"subTitle": "",
"title": "结束"
},
"inputs": {
"terminatePlan": "returnVariables",
"inputParameters": [
{
"name": "output",
"input": {
"type": "string",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "163698",
"name": "conversationId"
},
"rawMeta": {
"type": 1
}
}
}
}
]
}
}
},
{
"id": "163698",
"type": "39",
"meta": {
"position": {
"x": 640,
"y": 0
}
},
"data": {
"outputs": [
{
"type": "boolean",
"name": "isSuccess"
},
{
"type": "boolean",
"name": "isExisted"
},
{
"type": "string",
"name": "conversationId"
}
],
"nodeMeta": {
"title": "创建会话",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Conversation-Create.jpeg",
"description": "用于创建会话",
"mainColor": "#F2B600",
"subTitle": "创建会话"
},
"inputs": {
"inputParameters": [
{
"name": "conversationName",
"input": {
"type": "string",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "100001",
"name": "input"
},
"rawMeta": {
"type": 1
}
}
}
}
]
}
}
}
],
"edges": [
{
"sourceNodeID": "100001",
"targetNodeID": "163698"
},
{
"sourceNodeID": "163698",
"targetNodeID": "900001"
}
],
"versions": {
"loop": "v2"
}
}

View File

@ -0,0 +1,129 @@
{
"nodes": [
{
"id": "100001",
"type": "1",
"meta": {
"position": {
"x": -13.523809523809522,
"y": -25.294372294372295
}
},
"data": {
"nodeMeta": {
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start-v2.jpg",
"subTitle": "",
"title": "开始"
},
"outputs": [
{
"type": "string",
"name": "input",
"required": false
}
],
"trigger_parameters": []
}
},
{
"id": "900001",
"type": "2",
"meta": {
"position": {
"x": 890.3549783549786,
"y": -71.48917748917748
}
},
"data": {
"nodeMeta": {
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End-v2.jpg",
"subTitle": "",
"title": "结束"
},
"inputs": {
"terminatePlan": "returnVariables",
"inputParameters": [
{
"name": "output",
"input": {
"type": "boolean",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "118024",
"name": "isSuccess"
},
"rawMeta": {
"type": 3
}
}
}
}
]
}
}
},
{
"id": "118024",
"type": "52",
"meta": {
"position": {
"x": 423.6623376623378,
"y": -126.39999999999999
}
},
"data": {
"outputs": [
{
"type": "boolean",
"name": "isSuccess"
}
],
"nodeMeta": {
"title": "删除会话",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-删除会话.jpg",
"description": "用于删除会话",
"mainColor": "#F2B600",
"subTitle": "删除会话"
},
"inputs": {
"inputParameters": [
{
"name": "conversationName",
"input": {
"type": "string",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "100001",
"name": "input"
},
"rawMeta": {
"type": 1
}
}
}
}
]
}
}
}
],
"edges": [
{
"sourceNodeID": "100001",
"targetNodeID": "118024"
},
{
"sourceNodeID": "118024",
"targetNodeID": "900001"
}
],
"versions": {
"loop": "v2"
}
}

View File

@ -0,0 +1,191 @@
{
"nodes": [
{
"id": "100001",
"type": "1",
"meta": {
"position": {
"x": -243.67931247880136,
"y": -233.598184501318
}
},
"data": {
"nodeMeta": {
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start-v2.jpg",
"subTitle": "",
"title": "开始"
},
"outputs": [
{
"type": "string",
"name": "input",
"required": false
}
],
"trigger_parameters": []
}
},
{
"id": "900001",
"type": "2",
"meta": {
"position": {
"x": 911.2952705396514,
"y": -331.2250749763467
}
},
"data": {
"nodeMeta": {
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End-v2.jpg",
"subTitle": "",
"title": "结束"
},
"inputs": {
"terminatePlan": "returnVariables",
"inputParameters": [
{
"name": "output",
"input": {
"value": {
"type": "object_ref"
},
"type": "object",
"schema": [
{
"name": "isSuccess",
"input": {
"type": "boolean",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "122336",
"name": "isSuccess"
},
"rawMeta": {
"type": 3
}
}
}
},
{
"name": "isExisted",
"input": {
"type": "boolean",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "122336",
"name": "isExisted"
},
"rawMeta": {
"type": 3
}
}
}
},
{
"name": "conversationId",
"input": {
"type": "string",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "122336",
"name": "conversationId"
},
"rawMeta": {
"type": 1
}
}
}
}
]
}
}
]
}
}
},
{
"id": "122336",
"type": "51",
"meta": {
"position": {
"x": 343.08704991877585,
"y": -462.38794621339696
}
},
"data": {
"outputs": [
{
"type": "boolean",
"name": "isSuccess"
},
{
"type": "boolean",
"name": "isExisted"
},
{
"type": "string",
"name": "conversationId"
}
],
"nodeMeta": {
"title": "修改会话",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-编辑会话.jpg",
"description": "用于修改会话的名字",
"mainColor": "#F2B600",
"subTitle": "修改会话"
},
"inputs": {
"inputParameters": [
{
"name": "conversationName",
"input": {
"type": "string",
"value": {
"type": "literal",
"content": "template_v1",
"rawMeta": {
"type": 1
}
}
}
},
{
"name": "newConversationName",
"input": {
"type": "string",
"value": {
"type": "literal",
"content": "new",
"rawMeta": {
"type": 1
}
}
}
}
]
}
}
}
],
"edges": [
{
"sourceNodeID": "100001",
"targetNodeID": "122336"
},
{
"sourceNodeID": "122336",
"targetNodeID": "900001"
}
],
"versions": {
"loop": "v2"
}
}

View File

@ -0,0 +1,262 @@
{
"nodes": [
{
"id": "100001",
"type": "1",
"meta": {
"position": {
"x": 180,
"y": 13.700000000000003
}
},
"data": {
"nodeMeta": {
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start-v2.jpg",
"subTitle": "",
"title": "开始"
},
"outputs": [
{
"type": "string",
"name": "input",
"required": true
},
{
"type": "string",
"name": "new_name",
"required": true
}
],
"trigger_parameters": []
}
},
{
"id": "900001",
"type": "2",
"meta": {
"position": {
"x": 1560,
"y": 0.7000000000000028
}
},
"data": {
"nodeMeta": {
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End-v2.jpg",
"subTitle": "",
"title": "结束"
},
"inputs": {
"terminatePlan": "returnVariables",
"inputParameters": [
{
"name": "obj",
"input": {
"value": {
"type": "object_ref"
},
"type": "object",
"schema": [
{
"name": "isSuccess",
"input": {
"type": "boolean",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "193175",
"name": "isSuccess"
},
"rawMeta": {
"type": 3
}
}
}
},
{
"name": "isExisted",
"input": {
"type": "boolean",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "193175",
"name": "isExisted"
},
"rawMeta": {
"type": 3
}
}
}
},
{
"name": "conversationId",
"input": {
"type": "string",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "193175",
"name": "conversationId"
},
"rawMeta": {
"type": 1
}
}
}
}
]
}
}
]
}
}
},
{
"id": "139551",
"type": "39",
"meta": {
"position": {
"x": 627.929589270746,
"y": -36.21123218776195
}
},
"data": {
"outputs": [
{
"type": "boolean",
"name": "isSuccess"
},
{
"type": "boolean",
"name": "isExisted"
},
{
"type": "string",
"name": "conversationId"
}
],
"nodeMeta": {
"title": "创建会话",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Conversation-Create.jpeg",
"description": "用于创建会话",
"mainColor": "#F2B600",
"subTitle": "创建会话"
},
"inputs": {
"inputParameters": [
{
"name": "conversationName",
"input": {
"type": "string",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "100001",
"name": "input"
},
"rawMeta": {
"type": 1
}
}
}
}
]
}
}
},
{
"id": "193175",
"type": "51",
"meta": {
"position": {
"x": 1100,
"y": 0
}
},
"data": {
"outputs": [
{
"type": "boolean",
"name": "isSuccess"
},
{
"type": "boolean",
"name": "isExisted"
},
{
"type": "string",
"name": "conversationId"
}
],
"nodeMeta": {
"title": "修改会话",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-编辑会话.jpg",
"description": "用于修改会话的名字",
"mainColor": "#F2B600",
"subTitle": "修改会话"
},
"inputs": {
"inputParameters": [
{
"name": "conversationName",
"input": {
"type": "string",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "100001",
"name": "input"
},
"rawMeta": {
"type": 1
}
}
}
},
{
"name": "newConversationName",
"input": {
"type": "string",
"value": {
"type": "ref",
"content": {
"source": "block-output",
"blockID": "100001",
"name": "new_name"
},
"rawMeta": {
"type": 1
}
}
}
}
]
}
}
}
],
"edges": [
{
"sourceNodeID": "100001",
"targetNodeID": "139551"
},
{
"sourceNodeID": "193175",
"targetNodeID": "900001"
},
{
"sourceNodeID": "139551",
"targetNodeID": "193175"
}
],
"versions": {
"loop": "v2"
}
}

View File

@ -0,0 +1,234 @@
{
"nodes": [{
"blocks": [],
"data": {
"nodeMeta": {
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start-v2.jpg",
"subTitle": "",
"title": "开始"
},
"outputs": [{
"name": "USER_INPUT",
"required": false,
"type": "string"
}, {
"defaultValue": "Default",
"description": "本次请求绑定的会话,会自动写入消息、会从该会话读对话历史。",
"name": "CONVERSATION_NAME",
"required": false,
"type": "string"
}],
"trigger_parameters": []
},
"edges": null,
"id": "100001",
"meta": {
"position": {
"x": 0,
"y": 0
}
},
"type": "1"
}, {
"blocks": [],
"data": {
"inputs": {
"inputParameters": [{
"input": {
"type": "boolean",
"value": {
"content": {
"blockID": "195185",
"name": "isSuccess",
"source": "block-output"
},
"rawMeta": {
"type": 3
},
"type": "ref"
}
},
"name": "output"
}, {
"input": {
"type": "string",
"value": {
"content": {
"blockID": "195185",
"name": "message.messageId",
"source": "block-output"
},
"rawMeta": {
"type": 1
},
"type": "ref"
}
},
"name": "mID"
}],
"terminatePlan": "returnVariables"
},
"nodeMeta": {
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End-v2.jpg",
"subTitle": "",
"title": "结束"
}
},
"edges": null,
"id": "900001",
"meta": {
"position": {
"x": 1000,
"y": 0
}
},
"type": "2"
}, {
"blocks": [],
"data": {
"inputs": {
"inputParameters": [{
"input": {
"type": "string",
"value": {
"content": {
"blockID": "100001",
"name": "CONVERSATION_NAME",
"source": "block-output"
},
"rawMeta": {
"type": 1
},
"type": "ref"
}
},
"name": "conversationName"
}, {
"input": {
"type": "string",
"value": {
"content": "user",
"type": "literal"
}
},
"name": "role"
}, {
"input": {
"type": "string",
"value": {
"content": "1",
"rawMeta": {
"type": 1
},
"type": "literal"
}
},
"name": "content"
}]
},
"nodeMeta": {
"description": "用于创建消息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-创建消息.jpg",
"mainColor": "#F2B600",
"subTitle": "创建消息",
"title": "创建消息"
},
"outputs": [{
"name": "isSuccess",
"type": "boolean"
}, {
"name": "message",
"schema": [{
"name": "messageId",
"type": "string"
}, {
"name": "role",
"type": "string"
}, {
"name": "contentType",
"type": "string"
}, {
"name": "content",
"type": "string"
}],
"type": "object"
}]
},
"edges": null,
"id": "195185",
"meta": {
"position": {
"x": 482,
"y": -13
}
},
"type": "55"
}, {
"blocks": [],
"data": {
"inputs": {
"inputParameters": [{
"input": {
"type": "string",
"value": {
"content": {
"blockID": "100001",
"name": "CONVERSATION_NAME",
"source": "block-output"
},
"rawMeta": {
"type": 1
},
"type": "ref"
}
},
"name": "conversationName"
}]
},
"nodeMeta": {
"description": "用于创建会话",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Conversation-Create.jpeg",
"mainColor": "#F2B600",
"subTitle": "创建会话",
"title": "创建会话"
},
"outputs": [{
"name": "isSuccess",
"type": "boolean"
}, {
"name": "isExisted",
"type": "boolean"
}, {
"name": "conversationId",
"type": "string"
}]
},
"edges": null,
"id": "121849",
"meta": {
"position": {
"x": 302,
"y": -236
}
},
"type": "39"
}],
"edges": [{
"sourceNodeID": "100001",
"targetNodeID": "121849",
"sourcePortID": ""
}, {
"sourceNodeID": "195185",
"targetNodeID": "900001",
"sourcePortID": ""
}, {
"sourceNodeID": "121849",
"targetNodeID": "195185",
"sourcePortID": ""
}],
"versions": {
"loop": "v2"
}
}

View File

@ -0,0 +1,310 @@
{
"nodes": [{
"blocks": [],
"data": {
"nodeMeta": {
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start-v2.jpg",
"subTitle": "",
"title": "开始"
},
"outputs": [{
"name": "USER_INPUT",
"required": false,
"type": "string"
}, {
"defaultValue": "Default",
"description": "本次请求绑定的会话,会自动写入消息、会从该会话读对话历史。",
"name": "CONVERSATION_NAME",
"required": false,
"type": "string"
}],
"trigger_parameters": []
},
"edges": null,
"id": "100001",
"meta": {
"position": {
"x": 0,
"y": 0
}
},
"type": "1"
}, {
"blocks": [],
"data": {
"inputs": {
"inputParameters": [{
"input": {
"schema": {
"schema": [{
"name": "messageId",
"type": "string"
}, {
"name": "role",
"type": "string"
}, {
"name": "contentType",
"type": "string"
}, {
"name": "content",
"type": "string"
}],
"type": "object"
},
"type": "list",
"value": {
"content": {
"blockID": "132703",
"name": "messageList",
"source": "block-output"
},
"rawMeta": {
"type": 103
},
"type": "ref"
}
},
"name": "output"
}],
"terminatePlan": "returnVariables"
},
"nodeMeta": {
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End-v2.jpg",
"subTitle": "",
"title": "结束"
}
},
"edges": null,
"id": "900001",
"meta": {
"position": {
"x": 1000,
"y": 0
}
},
"type": "2"
}, {
"blocks": [],
"data": {
"inputs": {
"inputParameters": [{
"input": {
"type": "string",
"value": {
"content": {
"blockID": "100001",
"name": "CONVERSATION_NAME",
"source": "block-output"
},
"rawMeta": {
"type": 1
},
"type": "ref"
}
},
"name": "conversationName"
}]
},
"nodeMeta": {
"description": "用于查询消息列表",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Conversation-List.jpeg",
"mainColor": "#F2B600",
"subTitle": "查询消息列表",
"title": "查询消息列表"
},
"outputs": [{
"name": "messageList",
"schema": {
"schema": [{
"name": "messageId",
"type": "string"
}, {
"name": "role",
"type": "string"
}, {
"name": "contentType",
"type": "string"
}, {
"name": "content",
"type": "string"
}],
"type": "object"
},
"type": "list"
}, {
"name": "firstId",
"type": "string"
}, {
"name": "lastId",
"type": "string"
}, {
"name": "hasMore",
"type": "boolean"
}]
},
"edges": null,
"id": "132703",
"meta": {
"position": {
"x": 514,
"y": 96
}
},
"type": "37"
}, {
"blocks": [],
"data": {
"inputs": {
"inputParameters": [{
"input": {
"type": "string",
"value": {
"content": {
"blockID": "100001",
"name": "CONVERSATION_NAME",
"source": "block-output"
},
"rawMeta": {
"type": 1
},
"type": "ref"
}
},
"name": "conversationName"
}]
},
"nodeMeta": {
"description": "用于创建会话",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Conversation-Create.jpeg",
"mainColor": "#F2B600",
"subTitle": "创建会话",
"title": "创建会话"
},
"outputs": [{
"name": "isSuccess",
"type": "boolean"
}, {
"name": "isExisted",
"type": "boolean"
}, {
"name": "conversationId",
"type": "string"
}]
},
"edges": null,
"id": "166724",
"meta": {
"position": {
"x": 323,
"y": -332
}
},
"type": "39"
}, {
"blocks": [],
"data": {
"inputs": {
"inputParameters": [{
"input": {
"type": "string",
"value": {
"content": {
"blockID": "100001",
"name": "CONVERSATION_NAME",
"source": "block-output"
},
"rawMeta": {
"type": 1
},
"type": "ref"
}
},
"name": "conversationName"
}, {
"input": {
"type": "string",
"value": {
"content": "user",
"type": "literal"
}
},
"name": "role"
}, {
"input": {
"type": "string",
"value": {
"content": {
"blockID": "100001",
"name": "USER_INPUT",
"source": "block-output"
},
"rawMeta": {
"type": 1
},
"type": "ref"
}
},
"name": "content"
}]
},
"nodeMeta": {
"description": "用于创建消息",
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-创建消息.jpg",
"mainColor": "#F2B600",
"subTitle": "创建消息",
"title": "创建消息"
},
"outputs": [{
"name": "isSuccess",
"type": "boolean"
}, {
"name": "message",
"schema": [{
"name": "messageId",
"type": "string"
}, {
"name": "role",
"type": "string"
}, {
"name": "contentType",
"type": "string"
}, {
"name": "content",
"type": "string"
}],
"type": "object"
}]
},
"edges": null,
"id": "157061",
"meta": {
"position": {
"x": 479,
"y": -127
}
},
"type": "55"
}],
"edges": [{
"sourceNodeID": "100001",
"targetNodeID": "166724",
"sourcePortID": ""
}, {
"sourceNodeID": "132703",
"targetNodeID": "900001",
"sourcePortID": ""
}, {
"sourceNodeID": "157061",
"targetNodeID": "132703",
"sourcePortID": ""
}, {
"sourceNodeID": "166724",
"targetNodeID": "157061",
"sourcePortID": ""
}],
"versions": {
"loop": "v2"
}
}

View File

@ -32,7 +32,6 @@ import (
"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/execute"
"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/nodes"
"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/nodes/llm"
"github.com/coze-dev/coze-studio/backend/pkg/ctxcache"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
)
@ -47,8 +46,6 @@ func (r *WorkflowRunner) designateOptions(ctx context.Context) (context.Context,
sw = r.streamWriter
)
const tokenCallbackKey = "token_callback_key"
if wb.AppID != nil && exeCfg.AppID == nil {
exeCfg.AppID = wb.AppID
}
@ -124,12 +121,6 @@ func (r *WorkflowRunner) designateOptions(ctx context.Context) (context.Context,
opts = append(opts, einoCompose.WithCheckPointID(strconv.FormatInt(executeID, 10)))
}
if !ctxcache.HasKey(ctx, tokenCallbackKey) {
opts = append(opts, einoCompose.WithCallbacks(execute.GetTokenCallbackHandler()))
ctx = ctxcache.Init(ctx)
ctxcache.Store(ctx, tokenCallbackKey, true)
}
return ctx, opts, nil
}

View File

@ -19,6 +19,7 @@ package compose
import (
"context"
"fmt"
"runtime/debug"
"github.com/cloudwego/eino/compose"
@ -137,7 +138,11 @@ func (s *NodeSchema) New(ctx context.Context, inner compose.Runnable[map[string]
return nil, err
}
return invokableStreamableNodeWO(s, l.Chat, l.ChatStream, withCallbackOutputConverter(l.ToCallbackOutput)), nil
initFn := func(ctx context.Context) (context.Context, error) {
return ctxcache.Init(ctx), nil
}
return invokableStreamableNodeWO(s, l.Chat, l.ChatStream, withCallbackInputConverter(l.ToCallbackInput), withCallbackOutputConverter(l.ToCallbackOutput), withInit(initFn)), nil
case entity.NodeTypeSelector:
conf := s.ToSelectorConfig()
@ -388,7 +393,10 @@ func (s *NodeSchema) New(ctx context.Context, inner compose.Runnable[map[string]
if err != nil {
return nil, err
}
return invokableNode(s, r.Retrieve), nil
initFn := func(ctx context.Context) (context.Context, error) {
return ctxcache.Init(ctx), nil
}
return invokableNode(s, r.Retrieve, withCallbackInputConverter(r.ToCallbackInput), withInit(initFn)), nil
case entity.NodeTypeKnowledgeDeleter:
conf, err := s.ToKnowledgeDeleterConfig()
if err != nil {
@ -432,6 +440,61 @@ func (s *NodeSchema) New(ctx context.Context, inner compose.Runnable[map[string]
return nil, err
}
return invokableNode(s, r.Create), nil
case entity.NodeTypeConversationUpdate:
r := conversation.NewUpdateConversation(ctx)
if err != nil {
return nil, err
}
return invokableNode(s, r.Update), nil
case entity.NodeTypeConversationList:
r, err := conversation.NewConversationList(ctx)
if err != nil {
return nil, err
}
return invokableNode(s, r.List), nil
case entity.NodeTypeConversationDelete:
r := conversation.NewDeleteConversation(ctx)
if err != nil {
return nil, err
}
return invokableNode(s, r.Delete), nil
case entity.NodeTypeCreateMessage:
conf, err := s.ToCreateMessageConfig()
if err != nil {
return nil, err
}
r, err := conversation.NewCreateMessage(ctx, conf)
if err != nil {
return nil, err
}
return invokableNode(s, r.Create), nil
case entity.NodeTypeClearConversationHistory:
cfg, err := s.ToClearConversationHistoryConfig()
if err != nil {
return nil, err
}
r, err := conversation.NewClearConversationHistory(ctx, cfg)
if err != nil {
return nil, err
}
return invokableNode(s, r.Clear), nil
case entity.NodeTypeConversationHistory:
cfg, err := s.ToConversationHistoryConfig()
if err != nil {
return nil, err
}
r, err := conversation.NewConversationHistory(ctx, cfg)
if err != nil {
return nil, err
}
return invokableNode(s, r.HistoryMessages), nil
case entity.NodeTypeMessageList:
conf, err := s.ToMessageListConfig()
if err != nil {
@ -442,16 +505,26 @@ func (s *NodeSchema) New(ctx context.Context, inner compose.Runnable[map[string]
return nil, err
}
return invokableNode(s, r.List), nil
case entity.NodeTypeClearMessage:
conf, err := s.ToClearMessageConfig()
case entity.NodeTypeDeleteMessage:
conf, err := s.ToDeleteMessageConfig()
if err != nil {
return nil, err
}
r, err := conversation.NewClearMessage(ctx, conf)
r, err := conversation.NewDeleteMessage(ctx, conf)
if err != nil {
return nil, err
}
return invokableNode(s, r.Clear), nil
return invokableNode(s, r.Delete), nil
case entity.NodeTypeEditMessage:
conf, err := s.ToEditMessageConfig()
if err != nil {
return nil, err
}
r, err := conversation.NewEditMessage(ctx, conf)
if err != nil {
return nil, err
}
return invokableNode(s, r.Edit), nil
case entity.NodeTypeIntentDetector:
conf, err := s.ToIntentDetectorConfig(ctx)
if err != nil {
@ -461,8 +534,10 @@ func (s *NodeSchema) New(ctx context.Context, inner compose.Runnable[map[string]
if err != nil {
return nil, err
}
return invokableNode(s, r.Invoke), nil
initFn := func(ctx context.Context) (context.Context, error) {
return ctxcache.Init(ctx), nil
}
return invokableNode(s, r.Invoke, withCallbackInputConverter(r.ToCallbackInput), withInit(initFn)), nil
case entity.NodeTypeSubWorkflow:
conf, err := s.ToSubWorkflowConfig(ctx, sc.requireCheckPoint)
if err != nil {
@ -537,6 +612,9 @@ func (s *NodeSchema) IsEnableChatHistory() bool {
case entity.NodeTypeIntentDetector:
llmParam := mustGetKey[*model.LLMParams]("LLMParams", s.Configs)
return llmParam.EnableChatHistory
case entity.NodeTypeKnowledgeRetriever:
chatHistorySetting := getKeyOrZero[*vo.ChatHistorySetting]("ChatHistorySetting", s.Configs)
return chatHistorySetting != nil && chatHistorySetting.EnableChatHistory
default:
return false
}

Some files were not shown because too many files have changed in this diff Show More