Compare commits

...

53 Commits

Author SHA1 Message Date
ab465ff3eb feat(backend): Enable chat history in intent_detector, knowledge_retrieve and llm node 2025-07-31 15:12:52 +08:00
0d5c330b51 feat: add schema check for workflow_list on release 2025-07-31 12:41:11 +08:00
e678c24b0c fix: Correct role config versioning, sync on publish, and resolve chat flow copy bug 2025-07-31 12:29:26 +08:00
13d30605f1 feat(backend): add license 2025-07-30 15:23:13 +08:00
03b8d8e17a feat(backend):workflow support conversation manager & add conversation/message nodes 2025-07-30 12:44:10 +08:00
e3930873c6 feat: Chat supports Q&A nodes (#207)
Co-authored-by: yangyu.1 <yangyu.1@bytedance.com>
2025-07-30 03:41:27 +00:00
2ed2a60479 fix(plugin): parameter convert failed (#274) 2025-07-29 14:35:59 +00:00
f589bb94bb feat: add redis password (#201) 2025-07-29 13:34:44 +00:00
02b4171576 fix: error when parsing tables with only headers in docx/pdf (#271) 2025-07-29 13:16:17 +00:00
0d83a5e7bc docs: update CONTRIBUTING.md to reflect main branch usage (#172) 2025-07-29 13:09:32 +00:00
14ca189316 fix: escape '$' to '$$' in docker-compose.yml file (#234) 2025-07-29 12:58:05 +00:00
Ryo
4310dee4c2 feat(backend): Optimize HTTPS certificate path (#265) 2025-07-29 12:34:25 +00:00
4ca3e597ff fix: release http embedding (#214) 2025-07-29 11:44:02 +00:00
53345f58c2 fix(backend_plugin): plugin common header and parameter default value (#181) 2025-07-29 11:29:36 +00:00
8137b0aee5 feat: init improvements (#174) 2025-07-29 11:02:03 +00:00
b48c4c2792 feat(ci): update helm (#222) 2025-07-29 11:01:48 +00:00
ski
f8840810ce chore: add licenses to json stringify related files (#250) 2025-07-29 17:31:38 +08:00
Ryo
9b68bd5387 chore(ci): add default reviewer (#220) 2025-07-29 06:54:35 +00:00
a6f97f61f8 feat: add --es-address parameter to setup_es_index with .env support (#177) 2025-07-29 06:23:54 +00:00
ski
0965d69acc feat: add json-stringify node (#215)
Co-authored-by: zengxiaohui <zengxiaohui@bytedance.com>
2025-07-29 06:11:49 +00:00
183d0324bb feat(user): add ENV to control DISABLE_USER_REGISTRATION and ALLOW_REGISTRATION_EMAIL (#208) 2025-07-29 04:36:44 +00:00
04f0491454 docs: Add an explanation of how the frontend code is used (#182) 2025-07-29 00:53:39 +08:00
9ed2f8be67 feat: add s3 service (#188) 2025-07-28 15:19:52 +00:00
403128b5d3 fix: Optimize knowledge domain error codes to expose previous errors during retries (#185) 2025-07-28 14:53:53 +00:00
db7c95885d fix: Optimize the GetDraftIntelligenceList function and reduce the use of goroutine (#171) 2025-07-28 12:45:10 +00:00
2ee3fa68ab fix: user name (#180) 2025-07-28 12:28:02 +00:00
Ryo
a0d3bcf998 feat(ci): update helm config (#157) 2025-07-28 11:17:56 +00:00
cc593fc270 chore: backend owners (#170) 2025-07-28 11:13:33 +00:00
6e961bb60d fix: remove setup_es_index in makefile (#166) 2025-07-28 10:03:28 +00:00
f93b60512f feat: disable image_ocr by default in text processing stores (#149) 2025-07-28 10:03:03 +00:00
9dcdb70508 chore: setup increment ci env (#109) 2025-07-28 09:51:31 +00:00
4a44c0ddbd fix(memory): Use text as the underlying type of string (#129) 2025-07-28 09:40:33 +00:00
b299adacf3 fix(search): Fix logical issues with search domain code (#151) 2025-07-28 09:11:00 +00:00
0ce6a4da4c chore: add author (#113)
Co-authored-by: wenming.2020 <wenming.2020@bytedance.com>
2025-07-28 08:01:44 +00:00
be5178d57b docs: openapi token description (#130) 2025-07-28 06:40:39 +00:00
2d925ca241 fix: CODEOWNERS (#117) 2025-07-28 06:20:52 +00:00
Ryo
60a9c7a281 docs: update readme (#126) 2025-07-28 06:08:01 +00:00
376e563e4f fix: add plugin id conflict checker (#78) 2025-07-28 06:07:03 +00:00
Ryo
f0c339d231 chore(ci): Optimize Elasticsearch index init script, remove Docker im… (#106) 2025-07-28 04:26:58 +00:00
81b5867a62 chore: add author (#123)
Co-authored-by: tanjizhen <tanjizhen@bytedance.com>
2025-07-28 04:25:18 +00:00
5ac7b8d26e chore: allow all type email (#115) 2025-07-28 03:55:58 +00:00
48b52c3592 docs: add FAQ link (#94) 2025-07-28 01:29:04 +08:00
033df3f8ee fix: field about time type (#92) 2025-07-28 00:36:14 +08:00
Ryo
f294e19af5 docs: update readme (#85) 2025-07-27 21:25:34 +08:00
23a1f1cab0 docs: recommend use issues first, add discord and telegram (#57) 2025-07-27 11:41:11 +08:00
c3d8def0e6 docs: add acknowledgments for eino, hertz (#40) 2025-07-26 19:24:48 +08:00
32d9bf9a39 docs: add new acknowledgment (#25) 2025-07-26 15:04:45 +08:00
Ryo
be4aa3f2ca chore(ci): remove unused tool (#26) 2025-07-26 15:02:57 +08:00
bc68869493 fix: [app] api message event name (#20) 2025-07-26 14:37:12 +08:00
ccee6dcde8 docs: update the step after service the starting (#18) 2025-07-26 14:34:37 +08:00
d9ec4f60e0 docs(frontend): use new doc styles (#17)
* docs: use new doc styles

* docs: use new doc styles

* docs: use new doc styles
2025-07-26 13:51:05 +08:00
9ca1ae506f docs(frontend): use new doc styles (#11)
* docs: use new doc styles

* docs: remove comment

* docs: remove comment

* docs: remove comment

---------

Co-authored-by: tecvan <tecvan.fe@gmail.com>
2025-07-26 12:36:38 +08:00
Ryo
d045dea008 Merge pull request #13 from coze-dev/fix/semantic_pr_check
fix(ci): wrong headerPattern
2025-07-26 11:49:38 +08:00
300 changed files with 15596 additions and 2585 deletions

View File

@ -1,73 +0,0 @@
name: "ci@backend pipeline"
trigger:
change:
paths:
- "backend/**"
jobs:
unit_test:
image: hub.byted.org/codebase/ci_go_1_24:latest
name: Unit Test
steps:
- id: mysql_scripts
name: mysql scripts
uses: actions/mysql-scripts@v1
inputs:
db_name: opencoze
host: mysql
path: "docker/atlas/migrations/*.sql"
user: root
password: root
- name: Go Test
uses: actions/codecov
inputs:
driver: go
commands:
- modules=`find . -name "go.mod" -exec dirname {} \;`
- echo $modules
- list=""
- coverpkg=""
- if [[ ! -f "go.work" ]];then go work init;fi
- for module in $modules; do go work use $module; list=$module"/... "$list; coverpkg=$module"/...,"$coverpkg; done
- go work sync
- go test -coverprofile=coverage.out -gcflags="all=-l -N" -coverpkg=$coverpkg $list
config:
status:
project:
opencoze:
minimum_coverage: 0%
paths:
- "!tests"
- "!examples/**"
- "!*/examples/**"
- "!*/mock/**"
- "!*/model/**"
diff:
eino:
line_limit: 10 # 增量行数少于多少行时,默认置成功
minimum_coverage: 0%
paths:
- "!tests"
- "!examples/**"
- "!*/examples/**"
- "!*/mock/**"
- "!*/model/**"
- name: Sonar
uses: actions/sonar@v1
inputs:
coverage_path: /tmp/coverage.out
coverage_type: go
disable_quality_gates: true
exclusions_list:
- "!tests"
- "examples/**"
- "*/mock/**"
- "*/model/**"
language: go
services:
- id: mysql
image: hub.byted.org/ee/mysql:8.0
envs:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: opencoze

View File

@ -1,180 +0,0 @@
name: CI@fe-master
trigger:
cron:
push:
branches: []
paths:
- "frontend/**"
types: [branch]
notification:
when: failure
# 搜索群名Bot Studio Monorepo CI 报警群
to: ['7350862409475031044']
manual:
notification:
when: failure
jobs:
lint: &base_job
runs-on:
env: online
name: Lint
image: hub.byted.org/base/bot_monorepo_ci_env:ae543e9bbc6d8155cffbd8f5ed27fb73
# 从最近的 CI 执行记录看install 步骤有概率会等待超时
# 目前已 oncall这里先设置一个超时时间避免阻塞
timeout: 30
envs:
RUSH_BUILD_CACHE_WRITE_ALLOWED: ${{RUSH_BUILD_CACHE_WRITE_ALLOWED}}
RUSH_BUILD_CACHE_ENABLED: ${{RUSH_BUILD_CACHE_ENABLED}}
RUSH_BUILD_CACHE_CREDENTIAL: ${{RUSH_BUILD_CACHE_CREDENTIAL}}
ACCESS_TOKEN: ${{CI_BOT_ACCESS_TOKEN}}
CI: 'true'
RUN_BYTEST_COV: 'false' # disable Jupiter default behavior of invoke bytestcov
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 'true'
CYPRESS_INSTALL_BINARY: '0'
TAIKO_SKIP_CHROMIUM_DOWNLOAD: '0'
RE2_DOWNLOAD_SKIP_PATH: '1'
RE2_DOWNLOAD_MIRROR: https://bnpm.bytedance.net/mirrors
# https://typescript-eslint.io/packages/parser/#allowautomaticsingleruninference
TSESTREE_SINGLE_RUN: 'true'
# 使用云盘缓存https://bytedance.larkoffice.com/wiki/A60lwgolSinteMkOz71cFPvFneg
# TODO:不同job的缓存key暂时使用同一个后续可以考虑区分
caches:
- backend: ebs
key: bot-studio-monorepo-master-v2-lint
size: 256
steps:
- &checkout_step
id: Checkout
uses: actions/checkout
inputs:
depth: 1
- &proxy_step
name: SetupProxy
commands:
- bash .codebase/scripts/env.sh
- &init_env_step
name: Initialization
commands:
- printenv
- git config user.name ci_flow
- git config user.email ci_flow@bytedance.com
- &install_deps_step
name: Install dependencies
commands:
- node common/scripts/install-run-rush.js install
# 暂时不启用因为eslintignore文件移除会导致解析报错后期接入可考虑动态读取eslint ignore配置
# - name: PreLint
# parallel-with-next-step: true
# commands:
# - npx oxlint@latest -A all .
- name: Lint
commands:
- NODE_OPTIONS="--max-old-space-size=4096" node common/scripts/install-run-rush.js lint
ts_check:
<<: *base_job
name: Check TS Type
timeout: 30
caches:
- backend: ebs
key: bot-studio-monorepo-master-v2-ts-check
size: 256
steps:
- *checkout_step
- *proxy_step
- *init_env_step
- *install_deps_step
- name: Check TS Type
commands:
- node common/scripts/install-run-rush.js ts-check -v
build:
<<: *base_job
name: Build
caches:
- backend: ebs
key: bot-studio-monorepo-master-v2-build
size: 256
steps:
- *checkout_step
- *proxy_step
- *init_env_step
- *install_deps_step
- name: Build
commands:
- CUSTOM_VERSION=release REGION=sg BUILD_BRANCH=test node common/scripts/install-run-rush.js build -v
test:
<<: *base_job
caches:
- backend: ebs
key: bot-studio-monorepo-master-v2-test
size: 256
name: Test
services:
# 下面启动一个mysql和mongo服务主要用于coze.hub.core相关服务的集成测试
# id需要保持跟代码中连接的host一致
- id: hub_mysql
image: hub.byted.org/ee/mysql:5.7
envs:
MYSQL_ROOT_PASSWORD: test
MYSQL_DATABASE: coze_hub
MYSQL_USER: test
MYSQL_PASSWORD: test
MYSQL_TCP_PORT: 3306
- id: hub_mongo
image: hub.byted.org/ee/mongo:4.0.24
commands:
- mongod --replSet rs0
- id: mongo-init
image: hub.byted.org/ee/mongo:4.0.24
commands:
- sleep 10s
- mongo mongodb://127.0.0.1:27017 --eval 'rs.initiate()'
steps:
- *checkout_step
- *proxy_step
- *init_env_step
- *install_deps_step
- name: wait mongo ready
uses: actions/mongodb-scripts
inputs:
host: 127.0.0.1
port: 27017
- name: wait mysql ready
uses: actions/mysql-scripts
inputs:
host: hub_mysql
user: test
password: test
commands:
- mysql -u test -ptest -h hub_mysql -P 3306 -e 'show databases;'
# 服务发现
- id: consul
uses: actions/setup-consul
- name: Test Coverage
id: test
commands:
- node common/scripts/install-run-rush.js test:cov -v
# https://bytedance.feishu.cn/wiki/wikcn9Z4azEdfJalVPKcOs9vxsR
security_scan:
<<: *base_job
name: Security Scan
caches:
- backend: ebs
key: bot-studio-monorepo-master-v2-scan
size: 256
steps:
- *checkout_step
- *proxy_step
- *init_env_step
- *install_deps_step
- name: build
commands:
- CUSTOM_VERSION=release REGION=sg node common/scripts/install-run-rush.js build -o app-botstudio-main -v
- id: Argus
commands:
- npm i @ies/argus-scan@0.30.37 -g
- bash .codebase/scripts/argus-scan.sh

View File

@ -1,89 +0,0 @@
name: CI@fe-tsc
trigger:
manual:
change:
# 临时支持 project-ide 项目需求,临时修改为如下格式
# 后续需调整回 integration/**
types: [create, push, restore]
paths:
- "frontend/**"
notification:
when: failure
jobs:
ts_check: &base_job
runs-on:
env: online
image: hub.byted.org/base/bot_monorepo_ci_env:ae543e9bbc6d8155cffbd8f5ed27fb73
# 从最近的 CI 执行记录看install 步骤有概率会等待超时
# 目前已 oncall这里先设置一个超时时间避免阻塞
timeout: 30
if: ${{ !Event.Change.IsPreSubmit }}
envs:
targetBranch: ${{Event.Change.Target.Branch}}
RUSH_BUILD_CACHE_WRITE_ALLOWED: ${{RUSH_BUILD_CACHE_WRITE_ALLOWED}}
RUSH_BUILD_CACHE_ENABLED: ${{RUSH_BUILD_CACHE_ENABLED}}
RUSH_BUILD_CACHE_CREDENTIAL: ${{RUSH_BUILD_CACHE_CREDENTIAL}}
ACCESS_TOKEN: ${{CI_BOT_ACCESS_TOKEN}}
CI: 'true'
RUN_BYTEST_COV: 'false'
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 'true'
CYPRESS_INSTALL_BINARY: '0'
TAIKO_SKIP_CHROMIUM_DOWNLOAD: '0'
RE2_DOWNLOAD_SKIP_PATH: '1'
RE2_DOWNLOAD_MIRROR: https://bnpm.bytedance.net/mirrors
MERGE_REQUEST_TITLE: ${{Event.Change.Title}}
TSESTREE_SINGLE_RUN: 'true'
steps:
- &checkout_step
name: Checkout
uses: actions/checkout
inputs:
submodules: true
depth: 1
- &proxy_step
name: SetupProxy
commands:
- bash .codebase/scripts/env.sh
- &remote_info_step
id: RemoteInfo
uses: 'actions/mr-remote-info'
inputs:
info_list: ['behind_commits', 'changed_files', 'changed_files_path']
- &cache_step
id: Cache
uses: actions/cache
inputs:
key: flow-monorepo-${{Event.Change.Source.Branch}}
paths:
- common/temp/pnpm-store
restore_keys:
- flow-monorepo-${{Event.Change.Source.Branch}}
- flow-monorepo-store-master
- &init_env_step
id: InitEnv
name: Initialization
commands:
- printenv
- git config user.name ci_flow
- git config user.email ci_flow@bytedance.com
- npm config set registry=https://registry.npmjs.org
- pnpm config set network-concurrency 32
- echo "$(<${{Steps.RemoteInfo.Outputs.changed_files_path}})"
- echo "::set-output name=ShouldRunBuild::${{ Event.Change.IsPreSubmit || int(Steps.RemoteInfo.Outputs.behind_commits) <= 6 }}"
- &install_deps_step
name: Install dependencies
commands:
- npx why-is-node-running@v2.x common/scripts/install-run-rush.js increment --action install -p ${{Steps.RemoteInfo.Outputs.changed_files_path}}
- name: Prepare basic packages
commands:
- node common/scripts/install-run-rush.js pre-build -v
- name: Check TS Type
commands:
- node common/scripts/install-run-rush.js increment --action ts-check -p ${{Steps.RemoteInfo.Outputs.changed_files_path}}

View File

@ -1,184 +0,0 @@
name: CI@fe
trigger:
manual:
change:
source-branches: ['!release/**', '!task/**']
types: [create, push, restore]
paths:
- "frontend/**"
notification:
when: failure
jobs:
lint: &base_job
runs-on:
env: online
image: hub.byted.org/base/bot_monorepo_ci_env:ae543e9bbc6d8155cffbd8f5ed27fb73
# 从最近的 CI 执行记录看install 步骤有概率会等待超时
# 目前已 oncall这里先设置一个超时时间避免阻塞
timeout: 30
if: ${{ !Event.Change.IsPreSubmit }}
envs:
targetBranch: ${{Event.Change.Target.Branch}}
RUSH_BUILD_CACHE_WRITE_ALLOWED: ${{RUSH_BUILD_CACHE_WRITE_ALLOWED}}
RUSH_BUILD_CACHE_ENABLED: ${{RUSH_BUILD_CACHE_ENABLED}}
RUSH_BUILD_CACHE_CREDENTIAL: ${{RUSH_BUILD_CACHE_CREDENTIAL}}
ACCESS_TOKEN: ${{CI_BOT_ACCESS_TOKEN}}
CI: 'true'
RUN_BYTEST_COV: 'false'
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 'true'
CYPRESS_INSTALL_BINARY: '0'
TAIKO_SKIP_CHROMIUM_DOWNLOAD: '0'
BUILD_BRANCH: ${{Event.Change.Source.Branch}}
RE2_DOWNLOAD_SKIP_PATH: '1'
MERGE_REQUEST_TITLE: ${{Event.Change.Title}}
TSESTREE_SINGLE_RUN: 'true'
caches:
- backend: ebs
key: bot-studio-monorepo-ci-v2-lint
size: 256
steps:
- &checkout_step
name: Checkout
uses: actions/checkout
inputs:
submodules: true
depth: 1
- &proxy_step
name: SetupProxy
commands:
- bash .codebase/scripts/env.sh
- &remote_info_step
id: RemoteInfo
uses: 'actions/mr-remote-info'
inputs:
info_list: ['behind_commits', 'changed_files', 'changed_files_path']
- &cache_step
id: Cache
uses: actions/cache
inputs:
key: flow-monorepo-${{Event.Change.Source.Branch}}
paths:
- common/temp/pnpm-store
- packages/arch/idl/src/auto-generated
restore_keys:
- flow-monorepo-${{Event.Change.Source.Branch}}
- flow-monorepo-store-master
- &init_env_step
id: InitEnv
name: Initialization
commands:
- printenv
- git config user.name ci_flow
- git config user.email ci_flow@bytedance.com
- npm config set registry=https://registry.npmjs.org
- pnpm config set network-concurrency 32
- echo "$(<${{Steps.RemoteInfo.Outputs.changed_files_path}})"
- echo "::set-output name=ShouldRunBuild::${{ Event.Change.IsPreSubmit || int(Steps.RemoteInfo.Outputs.behind_commits) <= 6 }}"
- &install_deps_step
name: Install dependencies
commands:
- npx why-is-node-running@v2.x common/scripts/install-run-rush.js increment --action install -p ${{Steps.RemoteInfo.Outputs.changed_files_path}}
- name: Check Lint
commands:
- node common/scripts/install-run-rush.js increment --action lint -p ${{Steps.RemoteInfo.Outputs.changed_files_path}}
style_check:
<<: *base_job
caches:
- backend: ebs
key: bot-studio-monorepo-ci-v2-style-check
size: 256
name: Check Stylelint
steps:
- *checkout_step
- *proxy_step
- *remote_info_step
- *cache_step
- *init_env_step
- *install_deps_step
- name: Check Stylelint
commands:
- node common/scripts/install-run-rush.js increment --action style -p ${{Steps.RemoteInfo.Outputs.changed_files_path}}
build:
<<: *base_job
caches:
- backend: ebs
key: bot-studio-monorepo-ci-v2-build
size: 256
name: Build
timeout: 20
steps:
- *checkout_step
- *proxy_step
- *remote_info_step
- *cache_step
- *init_env_step
- *install_deps_step
- name: Build
id: build
if: ${{ Steps.InitEnv.Outputs.ShouldRunBuild }}
commands:
- node common/scripts/install-run-rush.js increment --action build -p ${{Steps.RemoteInfo.Outputs.changed_files_path}}
test:
<<: *base_job
caches:
- backend: ebs
key: bot-studio-monorepo-ci-v2-test
size: 256
name: Test Coverage
services:
# 下面启动一个mysql和mongo服务主要用于coze.hub.core相关服务的集成测试
# id需要保持跟代码中连接的host一致
- id: hub_mysql
image: hub.byted.org/ee/mysql:5.7
envs:
MYSQL_ROOT_PASSWORD: test
MYSQL_DATABASE: coze_hub
MYSQL_USER: test
MYSQL_PASSWORD: test
MYSQL_TCP_PORT: 3306
- id: hub_mongo
image: hub.byted.org/ee/mongo:4.0.24
commands:
- mongod --replSet rs0
- id: mongo-init
image: hub.byted.org/ee/mongo:4.0.24
commands:
- sleep 10s
- mongo mongodb://127.0.0.1:27017 --eval 'rs.initiate()'
steps:
- *checkout_step
- *proxy_step
- *remote_info_step
- *cache_step
- *init_env_step
- *install_deps_step
- name: wait mongo ready
uses: actions/mongodb-scripts
inputs:
host: 127.0.0.1
port: 27017
- name: wait mysql ready
uses: actions/mysql-scripts
inputs:
host: hub_mysql
user: test
password: test
commands:
- mysql -u test -ptest -h hub_mysql -P 3306 -e 'show databases;'
# 服务发现
- id: consul
uses: actions/setup-consul
- name: Test Coverage
commands:
- git fetch --filter=blob:none --unshallow -q
- node common/scripts/install-run-rush.js increment --action test:cov -p ${{Steps.RemoteInfo.Outputs.changed_files_path}}

View File

@ -1,14 +0,0 @@
#!/bin/bash
set -ex
# 暂时只扫描 app-botstudio-main 项目的产物
result=$(argus scm -c apps/bot/dist_sg/static -n obric/cloud/bot_studio_oversea -l)
if echo "$result" | grep -q '::add-message level=error:::'; then
if [ "$CI" ]; then
echo '::add-message level=info::本地验证命令:`npm install -g @ies/argus-scan@0.30.37 && REGION=sg CUSTOM_VERSION=release rush build -o app-botstudio-main && argus scm -c apps/bot/dist_sg/static -n obric/cloud/bot_studio_oversea -l`'
fi
exit 1
else
exit 0
fi

View File

@ -1,16 +0,0 @@
#!/bin/bash
set -ex
SOURCE_BRANCH=${SOURCE_BRANCH}
TARGET_BRANCH=${targetBranch}
if [[ $TARGET_BRANCH == "master" && !($SOURCE_BRANCH =~ ^release/ || $SOURCE_BRANCH =~ ^hotfix/ || $SOURCE_BRANCH =~ ^task/ || $SOURCE_BRANCH =~ ^fix/) ]]; then
# 检查$SOURCE_BRANCH是否以'release/'或'hotfix/'或'task/'或'fix/'开头
LATEST_BRANCH="release/$(date -d '+8 hour' +%Y%m%d)"
CONCLUSION="{\"name\": \"Target Branch\", \"conclusion\": \"failed\", \"output\":{\"summary\":\"Error: Please don't merge to master directly, use [$LATEST_BRANCH](https://code.byted.org/obric/bot-studio-monorepo/commits/$LATEST_BRANCH) instead.\n You can contact [@fanwenjie.fe](https://code.byted.org/fanwenjie.fe) to skip this error.\" }}"
else
CONCLUSION="{\"name\": \"Target Branch\", \"conclusion\": \"success\", \"output\":{\"summary\":\"Good Pratice\" }}"
fi
echo $CONCLUSION >>check-merge-target.log
echo "::update-check-run::check-merge-target.log"

View File

@ -1,55 +0,0 @@
#!/bin/bash
set -ex
# Your target branch
TARGET_BRANCH=$targetBranch
if [[ ${SOURCE_BRANCH} =~ ^integration/ || ${SOURCE_BRANCH} =~ ^release/ ]]; then
# integration -> xxx or release/xxx -> master SKIP check-mr-size.
echo "::add-message level=info::SKIP check-mr-size"
exit 0
fi
# Specify the pattern you want to exclude, using *space* as the separator
EXCLUDE_PATTERNS=(
'**/pnpm-lock.yaml'
'packages/arch/bot-api/src/auto-generate/**'
'apps/bot-op/src/services/bam-auto-generate/**'
'apps/prompt-platform/src/services/auto-generate/**'
"**/lib/**"
"**/.*/**"
'**/__tests__/**'
'**/__test__/**'
"**/__mocks__/**"
"**/__mock__/**"
"**/*.test.*/**"
"**/*.spec.*/**"
"**/__snapshots__/**"
"**/*.snap"
'**/*.svg'
'ee/e2e/bot-studio/**'
'common/changes/**'
'apps/fornax/**'
"apps/api-builder/**"
"packages/api-builder/**"
)
for pattern in "${EXCLUDE_PATTERNS[@]}"; do
EXCLUDE_STRING+=":(exclude)$pattern "
done
# Count the number of files changed but exclude certain files and folders
file_changes=$(git diff --name-only "origin/$TARGET_BRANCH..." $EXCLUDE_STRING | wc -l)
# Count the number of line changes but exclude certain files and folders
line_changes=$(git diff --shortstat "origin/$TARGET_BRANCH..." $EXCLUDE_STRING | awk '{print ($4>$6)?$4:$6}')
# Check if number of changed files is greater than 100 or if number of line changes is greater than 2000
if [ "$file_changes" -gt 100 ] || [ "$line_changes" -gt 2000 ]; then
CONCLUSION="{\"name\": \"MR Size\", \"conclusion\": \"failed\", \"output\":{\"summary\":\"Error: Too many changes. Number of changed files is **""$file_changes""**, number of changed lines is **""$line_changes""**.\n You can contact [@fanwenjie.fe](https://code.byted.org/fanwenjie.fe) to skip this error.\" }}"
else
CONCLUSION="{\"name\": \"MR Size\", \"conclusion\": \"success\", \"output\":{\"summary\":\"Good\" }}"
fi
echo $CONCLUSION >>check-mr-size.log
echo "::update-check-run::check-mr-size.log"

View File

@ -1,14 +0,0 @@
#!/bin/bash
set -ex
PRE_COMMITS=$1
# 按 codebase 给出的口径pre commits 超过 5 时容易导致 rebase 失败,因此主动给出警告,避免进入 CQ 后被弹出
if [ $PRE_COMMITS -gt 5 ]; then
CONCLUSION="{\"name\": \"Pre Commits Check\", \"conclusion\": \"failed\", \"output\":{\"summary\":\"分支已落后目标分支较多,非常容易导致进入 CQ 后被弹出,请执行 rebase/merge 同步代码后重试。\" }}"
else
CONCLUSION="{\"name\": \"Pre Commits Check\", \"conclusion\": \"success\", \"output\":{\"summary\":\"good\" }}"
fi
echo $CONCLUSION >> check-pre-commits.log
echo "::update-check-run::check-pre-commits.log"

View File

@ -1,54 +0,0 @@
#!/bin/bash
set -ex
basename=$(basename "$CHANGE_URL")
lastParam=$(echo "$basename" | cut -d'/' -f1)
targetBranch="${targetBranch}"
sourceBranch="${SOURCE_BRANCH}"
mrTitle="${MR_TITLE}"
mrDescription="${MR_DESCRIPTION}"
echo "::set-output name=mrId::$lastParam" # 输出 lastParam 的值
result=$(curl --location "https://code.byted.org/api/v4/projects/548801/merge_requests/$lastParam" \
--header "Private-Token: $GITLAB_TOKEN")
commits=$(curl --location "https://code.byted.org/api/v4/projects/548801/merge_requests/$lastParam/commits" \
--header "Private-Token: $GITLAB_TOKEN")
isSquash=$(echo "$result" | jq -r '.squash') # 使用jq提取isSquash的值
commitsCount=$(echo "$commits" | jq length)
echo "::set-output name=squash::$isSquash" # 输出 isSquash 的值
echo "::set-output name=commitsCount::$commitsCount" # 输出 commitsCount 的值
if [[ $isSquash == true ]]; then
# 勾选squash
if [[ $sourceBranch == release/* && $targetBranch == master ]]; then
echo "::add-message level=error::**release 分支合入 master 时,不可开启 squash **"
exit 1
fi
if [[ $mrDescription == \[no-squash\]* ]]; then
echo "::add-message level=error::**当前 MR 勾选了 Squash 选项,但是描述中包含[no-squash]**"
exit 1
fi
else
# 没有勾选squash
if [[ $mrDescription == \[no-squash\]* ||
$commitsCount -le 1 ||
$mrTitle == WIP:* ||
$mrTitle == wip:* ||
$sourceBranch == release/* ]]; then
exit 0
fi
echo "::add-message level=error::**当前 MR 应该勾选 Squash 选项**"
exit 1
fi

View File

@ -1,53 +0,0 @@
const fs = require('fs/promises');
const path = require('path');
const crypto = require('crypto');
// node scripts/checksum-by-change.js /usr/changed-path.json
// change-path 文件来自 ci
const changedPath = process.argv[2];
const readJson = async jsonFile => {
const content = await fs.readFile(jsonFile, 'utf-8');
let _val = null;
try {
eval(`_val = ${content}`);
return _val;
} catch (e) {
console.error(`json parse failure: `, e);
}
};
const readChangedPackages = async changedPath => {
const [changedFiles, { projects }] = await Promise.all([
readJson(changedPath),
readJson(path.resolve(__dirname, '../../rush.json')),
]);
const changedProjects = projects
.filter(project => {
const { projectFolder } = project;
const endsWithSlash = projectFolder.endsWith('/');
const compareFolder = `${projectFolder}${endsWithSlash ? '' : '/'}`;
if (!changedFiles) {
// changed-path.json 内容可能为null
return true;
}
const matched = changedFiles.find(file => file.startsWith(compareFolder));
return !!matched;
})
.map(({ packageName }) => packageName)
.sort((r1, r2) => r1.localeCompare(r2));
return changedProjects;
};
async function main() {
if (!changedPath || changedPath.length <= 0) {
throw new Error(`Please pass the correct "changedPath" path`);
}
const changedPackages = await readChangedPackages(changedPath);
const hash = crypto.createHash('md5');
changedPackages.forEach(r => hash.update(r));
const hashValue = hash.digest('hex');
console.log(`::set-output name=hash::${hashValue}`);
}
main();

View File

@ -1,26 +0,0 @@
#!/usr/bin/env bash
echo ::set-env name=no_proxy::cn.goofy.app,.cn.goofy.app,goofy.app,.goofy.app,localhost,.byted.org,byted.org,.bytedance.net,bytedance.net,127.0.0.1,127.0.0.0/8,169.254.0.0/16,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8,::1,fe80::/10,fd00::/8
echo ::set-env name=all_proxy::http://sys-proxy-rd-relay.byted.org:3128
echo ::set-env name=http_proxy::http://sys-proxy-rd-relay.byted.org:3128
echo ::set-env name=https_proxy::http://sys-proxy-rd-relay.byted.org:3128
#!/usr/bin/env bash
# Setup common env for CI & SCM
# 1. 忽略不影响构建的 install
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=true
export CYPRESS_INSTALL_BINARY=0
export TAIKO_SKIP_CHROMIUM_DOWNLOAD=0
export CUSTOM_VERSION="inhouse"
export RE2_DOWNLOAD_SKIP_PATH=1
export RE2_DOWNLOAD_MIRROR="https://bnpm.bytedance.net/mirrors"
export PUPPETEER_SKIP_DOWNLOAD=true
# 2. 在 CI 环境生效:
echo ::set-env name=PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD::$PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD
echo ::set-env name=CYPRESS_INSTALL_BINARY::$CYPRESS_INSTALL_BINARY
echo ::set-env name=TAIKO_SKIP_CHROMIUM_DOWNLOAD::$TAIKO_SKIP_CHROMIUM_DOWNLOAD
echo ::set-env name=RE2_DOWNLOAD_SKIP_PATH::$RE2_DOWNLOAD_SKIP_PATH
echo ::set-env name=RE2_DOWNLOAD_MIRROR::$RE2_DOWNLOAD_MIRROR
echo ::set-env name=PUPPETEER_SKIP_DOWNLOAD::$PUPPETEER_SKIP_DOWNLOAD

540
.github/CODEOWNERS vendored
View File

@ -1,268 +1,274 @@
* Tecvan-fe
* @Tecvan-fe @hi-pender @fanlv
/apps/coze-studio/ @Tecvan-fe @evan-crash @duwenhan2byte
/packages/agent-ide/agent-publish/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei @catee
/packages/agent-ide/commons/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/arch/bot-api/ @Tecvan-fe
/packages/arch/bot-http/ @Tecvan-fe
/packages/arch/logger/ @Tecvan-fe
/packages/arch/slardar-interface/ @Tecvan-fe @evan-crash
/config/eslint-config/ @Tecvan-fe @leeight @soonco
/infra/eslint-plugin/ @Tecvan-fe
/config/ts-config/ @leeight @Tecvan-fe
/config/vitest-config/ @Tecvan-fe
/packages/arch/bot-env/ @Tecvan-fe @leeight
/packages/arch/bot-env-adapter/ @dragooncjw @Tecvan-fe @leeight
/packages/arch/bot-typings/ @Tecvan-fe
/packages/arch/web-context/ @Tecvan-fe
/packages/components/bot-semi/ @Tecvan-fe
/packages/components/bot-icons/ @DingGao-Devin
/packages/arch/i18n/ @Tecvan-fe @leeight
/packages/arch/resources/studio-i18n-resource/ @dragooncjw @Tecvan-fe
/config/stylelint-config/ @Tecvan-fe
/packages/arch/idl/ @Tecvan-fe
/infra/utils/fs-enhance/ @Tecvan-fe
/packages/arch/bot-store/ @Tecvan-fe @catee @duwenhan2byte
/packages/arch/bot-error/ @haozhenfei @duwenhan2byte
/packages/foundation/space-store/ @evan-crash @duwenhan2byte
/packages/arch/bot-flags/ @Tecvan-fe
/packages/arch/report-events/ @Tecvan-fe
/packages/foundation/enterprise-store-adapter/ @evan-crash @duwenhan2byte
/packages/foundation/local-storage/ @duwenhan2byte @evan-crash
/packages/foundation/space-store-adapter/ @evan-crash @duwenhan2byte
/packages/arch/bot-tea/ @Tecvan-fe @catee @soonco
/packages/arch/tea/ @Tecvan-fe @evan-crash @soonco
/packages/arch/tea-adapter/ @dragooncjw @Tecvan-fe
/packages/arch/tea-interface/ @dragooncjw @Tecvan-fe
/packages/studio/stores/bot-detail/ @Hezi-crypto @catee @DingGao-Devin @duwenhan2byte @evan-crash
/packages/agent-ide/bot-input-length-limit/ @Hezi-crypto @catee @duwenhan2byte
/packages/agent-ide/tool-config/ @haozhenfei @catee
/packages/arch/bot-space-api/ @Tecvan-fe @duwenhan2byte
/packages/arch/bot-utils/ @Tecvan-fe
/packages/common/uploader-adapter/ @dragooncjw @Tecvan-fe
/packages/common/uploader-interface/ @dragooncjw @Tecvan-fe
/packages/studio/user-store/ @duwenhan2byte @catee @lihuiwen
/packages/arch/foundation-sdk/ @evan-crash @duwenhan2byte
/packages/common/chat-area/chat-core/ @haozhenfei @Hezi-crypto @evan-crash
/packages/common/chat-area/utils/ @Hezi-crypto @haozhenfei
/packages/arch/bot-md-box-adapter/ @Hezi-crypto @iu1340 @dragooncjw @Tecvan-fe
/packages/studio/common/file-kit/ @haozhenfei @evan-crash
/packages/arch/slardar-adapter/ @Tecvan-fe @dragooncjw
/packages/arch/default-slardar/ @Tecvan-fe @evan-crash
/packages/arch/fetch-stream/ @Hezi-crypto @haozhenfei
/packages/common/websocket-manager-adapter/ @haozhenfei @Hezi-crypto @catee
/packages/studio/autosave/ @catee
/packages/studio/bot-utils/ @catee @soonco @Hezi-crypto
/packages/common/flowgram-adapter/common/ @zxhfighter @xiamidaxia @dragooncjw
/packages/common/flowgram-adapter/free-layout-editor/ @zxhfighter @xiamidaxia @dragooncjw
/packages/agent-ide/space-bot/ @soonco @evan-crash @duwenhan2byte @catee @DingGao-Devin
/packages/agent-ide/space-bot/src/store/bot-list-filter/ @duwenhan2byte @lihuiwen
/packages/agent-ide/space-bot/src/store/bot-page/ @DingGao-Devin
/packages/agent-ide/space-bot/src/store/explore/ @Tecvan-fe
/packages/agent-ide/space-bot/src/store/risk-warning/ @lihuiwen @catee
/packages/agent-ide/context/ @evan-crash
/packages/agent-ide/bot-editor-context-store/ @Hezi-crypto @duwenhan2byte @catee
/packages/agent-ide/chat-background/ @catee
/packages/agent-ide/chat-background-config-content-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/chat-background-config-content/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/chat-background-shared/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/common/chat-area/chat-uikit/ @catee @Hezi-crypto @evan-crash @haozhenfei
/packages/common/chat-area/hooks/ @Hezi-crypto @evan-crash
/packages/common/chat-area/chat-uikit-shared/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/bot-audit-adapter/ @evan-crash @duwenhan2byte @Hezi-crypto @haozhenfei
/packages/agent-ide/bot-audit-base/ @evan-crash @duwenhan2byte @Hezi-crypto @haozhenfei
/packages/studio/components/ @soonco @evan-crash @duwenhan2byte @catee
/packages/arch/bot-hooks/ @catee @Tecvan-fe @soonco
/packages/arch/bot-hooks/src/page-jump/ @evan-crash @catee
/packages/arch/bot-hooks-adapter/ @catee @Tecvan-fe @soonco
/packages/arch/bot-hooks-base/ @catee @Tecvan-fe @soonco
/packages/arch/responsive-kit/ @Tecvan-fe @DingGao-Devin
/packages/common/chat-area/chat-area/ @Hezi-crypto @haozhenfei @evan-crash @haozhenfei
/packages/data/memory/llm-plugins/ @haozhenfei @catee @Hezi-crypto
/packages/data/common/reporter/ @soonco @catee @evan-crash
/packages/components/json-viewer/ @duwenhan2byte
/packages/components/scroll-view/ @evan-crash
/packages/common/assets/ @Tecvan-fe @catee
/packages/common/biz-components/ @duwenhan2byte
/packages/data/common/e2e/ @soonco @catee @evan-crash @haozhenfei @duwenhan2byte
/packages/agent-ide/debug-tool-list/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/model-manager/ @Hezi-crypto @catee
/packages/agent-ide/model-manager/src/components/multi-agent/ @catee
/packages/agent-ide/tool/ @catee
/packages/data/knowledge/knowledge-modal-base/ @haozhenfei @catee @Hezi-crypto
/packages/components/biz-tooltip-ui/ @Hezi-crypto @catee @evan-crash
/packages/components/table-view/ @lihuiwen
/packages/components/virtual-list/ @Tecvan-fe
/packages/data/common/utils/ @soonco @catee @evan-crash
/packages/data/knowledge/knowledge-resource-processor-core/ @haozhenfei @catee @Hezi-crypto
/packages/arch/pdfjs-shadow/ @Tecvan-fe
/packages/data/knowledge/common/stores/ @soonco @catee @evan-crash
/packages/foundation/global-store/ @duwenhan2byte @evan-crash
/config/postcss-config/ @Tecvan-fe
/config/tailwind-config/ @Tecvan-fe
/infra/utils/monorepo-kits/ @Tecvan-fe @evan-crash
/packages/studio/premium/premium-components-adapter/ @evan-crash
/packages/studio/premium/premium-store-adapter/ @evan-crash
/packages/agent-ide/onboarding/ @Hezi-crypto @catee
/packages/agent-ide/publish-to-base/ @catee
/packages/arch/report-tti/ @duwenhan2byte
/packages/data/memory/database-creator/ @haozhenfei @catee @Hezi-crypto
/packages/arch/hooks/ @Tecvan-fe @evan-crash
/packages/common/coze-mitt/ @evan-crash @duwenhan2byte
/packages/common/editor-plugins/ @haozhenfei @stream-pipe @Hezi-crypto
/packages/common/md-editor-adapter/ @haozhenfei @Hezi-crypto @catee
/packages/common/prompt-kit/main/ @haozhenfei @Hezi-crypto @catee
/packages/common/chat-area/chat-answer-action/ @Hezi-crypto @haozhenfei @lihuiwen
/packages/common/chat-area/plugin-message-grab/ @Hezi-crypto @haozhenfei
/packages/common/chat-area/text-grab/ @Hezi-crypto @haozhenfei
/packages/common/prompt-kit/adapter/ @haozhenfei @Hezi-crypto @catee
/packages/common/prompt-kit/base/ @haozhenfei @Hezi-crypto @catee
/packages/data/memory/database/ @haozhenfei @catee @Hezi-crypto
/packages/data/knowledge/knowledge-resource-processor-base/ @haozhenfei @catee @Hezi-crypto
/packages/arch/utils/ @Tecvan-fe @evan-crash
/packages/data/knowledge/common/components/ @haozhenfei @catee @Hezi-crypto
/packages/data/common/feature-register/ @haozhenfei @catee @Hezi-crypto
/packages/data/knowledge/common/hooks/ @Hezi-crypto @catee @evan-crash
/packages/data/knowledge/common/services/ @Hezi-crypto @catee @evan-crash
/packages/data/memory/database-v2-main/ @haozhenfei @catee @Hezi-crypto
/packages/data/memory/database-v2-adapter/ @haozhenfei @catee @Hezi-crypto
/packages/data/memory/database-v2-base/ @haozhenfei @catee @Hezi-crypto
/packages/data/knowledge/knowledge-data-set-for-agent/ @Hezi-crypto @catee @evan-crash
/packages/data/knowledge/knowledge-ide-base/ @haozhenfei @catee @Hezi-crypto
/packages/arch/bot-monaco-editor/ @Tecvan-fe
/packages/data/knowledge/knowledge-modal-adapter/ @haozhenfei @catee @Hezi-crypto
/packages/data/knowledge/knowledge-resource-processor-adapter/ @haozhenfei @catee @Hezi-crypto
/packages/devops/debug/debug-panel/ @soonco @evan-crash @catee
/packages/devops/json-link-preview/ @Maidang1 @Zhangchi123456
/packages/devops/common-modules/ @duwenhan2byte @evan-crash @catee
/packages/foundation/account-adapter/ @duwenhan2byte @evan-crash
/packages/foundation/account-base/ @evan-crash @duwenhan2byte
/packages/arch/api-schema/ @Tecvan-fe @evan-crash
/infra/idl/idl2ts-runtime/ @Tecvan-fe @evan-crash
/infra/idl/idl2ts-cli/ @Tecvan-fe @evan-crash
/infra/idl/idl2ts-generator/ @Tecvan-fe @evan-crash
/infra/idl/idl-parser/ @Tecvan-fe @evan-crash
/infra/utils/rush-logger/ @catee @Tecvan-fe
/infra/idl/idl2ts-helper/ @Tecvan-fe @evan-crash
/infra/idl/idl2ts-plugin/ @Tecvan-fe @evan-crash
/packages/studio/open-platform/open-env-adapter/ @soonco @Hezi-crypto @DingGao-Devin
/infra/plugins/pkg-root-webpack-plugin/ @Tecvan-fe
/packages/studio/publish-manage-hooks/ @duwenhan2byte @evan-crash
/packages/foundation/layout/ @evan-crash @duwenhan2byte
/packages/studio/open-platform/open-auth/ @evan-crash @DingGao-Devin
/packages/agent-ide/entry-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/entry/ @soonco @duwenhan2byte @catee @evan-crash
/packages/agent-ide/bot-config-area-adapter/ @haozhenfei @Hezi-crypto @duwenhan2byte @catee @evan-crash
/packages/agent-ide/bot-config-area/ @haozhenfei @Hezi-crypto @duwenhan2byte @catee @evan-crash
/packages/agent-ide/bot-plugin/entry/ @evan-crash @lihuiwen @catee
/packages/agent-ide/bot-plugin/export/ @lihuiwen @catee
/packages/agent-ide/bot-plugin/mock-set/ @lihuiwen @catee @duwenhan2byte
/packages/studio/mockset-edit-modal-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/studio/mockset-shared/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/studio/mockset-editor/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/studio/mockset-editor-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/bot-plugin/tools/ @lihuiwen @catee
/packages/studio/stores/bot-plugin/ @lihuiwen @Hezi-crypto @catee
/packages/studio/plugin-shared/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/plugin-modal-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/plugin-shared/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/community/component/ @DingGao-Devin @Hezi-crypto @evan-crash @duwenhan2byte
/packages/studio/plugin-form-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/workflow/base/ @xiamidaxia @zxhfighter
/packages/agent-ide/plugin-content-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/plugin-content/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/plugin-setting-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/plugin-setting/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/bot-plugin/plugin-risk-warning/ @catee @evan-crash
/packages/studio/plugin-publish-ui-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/studio/plugin-tool-columns-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/studio/plugin-tool-columns/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/chat-debug-area/ @soonco @duwenhan2byte @catee @Hezi-crypto @haozhenfei @evan-crash
/packages/agent-ide/chat-answer-action-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/chat-area-plugin-debug-common/ @Hezi-crypto @haozhenfei
/packages/agent-ide/chat-components-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/common/chat-area/plugin-chat-background/ @Tecvan-fe
/packages/common/chat-area/chat-area-plugin-reasoning/ @catee @Hezi-crypto
/packages/common/chat-area/plugin-resume/ @Tecvan-fe
/packages/common/chat-area/plugin-chat-shortcuts/ @haozhenfei @duwenhan2byte @Hezi-crypto
/packages/workflow/sdk/ @zxhfighter @xiamidaxia
/packages/workflow/components/ @LLLLeeJ @zxhfighter
/packages/components/loading-button/ @catee
/packages/components/mouse-pad-selector/ @zxhfighter
/packages/workflow/adapter/resources/ @LLLLeeJ @JxJuly @xiamidaxia @luics @zxhfighter @stream-pipe
/packages/common/chat-area/chat-workflow-render/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/onboarding-message-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/plugin-area-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/prompt-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/prompt/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/workflow/ @soonco @duwenhan2byte @catee
/packages/agent-ide/navigate/ @soonco @duwenhan2byte @catee
/packages/agent-ide/workflow-as-agent-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/workflow-item/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/workflow-card-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/workflow-modal/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/memory-tool-pane-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/skills-pane-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/layout-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/layout/ @soonco @duwenhan2byte @catee
/packages/agent-ide/chat-area-provider-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/agent-ide/chat-area-provider/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/studio/entity-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/foundation/account-ui-adapter/ @duwenhan2byte @evan-crash
/packages/foundation/account-ui-base/ @evan-crash @duwenhan2byte
/packages/foundation/foundation-sdk/ @evan-crash @duwenhan2byte
/packages/foundation/global/ @evan-crash @duwenhan2byte
/packages/studio/workspace/project-entity-adapter/ @Hezi-crypto @catee @duwenhan2byte
/packages/studio/workspace/project-entity-base/ @Hezi-crypto @catee @duwenhan2byte
/packages/foundation/global-adapter/ @evan-crash @duwenhan2byte
/packages/foundation/browser-upgrade-banner/ @evan-crash @duwenhan2byte
/packages/foundation/space-ui-adapter/ @evan-crash @duwenhan2byte
/packages/foundation/space-ui-base/ @evan-crash @duwenhan2byte
/packages/common/auth/ @evan-crash @duwenhan2byte
/packages/common/auth-adapter/ @evan-crash @duwenhan2byte
/packages/project-ide/main/ @dragooncjw @JxJuly @xiamidaxia @catee @lihuiwen
/packages/components/resource-tree/ @dragooncjw @xiamidaxia @JxJuly
/packages/common/flowgram-adapter/fixed-layout-editor/ @zxhfighter @xiamidaxia @dragooncjw
/packages/project-ide/biz-components/ @zxhfighter @xiamidaxia
/packages/project-ide/framework/ @dragooncjw @JxJuly @xiamidaxia
/packages/project-ide/base-adapter/ @dragooncjw @xiamidaxia
/packages/project-ide/base-interface/ @dragooncjw @xiamidaxia
/packages/project-ide/client/ @dragooncjw @JxJuly @xiamidaxia
/packages/project-ide/core/ @dragooncjw @JxJuly @xiamidaxia
/packages/project-ide/view/ @dragooncjw @JxJuly @xiamidaxia
/packages/project-ide/biz-data/ @haozhenfei @lihuiwen @catee @soonco
/packages/data/knowledge/knowledge-ide-adapter/ @haozhenfei @catee @Hezi-crypto
/packages/data/memory/variables/ @haozhenfei @catee @Hezi-crypto
/packages/project-ide/biz-plugin/ @xiamidaxia @lihuiwen @catee
/packages/project-ide/biz-plugin-registry-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/packages/project-ide/biz-workflow/ @dragooncjw @JxJuly @xiamidaxia
/packages/studio/open-platform/open-chat/ @soonco @Hezi-crypto @DingGao-Devin
/packages/workflow/playground/ @LLLLeeJ @xiamidaxia @luics @zxhfighter
/packages/arch/load-remote-worker/ @Tecvan-fe
/packages/devops/mockset-manage/ @soonco @evan-crash @catee
/packages/devops/testset-manage/ @mocayo @JxJuly
/packages/workflow/adapter/base/ @LLLLeeJ @JxJuly @xiamidaxia @luics @zxhfighter @stream-pipe
/packages/workflow/adapter/code-editor/ @LLLLeeJ @JxJuly @xiamidaxia @luics @zxhfighter @stream-pipe
/packages/workflow/fabric-canvas/ @xiamidaxia @zxhfighter
/packages/workflow/feature-encapsulate/ @zxhfighter
/packages/workflow/nodes/ @xiamidaxia @zxhfighter
/packages/workflow/setters/ @Tecvan-fe
/packages/workflow/variable/ @zxhfighter @LLLLeeJ @xiamidaxia
/packages/workflow/render/ @dragooncjw @xiamidaxia
/packages/workflow/history/ @xiamidaxia @zxhfighter
/packages/workflow/adapter/nodes/ @LLLLeeJ @JxJuly @xiamidaxia @luics @zxhfighter @stream-pipe
/packages/workflow/test-run/ @JxJuly @xiamidaxia @luics @zxhfighter @dragooncjw
/packages/workflow/test-run-next/main/ @JxJuly @LLLLeeJ @xiamidaxia @zxhfighter
/packages/workflow/test-run-next/form/ @JxJuly @LLLLeeJ @xiamidaxia @zxhfighter
/packages/workflow/test-run-next/shared/ @JxJuly @LLLLeeJ @xiamidaxia @zxhfighter
/packages/workflow/test-run-next/trace/ @JxJuly @LLLLeeJ @xiamidaxia @zxhfighter
/packages/project-ide/ui-adapter/ @dragooncjw @xiamidaxia
/packages/studio/workspace/project-publish/ @catee @lihuiwen
/packages/studio/workspace/entry-adapter/ @duwenhan2byte @evan-crash
/packages/studio/workspace/entry-base/ @duwenhan2byte @catee @evan-crash
/packages/workflow/adapter/playground/ @LLLLeeJ @JxJuly @xiamidaxia @luics @zxhfighter @stream-pipe
/infra/plugins/import-watch-loader/ @Tecvan-fe
/config/rsbuild-config/ @Tecvan-fe
/packages/community/explore/ @evan-crash @duwenhan2byte
/common/_templates/node-core/ @Tecvan-fe
/common/_templates/rspack-web/ @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
/frontend/packages/arch/bot-api/ @Tecvan-fe
/frontend/packages/arch/bot-http/ @Tecvan-fe
/frontend/packages/arch/logger/ @Tecvan-fe
/frontend/packages/arch/slardar-interface/ @Tecvan-fe @evan-crash
/frontend/config/eslint-config/ @Tecvan-fe @leeight @soonco
/frontend/infra/eslint-plugin/ @Tecvan-fe
/frontend/config/ts-config/ @leeight @Tecvan-fe
/frontend/config/vitest-config/ @Tecvan-fe
/frontend/packages/arch/bot-env/ @Tecvan-fe @leeight
/frontend/packages/arch/bot-env-adapter/ @dragooncjw @Tecvan-fe @leeight
/frontend/packages/arch/bot-typings/ @Tecvan-fe
/frontend/packages/arch/web-context/ @Tecvan-fe
/frontend/packages/components/bot-semi/ @Tecvan-fe
/frontend/packages/components/bot-icons/ @DingGao-Devin
/frontend/packages/arch/i18n/ @Tecvan-fe @leeight
/frontend/packages/arch/resources/studio-i18n-resource/ @dragooncjw @Tecvan-fe
/frontend/config/stylelint-config/ @Tecvan-fe
/frontend/packages/arch/idl/ @Tecvan-fe
/frontend/infra/utils/fs-enhance/ @Tecvan-fe
/frontend/packages/arch/bot-store/ @Tecvan-fe @catee @duwenhan2byte
/frontend/packages/arch/bot-error/ @haozhenfei @duwenhan2byte
/frontend/packages/foundation/space-store/ @evan-crash @duwenhan2byte
/frontend/packages/arch/bot-flags/ @Tecvan-fe
/frontend/packages/arch/report-events/ @Tecvan-fe
/frontend/packages/foundation/enterprise-store-adapter/ @evan-crash @duwenhan2byte
/frontend/packages/foundation/local-storage/ @duwenhan2byte @evan-crash
/frontend/packages/foundation/space-store-adapter/ @evan-crash @duwenhan2byte
/frontend/packages/arch/bot-tea/ @Tecvan-fe @catee @soonco
/frontend/packages/arch/tea/ @Tecvan-fe @evan-crash @soonco
/frontend/packages/arch/tea-adapter/ @dragooncjw @Tecvan-fe
/frontend/packages/arch/tea-interface/ @dragooncjw @Tecvan-fe
/frontend/packages/studio/stores/bot-detail/ @Hezi-crypto @catee @DingGao-Devin @duwenhan2byte @evan-crash
/frontend/packages/agent-ide/bot-input-length-limit/ @Hezi-crypto @catee @duwenhan2byte
/frontend/packages/agent-ide/tool-config/ @haozhenfei @catee
/frontend/packages/arch/bot-space-api/ @Tecvan-fe @duwenhan2byte
/frontend/packages/arch/bot-utils/ @Tecvan-fe
/frontend/packages/common/uploader-adapter/ @dragooncjw @Tecvan-fe
/frontend/packages/common/uploader-interface/ @dragooncjw @Tecvan-fe
/frontend/packages/studio/user-store/ @duwenhan2byte @catee @lihuiwen
/frontend/packages/arch/foundation-sdk/ @evan-crash @duwenhan2byte
/frontend/packages/common/chat-area/chat-core/ @haozhenfei @Hezi-crypto @evan-crash
/frontend/packages/common/chat-area/utils/ @Hezi-crypto @haozhenfei
/frontend/packages/arch/bot-md-box-adapter/ @Hezi-crypto @iu1340 @dragooncjw @Tecvan-fe
/frontend/packages/studio/common/file-kit/ @haozhenfei @evan-crash
/frontend/packages/arch/slardar-adapter/ @Tecvan-fe @dragooncjw
/frontend/packages/arch/default-slardar/ @Tecvan-fe @evan-crash
/frontend/packages/arch/fetch-stream/ @Hezi-crypto @haozhenfei
/frontend/packages/common/websocket-manager-adapter/ @haozhenfei @Hezi-crypto @catee
/frontend/packages/studio/autosave/ @catee
/frontend/packages/studio/bot-utils/ @catee @soonco @Hezi-crypto
/frontend/packages/common/flowgram-adapter/common/ @zxhfighter @xiamidaxia @dragooncjw
/frontend/packages/common/flowgram-adapter/free-layout-editor/ @zxhfighter @xiamidaxia @dragooncjw
/frontend/packages/agent-ide/space-bot/ @soonco @evan-crash @duwenhan2byte @catee @DingGao-Devin
/frontend/packages/agent-ide/space-bot/src/store/bot-list-filter/ @duwenhan2byte @lihuiwen
/frontend/packages/agent-ide/space-bot/src/store/bot-page/ @DingGao-Devin
/frontend/packages/agent-ide/space-bot/src/store/explore/ @Tecvan-fe
/frontend/packages/agent-ide/space-bot/src/store/risk-warning/ @lihuiwen @catee
/frontend/packages/agent-ide/context/ @evan-crash
/frontend/packages/agent-ide/bot-editor-context-store/ @Hezi-crypto @duwenhan2byte @catee
/frontend/packages/agent-ide/chat-background/ @catee
/frontend/packages/agent-ide/chat-background-config-content-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/chat-background-config-content/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/chat-background-shared/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/common/chat-area/chat-uikit/ @catee @Hezi-crypto @evan-crash @haozhenfei
/frontend/packages/common/chat-area/hooks/ @Hezi-crypto @evan-crash
/frontend/packages/common/chat-area/chat-uikit-shared/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/bot-audit-adapter/ @evan-crash @duwenhan2byte @Hezi-crypto @haozhenfei
/frontend/packages/agent-ide/bot-audit-base/ @evan-crash @duwenhan2byte @Hezi-crypto @haozhenfei
/frontend/packages/studio/components/ @soonco @evan-crash @duwenhan2byte @catee
/frontend/packages/arch/bot-hooks/ @catee @Tecvan-fe @soonco
/frontend/packages/arch/bot-hooks/src/page-jump/ @evan-crash @catee
/frontend/packages/arch/bot-hooks-adapter/ @catee @Tecvan-fe @soonco
/frontend/packages/arch/bot-hooks-base/ @catee @Tecvan-fe @soonco
/frontend/packages/arch/responsive-kit/ @Tecvan-fe @DingGao-Devin
/frontend/packages/common/chat-area/chat-area/ @Hezi-crypto @haozhenfei @evan-crash @haozhenfei
/frontend/packages/data/memory/llm-plugins/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/data/common/reporter/ @soonco @catee @evan-crash
/frontend/packages/components/json-viewer/ @duwenhan2byte
/frontend/packages/components/scroll-view/ @evan-crash
/frontend/packages/common/assets/ @Tecvan-fe @catee
/frontend/packages/common/biz-components/ @duwenhan2byte
/frontend/packages/data/common/e2e/ @soonco @catee @evan-crash @haozhenfei @duwenhan2byte
/frontend/packages/agent-ide/debug-tool-list/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/model-manager/ @Hezi-crypto @catee
/frontend/packages/agent-ide/model-manager/src/components/multi-agent/ @catee
/frontend/packages/agent-ide/tool/ @catee
/frontend/packages/data/knowledge/knowledge-modal-base/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/components/biz-tooltip-ui/ @Hezi-crypto @catee @evan-crash
/frontend/packages/components/table-view/ @lihuiwen
/frontend/packages/components/virtual-list/ @Tecvan-fe
/frontend/packages/data/common/utils/ @soonco @catee @evan-crash
/frontend/packages/data/knowledge/knowledge-resource-processor-core/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/arch/pdfjs-shadow/ @Tecvan-fe
/frontend/packages/data/knowledge/common/stores/ @soonco @catee @evan-crash
/frontend/packages/foundation/global-store/ @duwenhan2byte @evan-crash
/frontend/config/postcss-config/ @Tecvan-fe
/frontend/config/tailwind-config/ @Tecvan-fe
/frontend/infra/utils/monorepo-kits/ @Tecvan-fe @evan-crash
/frontend/packages/studio/premium/premium-components-adapter/ @evan-crash
/frontend/packages/studio/premium/premium-store-adapter/ @evan-crash
/frontend/packages/agent-ide/onboarding/ @Hezi-crypto @catee
/frontend/packages/agent-ide/publish-to-base/ @catee
/frontend/packages/arch/report-tti/ @duwenhan2byte
/frontend/packages/data/memory/database-creator/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/arch/hooks/ @Tecvan-fe @evan-crash
/frontend/packages/common/coze-mitt/ @evan-crash @duwenhan2byte
/frontend/packages/common/editor-plugins/ @haozhenfei @stream-pipe @Hezi-crypto
/frontend/packages/common/md-editor-adapter/ @haozhenfei @Hezi-crypto @catee
/frontend/packages/common/prompt-kit/main/ @haozhenfei @Hezi-crypto @catee
/frontend/packages/common/chat-area/chat-answer-action/ @Hezi-crypto @haozhenfei @lihuiwen
/frontend/packages/common/chat-area/plugin-message-grab/ @Hezi-crypto @haozhenfei
/frontend/packages/common/chat-area/text-grab/ @Hezi-crypto @haozhenfei
/frontend/packages/common/prompt-kit/adapter/ @haozhenfei @Hezi-crypto @catee
/frontend/packages/common/prompt-kit/base/ @haozhenfei @Hezi-crypto @catee
/frontend/packages/data/memory/database/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/data/knowledge/knowledge-resource-processor-base/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/arch/utils/ @Tecvan-fe @evan-crash
/frontend/packages/data/knowledge/common/components/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/data/common/feature-register/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/data/knowledge/common/hooks/ @Hezi-crypto @catee @evan-crash
/frontend/packages/data/knowledge/common/services/ @Hezi-crypto @catee @evan-crash
/frontend/packages/data/memory/database-v2-main/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/data/memory/database-v2-adapter/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/data/memory/database-v2-base/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/data/knowledge/knowledge-data-set-for-agent/ @Hezi-crypto @catee @evan-crash
/frontend/packages/data/knowledge/knowledge-ide-base/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/arch/bot-monaco-editor/ @Tecvan-fe
/frontend/packages/data/knowledge/knowledge-modal-adapter/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/data/knowledge/knowledge-resource-processor-adapter/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/devops/debug/debug-panel/ @soonco @evan-crash @catee
/frontend/packages/devops/json-link-preview/ @Maidang1 @Zhangchi123456
/frontend/packages/devops/common-modules/ @duwenhan2byte @evan-crash @catee
/frontend/packages/foundation/account-adapter/ @duwenhan2byte @evan-crash
/frontend/packages/foundation/account-base/ @evan-crash @duwenhan2byte
/frontend/packages/arch/api-schema/ @Tecvan-fe @evan-crash
/frontend/infra/idl/idl2ts-runtime/ @Tecvan-fe @evan-crash
/frontend/infra/idl/idl2ts-cli/ @Tecvan-fe @evan-crash
/frontend/infra/idl/idl2ts-generator/ @Tecvan-fe @evan-crash
/frontend/infra/idl/idl-parser/ @Tecvan-fe @evan-crash
/frontend/infra/utils/rush-logger/ @catee @Tecvan-fe
/frontend/infra/idl/idl2ts-helper/ @Tecvan-fe @evan-crash
/frontend/infra/idl/idl2ts-plugin/ @Tecvan-fe @evan-crash
/frontend/packages/studio/open-platform/open-env-adapter/ @soonco @Hezi-crypto @DingGao-Devin
/frontend/infra/plugins/pkg-root-webpack-plugin/ @Tecvan-fe
/frontend/packages/studio/publish-manage-hooks/ @duwenhan2byte @evan-crash
/frontend/packages/foundation/layout/ @evan-crash @duwenhan2byte
/frontend/packages/studio/open-platform/open-auth/ @evan-crash @DingGao-Devin
/frontend/packages/studio/open-platform/open-chat/ @tomasyu985 @DingGao-Devin
/frontend/packages/agent-ide/entry-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/entry/ @soonco @duwenhan2byte @catee @evan-crash
/frontend/packages/agent-ide/bot-config-area-adapter/ @haozhenfei @Hezi-crypto @duwenhan2byte @catee @evan-crash
/frontend/packages/agent-ide/bot-config-area/ @haozhenfei @Hezi-crypto @duwenhan2byte @catee @evan-crash
/frontend/packages/agent-ide/bot-plugin/entry/ @evan-crash @lihuiwen @catee
/frontend/packages/agent-ide/bot-plugin/export/ @lihuiwen @catee
/frontend/packages/agent-ide/bot-plugin/mock-set/ @lihuiwen @catee @duwenhan2byte
/frontend/packages/studio/mockset-edit-modal-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/studio/mockset-shared/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/studio/mockset-editor/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/studio/mockset-editor-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/bot-plugin/tools/ @lihuiwen @catee
/frontend/packages/studio/stores/bot-plugin/ @lihuiwen @Hezi-crypto @catee
/frontend/packages/studio/plugin-shared/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/plugin-modal-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/plugin-shared/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/community/component/ @DingGao-Devin @Hezi-crypto @evan-crash @duwenhan2byte
/frontend/packages/studio/plugin-form-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/workflow/base/ @xiamidaxia @zxhfighter
/frontend/packages/agent-ide/plugin-content-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/plugin-content/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/plugin-setting-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/plugin-setting/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/bot-plugin/plugin-risk-warning/ @catee @evan-crash
/frontend/packages/studio/plugin-publish-ui-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/studio/plugin-tool-columns-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/studio/plugin-tool-columns/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/chat-debug-area/ @soonco @duwenhan2byte @catee @Hezi-crypto @haozhenfei @evan-crash
/frontend/packages/agent-ide/chat-answer-action-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/chat-area-plugin-debug-common/ @Hezi-crypto @haozhenfei
/frontend/packages/agent-ide/chat-components-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/common/chat-area/plugin-chat-background/ @Tecvan-fe
/frontend/packages/common/chat-area/chat-area-plugin-reasoning/ @catee @Hezi-crypto
/frontend/packages/common/chat-area/plugin-resume/ @Tecvan-fe
/frontend/packages/common/chat-area/plugin-chat-shortcuts/ @haozhenfei @duwenhan2byte @Hezi-crypto
/frontend/packages/workflow/sdk/ @zxhfighter @xiamidaxia
/frontend/packages/workflow/components/ @LLLLeeJ @zxhfighter
/frontend/packages/components/loading-button/ @catee
/frontend/packages/components/mouse-pad-selector/ @zxhfighter
/frontend/packages/workflow/adapter/resources/ @LLLLeeJ @JxJuly @xiamidaxia @luics @zxhfighter @stream-pipe
/frontend/packages/common/chat-area/chat-workflow-render/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/onboarding-message-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/plugin-area-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/prompt-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/prompt/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/workflow/ @soonco @duwenhan2byte @catee
/frontend/packages/agent-ide/navigate/ @soonco @duwenhan2byte @catee
/frontend/packages/agent-ide/workflow-as-agent-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/workflow-item/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/workflow-card-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/workflow-modal/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/memory-tool-pane-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/skills-pane-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/layout-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/layout/ @soonco @duwenhan2byte @catee
/frontend/packages/agent-ide/chat-area-provider-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/agent-ide/chat-area-provider/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/studio/entity-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/foundation/account-ui-adapter/ @duwenhan2byte @evan-crash
/frontend/packages/foundation/account-ui-base/ @evan-crash @duwenhan2byte
/frontend/packages/foundation/foundation-sdk/ @evan-crash @duwenhan2byte
/frontend/packages/foundation/global/ @evan-crash @duwenhan2byte
/frontend/packages/studio/workspace/project-entity-adapter/ @Hezi-crypto @catee @duwenhan2byte
/frontend/packages/studio/workspace/project-entity-base/ @Hezi-crypto @catee @duwenhan2byte
/frontend/packages/foundation/global-adapter/ @evan-crash @duwenhan2byte
/frontend/packages/foundation/browser-upgrade-banner/ @evan-crash @duwenhan2byte
/frontend/packages/foundation/space-ui-adapter/ @evan-crash @duwenhan2byte
/frontend/packages/foundation/space-ui-base/ @evan-crash @duwenhan2byte
/frontend/packages/common/auth/ @evan-crash @duwenhan2byte
/frontend/packages/common/auth-adapter/ @evan-crash @duwenhan2byte
/frontend/packages/project-ide/main/ @dragooncjw @JxJuly @xiamidaxia @catee @lihuiwen
/frontend/packages/components/resource-tree/ @dragooncjw @xiamidaxia @JxJuly
/frontend/packages/common/flowgram-adapter/fixed-layout-editor/ @zxhfighter @xiamidaxia @dragooncjw
/frontend/packages/project-ide/biz-components/ @zxhfighter @xiamidaxia
/frontend/packages/project-ide/framework/ @dragooncjw @JxJuly @xiamidaxia
/frontend/packages/project-ide/base-adapter/ @dragooncjw @xiamidaxia
/frontend/packages/project-ide/base-interface/ @dragooncjw @xiamidaxia
/frontend/packages/project-ide/client/ @dragooncjw @JxJuly @xiamidaxia
/frontend/packages/project-ide/core/ @dragooncjw @JxJuly @xiamidaxia
/frontend/packages/project-ide/view/ @dragooncjw @JxJuly @xiamidaxia
/frontend/packages/project-ide/biz-data/ @haozhenfei @lihuiwen @catee @soonco
/frontend/packages/data/knowledge/knowledge-ide-adapter/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/data/memory/variables/ @haozhenfei @catee @Hezi-crypto
/frontend/packages/project-ide/biz-plugin/ @xiamidaxia @lihuiwen @catee
/frontend/packages/project-ide/biz-plugin-registry-adapter/ @Hezi-crypto @duwenhan2byte @catee @evan-crash @haozhenfei
/frontend/packages/project-ide/biz-workflow/ @dragooncjw @JxJuly @xiamidaxia
/frontend/packages/studio/open-platform/open-chat/ @soonco @Hezi-crypto @DingGao-Devin
/frontend/packages/workflow/playground/ @LLLLeeJ @xiamidaxia @luics @zxhfighter
/frontend/packages/arch/load-remote-worker/ @Tecvan-fe
/frontend/packages/devops/mockset-manage/ @soonco @evan-crash @catee
/frontend/packages/devops/testset-manage/ @mocayo @JxJuly
/frontend/packages/workflow/adapter/base/ @LLLLeeJ @JxJuly @xiamidaxia @luics @zxhfighter @stream-pipe
/frontend/packages/workflow/adapter/code-editor/ @LLLLeeJ @JxJuly @xiamidaxia @luics @zxhfighter @stream-pipe
/frontend/packages/workflow/fabric-canvas/ @xiamidaxia @zxhfighter
/frontend/packages/workflow/feature-encapsulate/ @zxhfighter
/frontend/packages/workflow/nodes/ @xiamidaxia @zxhfighter
/frontend/packages/workflow/setters/ @Tecvan-fe
/frontend/packages/workflow/variable/ @zxhfighter @LLLLeeJ @xiamidaxia
/frontend/packages/workflow/render/ @dragooncjw @xiamidaxia
/frontend/packages/workflow/history/ @xiamidaxia @zxhfighter
/frontend/packages/workflow/adapter/nodes/ @LLLLeeJ @JxJuly @xiamidaxia @luics @zxhfighter @stream-pipe
/frontend/packages/workflow/test-run/ @JxJuly @xiamidaxia @luics @zxhfighter @dragooncjw
/frontend/packages/workflow/test-run-next/main/ @JxJuly @LLLLeeJ @xiamidaxia @zxhfighter
/frontend/packages/workflow/test-run-next/form/ @JxJuly @LLLLeeJ @xiamidaxia @zxhfighter
/frontend/packages/workflow/test-run-next/shared/ @JxJuly @LLLLeeJ @xiamidaxia @zxhfighter
/frontend/packages/workflow/test-run-next/trace/ @JxJuly @LLLLeeJ @xiamidaxia @zxhfighter
/frontend/packages/project-ide/ui-adapter/ @dragooncjw @xiamidaxia
/frontend/packages/studio/workspace/project-publish/ @catee @lihuiwen
/frontend/packages/studio/workspace/entry-adapter/ @duwenhan2byte @evan-crash
/frontend/packages/studio/workspace/entry-base/ @duwenhan2byte @catee @evan-crash
/frontend/packages/workflow/adapter/playground/ @LLLLeeJ @JxJuly @xiamidaxia @luics @zxhfighter @stream-pipe
/frontend/infra/plugins/import-watch-loader/ @Tecvan-fe
/frontend/config/rsbuild-config/ @Tecvan-fe
/frontend/packages/community/explore/ @evan-crash @duwenhan2byte
/frontend/common/_templates/node-core/ @Tecvan-fe
/frontend/common/_templates/rspack-web/ @Tecvan-fe
/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

View File

@ -10,7 +10,6 @@ EXCLUDE_PATTERNS=(
'packages/arch/bot-api/src/auto-generate/**'
'apps/bot-op/src/services/bam-auto-generate/**'
'apps/prompt-platform/src/services/auto-generate/**'
".cursor/api/**"
"**/lib/**"
"**/.*/**"
'**/__tests__/**'
@ -24,9 +23,7 @@ EXCLUDE_PATTERNS=(
'**/e2e/**'
'common/changes/**'
'apps/fornax/**',
"packages/arch/semi-theme-hand01",
"packages/arch/arco-icon",
"packages/arch/resources/**"
"packages/arch/semi-theme-hand01"
)
for pattern in "${EXCLUDE_PATTERNS[@]}"; do

294
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,294 @@
name: CI
on:
pull_request:
branches: ['main']
paths:
- 'github/**'
- 'idl/**'
- 'frontend/**'
- 'common/**'
- 'rush.json'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
setup:
strategy:
matrix:
include:
- NodeVersion: 22.16.0
NodeVersionDisplayName: 22
OS: ubuntu-latest
name: Setup and Install Dependencies
runs-on: ${{ matrix.OS }}
outputs:
cache_file: ${{ steps.process-files.outputs.cache_file }}
matrix_node_version: ${{ matrix.NodeVersion }}
matrix_os: ${{ matrix.OS }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v45
- name: Process changed files
id: process-files
run: |
# 获取所有变更文件
all_files="${{ steps.changed-files.outputs.all_changed_files }}"
# 过滤掉 common/changes 目录下的文件
filtered_files=""
for file in $all_files; do
if [[ ! "$file" =~ ^common/changes/.* ]]; then
if [ -z "$filtered_files" ]; then
filtered_files="$file"
else
filtered_files="$filtered_files $file"
fi
fi
done
# 创建 JSON 格式的缓存文件
echo "[$( echo "$filtered_files" | sed 's/ /", "/g' | sed 's/^/"/' | sed 's/$/"/' )]" > changed-files-cache.json
# 输出缓存文件路径供后续步骤使用
echo "cache_file=changed-files-cache.json" >> $GITHUB_OUTPUT
echo "过滤前文件数量: $(echo $all_files | wc -w)"
echo "过滤后文件数量: $(echo $filtered_files | wc -w)"
echo "已生成缓存文件: changed-files-cache.json"
- name: Config Git User
# should be turn to ci user
run: |
git config --local user.name "flow_bot"
git config --local user.email "flow_bot@bytedance.com"
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.NodeVersion }}
- name: Upload changed files cache
uses: actions/upload-artifact@v4
with:
name: changed-files-cache
path: changed-files-cache.json
retention-days: 1
build:
needs: setup
runs-on: ${{ needs.setup.outputs.matrix_os }}
name: Increment Build
env:
BUILD_BRANCH: ${{ github.head_ref || github.ref_name }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- uses: actions/setup-node@v3
with:
node-version: ${{ needs.setup.outputs.matrix_node_version }}
- name: Cache
uses: actions/cache@v4
with:
path: |
common/temp/pnpm-local
common/temp/pnpm-store
common/temp/install-run
key: ${{ runner.os }}-rush-store-${{ hashFiles('common/config/subspaces/**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-rush-store-main
${{ runner.os }}-rush-store
- name: Download changed files cache
uses: actions/download-artifact@v4
with:
name: changed-files-cache
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libasound2-dev
node common/scripts/install-run-rush.js install --to tag:core
node common/scripts/install-run-rush.js update-autoinstaller --name plugins
node common/scripts/install-run-rush.js increment --action install -p "${{ needs.setup.outputs.cache_file }}"
- name: Increment Build
run: node common/scripts/install-run-rush.js increment --action build -p "${{ needs.setup.outputs.cache_file }}"
test:
needs: setup
runs-on: ${{ needs.setup.outputs.matrix_os }}
name: Increment Test Coverage
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- uses: actions/setup-node@v3
with:
node-version: ${{ needs.setup.outputs.matrix_node_version }}
- name: Cache
uses: actions/cache@v4
with:
path: |
common/temp/pnpm-local
common/temp/pnpm-store
common/temp/install-run
key: ${{ runner.os }}-rush-store-${{ hashFiles('common/config/subspaces/**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-rush-store-main
${{ runner.os }}-rush-store
- name: Download changed files cache
uses: actions/download-artifact@v4
with:
name: changed-files-cache
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libasound2-dev
node common/scripts/install-run-rush.js install --to tag:core
node common/scripts/install-run-rush.js update-autoinstaller --name plugins
node common/scripts/install-run-rush.js increment --action install -p "${{ needs.setup.outputs.cache_file }}"
- name: Increment Test:cov
run: node common/scripts/install-run-rush.js increment --action test:cov -p "${{ needs.setup.outputs.cache_file }}"
- name: Upload coverage reports
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
verbose: true
lint:
needs: setup
runs-on: ${{ needs.setup.outputs.matrix_os }}
name: Increment Lint
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- uses: actions/setup-node@v3
with:
node-version: ${{ needs.setup.outputs.matrix_node_version }}
- name: Cache
uses: actions/cache@v4
with:
path: |
common/temp/pnpm-local
common/temp/pnpm-store
common/temp/install-run
key: ${{ runner.os }}-rush-store-${{ hashFiles('common/config/subspaces/**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-rush-store-main
${{ runner.os }}-rush-store
- name: Download changed files cache
uses: actions/download-artifact@v4
with:
name: changed-files-cache
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libasound2-dev
node common/scripts/install-run-rush.js install --to tag:core
node common/scripts/install-run-rush.js update-autoinstaller --name plugins
node common/scripts/install-run-rush.js increment --action install -p "${{ needs.setup.outputs.cache_file }}"
- name: Increment Lint
run: node common/scripts/install-run-rush.js increment --action lint -p "${{ needs.setup.outputs.cache_file }}"
ts-check:
needs: setup
runs-on: ${{ needs.setup.outputs.matrix_os }}
name: Increment TS Check
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- uses: actions/setup-node@v3
with:
node-version: ${{ needs.setup.outputs.matrix_node_version }}
- name: Cache
uses: actions/cache@v4
with:
path: |
common/temp/pnpm-local
common/temp/pnpm-store
common/temp/install-run
key: ${{ runner.os }}-rush-store-${{ hashFiles('common/config/subspaces/**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-rush-store-main
${{ runner.os }}-rush-store
- name: Download changed files cache
uses: actions/download-artifact@v4
with:
name: changed-files-cache
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libasound2-dev
node common/scripts/install-run-rush.js install --to tag:core
node common/scripts/install-run-rush.js update-autoinstaller --name plugins
node common/scripts/install-run-rush.js increment --action install -p "${{ needs.setup.outputs.cache_file }}"
- name: Increment TS Check
run: node common/scripts/install-run-rush.js increment --action ts-check -p "${{ needs.setup.outputs.cache_file }}"
package-audit:
needs: setup
runs-on: ${{ needs.setup.outputs.matrix_os }}
name: Increment Package Audit
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- uses: actions/setup-node@v3
with:
node-version: ${{ needs.setup.outputs.matrix_node_version }}
- name: Cache
uses: actions/cache@v4
with:
path: |
common/temp/pnpm-local
common/temp/pnpm-store
common/temp/install-run
key: ${{ runner.os }}-rush-store-${{ hashFiles('common/config/subspaces/**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-rush-store-main
${{ runner.os }}-rush-store
- name: Download changed files cache
uses: actions/download-artifact@v4
with:
name: changed-files-cache
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libasound2-dev
node common/scripts/install-run-rush.js install --to tag:core
node common/scripts/install-run-rush.js update-autoinstaller --name plugins
node common/scripts/install-run-rush.js increment --action install -p "${{ needs.setup.outputs.cache_file }}"
- name: Increment Package Audit
run: node common/scripts/install-run-rush.js increment --action package-audit -p "${{ needs.setup.outputs.cache_file }}"

72
.github/workflows/ci@main.yml vendored Normal file
View File

@ -0,0 +1,72 @@
# should be optimize as increment build & test
name: CI@main
on:
push:
# test only
branches: ['main',"chore/setup-ci"]
paths:
- 'github/**'
- 'idl/**'
- 'frontend/**'
- 'common/**'
- 'rush.json'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
build:
strategy:
matrix:
include:
- NodeVersion: 22.16.0
NodeVersionDisplayName: 22
OS: ubuntu-latest
name: Node.js v${{ matrix.NodeVersionDisplayName }} (${{ matrix.OS }})
runs-on: ${{ matrix.OS }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Config Git User
# should be turn to ci user
run: |
git config --local user.name "flow_bot"
git config --local user.email "flow_bot@bytedance.com"
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.NodeVersion }}
- name: Cache
uses: actions/cache@v4
with:
path: |
common/temp/pnpm-local
common/temp/pnpm-store
common/temp/install-run
key: ${{ runner.os }}-rush-store-main
restore-keys: |
${{ runner.os }}-rush-store-main
${{ runner.os }}-rush-store
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libasound2-dev
node common/scripts/install-run-rush.js install
- name: Build all
run: node common/scripts/install-run-rush.js build --verbose
- name: Test:cov all
run: node common/scripts/install-run-rush.js test:cov --verbose
- name: Upload coverage reports
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
verbose: true
- name: Lint all
run: node common/scripts/install-run-rush.js lint --verbose

45
.github/workflows/common-pr-checks.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: PR Common Checks
on:
pull_request:
paths:
- 'github/**'
- 'idl/**'
- 'frontend/**'
- 'common/**'
- 'rush.json'
types: [opened, edited, synchronize, reopened]
jobs:
common-checks:
name: PR Common Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Config Git User
run: |
git config --local user.name "flow_bot"
git config --local user.email "flow_bot@bytedance.com"
- uses: actions/setup-node@v3
with:
node-version: 22.16.0
- name: Install Dependencies
run: node common/scripts/install-run-rush.js install
# PR Title Format Check
- name: Check PR Title Format
if: ${{ !contains(github.event.pull_request.title, 'WIP') && !contains(github.event.pull_request.title, 'wip') }}
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
node common/scripts/install-run-rush.js update-autoinstaller --name rush-commitlint && \
pushd common/autoinstallers/rush-commitlint && \
echo "$PR_TITLE" | npx commitlint --config commitlint.config.js && \
popd
# Add more common checks here
# For example: file size checks, specific file format validations, etc.

View File

@ -44,3 +44,5 @@ lingyibin.jason <lingyibin.jason@bytedance.com>
chenchen.dabaishu <chenchen.dabaishu@bytedance.com>
jiangxujin <jiangxujin@bytedance.com>
huyongbiao <huyongbiao@bytedance.com>
wenming.2020 <wenming.2020@bytedance.com>
tanjizhen <tanjizhen@bytedance.com>

View File

@ -28,7 +28,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
3. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the coze-dev {project_name} repo.
4. In your forked repository, make your changes in a new git branch:
```
git checkout -b my-fix-branch develop
git checkout -b my-fix-branch main
```
5. Create your patch, including appropriate test cases.
6. Follow our [Style Guides](#code-style-guides).
@ -38,7 +38,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
```
git push origin my-fix-branch
```
9. In GitHub, send a pull request to `{project_name}:develop`
9. In GitHub, send a pull request to `{project_name}:main`
## Contribution Prerequisites
- Our development environment keeps up with [Go Official](https://golang.org/project/).

View File

@ -13,6 +13,8 @@ MYSQL_SCHEMA := ./docker/volumes/mysql/schema.sql
MYSQL_INIT_SQL := ./docker/volumes/mysql/sql_init.sql
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
debug: env middleware python server
@ -26,7 +28,7 @@ fe:
@echo "Building frontend..."
@bash $(BUILD_FE_SCRIPT)
server: env
server: env setup_es_index
@if [ ! -d "$(STATIC_DIR)" ]; then \
echo "Static directory '$(STATIC_DIR)' not found, building frontend..."; \
$(MAKE) fe; \
@ -84,6 +86,11 @@ atlas-hash:
@echo "Rehash atlas migration files..."
@(cd ./docker/atlas && atlas migrate hash)
setup_es_index:
@echo "Setting up Elasticsearch index..."
@. $(ENV_FILE); \
bash $(ES_SETUP_SCRIPT) --index-dir $(ES_INDEX_SCHEMA) --docker-host false --es-address "$$ES_ADDR"
help:
@echo "Usage: make [target]"
@echo ""
@ -103,4 +110,5 @@ help:
@echo " clean - Stop the docker containers and clean volumes."
@echo " python - Setup python environment."
@echo " atlas-hash - Rehash atlas migration files."
@echo " setup_es_index - Setup elasticsearch index."
@echo " help - Show this help message."

View File

@ -17,12 +17,12 @@ English | [中文](README.zh_CN.md)
## What is Coze Studio?
[Coze Studio](https://www.coze.cn/home) is an all-in-one AI agent development tool. Providing the latest large models and tools, various development modes and frameworks, Coze Studio offers the most convenient AI agent development environment, from development to deployment. Tens of thousands of enterprises and millions of developers are using Coze Studio.
[Coze Studio](https://www.coze.cn/home) is an all-in-one AI agent development tool. Providing the latest large models and tools, various development modes and frameworks, Coze Studio offers the most convenient AI agent development environment, from development to deployment.
* **Provides all core technologies needed for AI agent development**: prompt, RAG, plugin, workflow, enabling developers to focus on creating the core value of AI.
* **Ready to use for professional AI agent development at the lowest cost**: Coze Studio provides developers with complete app templates and build frameworks, allowing you to quickly construct various AI agents and turn creative ideas into reality.
Coze Studio is the open-source version of ByteDance's new AI agent development platform **Coze**. Through Coze Studio's visual design and build tools, developers can quickly create and debug agents, apps, and workflows using no-code or low-code approaches, enabling powerful AI app development and more customized business logic. It's an ideal choice for building low-code AI products tailored . Coze Studio aims to lower the threshold for AI agent development and application, encouraging community co-construction and sharing for deeper exploration and practice in the AI field.
Coze Studio, derived from the "Coze Development Platform" which has served tens of thousands of enterprises and millions of developers, we have made its core engine completely open. It is a one-stop visual development tool for AI Agents that makes creating, debugging, and deploying AI Agents unprecedentedly simple. Through Coze Studio's visual design and build tools, developers can quickly create and debug agents, apps, and workflows using no-code or low-code approaches, enabling powerful AI app development and more customized business logic. It's an ideal choice for building low-code AI products tailored . Coze Studio aims to lower the threshold for AI agent development and application, encouraging community co-construction and sharing for deeper exploration and practice in the AI field.
The backend of Coze Studio is developed using Golang, the frontend uses React + TypeScript, and the overall architecture is based on microservices and built following domain-driven design (DDD) principles. Provide developers with a high-performance, highly scalable, and easy-to-customize underlying framework to help them address complex business needs.
## Feature list
@ -72,8 +72,10 @@ Deployment steps:
# Start the service
cd docker
cp .env.example .env
docker compose --profile '*' up -d
docker compose --profile "*" up -d
```
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.
## Developer Guide
@ -82,7 +84,7 @@ Deployment steps:
* [Model Configuration](https://github.com/coze-dev/coze-studio/wiki/3.-Model-configuration): Before deploying the open-source version of Coze Studio, you must configure the model service. Otherwise, you cannot select models when building agents, workflows, and apps.
* [Plugin Configuration](https://github.com/coze-dev/coze-studio/wiki/4.-Plugin-Configuration): To use official plugins from the plugin store, you must first configure the plugins and add the authentication keys for third-party services.
* [Basic Component Configuration](https://github.com/coze-dev/coze-studio/wiki/5.-Basic-component-configuration): Learn how to configure components such as image uploaders to use functions like image uploading in Coze Studio .
* [API Reference](https://github.com/coze-dev/coze-studio/wiki/6.-API-Reference): Unlike the commercial edition, the open-source version of Coze Studio only supports personal access token (PAT) authentication and supports APIs related to chat and workflows.
* [API Reference](https://github.com/coze-dev/coze-studio/wiki/6.-API-Reference): The Coze Studio Community Edition API and Chat SDK are authenticated using Personal Access Token, providing APIs for conversations and workflows.
* [Development Guidelines](https://github.com/coze-dev/coze-studio/wiki/7.-Development-Standards):
* [Project Architecture](https://github.com/coze-dev/coze-studio/wiki/7.-Development-Standards#project-architecture): Learn about the technical architecture and core components of the open-source version of Coze Studio.
* [Code Development and Testing](https://github.com/coze-dev/coze-studio/wiki/7.-Development-Standards#code-development-and-testing): Learn how to perform secondary development and testing based on the open-source version of Coze Studio.
@ -106,14 +108,33 @@ We welcome community contributions. For contribution guidelines, please refer to
## Security and privacy
If you discover potential security issues in the project, or believe you may have found a security issue, please notify the ByteDance security team through our [security center](https://security.bytedance.com/src) or [vulnerability reporting mailbox](https://code.byted.org/flowdevops/cozeloop/blob/feat/release/sec@bytedance.com).
Please **do not** create public GitHub Issues.
## Join the community
Scan the QR code below using the Lark mobile app to join the Coze Studio technical exchange group.
## Join Community
We are committed to building an open and friendly developer community. All developers interested in AI Agent development are welcome to join us!
### 🐛 Issue Reports & Feature Requests
To efficiently track and resolve issues while ensuring transparency and collaboration, we recommend participating through:
- **GitHub Issues**: [Submit bug reports or feature requests](https://github.com/coze-dev/coze-studio/issues)
- **Pull Requests**: [Contribute code or documentation improvements](https://github.com/coze-dev/coze-studio/pulls)
### 💬 Technical Discussion & Communication
Join our technical discussion groups to share experiences with other developers and stay updated with the latest project developments:
**Feishu Group Chat**
Scan the QR code below with Feishu mobile app to join:
![Image](https://p9-arcosite.byteimg.com/tos-cn-i-goo7wpa0wc/0a49081e8f3743e8bf3dcdded4bb571a~tplv-goo7wpa0wc-image.image)
**Discord Server**
Click to join: [Coze Community](https://discord.gg/sTVN9EVS4B)
**Telegram Group**
Click to join: Telegram Group [Coze](https://t.me/+pP9CkPnomDA0Mjgx)
## Acknowledgments
Thank you to all the developers and community members who have contributed to the Coze Studio project. Special thanks:
* LLM integration support provided by the Eino framework team
* High-performance framework developed by the Cloudwego team
* The [Eino](https://github.com/cloudwego/eino) framework team - providing powerful support for Coze Studio's agent and workflow runtime engines, model abstractions and implementations, and knowledge base indexing and retrieval
* The [FlowGram](https://github.com/bytedance/flowgram.ai) team - providing a high-quality workflow building engine for Coze Studio's frontend workflow canvas editor
* The [Hertz](https://github.com/cloudwego/hertz) team - Go HTTP framework with high-performance and strong-extensibility for building micro-services
* All users who participated in testing and feedback

View File

@ -17,14 +17,15 @@
## 什么是 Coze Studio
[Coze Studio](https://www.coze.cn/home) 是一站式 AI Agent 开发工具。提供各类最新大模型和工具、多种开发模式和框架,从开发到部署,为你提供最便捷的 AI Agent 开发环境。上万家企业、数百万开发者正在使 Coze Studio。
[Coze Studio](https://www.coze.cn/home) 是一站式 AI Agent 开发工具。提供各类最新大模型和工具、多种开发模式和框架,从开发到部署,为你提供最便捷的 AI Agent 开发环境。
* **提供 AI Agent 开发所需的全部核心技术**Prompt、RAG、Plugin、Workflow使得开发者可以聚焦创造 AI 核心价值。
* **开箱即用,用最低的成本开发最专业的 AI Agent**Coze Studio 为开发者提供了健全的应用模板和编排框架,你可以基于它们快速构建各种 AI Agent ,将创意变为现实。
Coze Studio 是字节跳动新一代 AI Agent 开发平台**扣子Coze**的**开源版本**。通过 Coze Studio 提供的可视化设计与编排工具,开发者可以通过零代码或低代码的方式,快速打造和调试智能体、应用和工作流,实现强大的 AI 应用开发和更多定制化业务逻辑,是构建低代码 AI 产品的理想选择。Coze Studio 致力于降低 AI Agent 开发与应用门槛,鼓励社区共建和分享交流,助你在 AI 领域进行更深层次的探索与实践。
Coze Studio,源自服务了上万家企业、数百万开发者的「扣子开发平台」,我们将它的核心引擎完全开放。它是一个一站式的 AI Agent 可视化开发工具,让 AI Agent 的创建、调试和部署变得前所未有的简单。通过 Coze Studio 提供的可视化设计与编排工具,开发者可以通过零代码或低代码的方式,快速打造和调试智能体、应用和工作流,实现强大的 AI 应用开发和更多定制化业务逻辑,是构建低代码 AI 产品的理想选择。Coze Studio 致力于降低 AI Agent 开发与应用门槛,鼓励社区共建和分享交流,助你在 AI 领域进行更深层次的探索与实践。
Coze Studio 的后端采用 Golang 开发,前端使用 React + TypeScript整体基于微服务架构并遵循领域驱动设计DDD原则构建。为开发者提供一个高性能、高扩展性、易于二次开发的底层框架助力开发者应对复杂的业务需求。
## 功能清单
| **功能模块** | **功能点** |
| --- | --- |
@ -71,9 +72,11 @@ Coze Studio 的后端采用 Golang 开发,前端使用 React + TypeScript
# 启动服务
cd docker
cp .env.example .env
docker compose --profile '*' up -d
docker compose --profile "*" up -d
```
服务启动之后`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。
## 开发指南
@ -81,7 +84,7 @@ Coze Studio 的后端采用 Golang 开发,前端使用 React + TypeScript
* [模型配置](https://github.com/coze-dev/coze-studio/wiki/3.-模型配置):部署 Coze Studio 开源版之前,必须配置模型服务,否则无法在搭建智能体、工作流和应用时选择模型。
* [插件配置](https://github.com/coze-dev/coze-studio/wiki/4.-插件配置):如需使用插件商店中的官方插件,必须先配置插件,添加第三方服务的鉴权秘钥。
* [基础组件配置](https://github.com/coze-dev/coze-studio/wiki/5.-基础组件配置):了解如何配置图片上传等组件,以便在 Coze Studio 中使用上传图片等功能。
* [API 参考](https://github.com/coze-dev/coze-studio/wiki/6.-API-参考)和商业版不同,Coze Studio 开源版仅支持个人访问秘钥PAT鉴权并支持对话和工作流相关 API。
* [API 参考](https://github.com/coze-dev/coze-studio/wiki/6.-API-参考)Coze Studio 社区版 API 和 Chat SDK 通过个人访问令牌鉴权,提供对话和工作流相关 API。
* [开发规范](https://github.com/coze-dev/coze-studio/wiki/7.-开发规范)
* [项目架构](https://github.com/coze-dev/coze-studio/wiki/7.-%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83#%E9%A1%B9%E7%9B%AE%E6%9E%B6%E6%9E%84):了解 Coze Studio 开源版的技术架构与核心组件。
* [代码开发与测试](https://github.com/coze-dev/coze-studio/wiki/7.-%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83#%E4%BB%A3%E7%A0%81%E5%BC%80%E5%8F%91%E4%B8%8E%E6%B5%8B%E8%AF%95):了解如何基于 Coze Studio 开源版进行二次开发与测试。
@ -106,13 +109,32 @@ Coze Studio 的后端采用 Golang 开发,前端使用 React + TypeScript
如果你在该项目中发现潜在的安全问题,或你认为可能发现了安全问题,请通过我们的[安全中心](https://security.bytedance.com/src) 或[漏洞报告邮箱](https://code.byted.org/flowdevops/cozeloop/blob/feat/release/sec@bytedance.com)通知字节跳动安全团队。
请**不要**创建公开的 GitHub Issue。
## 加入社区
飞书移动端扫描以下二维码,加入 Coze Studio 技术交流群。
我们致力于构建一个开放、友好的开发者社区,欢迎所有对 AI Agent 开发感兴趣的开发者加入我们!
### 🐛 问题反馈与功能建议
为了更高效地跟踪和解决问题,保证信息透明和便于协同,我们推荐通过以下方式参与:
- **GitHub Issues**[提交 Bug 报告或功能请求](https://github.com/coze-dev/coze-studio/issues)
- **Pull Requests**[贡献代码或文档改进](https://github.com/coze-dev/coze-studio/pulls)
### 💬 技术交流与讨论
加入我们的技术交流群,与其他开发者分享经验、获取项目最新动态:
**飞书群聊**
使用飞书移动端扫描下方二维码加入:
![Image](https://p9-arcosite.byteimg.com/tos-cn-i-goo7wpa0wc/0a49081e8f3743e8bf3dcdded4bb571a~tplv-goo7wpa0wc-image.image)
**Discord 服务器**
点击加入:[Coze Community](https://discord.gg/sTVN9EVS4B)
**Telegram 群组**
点击加入Telegram Group [Coze](https://t.me/+pP9CkPnomDA0Mjgx)
## 致谢
感谢所有为 Coze Studio 项目做出贡献的开发者和社区成员。特别感谢:
* Eino 框架团队提供的 LLM 集成支持
* Cloudwego 团队开发的高性能框架
* [Eino](https://github.com/cloudwego/eino) 框架团队 - 为 Coze Studio 的智能体和工作流运行时、模型抽象封装、知识库索引构建和检索提供了强大的支持
* [FlowGram](https://github.com/bytedance/flowgram.ai) 团队 - 为 Coze Studio 的工作流画布编辑页提供了高质量的流程搭建引擎
* [Hertz](https://github.com/cloudwego/hertz) 团队 - 高性能、强扩展性的 Go HTTP 框架,用于构建微服务
* 所有参与测试和反馈的用户

View File

@ -31,7 +31,7 @@ WORKDIR /app
# Install runtime dependencies for Go app and base for Python
# pax-utils for scanelf, python3 for running Python, python3-dev for headers/shared libs
# bind-tools for nslookup etc., file for debugging file types
RUN apk add --no-cache pax-utils python3 python3-dev bind-tools file deno
RUN apk add --no-cache pax-utils python3 python3-dev bind-tools file deno curl
# Install Python build dependencies, create venv, install packages, then remove build deps
RUN apk add --no-cache --virtual .python-build-deps build-base py3-pip git && \
@ -81,6 +81,9 @@ EXPOSE 8888
# Use a script to start both applications
COPY backend/script/bootstrap.sh /app/bootstrap.sh
RUN chmod +x /app/bootstrap.sh
COPY docker/volumes/elasticsearch/setup_es.sh /app/setup_es.sh
COPY docker/volumes/elasticsearch/es_index_schema /app/es_index_schemas
RUN chmod +x /app/bootstrap.sh /app/setup_es.sh
CMD ["/app/bootstrap.sh"]

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

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

@ -263,6 +263,7 @@ func toAPIParameter(paramMeta paramMetaInfo, sc *openapi3.Schema) (*common.APIPa
}
if sc.Default != nil {
apiParam.GlobalDefault = ptr.Of(fmt.Sprintf("%v", sc.Default))
apiParam.LocalDefault = ptr.Of(fmt.Sprintf("%v", sc.Default))
}

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

@ -60,6 +60,7 @@ import (
"github.com/coze-dev/coze-studio/backend/pkg/lang/conv"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
"github.com/coze-dev/coze-studio/backend/pkg/logs"
"github.com/coze-dev/coze-studio/backend/pkg/safego"
"github.com/coze-dev/coze-studio/backend/pkg/taskgroup"
"github.com/coze-dev/coze-studio/backend/types/consts"
"github.com/coze-dev/coze-studio/backend/types/errno"
@ -183,18 +184,19 @@ func (a *APPApplicationService) DraftProjectDelete(ctx context.Context, req *pro
logs.CtxErrorf(ctx, "publish project '%d' failed, err=%v", req.ProjectID, err)
}
err = a.deleteAPPResources(ctx, req.ProjectID)
if err != nil {
logs.CtxErrorf(ctx, "delete app '%d' resources failed, err=%v", req.ProjectID, err)
}
safego.Go(ctx, func() {
// When an app is deleted, resource deletion is currently handled as a weak dependency, meaning some resources might not be deleted, but they will be inaccessible to the user.
// TODO:: Application resources need to check the deletion status of the application
a.deleteAPPResources(ctx, req.ProjectID)
})
resp = &projectAPI.DraftProjectDeleteResponse{}
return resp, nil
}
func (a *APPApplicationService) deleteAPPResources(ctx context.Context, appID int64) (err error) {
err = plugin.PluginApplicationSVC.DeleteAPPAllPlugins(ctx, appID)
func (a *APPApplicationService) deleteAPPResources(ctx context.Context, appID int64) {
err := plugin.PluginApplicationSVC.DeleteAPPAllPlugins(ctx, appID)
if err != nil {
logs.CtxErrorf(ctx, "delete app '%d' plugins failed, err=%v", appID, err)
}
@ -218,8 +220,6 @@ func (a *APPApplicationService) deleteAPPResources(ctx context.Context, appID in
if err != nil {
logs.CtxErrorf(ctx, "delete app '%d' workflow failed, err=%v", appID, err)
}
return nil
}
func (a *APPApplicationService) DraftProjectUpdate(ctx context.Context, req *projectAPI.DraftProjectUpdateRequest) (resp *projectAPI.DraftProjectUpdateResponse, err error) {

View File

@ -20,9 +20,8 @@ import (
"net/http"
"strconv"
"github.com/getkin/kin-openapi/openapi3"
"github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
"github.com/getkin/kin-openapi/openapi3"
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
common "github.com/coze-dev/coze-studio/backend/api/model/plugin_develop_common"
@ -33,14 +32,6 @@ import (
func APIParamsToOpenapiOperation(reqParams, respParams []*common.APIParameter) (*openapi3.Operation, error) {
op := &openapi3.Operation{}
if reqParams != nil && len(reqParams) == 0 {
op.Parameters = []*openapi3.ParameterRef{}
op.RequestBody = entity.DefaultOpenapi3RequestBody()
}
if respParams != nil && len(respParams) == 0 {
op.Responses = entity.DefaultOpenapi3Responses()
}
hasSetReqBody := false
hasSetParams := false
@ -136,6 +127,16 @@ func APIParamsToOpenapiOperation(reqParams, respParams []*common.APIParameter) (
}
}
if op.Parameters == nil {
op.Parameters = []*openapi3.ParameterRef{}
}
if op.RequestBody == nil {
op.RequestBody = entity.DefaultOpenapi3RequestBody()
}
if op.Responses == nil {
op.Responses = entity.DefaultOpenapi3Responses()
}
return op, nil
}

View File

@ -140,7 +140,7 @@ func (c *ConversationApplicationService) CreateConversation(ctx context.Context,
Id: conversationData.ID,
LastSectionID: &conversationData.SectionID,
ConnectorID: &conversationData.ConnectorID,
CreatedAt: conversationData.CreatedAt,
CreatedAt: conversationData.CreatedAt / 1000,
}
return resp, nil
}
@ -176,7 +176,7 @@ func (c *ConversationApplicationService) ListConversation(ctx context.Context, r
Id: conv.ID,
LastSectionID: &conv.SectionID,
ConnectorID: &conv.ConnectorID,
CreatedAt: conv.CreatedAt,
CreatedAt: conv.CreatedAt / 1000,
}
})

View File

@ -299,6 +299,7 @@ func buildARSM2ApiMessage(chunk *entity.AgentRunResponse) []byte {
MetaData: chunkMessageItem.Ext,
ChatID: strconv.FormatInt(chunkMessageItem.RunID, 10),
ReasoningContent: chunkMessageItem.ReasoningContent,
CreatedAt: ptr.Of(chunkMessageItem.CreatedAt / 1000),
}
mCM, _ := json.Marshal(chunkMessage)

View File

@ -110,8 +110,8 @@ func (m *OpenapiMessageApplication) buildMessageListResponse(ctx context.Context
Content: content,
ContentType: string(dm.ContentType),
SectionID: strconv.FormatInt(dm.SectionID, 10),
CreatedAt: dm.CreatedAt,
UpdatedAt: dm.UpdatedAt,
CreatedAt: dm.CreatedAt / 1000,
UpdatedAt: dm.UpdatedAt / 1000,
ChatID: dm.RunID,
MetaData: dm.Ext,
}

View File

@ -26,6 +26,7 @@ import (
"time"
"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"
@ -35,6 +36,7 @@ import (
"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"
@ -111,6 +113,9 @@ func InitService(c *ServiceComponents) (*KnowledgeApplicationService, error) {
case "ve":
ocrAK := os.Getenv("VE_OCR_AK")
ocrSK := os.Getenv("VE_OCR_SK")
if ocrAK == "" || ocrSK == "" {
logs.Warnf("[ve_ocr] ak / sk not configured, ocr might not work well")
}
inst := visual.NewInstance()
inst.Client.SetAccessKey(ocrAK)
inst.Client.SetSecretKey(ocrSK)
@ -346,6 +351,42 @@ func getEmbedding(ctx context.Context) (embedding.Embedder, error) {
if err != nil {
return nil, fmt.Errorf("init ark embedding client failed, err=%w", err)
}
case "ollama":
var (
ollamaEmbeddingBaseURL = os.Getenv("OLLAMA_EMBEDDING_BASE_URL")
ollamaEmbeddingModel = os.Getenv("OLLAMA_EMBEDDING_MODEL")
ollamaEmbeddingDims = os.Getenv("OLLAMA_EMBEDDING_DIMS")
)
dims, err := strconv.ParseInt(ollamaEmbeddingDims, 10, 64)
if err != nil {
return nil, fmt.Errorf("init ollama embedding dims failed, err=%w", err)
}
emb, err = wrap.NewOllamaEmbedder(ctx, &ollamaEmb.EmbeddingConfig{
BaseURL: ollamaEmbeddingBaseURL,
Model: ollamaEmbeddingModel,
}, dims)
if err != nil {
return nil, fmt.Errorf("init ollama embedding failed, err=%w", err)
}
case "http":
var (
httpEmbeddingBaseURL = os.Getenv("HTTP_EMBEDDING_ADDR")
httpEmbeddingDims = os.Getenv("HTTP_EMBEDDING_DIMS")
)
dims, err := strconv.ParseInt(httpEmbeddingDims, 10, 64)
if err != nil {
return nil, fmt.Errorf("init http embedding dims failed, err=%w", err)
}
emb, err = http.NewEmbedding(httpEmbeddingBaseURL, dims)
if err != nil {
return nil, fmt.Errorf("init http embedding failed, err=%w", err)
}
default:
return nil, fmt.Errorf("init knowledge embedding failed, type not configured")
}

View File

@ -438,7 +438,7 @@ func (k *KnowledgeApplicationService) GetDocumentProgress(ctx context.Context, r
DocumentID: domainResp.ProgressList[i].ID,
Progress: int32(domainResp.ProgressList[i].Progress),
Status: convertDocumentStatus2Model(domainResp.ProgressList[i].Status),
StatusDescript: ptr.Of(convertDocumentStatus2Model(domainResp.ProgressList[i].Status).String()),
StatusDescript: &domainResp.ProgressList[i].StatusMsg,
DocumentName: domainResp.ProgressList[i].Name,
RemainingTime: &domainResp.ProgressList[i].RemainingSec,
Size: &domainResp.ProgressList[i].Size,

View File

@ -18,9 +18,12 @@ package plugin
import (
"context"
"strconv"
"strings"
"gorm.io/gorm"
"github.com/coze-dev/coze-studio/backend/domain/plugin/conf"
pluginConf "github.com/coze-dev/coze-studio/backend/domain/plugin/conf"
"github.com/coze-dev/coze-studio/backend/domain/plugin/repository"
"github.com/coze-dev/coze-studio/backend/domain/plugin/service"
@ -28,6 +31,9 @@ import (
user "github.com/coze-dev/coze-studio/backend/domain/user/service"
"github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
"github.com/coze-dev/coze-studio/backend/infra/contract/storage"
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
"github.com/coze-dev/coze-studio/backend/pkg/lang/slices"
"github.com/coze-dev/coze-studio/backend/types/errno"
)
type ServiceComponents struct {
@ -68,6 +74,11 @@ func InitService(ctx context.Context, components *ServiceComponents) (*PluginApp
OAuthRepo: oauthRepo,
})
err = checkIDExist(ctx, pluginSVC)
if err != nil {
return nil, err
}
PluginApplicationSVC.DomainSVC = pluginSVC
PluginApplicationSVC.eventbus = components.EventBus
PluginApplicationSVC.oss = components.OSS
@ -77,3 +88,51 @@ func InitService(ctx context.Context, components *ServiceComponents) (*PluginApp
return PluginApplicationSVC, nil
}
func checkIDExist(ctx context.Context, pluginService service.PluginService) error {
pluginProducts := conf.GetAllPluginProducts()
pluginIDs := make([]int64, 0, len(pluginProducts))
var toolIDs []int64
for _, p := range pluginProducts {
pluginIDs = append(pluginIDs, p.Info.ID)
toolIDs = append(toolIDs, p.ToolIDs...)
}
pluginInfos, err := pluginService.MGetDraftPlugins(ctx, pluginIDs)
if err != nil {
return err
}
if len(pluginInfos) > 0 {
conflictsIDs := make([]int64, 0, len(pluginInfos))
for _, p := range pluginInfos {
conflictsIDs = append(conflictsIDs, p.ID)
}
return errorx.New(errno.ErrPluginIDExist,
errorx.KV("plugin_id", strings.Join(slices.Transform(conflictsIDs, func(id int64) string {
return strconv.FormatInt(id, 10)
}), ",")),
)
}
tools, err := pluginService.MGetDraftTools(ctx, toolIDs)
if err != nil {
return err
}
if len(tools) > 0 {
conflictsIDs := make([]int64, 0, len(tools))
for _, t := range tools {
conflictsIDs = append(conflictsIDs, t.ID)
}
return errorx.New(errno.ErrToolIDExist,
errorx.KV("tool_id", strings.Join(slices.Transform(conflictsIDs, func(id int64) string {
return strconv.FormatInt(id, 10)
}), ",")),
)
}
return nil
}

View File

@ -250,7 +250,7 @@ func (p *PluginApplicationService) RegisterPluginMeta(ctx context.Context, req *
if req.GetLocation() == common.AuthorizationServiceLocation_Query {
loc = model.ParamInQuery
} else if req.GetLocation() == common.AuthorizationServiceLocation_Header {
loc = model.ParamInPath
loc = model.ParamInHeader
} else {
return nil, fmt.Errorf("invalid location '%s'", req.GetLocation())
}

View File

@ -76,28 +76,38 @@ func (s *SearchApplicationService) GetDraftIntelligenceList(ctx context.Context,
intelligenceDataList := make([]*intelligence.IntelligenceData, len(searchResp.Data))
logs.CtxDebugf(ctx, "[GetDraftIntelligenceList] searchResp.Data: %v", conv.DebugJsonToStr(searchResp.Data))
if len(searchResp.Data) > 1 {
for idx := range searchResp.Data[1:] {
index := idx + 1
data := searchResp.Data[index]
tasks.Go(func() error {
info, err := s.packIntelligenceData(ctx, data)
if err != nil {
logs.CtxErrorf(ctx, "[packIntelligenceData] failed id %v, type %d , name %s, err: %v", data.ID, data.Type, data.GetName(), err)
return err
}
for idx := range searchResp.Data {
data := searchResp.Data[idx]
index := idx
tasks.Go(func() error {
info, err := s.packIntelligenceData(ctx, data)
if err != nil {
logs.CtxErrorf(ctx, "[packIntelligenceData] failed id %v, type %d , name %s, err: %v", data.ID, data.Type, data.GetName(), err)
return err
}
lock.Lock()
defer lock.Unlock()
intelligenceDataList[index] = info
return nil
})
s.packIntelligenceData(ctx, data)
lock.Lock()
defer lock.Unlock()
intelligenceDataList[index] = info
return nil
})
}
}
if len(searchResp.Data) != 0 {
info, err := s.packIntelligenceData(ctx, searchResp.Data[0])
if err != nil {
logs.CtxErrorf(ctx, "[packIntelligenceData] failed id %v, type %d , name %s, err: %v", searchResp.Data[0].ID, searchResp.Data[0].Type, searchResp.Data[0].GetName(), err)
return nil, err
}
lock.Lock()
intelligenceDataList[0] = info
lock.Unlock()
}
err = tasks.Wait()
if err != nil {
return nil, err
}
_ = tasks.Wait()
filterDataList := make([]*intelligence.IntelligenceData, 0)
for _, data := range intelligenceDataList {
if data != nil {

View File

@ -86,25 +86,39 @@ func (s *SearchApplicationService) LibraryResourceList(ctx context.Context, req
lock := sync.Mutex{}
tasks := taskgroup.NewUninterruptibleTaskGroup(ctx, 10)
resources := make([]*common.ResourceInfo, len(searchResp.Data))
for idx := range searchResp.Data {
v := searchResp.Data[idx]
index := idx
tasks.Go(func() error {
ri, err := s.packResource(ctx, v)
if err != nil {
logs.CtxErrorf(ctx, "[LibraryResourceList] packResource failed, will ignore resID: %d, Name : %s, resType: %d, err: %v",
v.ResID, v.GetName(), v.ResType, err)
return err
}
if len(searchResp.Data) > 1 {
for idx := range searchResp.Data[1:] {
index := idx + 1
v := searchResp.Data[index]
tasks.Go(func() error {
ri, err := s.packResource(ctx, v)
if err != nil {
logs.CtxErrorf(ctx, "[LibraryResourceList] packResource failed, will ignore resID: %d, Name : %s, resType: %d, err: %v",
v.ResID, v.GetName(), v.ResType, err)
return err
}
lock.Lock()
defer lock.Unlock()
resources[index] = ri
return nil
})
lock.Lock()
defer lock.Unlock()
resources[index] = ri
return nil
})
}
}
if len(searchResp.Data) != 0 {
ri, err := s.packResource(ctx, searchResp.Data[0])
if err != nil {
return nil, err
}
lock.Lock()
resources[0] = ri
lock.Unlock()
}
err = tasks.Wait()
if err != nil {
return nil, err
}
_ = tasks.Wait()
filterResource := make([]*common.ResourceInfo, 0)
for _, res := range resources {
if res == nil {

View File

@ -19,7 +19,10 @@ package user
import (
"context"
"net/mail"
"os"
"slices"
"strconv"
"strings"
"github.com/coze-dev/coze-studio/backend/api/model/ocean/cloud/developer_api"
"github.com/coze-dev/coze-studio/backend/api/model/ocean/cloud/playground"
@ -30,7 +33,8 @@ import (
"github.com/coze-dev/coze-studio/backend/infra/contract/storage"
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
"github.com/coze-dev/coze-studio/backend/pkg/lang/slices"
langSlices "github.com/coze-dev/coze-studio/backend/pkg/lang/slices"
"github.com/coze-dev/coze-studio/backend/types/consts"
"github.com/coze-dev/coze-studio/backend/types/errno"
)
@ -56,6 +60,11 @@ func (u *UserApplicationService) PassportWebEmailRegisterV2(ctx context.Context,
return nil, "", errorx.New(errno.ErrUserInvalidParamCode, errorx.KV("msg", "Invalid email"))
}
// Allow Register Checker
if !u.allowRegisterChecker(req.GetEmail()) {
return nil, "", errorx.New(errno.ErrNotAllowedRegisterCode)
}
userInfo, err := u.DomainSVC.Create(ctx, &user.CreateUserRequest{
Email: req.GetEmail(),
Password: req.GetPassword(),
@ -77,6 +86,20 @@ func (u *UserApplicationService) PassportWebEmailRegisterV2(ctx context.Context,
}, userInfo.SessionKey, nil
}
func (u *UserApplicationService) allowRegisterChecker(email string) bool {
disableUserRegistration := os.Getenv(consts.DisableUserRegistration)
if strings.ToLower(disableUserRegistration) != "true" {
return true
}
allowedEmails := os.Getenv(consts.AllowRegistrationEmail)
if allowedEmails == "" {
return false
}
return slices.Contains(strings.Split(allowedEmails, ","), strings.ToLower(email))
}
// PassportWebLogoutGet 处理用户登出请求
func (u *UserApplicationService) PassportWebLogoutGet(ctx context.Context, req *passport.PassportWebLogoutGetRequest) (
resp *passport.PassportWebLogoutGetResponse, err error,
@ -204,7 +227,7 @@ func (u *UserApplicationService) GetSpaceListV2(ctx context.Context, req *playgr
return nil, err
}
botSpaces := slices.Transform(spaces, func(space *entity.Space) *playground.BotSpaceV2 {
botSpaces := langSlices.Transform(spaces, func(space *entity.Space) *playground.BotSpaceV2 {
return &playground.BotSpaceV2{
ID: space.ID,
Name: space.Name,
@ -230,7 +253,7 @@ func (u *UserApplicationService) GetSpaceListV2(ctx context.Context, req *playgr
func (u *UserApplicationService) MGetUserBasicInfo(ctx context.Context, req *playground.MGetUserBasicInfoRequest) (
resp *playground.MGetUserBasicInfoResponse, err error,
) {
userIDs, err := slices.TransformWithErrorCheck(req.GetUserIds(), func(s string) (int64, error) {
userIDs, err := langSlices.TransformWithErrorCheck(req.GetUserIds(), func(s string) (int64, error) {
return strconv.ParseInt(s, 10, 64)
})
if err != nil {
@ -243,7 +266,7 @@ func (u *UserApplicationService) MGetUserBasicInfo(ctx context.Context, req *pla
}
return &playground.MGetUserBasicInfoResponse{
UserBasicInfoMap: slices.ToMap(userInfos, func(userInfo *entity.User) (string, *playground.UserBasicInfo) {
UserBasicInfoMap: langSlices.ToMap(userInfos, func(userInfo *entity.User) (string, *playground.UserBasicInfo) {
return strconv.FormatInt(userInfo.UserID, 10), userDo2PlaygroundTo(userInfo)
}),
Code: 0,

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,10 +17,12 @@
package workflow
import (
"github.com/cloudwego/eino/callbacks"
"github.com/cloudwego/eino/compose"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
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"
@ -34,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"
@ -41,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"
@ -78,6 +82,8 @@ func InitService(components *ServiceComponents) *ApplicationService {
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

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_JsonSerialization, 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

@ -157,7 +157,7 @@ meta:
top_k: 0
stop: []
openai:
by_azure: true
by_azure: false
api_version: ""
response_format:
type: text

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

@ -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

@ -34,13 +34,13 @@ const (
type RunEvent string
const (
RunEventCreated RunEvent = "conversation.run.created"
RunEventInProgress RunEvent = "conversation.run.in_progress"
RunEventCompleted RunEvent = "conversation.run.completed"
RunEventFailed RunEvent = "conversation.run.failed"
RunEventExpired RunEvent = "conversation.run.expired"
RunEventCancelled RunEvent = "conversation.run.cancelled"
RunEventRequiredAction RunEvent = "conversation.run.required_action"
RunEventCreated RunEvent = "conversation.chat.created"
RunEventInProgress RunEvent = "conversation.chat.in_progress"
RunEventCompleted RunEvent = "conversation.chat.completed"
RunEventFailed RunEvent = "conversation.chat.failed"
RunEventExpired RunEvent = "conversation.chat.expired"
RunEventCancelled RunEvent = "conversation.chat.cancelled"
RunEventRequiredAction RunEvent = "conversation.chat.required_action"
RunEventMessageDelta RunEvent = "conversation.message.delta"
RunEventMessageCompleted RunEvent = "conversation.message.completed"

View File

@ -58,9 +58,7 @@ func (dao *KnowledgeDocumentSliceDAO) BatchCreate(ctx context.Context, slices []
func (dao *KnowledgeDocumentSliceDAO) BatchSetStatus(ctx context.Context, ids []int64, status int32, reason string) error {
s := dao.Query.KnowledgeDocumentSlice
updates := map[string]any{s.Status.ColumnName().String(): status}
if reason != "" {
updates[s.FailReason.ColumnName().String()] = reason
}
updates[s.FailReason.ColumnName().String()] = reason
updates[s.UpdatedAt.ColumnName().String()] = time.Now().UnixMilli()
_, err := s.WithContext(ctx).Where(s.ID.In(ids...)).Updates(updates)
return err

View File

@ -166,11 +166,22 @@ func (k *knowledgeSVC) indexDocument(ctx context.Context, event *entity.Event) (
return
}
if err != nil {
var errMsg string
var statusError errorx.StatusError
if errors.As(err, &statusError) && statusError.Code() == errno.ErrKnowledgeNonRetryableCode {
if setStatusErr := k.documentRepo.SetStatus(ctx, event.Document.ID, int32(entity.DocumentStatusFailed), err.Error()); setStatusErr != nil {
logs.CtxErrorf(ctx, "[indexDocument] set document status failed, err: %v", setStatusErr)
var status int32
if errors.As(err, &statusError) {
errMsg = errorx.ErrorWithoutStack(statusError)
if statusError.Code() == errno.ErrKnowledgeNonRetryableCode {
status = int32(entity.DocumentStatusFailed)
} else {
status = int32(entity.DocumentStatusChunking)
}
} else {
errMsg = err.Error()
status = int32(entity.DocumentStatusChunking)
}
if setStatusErr := k.documentRepo.SetStatus(ctx, event.Document.ID, status, errMsg); setStatusErr != nil {
logs.CtxErrorf(ctx, "[indexDocument] set document status failed, err: %v", setStatusErr)
}
}
}()

View File

@ -545,6 +545,12 @@ func (k *knowledgeSVC) MGetDocumentProgress(ctx context.Context, request *MGetDo
if documents[i].Status == int32(entity.DocumentStatusEnable) || documents[i].Status == int32(entity.DocumentStatusFailed) {
item.Progress = progressbar.ProcessDone
} else {
if documents[i].FailReason != "" {
item.StatusMsg = documents[i].FailReason
item.Status = entity.DocumentStatusFailed
progresslist = append(progresslist, &item)
continue
}
err = k.getProgressFromCache(ctx, &item)
if err != nil {
logs.CtxErrorf(ctx, "get progress from cache failed, err: %v", err)
@ -564,8 +570,9 @@ func (k *knowledgeSVC) getProgressFromCache(ctx context.Context, documentProgres
documentProgress.Progress = int(percent)
documentProgress.RemainingSec = int64(remainSec)
if len(errMsg) != 0 {
documentProgress.Progress = 0
documentProgress.Status = entity.DocumentStatusChunking
documentProgress.Status = entity.DocumentStatusFailed
documentProgress.StatusMsg = errMsg
return err
}
return err
}
@ -1276,6 +1283,16 @@ func (k *knowledgeSVC) fromModelDocument(ctx context.Context, document *model.Kn
documentEntity.TableInfo.Columns = append(documentEntity.TableInfo.Columns, document.TableInfo.Columns[i])
}
}
switch document.Status {
case int32(entity.DocumentStatusChunking), int32(entity.DocumentStatusInit), int32(entity.DocumentStatusUploading):
if document.FailReason != "" {
documentEntity.Status = entity.DocumentStatusFailed
documentEntity.StatusMsg = document.FailReason
}
case int32(entity.DocumentStatusFailed):
documentEntity.StatusMsg = document.FailReason
default:
}
if len(document.URI) != 0 {
objUrl, err := k.storage.GetObjectUrl(ctx, document.URI)
if err != nil {

View File

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

View File

@ -33,7 +33,7 @@ const (
func SwitchToDataType(itemType table.FieldItemType) entity.DataType {
switch itemType {
case table.FieldItemType_Text:
return entity.TypeVarchar
return entity.TypeText
case table.FieldItemType_Number:
return entity.TypeBigInt
case table.FieldItemType_Date:

View File

@ -157,7 +157,6 @@ func NewDefaultPluginManifest() *PluginManifest {
Value: "Coze/1.0",
},
},
model.ParamInPath: {},
model.ParamInQuery: {},
},
}

View File

@ -27,6 +27,7 @@ import (
"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"
"github.com/coze-dev/coze-studio/backend/domain/plugin/internal/dal/model"
"github.com/coze-dev/coze-studio/backend/domain/plugin/internal/dal/query"
@ -88,7 +89,7 @@ func (p *PluginDraftDAO) getSelected(opt *PluginSelectedOption) (selected []fiel
}
func (p *PluginDraftDAO) Create(ctx context.Context, plugin *entity.PluginInfo) (pluginID int64, err error) {
id, err := p.idGen.GenID(ctx)
id, err := p.genPluginID(ctx)
if err != nil {
return 0, err
}
@ -117,6 +118,25 @@ func (p *PluginDraftDAO) Create(ctx context.Context, plugin *entity.PluginInfo)
return id, nil
}
func (p *PluginDraftDAO) genPluginID(ctx context.Context) (id int64, err error) {
retryTimes := 5
for i := 0; i < retryTimes; i++ {
id, err = p.idGen.GenID(ctx)
if err != nil {
return 0, err
}
if _, ok := conf.GetPluginProduct(id); !ok {
break
}
if i == retryTimes-1 {
return 0, fmt.Errorf("id %d is confilict with product plugin id.", id)
}
}
return id, nil
}
func (p *PluginDraftDAO) Get(ctx context.Context, pluginID int64, opt *PluginSelectedOption) (plugin *entity.PluginInfo, exist bool, err error) {
table := p.query.PluginDraft
pl, err := table.WithContext(ctx).
@ -262,7 +282,7 @@ func (p *PluginDraftDAO) Update(ctx context.Context, plugin *entity.PluginInfo)
}
func (p *PluginDraftDAO) CreateWithTX(ctx context.Context, tx *query.QueryTx, plugin *entity.PluginInfo) (pluginID int64, err error) {
id, err := p.idGen.GenID(ctx)
id, err := p.genPluginID(ctx)
if err != nil {
return 0, err
}

View File

@ -27,6 +27,7 @@ import (
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/plugin"
common "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"
"github.com/coze-dev/coze-studio/backend/domain/plugin/internal/dal/model"
"github.com/coze-dev/coze-studio/backend/domain/plugin/internal/dal/query"
@ -90,7 +91,8 @@ func (t *ToolDraftDAO) getSelected(opt *ToolSelectedOption) (selected []field.Ex
}
func (t *ToolDraftDAO) Create(ctx context.Context, tool *entity.ToolInfo) (toolID int64, err error) {
id, err := t.idGen.GenID(ctx)
id, err := t.genToolID(ctx)
if err != nil {
return 0, err
}
@ -111,6 +113,27 @@ func (t *ToolDraftDAO) Create(ctx context.Context, tool *entity.ToolInfo) (toolI
return id, nil
}
func (t *ToolDraftDAO) genToolID(ctx context.Context) (id int64, err error) {
retryTimes := 5
for i := 0; i < retryTimes; i++ {
id, err = t.idGen.GenID(ctx)
if err != nil {
return 0, err
}
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 id, nil
}
func (t *ToolDraftDAO) Get(ctx context.Context, toolID int64) (tool *entity.ToolInfo, exist bool, err error) {
table := t.query.ToolDraft
tl, err := table.WithContext(ctx).
@ -335,7 +358,7 @@ func (t *ToolDraftDAO) BatchCreateWithTX(ctx context.Context, tx *query.QueryTx,
tls := make([]*model.ToolDraft, 0, len(tools))
for _, tool := range tools {
id, err := t.idGen.GenID(ctx)
id, err := t.genToolID(ctx)
if err != nil {
return nil, err
}

View File

@ -29,6 +29,7 @@ import (
"github.com/bytedance/sonic"
"github.com/getkin/kin-openapi/openapi3"
"github.com/tidwall/sjson"
einoCompose "github.com/cloudwego/eino/compose"
@ -479,11 +480,6 @@ func (t *toolExecutor) execute(ctx context.Context, argumentsInJson string) (res
return nil, err
}
requestStr, err := sonic.MarshalString(args)
if err != nil {
return nil, err
}
httpReq, err := t.buildHTTPRequest(ctx, args)
if err != nil {
return nil, err
@ -504,18 +500,29 @@ func (t *toolExecutor) execute(ctx context.Context, argumentsInJson string) (res
}
var reqBodyBytes []byte
if httpReq.Body != nil {
reqBodyBytes, err = io.ReadAll(httpReq.Body)
if httpReq.GetBody != nil {
reqBody, err := httpReq.GetBody()
if err != nil {
return nil, err
}
defer reqBody.Close()
reqBodyBytes, err = io.ReadAll(reqBody)
if err != nil {
return nil, err
}
}
requestStr, err := genRequestString(httpReq, reqBodyBytes)
if err != nil {
return nil, err
}
restyReq := t.svc.httpCli.NewRequest()
restyReq.Header = httpReq.Header
restyReq.Method = httpReq.Method
restyReq.URL = httpReq.URL.String()
if len(reqBodyBytes) > 0 {
if reqBodyBytes != nil {
restyReq.SetBody(reqBodyBytes)
}
restyReq.SetContext(ctx)
@ -559,6 +566,46 @@ func (t *toolExecutor) execute(ctx context.Context, argumentsInJson string) (res
}, nil
}
func genRequestString(req *http.Request, body []byte) (string, error) {
type Request struct {
Path string `json:"path"`
Header map[string]string `json:"header"`
Query map[string]string `json:"query"`
Body *[]byte `json:"body"`
}
req_ := &Request{
Path: req.URL.Path,
Header: map[string]string{},
Query: map[string]string{},
}
if len(req.Header) > 0 {
for k, v := range req.Header {
req_.Header[k] = v[0]
}
}
if len(req.URL.Query()) > 0 {
for k, v := range req.URL.Query() {
req_.Query[k] = v[0]
}
}
requestStr, err := sonic.MarshalString(req_)
if err != nil {
return "", fmt.Errorf("[genRequestString] marshal failed, err=%s", err)
}
if body != nil {
requestStr, err = sjson.SetRaw(requestStr, "body", string(body))
if err != nil {
return "", fmt.Errorf("[genRequestString] set body failed, err=%s", err)
}
}
return requestStr, nil
}
func (t *toolExecutor) preprocessArgumentsInJson(ctx context.Context, argumentsInJson string) (args map[string]any, err error) {
args, err = t.prepareArguments(ctx, argumentsInJson)
if err != nil {
@ -653,23 +700,13 @@ func (t *toolExecutor) buildHTTPRequest(ctx context.Context, argMaps map[string]
return nil, err
}
reqURL, err := locArgs.buildHTTPRequestURL(ctx, rawURL)
commonParams := t.plugin.Manifest.CommonParams
reqURL, err := locArgs.buildHTTPRequestURL(ctx, rawURL, commonParams)
if err != nil {
return nil, err
}
httpReq, err = http.NewRequestWithContext(ctx, tool.GetMethod(), reqURL.String(), nil)
if err != nil {
return nil, err
}
header, err := locArgs.buildHTTPRequestHeader(ctx)
if err != nil {
return nil, err
}
httpReq.Header = header
bodyArgs := map[string]any{}
for k, v := range argMaps {
if _, ok := locArgs.header[k]; ok {
@ -684,13 +721,27 @@ func (t *toolExecutor) buildHTTPRequest(ctx context.Context, argMaps map[string]
bodyArgs[k] = v
}
bodyBytes, contentType, err := t.buildRequestBody(ctx, tool.Operation, bodyArgs)
commonBody := commonParams[model.ParamInBody]
bodyBytes, contentType, err := t.buildRequestBody(ctx, tool.Operation, bodyArgs, commonBody)
if err != nil {
return nil, err
}
httpReq, err = http.NewRequestWithContext(ctx, tool.GetMethod(), reqURL.String(), bytes.NewBuffer(bodyBytes))
if err != nil {
return nil, err
}
commonHeader := commonParams[model.ParamInHeader]
header, err := locArgs.buildHTTPRequestHeader(ctx, commonHeader)
if err != nil {
return nil, err
}
httpReq.Header = header
if len(bodyBytes) > 0 {
httpReq.Header.Set("Content-Type", contentType)
httpReq.Body = io.NopCloser(bytes.NewReader(bodyBytes))
}
return httpReq, nil
@ -698,13 +749,6 @@ func (t *toolExecutor) buildHTTPRequest(ctx context.Context, argMaps map[string]
func (t *toolExecutor) prepareArguments(_ context.Context, argumentsInJson string) (map[string]any, error) {
args := map[string]any{}
for loc, params := range t.plugin.Manifest.CommonParams {
for _, p := range params {
if loc != model.ParamInBody {
args[p.Name] = p.Value
}
}
}
decoder := sonic.ConfigDefault.NewDecoder(bytes.NewBufferString(argumentsInJson))
decoder.UseNumber()
@ -1175,7 +1219,9 @@ type valueWithSchema struct {
paramSchema *openapi3.Parameter
}
func (l *locationArguments) buildHTTPRequestURL(_ context.Context, rawURL string) (reqURL *url.URL, err error) {
func (l *locationArguments) buildHTTPRequestURL(_ context.Context, rawURL string,
commonParams map[model.HTTPParamLocation][]*common.CommonParamSchema) (reqURL *url.URL, err error) {
if len(l.path) > 0 {
for k, v := range l.path {
vStr, err := encoder.EncodeParameter(v.paramSchema, v.argValue)
@ -1186,9 +1232,8 @@ func (l *locationArguments) buildHTTPRequestURL(_ context.Context, rawURL string
}
}
encodeQuery := ""
query := url.Values{}
if len(l.query) > 0 {
query := url.Values{}
for k, val := range l.query {
switch v := val.argValue.(type) {
case []any:
@ -1199,10 +1244,18 @@ func (l *locationArguments) buildHTTPRequestURL(_ context.Context, rawURL string
query.Add(k, encoder.MustString(v))
}
}
encodeQuery = query.Encode()
}
commonQuery := commonParams[model.ParamInQuery]
for _, v := range commonQuery {
if _, ok := l.query[v.Name]; ok {
continue
}
query.Add(v.Name, v.Value)
}
encodeQuery := query.Encode()
reqURL, err = url.Parse(rawURL)
if err != nil {
return nil, err
@ -1217,7 +1270,7 @@ func (l *locationArguments) buildHTTPRequestURL(_ context.Context, rawURL string
return reqURL, nil
}
func (l *locationArguments) buildHTTPRequestHeader(_ context.Context) (http.Header, error) {
func (l *locationArguments) buildHTTPRequestHeader(_ context.Context, commonHeaders []*common.CommonParamSchema) (http.Header, error) {
header := http.Header{}
if len(l.header) > 0 {
for k, v := range l.header {
@ -1232,44 +1285,64 @@ func (l *locationArguments) buildHTTPRequestHeader(_ context.Context) (http.Head
}
}
for _, h := range commonHeaders {
if header.Get(h.Name) != "" {
continue
}
header.Add(h.Name, h.Value)
}
return header, nil
}
func (t *toolExecutor) buildRequestBody(ctx context.Context, op *model.Openapi3Operation, bodyArgs map[string]any) (body []byte, contentType string, err error) {
func (t *toolExecutor) buildRequestBody(ctx context.Context, op *model.Openapi3Operation, bodyArgs map[string]any,
commonBody []*common.CommonParamSchema) (body []byte, contentType string, err error) {
var bodyMap map[string]any
contentType, bodySchema := t.getReqBodySchema(op)
if bodySchema == nil || bodySchema.Value == nil {
return nil, "", nil
}
if len(bodySchema.Value.Properties) == 0 {
return nil, "", nil
}
bodyMap, err := t.injectRequestBodyDefaultValue(ctx, bodySchema.Value, bodyArgs)
if err != nil {
return nil, "", err
}
for paramName, prop := range bodySchema.Value.Properties {
value, ok := bodyMap[paramName]
if !ok {
continue
}
_value, err := encoder.TryFixValueType(paramName, prop, value)
if bodySchema != nil && len(bodySchema.Value.Properties) > 0 {
bodyMap, err = t.injectRequestBodyDefaultValue(ctx, bodySchema.Value, bodyArgs)
if err != nil {
return nil, "", err
}
bodyMap[paramName] = _value
for paramName, prop := range bodySchema.Value.Properties {
value, ok := bodyMap[paramName]
if !ok {
continue
}
_value, err := encoder.TryFixValueType(paramName, prop, value)
if err != nil {
return nil, "", err
}
bodyMap[paramName] = _value
}
body, err = encoder.EncodeBodyWithContentType(contentType, bodyMap)
if err != nil {
return nil, "", fmt.Errorf("[buildRequestBody] EncodeBodyWithContentType failed, err=%v", err)
}
}
reqBodyStr, err := encoder.EncodeBodyWithContentType(contentType, bodyMap)
if err != nil {
return nil, "", fmt.Errorf("[buildRequestBody] EncodeBodyWithContentType failed, err=%v", err)
commonBody_ := make([]*common.CommonParamSchema, 0, len(commonBody))
for _, v := range commonBody {
if _, ok := bodyMap[v.Name]; ok {
continue
}
commonBody_ = append(commonBody_, v)
}
return reqBodyStr, contentType, nil
for _, v := range commonBody_ {
body, err = sjson.SetRawBytes(body, v.Name, []byte(v.Value))
if err != nil {
return nil, "", fmt.Errorf("[buildRequestBody] SetRawBytes failed, err=%v", err)
}
}
return body, contentType, nil
}
func (t *toolExecutor) injectRequestBodyDefaultValue(ctx context.Context, sc *openapi3.Schema, vals map[string]any) (newVals map[string]any, err error) {
@ -1327,7 +1400,7 @@ func (t *toolExecutor) injectRequestBodyDefaultValue(ctx context.Context, sc *op
}
func (t *toolExecutor) getReqBodySchema(op *model.Openapi3Operation) (string, *openapi3.SchemaRef) {
if op.RequestBody == nil || op.RequestBody.Value == nil || len(op.RequestBody.Value.Content) == 0 {
if op.RequestBody == nil || len(op.RequestBody.Value.Content) == 0 {
return "", nil
}

View File

@ -0,0 +1,49 @@
/*
* 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 service
import (
"net/http"
"net/url"
"testing"
. "github.com/bytedance/mockey"
"github.com/stretchr/testify/assert"
)
func TestGenRequestString(t *testing.T) {
PatchConvey("", t, func() {
requestStr, err := genRequestString(&http.Request{
Header: http.Header{
"Content-Type": []string{"application/json"},
},
Method: http.MethodPost,
URL: &url.URL{Path: "/test"},
}, []byte(`{"a": 1}`))
assert.NoError(t, err)
assert.Equal(t, `{"header":{"Content-Type":["application/json"]},"query":null,"path":"/test","body":{"a": 1}}`, requestStr)
})
PatchConvey("", t, func() {
var body []byte
requestStr, err := genRequestString(&http.Request{
URL: &url.URL{Path: "/test"},
}, body)
assert.NoError(t, err)
assert.Equal(t, `{"header":null,"query":null,"path":"/test","body":null}`, requestStr)
})
}

View File

@ -46,6 +46,7 @@ import (
func (p *pluginServiceImpl) CreateDraftPlugin(ctx context.Context, req *CreateDraftPluginRequest) (pluginID int64, err error) {
mf := entity.NewDefaultPluginManifest()
mf.CommonParams = map[model.HTTPParamLocation][]*plugin_develop_common.CommonParamSchema{}
mf.NameForHuman = req.Name
mf.NameForModel = req.Name
mf.DescriptionForHuman = req.Desc
@ -65,11 +66,11 @@ func (p *pluginServiceImpl) CreateDraftPlugin(ctx context.Context, req *CreateDr
return 0, fmt.Errorf("invalid location '%s'", loc.String())
}
for _, param := range params {
mParams := mf.CommonParams[location]
mParams = append(mParams, &plugin_develop_common.CommonParamSchema{
Name: param.Name,
Value: param.Value,
})
mf.CommonParams[location] = append(mf.CommonParams[location],
&plugin_develop_common.CommonParamSchema{
Name: param.Name,
Value: param.Value,
})
}
}

View File

@ -194,7 +194,7 @@ func (p *pluginServiceImpl) getAccessTokenByAuthorizationCode(ctx context.Contex
meta := ci.Meta
info, exist, err := p.oauthRepo.GetAuthorizationCode(ctx, ci.Meta)
if err != nil {
return "", errorx.Wrapf(err, "GetAuthorizationCode failed, userID=%s, pluginID=%d, isDraft=%p",
return "", errorx.Wrapf(err, "GetAuthorizationCode failed, userID=%s, pluginID=%d, isDraft=%t",
meta.UserID, meta.PluginID, meta.IsDraft)
}
if !exist {

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

@ -20,11 +20,11 @@ import (
"context"
"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
@ -38,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
@ -52,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)
@ -94,8 +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
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
}

View File

@ -86,6 +86,8 @@ func init() {
_ = compose.RegisterSerializableType[vo.Locator]("wf_locator")
_ = compose.RegisterSerializableType[vo.BizType]("biz_type")
_ = compose.RegisterSerializableType[*variableassigner.AppVariables]("app_variables")
_ = compose.RegisterSerializableType[workflow2.WorkflowMode]("workflow_mode")
}
func (s *State) SetAppVariableValue(key string, value any) {

View File

@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
"runtime/debug"
"strconv"
"time"
@ -96,6 +97,13 @@ func (s *NodeSchema) ToLLMConfig(ctx context.Context) (*llm.Config, error) {
modelWithInfo llm.ModelWithInfo
)
if llmParams.EnableChatHistory {
llmConf.ChatHistorySetting = &vo.ChatHistorySetting{
EnableChatHistory: llmParams.EnableChatHistory,
ChatHistoryRound: llmParams.ChatHistoryRound,
}
}
chatModel, info, err = model.GetManager().GetModel(ctx, llmParams)
if err != nil {
return nil, err
@ -502,6 +510,7 @@ func (s *NodeSchema) ToDatabaseCustomSQLConfig() (*database.CustomSQLConfig, err
OutputConfig: s.OutputTypes,
CustomSQLExecutor: crossdatabase.GetDatabaseOperator(),
}, nil
}
func (s *NodeSchema) ToDatabaseQueryConfig() (*database.QueryConfig, error) {
@ -517,6 +526,7 @@ func (s *NodeSchema) ToDatabaseQueryConfig() (*database.QueryConfig, error) {
}
func (s *NodeSchema) ToDatabaseInsertConfig() (*database.InsertConfig, error) {
return &database.InsertConfig{
DatabaseInfoID: mustGetKey[int64]("DatabaseInfoID", s.Configs),
OutputConfig: s.OutputTypes,
@ -534,6 +544,7 @@ func (s *NodeSchema) ToDatabaseDeleteConfig() (*database.DeleteConfig, error) {
}
func (s *NodeSchema) ToDatabaseUpdateConfig() (*database.UpdateConfig, error) {
return &database.UpdateConfig{
DatabaseInfoID: mustGetKey[int64]("DatabaseInfoID", s.Configs),
ClauseGroup: mustGetKey[*crossdatabase.ClauseGroup]("ClauseGroup", s.Configs),
@ -553,9 +564,10 @@ func (s *NodeSchema) ToKnowledgeIndexerConfig() (*knowledge.IndexerConfig, error
func (s *NodeSchema) ToKnowledgeRetrieveConfig() (*knowledge.RetrieveConfig, error) {
return &knowledge.RetrieveConfig{
KnowledgeIDs: mustGetKey[[]int64]("KnowledgeIDs", s.Configs),
RetrievalStrategy: mustGetKey[*crossknowledge.RetrievalStrategy]("RetrievalStrategy", s.Configs),
Retriever: crossknowledge.GetKnowledgeOperator(),
KnowledgeIDs: mustGetKey[[]int64]("KnowledgeIDs", s.Configs),
RetrievalStrategy: mustGetKey[*crossknowledge.RetrievalStrategy]("RetrievalStrategy", s.Configs),
Retriever: crossknowledge.GetKnowledgeOperator(),
ChatHistorySetting: getKeyOrZero[*vo.ChatHistorySetting]("ChatHistorySetting", s.Configs),
}, nil
}
@ -573,6 +585,7 @@ func (s *NodeSchema) ToPluginConfig() (*plugin.Config, error) {
PluginVersion: mustGetKey[string]("PluginVersion", s.Configs),
PluginService: crossplugin.GetPluginService(),
}, nil
}
func (s *NodeSchema) ToCodeRunnerConfig() (*code.Config, error) {
@ -586,27 +599,51 @@ func (s *NodeSchema) ToCodeRunnerConfig() (*code.Config, error) {
func (s *NodeSchema) ToCreateConversationConfig() (*conversation.CreateConversationConfig, error) {
return &conversation.CreateConversationConfig{
Creator: crossconversation.ConversationManagerImpl,
Manager: crossconversation.GetConversationManager(),
}, nil
}
func (s *NodeSchema) ToClearMessageConfig() (*conversation.ClearMessageConfig, error) {
return &conversation.ClearMessageConfig{
Clearer: crossconversation.ConversationManagerImpl,
func (s *NodeSchema) ToDeleteMessageConfig() (*conversation.DeleteMessageConfig, error) {
return &conversation.DeleteMessageConfig{
Manager: crossconversation.GetConversationManager(),
}, nil
}
func (s *NodeSchema) ToEditMessageConfig() (*conversation.EditMessageConfig, error) {
return &conversation.EditMessageConfig{
Manager: crossconversation.GetConversationManager(),
}, nil
}
func (s *NodeSchema) ToCreateMessageConfig() (*conversation.CreateMessageConfig, error) {
return &conversation.CreateMessageConfig{
Creator: crossconversation.GetConversationManager(),
}, nil
}
func (s *NodeSchema) ToMessageListConfig() (*conversation.MessageListConfig, error) {
return &conversation.MessageListConfig{
Lister: crossconversation.ConversationManagerImpl,
Lister: crossconversation.GetConversationManager(),
}, nil
}
func (s *NodeSchema) ToClearConversationHistoryConfig() (*conversation.ClearConversationHistoryConfig, error) {
return &conversation.ClearConversationHistoryConfig{
Manager: crossconversation.GetConversationManager(),
}, nil
}
func (s *NodeSchema) ToConversationHistoryConfig() (*conversation.ConversationHistoryConfig, error) {
return &conversation.ConversationHistoryConfig{
Manager: crossconversation.GetConversationManager(),
}, nil
}
func (s *NodeSchema) ToIntentDetectorConfig(ctx context.Context) (*intentdetector.Config, error) {
cfg := &intentdetector.Config{
Intents: mustGetKey[[]string]("Intents", s.Configs),
SystemPrompt: getKeyOrZero[string]("SystemPrompt", s.Configs),
IsFastMode: getKeyOrZero[bool]("IsFastMode", s.Configs),
Intents: mustGetKey[[]string]("Intents", s.Configs),
SystemPrompt: getKeyOrZero[string]("SystemPrompt", s.Configs),
IsFastMode: getKeyOrZero[bool]("IsFastMode", s.Configs),
ChatHistorySetting: getKeyOrZero[*vo.ChatHistorySetting]("ChatHistorySetting", s.Configs),
}
llmParams := mustGetKey[*model.LLMParams]("LLMParams", s.Configs)

View File

@ -0,0 +1,122 @@
/*
* 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"
"errors"
"fmt"
wf "github.com/coze-dev/coze-studio/backend/domain/workflow"
"github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/conversation"
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/execute"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ternary"
"github.com/coze-dev/coze-studio/backend/types/errno"
)
type ClearConversationHistoryConfig struct {
Manager conversation.ConversationManager
}
type ClearConversationHistory struct {
cfg *ClearConversationHistoryConfig
}
func NewClearConversationHistory(_ context.Context, cfg *ClearConversationHistoryConfig) (*ClearConversationHistory, error) {
if cfg == nil {
return nil, errors.New("config is required")
}
if cfg.Manager == nil {
return nil, errors.New("manager is required")
}
return &ClearConversationHistory{
cfg: cfg,
}, nil
}
func (c *ClearConversationHistory) Clear(ctx context.Context, in map[string]any) (map[string]any, error) {
var (
execCtx = execute.GetExeCtx(ctx)
env = ternary.IFElse(execCtx.ExeCfg.Mode == vo.ExecuteModeRelease, vo.Online, vo.Draft)
appID = execCtx.ExeCfg.AppID
agentID = execCtx.ExeCfg.AgentID
connectorID = execCtx.ExeCfg.ConnectorID
userID = execCtx.ExeCfg.Operator
version = execCtx.ExeCfg.Version
)
if agentID != nil {
return nil, vo.WrapError(errno.ErrConversationNodesNotAvailable, fmt.Errorf("in the agent scenario, query conversation list is not available"))
}
if appID == nil {
return nil, vo.WrapError(errno.ErrConversationNodesNotAvailable, fmt.Errorf("query conversation list node, app id is required"))
}
conversationName, ok := in["conversationName"].(string)
if !ok {
return nil, vo.WrapError(errno.ErrInvalidParameter, errors.New("conversation name is required"))
}
t, existed, err := wf.GetRepository().GetConversationTemplate(ctx, env, vo.GetConversationTemplatePolicy{
AppID: appID,
Name: ptr.Of(conversationName),
Version: ptr.Of(version),
})
if err != nil {
return nil, vo.WrapError(errno.ErrConversationNodesNotAvailable, err)
}
var conversationID int64
if existed {
ret, existed, err := wf.GetRepository().GetStaticConversationByTemplateID(ctx, env, userID, connectorID, t.TemplateID)
if err != nil {
return nil, vo.WrapError(errno.ErrConversationNodesNotAvailable, err)
}
if existed {
conversationID = ret.ConversationID
}
} else {
ret, existed, err := wf.GetRepository().GetDynamicConversationByName(ctx, env, *appID, connectorID, userID, conversationName)
if err != nil {
return nil, vo.WrapError(errno.ErrConversationNodesNotAvailable, err)
}
if existed {
conversationID = ret.ConversationID
}
}
if !existed {
return map[string]any{
"isSuccess": false,
}, nil
}
err = c.cfg.Manager.ClearConversationHistory(ctx, &conversation.ClearConversationHistoryReq{
ConversationID: conversationID,
})
if err != nil {
return nil, vo.WrapError(errno.ErrConversationNodesNotAvailable, err)
}
return map[string]any{
"isSuccess": true,
}, nil
}

View File

@ -1,64 +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 conversation
import (
"context"
"errors"
"github.com/cloudwego/eino/compose"
"github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/conversation"
"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/nodes"
)
type ClearMessageConfig struct {
Clearer conversation.ConversationManager
}
type MessageClear struct {
config *ClearMessageConfig
}
func NewClearMessage(ctx context.Context, cfg *ClearMessageConfig) (*MessageClear, error) {
if cfg == nil {
return nil, errors.New("config is required")
}
if cfg.Clearer == nil {
return nil, errors.New("clearer is required")
}
return &MessageClear{
config: cfg,
}, nil
}
func (c *MessageClear) Clear(ctx context.Context, input map[string]any) (map[string]any, error) {
name, ok := nodes.TakeMapValue(input, compose.FieldPath{"ConversationName"})
if !ok {
return nil, errors.New("input map should contains 'ConversationName' key ")
}
response, err := c.config.Clearer.ClearMessage(ctx, &conversation.ClearMessageRequest{
Name: name.(string),
})
if err != nil {
return nil, err
}
return map[string]any{
"isSuccess": response.IsSuccess,
}, nil
}

View File

@ -0,0 +1,229 @@
/*
* 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"
"errors"
"fmt"
"strings"
"github.com/cloudwego/eino/schema"
oceanworkflow "github.com/coze-dev/coze-studio/backend/api/model/ocean/cloud/workflow"
"github.com/coze-dev/coze-studio/backend/domain/workflow"
"github.com/coze-dev/coze-studio/backend/domain/workflow/crossdomain/conversation"
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/execute"
"github.com/coze-dev/coze-studio/backend/pkg/ctxcache"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
"github.com/coze-dev/coze-studio/backend/pkg/logs"
"github.com/coze-dev/coze-studio/backend/types/errno"
)
type contextKey string
const chatHistoryKey contextKey = "chatHistory"
func ConvertMessageToString(ctx context.Context, msg *conversation.Message) (string, error) {
if msg.MultiContent != nil {
var textContents []string
var otherContents []string
for _, m := range msg.MultiContent {
if m.Text != nil {
textContents = append(textContents, ptr.From(m.Text))
} else if m.Uri != nil {
url, err := workflow.GetRepository().GetObjectUrl(ctx, ptr.From(m.Uri))
if err != nil {
return "", err
}
otherContents = append(otherContents, url)
}
}
var allParts []string
if len(textContents) > 0 {
allParts = append(allParts, textContents...)
}
if len(otherContents) > 0 {
allParts = append(allParts, otherContents...)
}
return strings.Join(allParts, ","), nil
} else if msg.Text != nil {
return ptr.From(msg.Text), nil
} else {
return "", vo.WrapError(errno.ErrInvalidParameter, errors.New("message is invalid"))
}
}
func ConvertMessageToSchema(ctx context.Context, msg *conversation.Message) (*schema.Message, error) {
schemaMsg := &schema.Message{}
switch msg.Role {
case "user":
schemaMsg.Role = schema.User
case "assistant":
schemaMsg.Role = schema.Assistant
default:
return nil, fmt.Errorf("unknown role: %s", msg.Role)
}
if msg.Text != nil && *msg.Text != "" {
schemaMsg.Content = *msg.Text
return schemaMsg, nil
}
if len(msg.MultiContent) > 0 {
multiContent := make([]schema.ChatMessagePart, 0, len(msg.MultiContent))
for _, part := range msg.MultiContent {
schemaPart, err := convertContentPart(ctx, part)
if err != nil {
logs.CtxWarnf(ctx, "failed to convert content part, skipping: %v", err)
continue
}
multiContent = append(multiContent, schemaPart)
}
schemaMsg.MultiContent = multiContent
return schemaMsg, nil
}
return nil, fmt.Errorf("message has no content")
}
func convertContentPart(ctx context.Context, part *conversation.Content) (schema.ChatMessagePart, error) {
schemaPart := schema.ChatMessagePart{}
uri := ""
if part.Uri != nil {
uri = *part.Uri
}
switch part.Type {
case "text":
schemaPart.Type = schema.ChatMessagePartTypeText
if part.Text == nil || *part.Text == "" {
return schema.ChatMessagePart{}, fmt.Errorf("text is empty for text content part type")
}
schemaPart.Text = *part.Text
case "image":
schemaPart.Type = schema.ChatMessagePartTypeImageURL
url, err := workflow.GetRepository().GetObjectUrl(ctx, uri)
if err != nil {
return schema.ChatMessagePart{}, fmt.Errorf("failed to get object url: %w", err)
}
schemaPart.ImageURL = &schema.ChatMessageImageURL{URL: url}
case "audio":
schemaPart.Type = schema.ChatMessagePartTypeAudioURL
url, err := workflow.GetRepository().GetObjectUrl(ctx, uri)
if err != nil {
return schema.ChatMessagePart{}, fmt.Errorf("failed to get object url: %w", err)
}
schemaPart.AudioURL = &schema.ChatMessageAudioURL{URL: url}
case "video":
schemaPart.Type = schema.ChatMessagePartTypeVideoURL
url, err := workflow.GetRepository().GetObjectUrl(ctx, uri)
if err != nil {
return schema.ChatMessagePart{}, fmt.Errorf("failed to get object url: %w", err)
}
schemaPart.VideoURL = &schema.ChatMessageVideoURL{URL: url}
case "file":
schemaPart.Type = schema.ChatMessagePartTypeFileURL
url, err := workflow.GetRepository().GetObjectUrl(ctx, uri)
if err != nil {
return schema.ChatMessagePart{}, fmt.Errorf("failed to get object url: %w", err)
}
schemaPart.FileURL = &schema.ChatMessageFileURL{URL: url}
default:
return schema.ChatMessagePart{}, fmt.Errorf("unknown content part type: %s", part.Type)
}
if schemaPart.Type != schema.ChatMessagePartTypeText && uri == "" {
return schema.ChatMessagePart{}, fmt.Errorf("uri is empty for non-text content part type %s", part.Type)
}
return schemaPart, nil
}
func GetConversationHistoryFromCtx(ctx context.Context, rounds int64) ([]any, error) {
exeCtx := execute.GetExeCtx(ctx)
if exeCtx == nil {
logs.CtxWarnf(ctx, "execute context is nil, skipping chat history")
return nil, nil
}
if exeCtx.ExeCfg.WorkflowMode != oceanworkflow.WorkflowMode_ChatFlow {
return nil, nil
}
convID := exeCtx.ExeCfg.ConversationID
agentID := exeCtx.ExeCfg.AgentID
appID := exeCtx.ExeCfg.AppID
userID := exeCtx.ExeCfg.Operator
if convID == nil || *convID == 0 {
logs.CtxWarnf(ctx, "ConversationID is 0 or nil, skipping chat history")
return nil, nil
}
var appIDVal int64
if appID != nil {
appIDVal = *appID
} else if agentID != nil {
appIDVal = *agentID
} else {
logs.CtxWarnf(ctx, "AppID and AgentID are both nil, skipping chat history")
return nil, nil
}
runIdsReq := &conversation.GetLatestRunIDsRequest{
ConversationID: *convID,
AppID: appIDVal,
UserID: userID,
Rounds: rounds,
}
runIds, err := conversation.GetConversationManager().GetLatestRunIDs(ctx, runIdsReq)
if err != nil {
logs.CtxErrorf(ctx, "failed to get conversation history: %v", err)
return nil, nil
}
if len(runIds) <= 1 {
return []any{}, nil
}
runIds = runIds[1:]
response, err := conversation.GetConversationManager().GetMessagesByRunIDs(ctx, &conversation.GetMessagesByRunIDsRequest{
ConversationID: *convID,
RunIDs: runIds,
})
if err != nil {
logs.CtxErrorf(ctx, "failed to get conversation history: %v", err)
return nil, nil
}
ctxcache.Store(ctx, chatHistoryKey, response.Messages)
messageList := make([]any, 0, len(response.Messages))
for _, msg := range response.Messages {
content, err := ConvertMessageToString(ctx, msg)
if err != nil {
return nil, nil
}
messageList = append(messageList, map[string]any{
"role": msg.Role,
"content": content,
})
}
return messageList, nil
}

View File

@ -0,0 +1,128 @@
/*
* 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"
"fmt"
"strconv"
"github.com/coze-dev/coze-studio/backend/domain/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/domain/workflow/internal/execute"
"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/types/errno"
)
type ConversationList struct {
}
func NewConversationList(_ context.Context) (*ConversationList, error) {
return &ConversationList{}, nil
}
type conversationInfo struct {
conversationName string
conversationId string
}
func (c *ConversationList) List(ctx context.Context, _ map[string]any) (map[string]any, error) {
var (
execCtx = execute.GetExeCtx(ctx)
env = ternary.IFElse(execCtx.ExeCfg.Mode == vo.ExecuteModeRelease, vo.Online, vo.Draft)
appID = execCtx.ExeCfg.AppID
agentID = execCtx.ExeCfg.AgentID
connectorID = execCtx.ExeCfg.ConnectorID
userID = execCtx.ExeCfg.Operator
version = execCtx.ExeCfg.Version
)
if agentID != nil {
return nil, vo.WrapError(errno.ErrConversationNodesNotAvailable, fmt.Errorf("in the agent scenario, query conversation list is not available"))
}
if appID == nil {
return nil, vo.WrapError(errno.ErrConversationNodesNotAvailable, fmt.Errorf("query conversation list node, app id is required"))
}
templates, err := workflow.GetRepository().ListConversationTemplate(ctx, env, &vo.ListConversationTemplatePolicy{
AppID: *appID,
Version: ptr.Of(version),
})
if err != nil {
return nil, err
}
templateIds := make([]int64, 0, len(templates))
for _, template := range templates {
templateIds = append(templateIds, template.TemplateID)
}
staticConversations, err := workflow.GetRepository().MGetStaticConversation(ctx, env, userID, connectorID, templateIds)
if err != nil {
return nil, err
}
templateIDToConvID := slices.ToMap(staticConversations, func(conv *entity.StaticConversation) (int64, int64) {
return conv.TemplateID, conv.ConversationID
})
var conversationList []conversationInfo
for _, template := range templates {
convID, ok := templateIDToConvID[template.TemplateID]
if !ok {
convID = 0
}
conversationList = append(conversationList, conversationInfo{
conversationName: template.Name,
conversationId: strconv.FormatInt(convID, 10),
})
}
dynamicConversations, err := workflow.GetRepository().ListDynamicConversation(ctx, env, &vo.ListConversationPolicy{
ListConversationMeta: vo.ListConversationMeta{
APPID: *appID,
UserID: userID,
ConnectorID: connectorID,
},
})
if err != nil {
return nil, err
}
for _, conv := range dynamicConversations {
conversationList = append(conversationList, conversationInfo{
conversationName: conv.Name,
conversationId: strconv.FormatInt(conv.ConversationID, 10),
})
}
resultList := make([]any, len(conversationList))
for i, v := range conversationList {
resultList[i] = map[string]any{
"conversationName": v.conversationName,
"conversationId": v.conversationId,
}
}
return map[string]any{
"conversationList": resultList,
}, nil
}

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