Compare commits

...

1250 Commits

Author SHA1 Message Date
hjlarry
6134f46c2a fix(api): propagate audio stream closure to providers 2026-09-24 11:08:48 +08:00
非法操作
bd08f6b0e3 refactor(api): migrate installed app audio to shared runtime (#42056) 2026-09-24 02:56:38 +00:00
Stephen Zhou
8785f45e93 chore(deps): enable automatic pnpm deduplication (#42862) 2026-09-24 02:49:56 +00:00
落尘
5df8a4c89c fix(api): close failed SSE connection before MCP fallback (#42761) 2026-09-24 02:44:23 +00:00
Stephen Zhou
8300811b8c perf(web): replace runtime pinyin with provider initials lookup (#42861) 2026-09-24 02:32:05 +00:00
Stephen Zhou
28ed561740 ci(web): highlight Vinext bundle size changes (#42858) 2026-09-24 02:04:37 +00:00
非法操作
ff27ef544e refactor(web): migrate account name and avatar updates to profile API (#42843) 2026-09-24 02:02:50 +00:00
Stephen Zhou
7feebe3405 fix(web): defer Amplitude SDK loading until analytics consent (#42857) 2026-09-24 01:48:27 +00:00
Stephen Zhou
2f9fa672d0 ci(web): skip bundle comments for minor size changes (#42856) 2026-09-24 01:28:45 +00:00
非法操作
70e973b9ab refactor(api): isolate installed app messages (#42016) 2026-09-24 01:08:56 +00:00
Nehal kanak kuniyil thottathil
580a4165f2 refactor(api): inject InstalledAppsListQuery via the decorator (#42769)
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
2026-09-23 20:21:11 +00:00
Dorian
7cc0898f58 refactor(models): pass session into Conversation.to_dict and Message.to_dict (#42834)
Co-authored-by: Dorian <dorian0326@users.noreply.github.com>
2026-09-23 19:47:44 +00:00
Stephen Zhou
e43ea0986e ci(web): report Vinext bundles using Rolldown analysis (#42849) 2026-09-23 14:06:57 +00:00
Stephen Zhou
e9550e50d0 fix(web): reduce chart and syntax highlighting bundles (#42845) 2026-09-23 12:32:31 +00:00
Sourav Rajvi
200da319f2 refactor(web): defer tldts load for MCP favicon URL blur (#42486) 2026-09-23 11:48:40 +00:00
非法操作
4144850837 refactor(api): use route decorators in trial controller (#42815) 2026-09-23 11:47:53 +00:00
Stephen Zhou
d3d3248e3b fix(web): lazy-load markdown chart and music renderers (#42840) 2026-09-23 11:32:00 +00:00
Weike Zhang
f832cff60c fix(api): keep creator access to datasets created via console upload and RAG pipeline (#42507)
Co-authored-by: weike-zhang <wangweike@gmail.com>
Co-authored-by: Crazywoola <100913391+crazywoola@users.noreply.github.com>
2026-09-23 11:27:54 +00:00
非法操作
53aa73e416 refactor(api): isolate installed app conversations (#41971) 2026-09-23 11:11:33 +00:00
Stephen Zhou
16100f839f fix(web): preserve i18n translation type safety (#42839) 2026-09-23 11:04:16 +00:00
Joel
67c0157d00 fix(web): improve dataset document segment accessibility (#42833)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-09-23 10:56:41 +00:00
Stephen Zhou
eb7235f17c refactor(web): generate type-safe i18n resources (#42835) 2026-09-23 10:25:38 +00:00
yyh
5f66253fab fix(explore): improve template preview states and accessibility (#42830) 2026-09-23 09:57:19 +00:00
zyileven
699489b95f fix(web): localize workflow statuses across logs and run details (#42699)
Co-authored-by: XiaoBa Yu <xb1823725853@gmail.com>
2026-09-23 09:34:51 +00:00
非法操作
d1391c2ed5 refactor(api): isolate installed app management (#41957) 2026-09-23 09:24:22 +00:00
Joel
518bd238f5 fix: improve document list accessibility (#42826) 2026-09-23 09:22:34 +00:00
XiaoBa
f6561d8cc0 fix(api): enforce dataset operation permissions (#42758) 2026-09-23 09:03:16 +00:00
Taranum Wasu
486bcb0b08 fix(tools): translate PluginNotFoundError to ToolProviderNotFoundError (#41837)
Co-authored-by: Taranum01 <taranumwasu@users.noreply.github.com>
2026-09-23 08:59:58 +00:00
yyh
236b90a90c fix(home): search templates by name and description (#42828) 2026-09-23 08:57:02 +00:00
林玮 (Jade Lin)
6f636b2f49 fix(api): resolve provider credential aliases (#42790) 2026-09-23 08:53:03 +00:00
林玮 (Jade Lin)
11883543d1 fix(api): include node plugins in DSL dependencies (#42816) 2026-09-23 08:53:02 +00:00
Byron.wang
8b7f903cff refactor(api): centralize API key management (#42489)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-23 08:38:48 +00:00
yyh
1a09485013 chore(deps): upgrade shadcn lint and cn (#42818) 2026-09-23 08:35:55 +00:00
Wu Tianwei
efac2084e1 fix(workflow): sync imported graph with collaboration state (#42817) 2026-09-23 08:31:05 +00:00
Stephen Zhou
7f4a5c2413 refactor(web): load and stream translation namespaces on demand (#42812) 2026-09-23 08:30:08 +00:00
非法操作
f0078b7935 refactor(api): migrate installed app workflow and task control (#41912) 2026-09-23 08:25:37 +00:00
Joel
14d8eecea6 fix: improve dataset settings accessibility (#42796) 2026-09-23 08:19:22 +00:00
非法操作
bc94f7ead5 refactor(api): migrate installed app chat generation (#41905) 2026-09-23 08:01:39 +00:00
Neo1Noir
0a428a52e6 fix(web): remove a Code node output variable once the removal is confirmed (#42678) 2026-09-23 07:52:25 +00:00
非法操作
3bf5f2c47e refactor(api): isolate installed app completion generation (#41894) 2026-09-23 07:38:37 +00:00
Byron.wang
584b62adc5 fix(test): return inserted primary keys from add_texts (#41037) 2026-09-23 07:34:04 +00:00
LII
286ee05711 test: sync Redis subscriptions before publishing in test_topic_isolation (#42674)
Co-authored-by: GyberExperiment <themacroeconomicdao@tuta.io>
Co-authored-by: -LAN- <laipz8200@outlook.com>
2026-09-23 07:20:45 +00:00
-LAN-
3113343c4d refactor(rag): remove unused splitter tokenization (#42804) 2026-09-23 07:20:39 +00:00
-LAN-
1d76929d5a test(api): remove duplicate and implementation-only coverage (#42803) 2026-09-23 07:18:02 +00:00
dependabot[bot]
401490c033 chore(deps-dev): bump the dev group in /api with 15 updates (#42616)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: -LAN- <laipz8200@outlook.com>
2026-09-23 07:13:17 +00:00
Wu Tianwei
2fa4c57026 fix(web): gray preview-only agent cards (#42809) 2026-09-23 07:06:10 +00:00
Joel
79e962d664 fix: improve retrieval testing accessibility (#42785) 2026-09-23 07:04:49 +00:00
非法操作
9daff34bb5 refactor(api): migrate explore task stop admission (#41874) 2026-09-23 06:53:44 +00:00
yyh
4af693ccef fix(agent): initialize model in new agent snapshot (#42781) 2026-09-23 06:51:40 +00:00
Byron.wang
16d3302bff refactor(api): isolate OAuth device flow orchestration (#41361) 2026-09-23 06:36:48 +00:00
yyh
792d154608 refactor(web): remove redundant root locale fallback (#42791) 2026-09-23 06:11:43 +00:00
yyh
d08f554ff4 fix(app): align package import and export feedback (#42732) 2026-09-23 06:11:21 +00:00
Xin Zhang
e11a20ace4 fix: handle missing optional tracing dependencies (#42724) 2026-09-23 05:58:01 +00:00
yyh
1aafa481f3 test(e2e): align Agent v2 publish scenarios with guidance (#42787) 2026-09-23 05:35:43 +00:00
yyh
9c210ea670 fix(web): keep copy tooltip visible after click (#42789) 2026-09-23 05:35:28 +00:00
非法操作
0c36f4a104 refactor(api): remove legacy Baichuan prompt handling (#42779) 2026-09-23 05:12:46 +00:00
Stephen Zhou
5f899e428a fix(web): declare translation namespace dependencies (#42784) 2026-09-23 04:43:00 +00:00
yyh
232f29c355 chore: migrate workspace to Vitest 5 (#42782) 2026-09-23 04:13:42 +00:00
平衡世界的BOY
1bbb604d32 fix(web): make human input button style selector accessible (#42747) 2026-09-23 03:42:22 +00:00
Joel
960bea8bae fix(web): initialize empty agent models from workspace defaults (#42736) 2026-09-23 02:51:25 +00:00
Samuel K Quansah
654a49d1bc fix(otel): fix typo Convertions -> conventions in ext_otel resource comment (#42771) 2026-09-23 10:51:22 +08:00
非法操作
75ba8d1dc4 refactor(api): isolate installed app admission for saved messages and parameters (#41872) 2026-09-23 02:33:34 +00:00
yyh
d566192843 fix(web): upgrade Next.js to 16.3.6 for CVE-2026-94545 (#42772) 2026-09-23 02:22:15 +00:00
非法操作
c592610ed3 refactor: isolate saved message persistence (#41857) 2026-09-23 02:05:01 +00:00
Fatih Farhat Asshidiq
2480e5a274 docs: add Indonesian translation of README (docs/id-ID/README.md) (#42768)
Signed-off-by: imanecdoche <kazokuhairy@gmail.com>
2026-09-23 09:46:33 +08:00
yetuge
f547ef8726 docs(api): align node execution repository docstrings with signatures (#42763) 2026-09-23 09:41:49 +08:00
yetuge
639d410108 docs(tasks): align trigger task docstrings with signatures (#42765) 2026-09-23 09:41:39 +08:00
yyh
c9e59a5590 fix(web): use concise accessible names for infotips (#42751)
Co-authored-by: zhsama <torvalds@linux.do>
2026-09-23 01:02:21 +00:00
盐粒 Yanli
0f6a6227f5 feat(agent): add output routes to Agent v2 (#42599)
Co-authored-by: zhsama <torvalds@linux.do>
2026-09-22 21:28:07 +00:00
xuan
a068c47ea9 refactor: pass db.session explicitly to get_message_data (#37403) (#42753) 2026-09-22 15:22:41 +00:00
Miku_Tog
bacf0c761f refactor(api): pass active session to from_db in ApiToolManageService… (#42602) 2026-09-22 15:10:11 +00:00
Stephen Zhou
3a017e8ccc perf(ci): reuse coverage XML totals for API summary (#42745) 2026-09-22 11:31:21 +00:00
Stephen Zhou
1fc040183c chore: replace tsx with native Node TypeScript execution (#42742) 2026-09-22 11:23:20 +00:00
Byron.wang
5b6e850b25 refactor(api): thin console app controllers (#42511) 2026-09-22 11:23:19 +00:00
Byron.wang
ce2eaab9f7 refactor(api): move app-scoped end users behind application services (#41398) 2026-09-22 11:20:45 +00:00
Coding On Star
9badde4d65 fix(marketplace): support single-page carousel navigation (#42743)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
2026-09-22 11:12:18 +00:00
Xiyuan Chen
dfc3bbe513 feat(openapi): publish the op catalog, per-mode run ops and server-built hints on /openapi/v1 (#42487)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-22 10:44:50 +00:00
Stephen Zhou
dced031c95 fix(web): remove extra divider beside home navigation (#42738) 2026-09-22 10:29:03 +00:00
yyh
509f3407a5 chore: enforce erasable syntax in shared tsconfig (#42735) 2026-09-22 09:53:25 +00:00
Joel
963a28b497 fix: improve accessibility in legacy knowledge creation (#42726) 2026-09-22 09:37:03 +00:00
Coding On Star
db9b9b8993 fix: download preview images instead of opening a new tab (#42419)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: CodingOnStar <hanxujiang@jianghanxudeMacBook-Pro-2.local>
2026-09-22 09:11:19 +00:00
Asuka Minato
f117048f41 test: run message cleanup coverage on SQLite (#40715) 2026-09-22 08:29:00 +00:00
Yunlu Wen
1ea81f33ff chore(deps): bump setuptools, pydantic-settings, guzzle for CVE fixes (#42717)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-22 08:27:58 +00:00
yyh
54cc748a36 feat(agent): add publish success guidance (#42645)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jingyi <jingyi.qi@dify.ai>
2026-09-22 08:17:07 +00:00
yyh
0cc53efe06 refactor(files): migrate supported format queries to generated contracts (#42727) 2026-09-22 08:14:04 +00:00
yyh
8253405c0b refactor(plugins): migrate readme queries to generated contracts (#42725) 2026-09-22 08:14:03 +00:00
wangxiaolei
7ed4ab0b32 chore: remove app and dataset detail rbac check (#42729) 2026-09-22 08:08:24 +00:00
Coding On Star
c3d5b5fa4d fix(web): open Marketplace details externally for self-hosted editions (#42576)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
2026-09-22 08:00:15 +00:00
yyh
a78f716216 fix(ui): preserve Base UI callback and ref contracts (#42721) 2026-09-22 07:31:59 +00:00
yyh
c1316dd30a refactor(web): migrate custom React icons to Iconify CSS (#42716) 2026-09-22 07:10:38 +00:00
非法操作
27ea5e5790 refactor(api): migrate trial audio through shared app audio (#42672) 2026-09-22 06:42:30 +00:00
yyh
cb044daf60 refactor(web): use standard sizes for icon actions (#42684) 2026-09-22 06:20:45 +00:00
yyh
26d4b9ead8 refactor(datasources): migrate catalog consumers to generated queries (#42713) 2026-09-22 05:55:00 +00:00
yyh
67ab356116 refactor(explore): migrate catalog consumers to generated queries (#42702) 2026-09-22 05:55:00 +00:00
yyh
4192299ad4 refactor(plugins): migrate agent strategy queries to generated contracts (#42696) 2026-09-22 05:54:59 +00:00
yyh
6d358eb680 refactor(plugins): migrate endpoint queries to generated contracts (#42685) 2026-09-22 05:54:58 +00:00
Stephen Zhou
f7dbed5d86 ci: reduce Redis middleware readiness polling delay (#42710) 2026-09-22 05:16:23 +00:00
Stephen Zhou
93c8b0fe18 ci: prepare core E2E browsers alongside web builds (#42706) 2026-09-22 05:16:23 +00:00
Stephen Zhou
ab666bc448 ci: avoid redundant E2E dependency cache saves (#42695) 2026-09-22 05:16:22 +00:00
非法操作
2da0db7df5 refactor(api): isolate trial app and workflow detail previews (#42659)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-22 05:03:49 +00:00
Yunlu Wen
6f61401a91 feat(agent): export agent trajectory with a separated tracer (#42651) 2026-09-22 04:35:33 +00:00
yyh
404ba6a805 refactor(web): simplify equivalent regular expressions (#42703) 2026-09-22 04:08:37 +00:00
yyh
7a590a5b41 refactor: clear small lint suppression scopes (#42701) 2026-09-22 04:08:36 +00:00
Stephen Zhou
01b64254de chore: revert "chore(deps): upgrade vinext and simplify CSS asset aliases" (#42700) 2026-09-22 04:03:42 +00:00
yyh
fe09df3afb refactor(web): use Number.isNaN for numeric guards (#42692) 2026-09-22 03:51:14 +00:00
yyh
1ff8a5ff4f refactor(web): replace constructor parameter properties (#42691) 2026-09-22 03:51:13 +00:00
yyh
a50918dfe8 refactor(web): consolidate duplicate type imports (#42690) 2026-09-22 03:51:13 +00:00
yyh
86cfb29b28 refactor(web): replace suppressed string enums with const objects (#42689) 2026-09-22 03:51:12 +00:00
Neo1Noir
a7b1151a2c fix(web): persist reranking_enable for Hybrid Search datasets (#42563) 2026-09-22 03:38:01 +00:00
非法操作
89ffe391be refactor(api): isolate trial preview queries and admission (#42644) 2026-09-22 03:35:02 +00:00
Stephen Zhou
6fa4c9458a chore(deps): upgrade vinext and simplify CSS asset aliases (#42688) 2026-09-22 03:33:25 +00:00
Xx.Zhao
139ce49430 fix(web): refresh provider credentials after editing (#42679) 2026-09-22 03:20:17 +00:00
非法操作
f25c4a3eaf refactor(api): share suggested questions runtime for trial apps (#42635) 2026-09-22 03:10:37 +00:00
yyh
befb18a86f refactor(ui): align Infotip hints and enforce content styling contracts (#42682) 2026-09-22 03:09:19 +00:00
yyh
bfcd0fdb39 refactor(ui): expose compound Infotip primitives and migrate consumers (#42681) 2026-09-22 03:09:18 +00:00
非法操作
206cf8fb94 refactor(api): isolate trial file upload admission and actors (#42625) 2026-09-22 02:43:22 +00:00
非法操作
a6b41ac6d1 refactor(api): isolate trial generation admission and execution (#42130) 2026-09-22 01:55:33 +00:00
Hao Sun
151680eef9 ci: add deterministic pull request size labels (#42615)
Co-authored-by: surbadest <surbadest@users.noreply.github.com>
2026-09-22 01:40:46 +00:00
chenshiyang
d6491af103 fix(web): resolve tilde-alias SVG URLs at build time (#42654)
Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com>
2026-09-21 13:33:07 +00:00
Stephen Zhou
11be5332bb refactor(i18n): align analysis with Vite environment graphs (#42656) 2026-09-21 13:17:54 +00:00
林玮 (Jade Lin)
a625c62a1d fix(web): skip parameter rules for unavailable models (#42660) 2026-09-21 12:33:26 +00:00
yyh
fb2dba0cf7 chore(web): enforce tooltip styling with shadcn lint (#42667) 2026-09-21 11:45:05 +00:00
yyh
e811c23d37 fix: normalize field label spacing (#42670) 2026-09-21 11:43:01 +00:00
yyh
4ee8095161 fix: correct schedule trigger labels and theme colors (#42665) 2026-09-21 10:47:05 +00:00
Joel
8fb2741963 fix(web): preserve model warnings when reset is unavailable (#42663) 2026-09-21 10:32:56 +00:00
yyh
905afb5be1 fix(ui): align tooltip surfaces with design tokens (#42661) 2026-09-21 10:31:07 +00:00
Joel
f7b594bf79 fix: keep unset agent web app descriptions no fallback (#42662) 2026-09-21 10:22:11 +00:00
zxhlyh
863ce1d70d fix(skills): fix import, tags, duplication, model selection, and TXT icons (#42653) 2026-09-21 09:57:39 +00:00
Ultronen
9089ed40b9 refactor(dataset): replace available_segment_count property with get_available_segment_count(session) (#42523)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
2026-09-21 09:57:27 +00:00
Stream
ad7849a82b chore(api): add union-type and default-param rules to AGENTS.md (#42652) 2026-09-21 09:49:30 +00:00
Wu Tianwei
9a0961a4a9 fix(web): align access point permissions (#42648) 2026-09-21 08:33:38 +00:00
yyh
1dfa8733a5 fix(i18n): avoid hiding the app during hydration (#42647) 2026-09-21 08:15:40 +00:00
Joel
05df951373 fix: rag pipeline accessibility (#42589) 2026-09-21 08:15:03 +00:00
Stephen Zhou
bbaaaf980f fix(web): align icon picker with updated design (#42636) 2026-09-21 07:59:11 +00:00
zyssyz123
af4f19840c feat(agent): record E2B sandbox runtime usage (#42554) 2026-09-21 07:11:12 +00:00
林玮 (Jade Lin)
1fa3245e83 fix(app): include image icons in app packages (#42626) 2026-09-21 06:32:21 +00:00
dependabot[bot]
6ba3d29f07 chore(deps): bump google-cloud-aiplatform from 2.1.0 to 2.1.3 in /api in the google group (#42610)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-21 06:18:59 +00:00
yyh
bf100beb97 refactor(web): let primitives own local interaction state (#42633) 2026-09-21 06:17:07 +00:00
yyh
22aff66cc0 fix(dify-ui): preserve state-aware styling callbacks (#42631) 2026-09-21 05:56:21 +00:00
林玮 (Jade Lin)
11038c5936 fix(agent): auto-rename duplicate package imports (#42623) 2026-09-21 05:54:22 +00:00
林玮 (Jade Lin)
99df0ca07f fix(api): mark unavailable legacy DSL agent assets as missing (#42622) 2026-09-21 05:54:14 +00:00
Sourav Rajvi
f9129bc500 fix(api): grant in-run tool files to later workflow nodes (#41300) 2026-09-21 05:39:04 +00:00
盐粒 Yanli
2a66bf6f09 refactor(workflow): prepare inputs by entry node type 🤖🤖🤖 (#42595) 2026-09-21 05:26:23 +00:00
Baki Burak Öğün
ee0efe0bdb fix(i18n): check placeholder and tag parity in i18n:check (#42532)
Signed-off-by: Baki Burak Öğün <63836730+bakiburakogun@users.noreply.github.com>
2026-09-21 05:17:54 +00:00
yyh
679785b67e fix(web): align app creation and pricing separators (#42629) 2026-09-21 05:12:47 +00:00
Stephen Zhou
57c140144a ci(web): check image compression savings and support local fixes (#42618) 2026-09-21 05:10:06 +00:00
zyssyz123
d2c2e39ba7 fix(agent): preserve sandbox file errors in stdout 🤖🤖🤖 (#42624) 2026-09-21 04:34:28 +00:00
yyh
3e787d11cc feat(ui): replace legacy Divider with semantic Separator (#42627) 2026-09-21 04:28:29 +00:00
林玮 (Jade Lin)
e78ce4db64 feat(app): package inline agents in workflow and chatflow exports (#42579) 2026-09-21 02:54:43 +00:00
yyh
efd1fb0967 fix(web): migrate legacy plain tabs to Dify UI (#42620) 2026-09-21 02:51:58 +00:00
dependabot[bot]
08a9d40096 chore(deps): bump flask-compress from 1.24 to 1.25 in /api in the flask group (#42609)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-21 02:28:09 +00:00
Sourav Rajvi
126dee3da6 chore(web): optimize in-site notification header background asset (#42479)
Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com>
2026-09-21 02:13:42 +00:00
dependabot[bot]
bfa243aecf chore(deps): bump the github-actions-dependencies group with 4 updates (#42612)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-21 01:51:00 +00:00
github-actions[bot]
38be7489ec chore(i18n): sync translations with en-US (#42600)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
2026-09-21 00:54:09 +00:00
yyh
e2bdeec273 refactor(web): encapsulate agent version restore interaction (#42597) 2026-09-20 14:14:57 +00:00
Stephen Zhou
de8433277f refactor(i18n): scope and validate sign-in translation namespaces (#42506) 2026-09-20 14:14:06 +00:00
Stephen Zhou
dace5ba264 refactor(i18n): use shared namespaces for reusable UI (#42504) 2026-09-20 14:14:05 +00:00
Stephen Zhou
bf189daea9 ci(api): balance unit shards with shared test timings (#42593) 2026-09-20 13:42:38 +00:00
Stephen Zhou
e6f0c8908c ci: run controllers separately with two unit test shards (#42587) 2026-09-20 13:42:37 +00:00
Stephen Zhou
7f018fce7b ci: shard API unit test files before collection (#42586) 2026-09-20 13:42:36 +00:00
Stephen Zhou
6f61b91b6b test(api): avoid real waits in retry and collection tests (#42585) 2026-09-20 13:42:35 +00:00
Stephen Zhou
57b9d73efe ci: retain API unit test reports (#42583) 2026-09-20 13:42:34 +00:00
Asuka Minato
3a0acfedbb test: clarify container boundaries for storage and Redis (#40714) 2026-09-20 10:39:00 +00:00
Xin Zhang
d96867fa58 chore(deps): bump gitpython from 3.1.59 to 3.1.62 in /api (#42584) 2026-09-20 10:09:43 +00:00
Joel
bc38ce59f7 fix: load Agent text-to-speech voices from the dedicated endpoint (#42575)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: 林玮 (Jade Lin) <linw1995@icloud.com>
2026-09-20 10:03:25 +00:00
XiaoBa
039bf03a31 fix(api): align dataset RBAC permission checks (#42392)
Co-authored-by: Crazywoola <100913391+crazywoola@users.noreply.github.com>
2026-09-20 09:39:32 +00:00
Joel
90f0494c81 fix(e2e): confirm Agent v2 version restoration tests (#42581) 2026-09-20 09:30:54 +00:00
Stephen Zhou
d02666d632 ci: reduce API test middleware readiness and shutdown waits (#42578) 2026-09-20 09:21:04 +00:00
林玮 (Jade Lin)
8e5b097011 fix(api): remove omitted tenant default models (#42572)
Co-authored-by: Joel <iamjoel007@gmail.com>
2026-09-20 09:19:51 +00:00
林玮 (Jade Lin)
2a3cf7edf5 feat(app): default exports to ifpkg across app types (#42552) 2026-09-20 08:36:40 +00:00
非法操作
ecadd45886 fix(workflow): preserve LLM prompts and model changes in collaboration (#42124) 2026-09-20 08:25:01 +00:00
Stephen Zhou
90df30cce9 ci: run API integration tests without sharding (#42573) 2026-09-20 08:18:18 +00:00
Wu Tianwei
2590d90769 fix(web): align Web App launch permissions (#42574) 2026-09-20 07:58:58 +00:00
Bruce-Yii
b2a5b3beba fix(rag): preserve Notion mention and equation text (#42171) 2026-09-20 07:53:31 +00:00
Stephen Zhou
6bb5e525d3 ci: speed up integration database cleanup with tmpfs (#42558) 2026-09-20 07:26:05 +00:00
Joel
8dd33b88d8 feat: add agent version restore (#42567) 2026-09-20 07:23:37 +00:00
yyh
dec1658200 fix(workflow): preserve change node menu semantics and focus (#42568) 2026-09-20 07:04:42 +00:00
林玮 (Jade Lin)
b53e5f23c6 fix(agent): require paid plan for version restore (#42551) 2026-09-20 06:12:32 +00:00
yyh
e95698d2cd fix(web): keep workflow panel resize focus indicator continuous (#42565) 2026-09-20 06:05:47 +00:00
Wu Tianwei
78d338fe11 fix(dataset): disable chunk actions without edit permission (#42561) 2026-09-20 06:00:16 +00:00
Joel
77cdee1f5b chore: improve accessibility on the datasets list page (#42512) 2026-09-20 05:34:27 +00:00
yyh
6f9cb382c4 refactor(web): standardize lazy ref initialization (#42562) 2026-09-20 05:15:48 +00:00
Sourav Rajvi
f16702de80 fix(web): align nuqs peer context for nuqs-jotai dev resolution (#42517) 2026-09-20 04:31:13 +00:00
Stephen Zhou
3a88bc70a3 chore(ci): speed up E2E preparation and API coverage reporting (#42549) 2026-09-20 04:16:36 +00:00
Asuka Minato
37bece696c chore: use pydantic to verify (#38365)
Co-authored-by: Byron.wang <byron@dify.ai>
2026-09-20 03:42:47 +00:00
yyh
b8a5c112a0 docs: clarify contribution guidelines (#42550) 2026-09-20 03:26:09 +00:00
Stephen Zhou
969a831b5b refactor(i18n): report route namespace dependencies (#42503) 2026-09-20 03:13:49 +00:00
yyh
61dc68c3a7 chore(deps): upgrade foxact and Vite+ (#42546) 2026-09-20 02:52:47 +00:00
william
4da38ea322 chore(models): remove redundant server_default from trigger models (#42519) 2026-09-20 02:47:05 +00:00
william
77d701ae81 chore(models): remove redundant server_default from oauth, source, onboarding and comment models (#42520) 2026-09-20 02:46:17 +00:00
william
6523584762 chore(models): remove redundant server_default from provider, tools and workflow models (#42521) 2026-09-20 02:42:06 +00:00
william
214aacd780 chore(models): remove redundant server_default from account models (#42524) 2026-09-20 02:40:35 +00:00
william
d913f3dd0e chore(models): remove redundant server_default from dataset models (#42525) 2026-09-20 02:38:59 +00:00
william
62e1c53c84 chore(models): remove redundant server_default from app, message and site models (#42526) 2026-09-20 02:36:19 +00:00
yyh
9bd86bf474 fix(agent): correct version history e2e locator and menu styling (#42533) 2026-09-20 02:16:20 +00:00
dependabot[bot]
af36832bbf chore(deps): bump anyio from 4.13.0 to 4.14.2 in /dify-agent (#42527)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-20 02:02:44 +00:00
dependabot[bot]
fad2a6c96f chore(deps): bump anyio from 4.14.1 to 4.14.2 in /api (#42528)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-20 02:02:19 +00:00
Xiyuan Chen
a24135ae83 fix(account): make case-fallback email lookup work in both directions (#42541) 2026-09-20 02:01:55 +00:00
Jeremy Schoemaker
ad9b85932b fix(api): report Service API knowledge list has_more from the page the query served (#42531) 2026-09-20 01:51:25 +00:00
Stephen Zhou
9df3e45719 chore: remove redundant CLAUDE.md symlinks (#42544) 2026-09-20 01:47:56 +00:00
yyh
9e13e339b8 feat(ui): add reusable breadcrumbs and migrate Web navigation (#42534) 2026-09-20 01:40:49 +00:00
Asuka Minato
697a244280 test: migrate core service residuals sessions and ORM models to SQLite (#40547)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-19 09:27:05 +00:00
Asuka Minato
3a9636faad test: migrate console snippets sessions and ORM models to SQLite (#40565) 2026-09-19 09:26:43 +00:00
Asuka Minato
ceacf275b1 test: migrate application pipelines sessions and ORM models to SQLite (#40510) 2026-09-19 09:26:25 +00:00
Asuka Minato
ce5dc6f314 chore: add some type (#37173) 2026-09-19 09:26:15 +00:00
Asuka Minato
b520bef0b1 test: move plugin lifecycle coverage to SQLite (#40713)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-19 09:25:48 +00:00
Asuka Minato
d7630db953 chore: port WorkflowNodeExecutionOffload to TypeBase (#35633)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-19 09:25:31 +00:00
Ingram Z
fae098c6c0 fix(retention): harden V2 archive deletion recovery (#41358) 2026-09-19 07:54:35 +00:00
JessYan
5568aa0ea5 refactor(models): pass session into App.tenant (#42518)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 18:31:45 +00:00
yyh
f12a876730 fix(web): prevent compliance download actions from shrinking (#42515) 2026-09-18 10:32:47 +00:00
yyh
fbe69a114c fix(ui): standardize icon-only controls (#42513) 2026-09-18 10:17:43 +00:00
Sourav Rajvi
d39d9ddb74 fix: wire UV_CACHE_DIR for plugin_daemon (Fixes #42433) (#42481) 2026-09-18 09:42:43 +00:00
Yunlu Wen
dbeebfc15d refactor: reduce ambiguity in mcp queries (#42395)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-18 09:17:36 +00:00
林玮 (Jade Lin)
f495581a05 fix: preserve default model display after plugin removal (#42498)
Co-authored-by: Joel <iamjoel007@gmail.com>
2026-09-18 09:17:35 +00:00
林玮 (Jade Lin)
db6a4f5cd3 feat(agent): support exporting published versions (#42485)
Co-authored-by: Joel <iamjoel007@gmail.com>
2026-09-18 08:59:54 +00:00
Xiyuan Chen
bc0636c4db refactor(openapi): route /openapi/v1 auth by subject (#41420)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-18 08:42:53 +00:00
Joel
e1ebd90bbe fix: restore full-width OAuth client settings labels (#42508) 2026-09-18 08:40:46 +00:00
wangxiaolei
c4dbc4c830 feat: skill detail page draft content support export (#42502) 2026-09-18 08:18:15 +00:00
yyh
b43889821d feat(agent): align app package import and export experience (#42497) 2026-09-18 07:54:35 +00:00
yyh
a81f6fed67 fix(i18n): complete missing Azerbaijani translations (#42495) 2026-09-18 07:38:54 +00:00
yyh
5fa40f1a5f fix(web): keep agent configure model selection explicit (#42488) 2026-09-18 07:35:05 +00:00
Joel
13c12c1b38 fix(web): show full unavailable model names on hover (#42492) 2026-09-18 07:15:22 +00:00
Joel
a60167b390 feat(web): add copy action to error toasts (#42480)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-09-18 07:02:22 +00:00
wangxiaolei
41bc173636 chore: remove skill export dialog (#42491) 2026-09-18 06:50:39 +00:00
Xiyuan Chen
3d436a7e31 refactor(openapi): delete the dead bearer→handler auth adapter (#41311) 2026-09-18 06:27:49 +00:00
Mesrop Harutyunyan
104f3b239b refactor(api): merge duplicated model builders in unit tests (#41549)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-18 05:10:25 +00:00
yyh
3b8fc9c9b6 refactor(i18n): colocate runtime and locale resources (#42482) 2026-09-18 04:13:14 +00:00
Keith
21d5e4e020 refactor(models): pass session into WorkflowRun accessors and drop dead ones (#42034) 2026-09-18 04:12:28 +00:00
Yunlu Wen
b85102c8e8 fix: normalize json part before deduplicating (#42196) 2026-09-18 03:56:45 +00:00
yyh
48189fedb4 refactor(ui): separate spinner primitives from loading layouts (#42473) 2026-09-18 03:43:55 +00:00
Stephen Zhou
d2fc434dc9 refactor(i18n): check unused translations in Vite builds (#42467) 2026-09-18 03:25:40 +00:00
Joel
331e618961 chore: improve agent web app accessibility (#42475) 2026-09-18 03:23:49 +00:00
william
26831445d0 refactor(api): update tag name with a single UPDATE statement (#42449) 2026-09-18 03:22:44 +00:00
yyh
d43594822b refactor(i18n): separate runtime and metadata boundaries (#42471) 2026-09-18 03:08:44 +00:00
Stephen Zhou
228541c15d refactor(web): replace emoji-mart with self-hosted Frimousse picker (#42469) 2026-09-18 03:04:57 +00:00
yyh
6d88df5d7c docs: clarify Dify Enterprise offering (#42474) 2026-09-18 02:58:11 +00:00
yyh
6024141c26 fix(i18n): normalize locale inputs before language selection (#42466) 2026-09-18 02:52:26 +00:00
dependabot[bot]
7069c7127b chore(deps): bump soupsieve from 2.8.4 to 2.9 in /api (#42455)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-18 02:42:28 +00:00
yyh
bfa4a8b5e4 refactor(web): remove obsolete maintenance notice (#42464) 2026-09-18 02:31:51 +00:00
Asuka Minato
a504dfee27 ci: Update pyrefly dependency version to 1.3.0 (#42445) 2026-09-18 02:16:47 +00:00
Wu Tianwei
1762181e58 fix(webapp): submit JSON object inputs as objects (#42431) 2026-09-18 02:01:52 +00:00
yyh
9a5bd65e33 fix(web): center agent detail loading indicator (#42457) 2026-09-18 02:00:14 +00:00
Jamal Kamaladdinoglu
f418261c50 feat(i18n): add Azerbaijani (az-AZ) translation (#42291) 2026-09-18 01:39:24 +00:00
yyh
c4d4a42b4c docs: update README enterprise links (#42454) 2026-09-18 01:27:18 +00:00
Dorian
ad9a5be560 refactor(models): pass session into MCPToolProvider.load_user (#42432) 2026-09-17 13:37:27 +00:00
Syrinx.
40f859450a refactor(files): dep-inject query params with @model_validate (#42314) 2026-09-17 12:59:55 +00:00
林玮 (Jade Lin)
223039128f feat(agent): support package imports from external URLs (#42425) 2026-09-17 12:54:40 +00:00
Joel
35e4e4f6bd chore: improve accessibility of basic app orchestration pages (#42428) 2026-09-17 12:11:14 +00:00
zyssyz123
d21518f8fe fix(agent): keep composer reads free of draft mutations (#42427) 2026-09-17 12:02:45 +00:00
wangxiaolei
3edbba21b4 feat: dataset automatic_include_workspace_members should be false (#42430) 2026-09-17 11:00:49 +00:00
wangxiaolei
bfd7ac015f feat: skill support duplicate and export use the latest draft (#42424)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-17 09:52:25 +00:00
Joel
99ba5a57de fix(home): match banner image aspect ratio to blog (#42422) 2026-09-17 09:26:13 +00:00
yyh
ee151ee952 fix(e2e): align Agent node selection and workflow triggers (#42421) 2026-09-17 09:20:08 +00:00
Joel
f384f9e7cf chore: improve accessibility of app logs (#42417)
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-09-17 09:02:59 +00:00
Coding On Star
e18c171f74 fix(marketplace): fix preview overlay and image clipboard actions (#42411)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
2026-09-17 08:35:49 +00:00
yyh
b107d5abd4 fix(workflow): restore node focus and standardize panel controls (#42410) 2026-09-17 08:01:15 +00:00
Keith
bba566540d refactor(models): drop implicit-session getters on Conversation/Message inputs and model_config (#42335) 2026-09-17 07:59:00 +00:00
林玮 (Jade Lin)
3d7f6f4496 feat(agent): import roster packages through App routes (#42189)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-17 07:28:18 +00:00
Joel
5e76fc45b7 chore: improve workflow editor accessibility (#42403) 2026-09-17 07:21:44 +00:00
wangxiaolei
7dad80b1f9 fix: fix upload skill zip has an empty directory (#42407) 2026-09-17 07:17:45 +00:00
林玮 (Jade Lin)
93895fa7fb feat(agent): add roster package export foundation (#42066) 2026-09-17 07:05:02 +00:00
yyh
5e9017dbb8 refactor(web): finish legacy input migration (#42405) 2026-09-17 07:04:27 +00:00
yyh
81aa065608 chore: assign frontend tooling code owners and remove invalid owner (#42409) 2026-09-17 06:55:55 +00:00
Stephen Zhou
81a65bd750 chore(deps): upgrade compatible workspace dependencies (#42397)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-17 06:23:43 +00:00
invalid w
cddfe06a86 chore(web): add tooltips to workflow header buttons (#42307) 2026-09-17 06:02:51 +00:00
wangxiaolei
0825e6ca51 fix: fix some skill issues (#42389) 2026-09-17 05:54:38 +00:00
Mesrop Harutyunyan
153cc72f17 fix(web): correct Slovenian display language name (#42361) 2026-09-17 05:53:04 +00:00
JessYan
28e2682986 refactor(models): pass session into App.app_model_config and App.workflow (#42190)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 05:43:18 +00:00
yyh
64deb9c421 docs: update README agent feature description (#42399) 2026-09-17 05:39:40 +00:00
yyh
a14ebae6a4 refactor(web): use Zod validate for boolean form checks (#42398) 2026-09-17 05:20:07 +00:00
yyh
d528dea83e fix(web): remove ineffective Tailwind classes (#42394) 2026-09-17 04:55:31 +00:00
Stephen Zhou
8006e8e13d ci: reduce API unit tests to three shards (#42390) 2026-09-17 04:04:25 +00:00
Stephen Zhou
1203aa72c1 perf(api): avoid duplicate Redis client scans in unit tests (#42379) 2026-09-17 03:45:32 +00:00
Syrinx.
ff18b6de09 refactor(console): dep-inject the snippet draft-variable query with @model_validate (#42315) 2026-09-17 03:42:31 +00:00
Syrinx.
66bf75d1d8 refactor(models): pass session into AppMCPServer.generate_server_code (#42316) 2026-09-17 03:41:45 +00:00
Eddy ZHANG
f66773b63c refactor(api): dep-inject the last three GET query models with @model_validate (#42318) 2026-09-17 03:40:50 +00:00
Keith
4afc3cbd4f refactor(models): drop dead to_dict serializers and remaining global-session accessors on dataset models (#42324)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-17 03:39:04 +00:00
xiaodu55
8b3c4db9ac refactor(models): pass session into recommended-app and trial-record accessors (#42325) 2026-09-17 03:38:18 +00:00
Stephen Zhou
f7ed1c0176 fix(ci): preserve system npx in shared web setup (#42382) 2026-09-17 03:26:13 +00:00
陈志谦
47a28bf262 fix(api): align the datasource docstrings with their signatures (#42369)
Signed-off-by: simpleqt <89645338+simpleqt@users.noreply.github.com>
2026-09-17 03:13:48 +00:00
coderdailyone
fea6b6e999 fix(rag): derive url file suffix from content-type media type in load_from_url (#42364)
Co-authored-by: coderdailyone <codecookie@proton.me>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 02:56:58 +00:00
Stephen Zhou
e58e5b0879 ci: shard API unit tests across runners (#42374) 2026-09-17 02:50:31 +00:00
陈志谦
a9227cf6f9 fix: remove duplicated word in the code-regeneration prompt (#42368)
Signed-off-by: simpleqt <89645338+simpleqt@users.noreply.github.com>
2026-09-17 02:35:02 +00:00
Stephen Zhou
bffaccbc95 fix(ci): allow Codecov uploads from fork PRs (#42376) 2026-09-17 02:31:54 +00:00
Pranav Agarwal
b721e438ba fix(api): use inclusive <= for completion-conversation end-time filter (#42321) 2026-09-17 01:43:46 +00:00
yyh
e6b13eaab9 chore: upgrade workspace dependencies and CI actions (#42372) 2026-09-17 01:35:43 +00:00
yyh
9e7856f22b test(web): tighten browser test admission and trim low-value coverage (#42373) 2026-09-17 01:19:03 +00:00
Asuka Minato
9c6c48b50b test: keep API token cache coverage on real Redis (#40712) 2026-09-16 13:41:22 +00:00
Asuka Minato
8b9f3016e6 test: remove duplicate integration-only coverage (#40711) 2026-09-16 13:41:21 +00:00
Asuka Minato
9e4f0fa23a test: isolate parameter extractor model fixtures (#40710) 2026-09-16 13:41:21 +00:00
Lukas Buck
38f9d85d5a fix(api): report has_more from the page the query served (#42323) 2026-09-16 07:45:48 +00:00
Xx.Zhao
6a0039812d fix(agent): validate workspace skill references on publish (#42299) 2026-09-16 03:27:00 +00:00
Sean Sun
a4d74f7c82 refactor(api): pass session explicitly into RBACResourceService (#37403) (#42248)
Co-authored-by: lyfuci <lyfuci11@gmail.com>
2026-09-15 12:45:34 +00:00
Liu Ziming
07dc8ebdbb feat(trace-aliyun): capture HTTP request snapshots and task node types (#42296) 2026-09-15 03:03:43 +00:00
Mesrop Harutyunyan
cb4d8ab5ea perf(api): batch dataset list detail fields (#42279) 2026-09-15 02:33:42 +00:00
Jinsong Zhou
6afe07f944 feat(dify-agent): add OpenShell runtime backend (#41076)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-14 13:05:16 +00:00
human
a5bc77eaa7 fix(api): delete offload objects outside the DB transaction (#42258) 2026-09-14 10:10:05 +00:00
Richard_0x
6b88a6e0c4 fix(workflow): remap assigner references on snippet insertion (#42229) 2026-09-14 08:55:32 +00:00
dxdx123
fe3600f149 fix(web): prevent dataset tag popup from triggering card navigation (#42269)
Co-authored-by: ding <dingxing@xd.com>
2026-09-14 08:52:14 +00:00
dependabot[bot]
b83f02be9a chore(deps): bump opentelemetry-propagator-b3 from 1.41.1 to 1.44.0 in /api in the opentelemetry group across 1 directory (#41123)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-14 08:51:48 +00:00
Priyansh Nandwana
04dd4d11ec fix(agent): allow a conversation to continue after a cancelled tool call (#41846)
Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai>
2026-09-14 08:44:56 +00:00
Weike Zhang
318964e4dc fix(api): compute annotation list has_more with the effective page size (#41876) 2026-09-14 03:58:22 +00:00
dependabot[bot]
51dd877de9 chore(deps): bump the github-actions-dependencies group with 2 updates (#42260)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-14 02:28:15 +00:00
CoralGarden52
5389f70719 fix(api): avoid per-row MAX(position) queries in batch segment import (#42233) 2026-09-14 02:11:23 +00:00
CoralGarden52
43ac0fce5c fix(rag): cap QA indexing preview results (#42205) 2026-09-13 13:25:14 +00:00
Bruce-Yii
79effdd498 fix(api): preserve literal NA in annotation CSV imports (#42221)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-12 04:55:53 +00:00
Bruce-Yii
7f7afc576c fix: ignore blank keyword rows in moderation limit (#42212) 2026-09-12 02:32:19 +00:00
KingPointer
085a79c748 chore(models): add Mapped annotations to EndUser and ApiToken (#42146)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-11 17:07:04 +00:00
Jyong
e7981a6f19 fix: allow Knowledge deployment to finish Celery drain (#42201) 2026-09-11 12:46:31 +00:00
Joel
bb02d3efcc fix: exclude installed plugins and localize marketplace search results (#42199) 2026-09-11 09:59:13 +00:00
小小涂涂
92d64db112 docs: sync zh-CN README — observability integrations and "Seeking help" section (#42161) 2026-09-11 09:29:52 +00:00
CoralGarden52
c6c891430c fix(rag): preserve CSV cell text in batch segment import (#42159) 2026-09-11 08:17:13 +00:00
Coding On Star
b769a3469b feat(web): report marketplace utm_source on OAuth registration (#42193)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
2026-09-11 08:14:31 +00:00
Sourav Rajvi
aa6dceeece fix(web): send model delete params as query string on DELETE (#42162)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-11 08:09:48 +00:00
Harsh Kashyap
4cc2f632b8 fix: parse Redis Sentinel and Cluster nodes safely (#42111) 2026-09-11 08:06:32 +00:00
CoralGarden52
5b58f51460 perf(api): batch indexing status segment counts (#42191) 2026-09-11 07:43:16 +00:00
yyh
0cdb9e82c0 fix(web): deduplicate server locale resolution (#42187) 2026-09-11 06:56:42 +00:00
yyh
122f95b976 fix(web): consolidate marketplace logo and correct public asset path (#42185) 2026-09-11 06:19:37 +00:00
yyh
2de83835cd refactor(web): migrate variable configuration inputs (#42179) 2026-09-11 06:16:12 +00:00
yyh
10658e428b fix(web): deduplicate server i18next initialization (#42183) 2026-09-11 06:03:44 +00:00
yyh
6c252e9059 refactor(web): migrate numeric variable constraints to NumberField (#42178) 2026-09-11 04:30:25 +00:00
Coding On Star
bad3a86816 feat(marketplace): use stable template UUID links in the Dify embed (#42172)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: CodingOnStar <hanxujiang@jianghanxudeMacBook-Pro-2.local>
2026-09-11 03:59:30 +00:00
Coding On Star
98d9298828 fix(web): use delivered cover_image for blog banners (#42173)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
2026-09-11 03:59:26 +00:00
Charles Yao
96c8cc4383 fix(mcp): stop returning opaque 500s from MCP connect/auth (#42177)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 03:46:23 +00:00
Joel
8e022cc3c1 fix: keep agent conversation opener state in sync after saving (#42175) 2026-09-11 03:45:46 +00:00
林玮 (Jade Lin)
cd61738f33 feat(api): normalize additional email providers (#42112) 2026-09-11 02:41:35 +00:00
Baki Burak Öğün
a3b2786b32 fix(i18n): translate the tr-TR labels still left in English (#41825)
Signed-off-by: Baki Burak Öğün <63836730+bakiburakogun@users.noreply.github.com>
2026-09-11 01:47:16 +00:00
Harsh Kashyap
a1bb317892 fix: strip keyword rows in keyword moderation (#42048) 2026-09-11 01:02:22 +00:00
yyh
f0d69e7b39 fix(web): align workflow and agent sidebar identity layouts (#42145) 2026-09-10 11:55:16 +00:00
Bond Zhu
c96bbe2e08 fix(ci): set up Node.js in sync-e2b-template job (#42138) 2026-09-10 09:59:00 +00:00
Joel
d441dd543e chore: restore grid and table accessibility (#42125) 2026-09-10 09:52:11 +00:00
Soroush Ahmadi
a425a127f5 fix(api): require exact match for CSRF whitelist paths (#42118)
Co-authored-by: soroush5 <soroush5@users.noreply.github.com>
2026-09-10 09:49:29 +00:00
wangxiaolei
66f1e6568e fix: fix sample_size not work (#42131) 2026-09-10 09:43:50 +00:00
Yunlu Wen
8387590ace chore: bump version to 1.17.1 (#42129)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-10 09:06:31 +00:00
yyh
2c206d2792 fix(web): delegate overlay initial focus to popup owners (#42121) 2026-09-10 08:25:48 +00:00
Stephen Zhou
b3c978caa6 fix(web): center settings dialog across viewport sizes (#42120) 2026-09-10 07:34:30 +00:00
Joel
1a415d1e02 fix(web): correct form labels, validation feedback, and keyboard navigation (#42096) 2026-09-10 07:30:50 +00:00
yyh
445b194938 fix(ui): let collapsible consumers own trigger styling (#42116) 2026-09-10 07:23:11 +00:00
yyh
44dac4a90d fix(web): preserve web apps navigation focus and disclosure semantics (#42113) 2026-09-10 06:26:54 +00:00
QuantumGhost
798fd5e4c0 fix(api): restore Human Input completion events (#42082) 2026-09-10 06:20:44 +00:00
zyssyz123
c3f603c4dc fix(agent): pin chatflow participant versions across config updates (#42105) 2026-09-10 06:10:16 +00:00
yyh
1dcfe38171 fix(dify-ui): preserve invalid focus colors and remove shadow transitions (#42108) 2026-09-10 05:09:30 +00:00
Asuka Minato
31dfa21f36 test: migrate RAG retrieval sessions and ORM models to SQLite (#40512) 2026-09-10 04:15:40 +00:00
Asuka Minato
3ecceb744b test: move mocked model and tool cases to unit tests (#40708) 2026-09-10 04:14:49 +00:00
Asuka Minato
a43583a4a0 test: migrate agent workspace and publishing sessions and ORM models to SQLite (#40506) 2026-09-10 04:13:53 +00:00
Asuka Minato
c529c3b2f1 test: migrate Service API app sessions and ORM models to SQLite (#40558) 2026-09-10 04:13:36 +00:00
Asuka Minato
e7cfe142bc test: migrate console dataset metadata sessions and ORM models to SQLite (#40520) 2026-09-10 04:12:43 +00:00
Asuka Minato
4e0d7f7f9b test: migrate custom tool provider ORM model to real entity (#40653) 2026-09-10 04:11:52 +00:00
Asuka Minato
d201dc1f09 test: migrate summary index sessions and ORM models to SQLite (#40505) 2026-09-10 04:11:28 +00:00
yyh
f1da874b53 fix(web): remove local search delay and clean up component lifecycles (#42102) 2026-09-10 03:39:46 +00:00
wangxiaolei
63bd623a63 fix: fix agent.id mismatch lead permission keys are empty (#42095) 2026-09-10 03:18:36 +00:00
yyh
3e44b51d7a fix(web): restore content-sized search shortcut keycap (#42097) 2026-09-10 03:15:49 +00:00
yyh
f1d88f79f4 chore(web): upgrade React to 19.3 and adopt browser-only rendering (#42093) 2026-09-10 03:08:38 +00:00
Stephen Zhou
5f02726207 fix(web): stabilize install footer and shortcut initial rendering (#42088)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-09-10 02:40:56 +00:00
Zhu Lei
d4d2a4a988 fix(web): prefetch skill feature flag for initial navigation (#41731)
Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com>
2026-09-10 02:07:33 +00:00
Tv
90aa225cf0 docs(api): fix working directory instructions for running tests (#42085) 2026-09-10 01:28:15 +00:00
-LAN-
dfe27fc75b chore(codeowners): assign dify-agent to Yanli (#42091) 2026-09-10 00:53:36 +00:00
Evan
8bdf01be3b refactor(api): remove legacy end user type fallback (#39662) 2026-09-09 20:16:48 +00:00
Stephen Zhou
7f59b65b6a feat(nuqs-jotai): bridge nuqs URL state into Jotai (#42086) 2026-09-09 16:11:08 +00:00
Asuka Minato
608b1d10e0 test: split CodeNode validation from sandbox coverage (#40707) 2026-09-09 14:45:16 +00:00
yyh
5feddcb96a fix(ci): refresh E2E runtime path filters (#42079) 2026-09-09 12:04:19 +00:00
Joel
bce802c61f fix: prevent note bold shortcut from toggling the sidebar (#42078) 2026-09-09 10:31:54 +00:00
Joel
9e6c0c20d2 chore: fit the app canvas when create from DSL and preview (#42073)
Co-authored-by: 林玮 (Jade Lin) <linw1995@icloud.com>
2026-09-09 10:22:33 +00:00
dependabot[bot]
e7eb030109 chore(deps): bump the google group across 1 directory with 2 updates (#41121)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-09 10:13:32 +00:00
yyh
40955e72a9 fix(web): align card tag masks with design tokens (#42074) 2026-09-09 09:58:31 +00:00
dependabot[bot]
8c208ad206 chore(deps): bump unstructured from 0.21.5 to 0.24.0 in /dify-agent (#41765)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-09 09:53:12 +00:00
Asuka Minato
4724779fe2 refactor(api): migrate remaining RESTX models to Pydantic (#40399)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Byron Wang <byron@dify.ai>
2026-09-09 09:50:44 +00:00
yyh
f70cea50c9 refactor(web): migrate conversation variable inputs (#42070) 2026-09-09 09:36:24 +00:00
dependabot[bot]
0468eee02f chore(deps): bump gunicorn from 26.1.0 to 26.2.0 in /api in the flask group across 1 directory (#41505)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-09 09:30:17 +00:00
Joel
06f58809f2 fix(web): restore focus after closing overlays (#42068) 2026-09-09 09:27:56 +00:00
Duda Nogueira
647fec55d9 chore(release): Upgrade self-hosted Weaviate to 1.39 (#38214)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 09:19:54 +00:00
dependabot[bot]
a1a1a42c42 chore(deps): bump the storage group across 1 directory with 4 updates (#41893)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-09 09:18:06 +00:00
dependabot[bot]
87b4de1cda chore(deps): bump httpx2 from 2.5.0 to 2.12.0 in /dify-agent (#42011)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-09 09:16:40 +00:00
Coding On Star
8c5dd51446 fix(web): open embedded recommend banner templates in the detail dialog (#42063)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
2026-09-09 09:15:40 +00:00
dependabot[bot]
1007cf8294 chore(deps): bump httpx2 from 2.5.0 to 2.12.0 in /api (#42012)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-09 09:15:16 +00:00
wangxiaolei
adedbf1f12 fix: fix undo delete operation for webhook trigger node lead the url … (#42069) 2026-09-09 09:02:04 +00:00
github-actions[bot]
57a6ccbd3b chore(i18n): sync translations with en-US (#42064)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-09-09 08:57:53 +00:00
dependabot[bot]
ebb875e0a5 chore(deps): bump h2 from 4.3.0 to 4.4.1 in /dify-agent (#40119)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-09 08:57:27 +00:00
Asuka Minato
677f2cf43f chore: example to add Mapped (#40374) 2026-09-09 08:56:41 +00:00
Asuka Minato
f71047f31a test: migrate console workflow sessions and ORM models to SQLite (#40563)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-09 08:50:02 +00:00
Asuka Minato
a062155841 test: migrate console app response sessions and ORM models to SQLite (#40562) 2026-09-09 08:47:46 +00:00
Yunlu Wen
a7cc6913af chore(deps): bump unstructured from 0.21.5 to 0.27.5 in /api (#42053)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-09 08:47:09 +00:00
Stephen Zhou
8e8a3c5414 chore(lint): respect gitignore in ESLint flat config (#42065) 2026-09-09 07:58:29 +00:00
Wu Tianwei
f89c3e6ec7 feat: add agent resource permissions (#41940)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-09 07:30:39 +00:00
Joel
e8c3419d1f fix(web): hide decorative content and redundant list semantics (#42060) 2026-09-09 07:28:22 +00:00
Asuka Minato
4f29213cdc test: migrate workflow node execution task sessions and ORM models to SQLite (#40571) 2026-09-09 07:27:39 +00:00
Stephen Zhou
0f232ac4e7 chore(lint): tighten inline disable enforcement (#42058) 2026-09-09 07:21:02 +00:00
Yunlu Wen
4223b8eb0f chore(deps): bump transformers from 5.6.2 to 5.16.1 in /dify-agent (#42054)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-09 07:13:17 +00:00
Asuka Minato
62adebcf4e test: reclassify response filter regressions (#40706) 2026-09-09 07:12:01 +00:00
Joel
b577502a9b fix: make sortable lists keyboard accessible (#41974) 2026-09-09 07:01:54 +00:00
yyh
603176c17d refactor(web): migrate workflow numeric configuration inputs (#42052) 2026-09-09 06:51:29 +00:00
Harsh Kashyap
6e91ee2694 fix: join all rich_text/title segments in Notion database extraction (#42050) 2026-09-09 06:36:13 +00:00
yyh
cb1a8963e3 chore(lint): share and tighten JSX accessibility rules (#42045) 2026-09-09 06:26:57 +00:00
Harsh Kashyap
112e33a665 fix: escape double quotes in .xls cell values like the .xlsx path (#42049) 2026-09-09 06:05:08 +00:00
jiao-weiran
3f76dbe67a refactor(controllers/console): inject saved-message query with @model_validate (#41819)
Co-authored-by: 几一奥 <2481680240@qq.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-09 05:41:53 +00:00
nehal-a2z
cd031ab423 refactor(api): remove InstalledApp implicit-session accessor (#42000)
Co-authored-by: nehal-a2z <183489955+nehal-a2z@users.noreply.github.com>
2026-09-09 05:31:08 +00:00
Eddy ZHANG
dc9dd9c559 refactor(models): remove the App.bound_agent_id and App.is_agent wrappers (#41998) 2026-09-09 05:17:29 +00:00
Eddy ZHANG
e65f45f17a refactor(models): remove the unread App.author_name db.session wrapper (#41983) 2026-09-09 05:16:14 +00:00
L1nSn0w
8a69aa2884 fix(console): stop advertising the education plan outside Dify Cloud (#41898) 2026-09-09 04:38:50 +00:00
dependabot[bot]
f1c2917038 chore(deps): bump gitpython from 3.1.58 to 3.1.59 in /api (#42017)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-09 04:21:32 +00:00
Joel
0669e4e033 fix: deduplicate repeated GET request error notifications (#42035) 2026-09-09 03:39:41 +00:00
Harsh Kashyap
0b8435da71 fix(api): accept valid epoch-0 timestamps in localtime conversion tool (#42027) 2026-09-09 03:38:58 +00:00
yyh
bb1797722f chore(deps): upgrade jotai to v3 and jotai-scope (#42036) 2026-09-09 03:30:13 +00:00
Harsh Kashyap
455951056a fix: keep Notion table cell rich text segments in one Markdown column (#41993) 2026-09-09 03:23:01 +00:00
林玮 (Jade Lin)
0c443b7666 fix(api): handle invalid app icon references (#41977)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-09 02:56:40 +00:00
Harsh Kashyap
cc44574543 fix(api): reject trailing newlines in user_input_form variable names (#42029) 2026-09-09 02:51:46 +00:00
Harsh Kashyap
75f203cdb9 fix: validate declared blob size before allocating buffer in chunk merger (#42021)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-09 02:47:25 +00:00
Harsh Kashyap
da6a1b01d1 fix(api): fall back to first server URL when X-Request-Env is set but servers lack env key (#42025) 2026-09-09 02:38:44 +00:00
FFXN
bb2945a1a3 fix: dataset hitcount deadlock (#41979)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-09 02:37:26 +00:00
yyh
bec2c6787a chore(deps): upgrade pnpm to 12.3.4 and Vite+ to 0.3.1 (#42005) 2026-09-09 01:55:47 +00:00
CoralGarden52
89df7a670a fix(dataset): clean multimodal attachments during reindex (#41999) 2026-09-09 01:43:57 +00:00
-LAN-
d60873dd76 docs(CODEOWNERS): Assign domain owners for backend test paths (#42007) 2026-09-09 00:43:49 +00:00
Harsh Kashyap
fc062bfc6d fix(api): allow PDF extraction from URLs without an upload file (#41950) 2026-09-08 11:01:52 +00:00
JackSmith
e34b2f9045 fix(api): resolve aliases in remote settings sources (#41463) 2026-09-08 10:45:36 +00:00
Coding On Star
dfe5d8af31 fix(web): keep recommend banner logos intact and show template authors (#41980)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
2026-09-08 10:33:31 +00:00
Harsh Kashyap
09ddcbe01e fix(api): flatten readabilipy plain_text items in web reader tool (#41954) 2026-09-08 10:28:20 +00:00
Asuka Minato
398addf0c1 test: migrate segment service sessions and ORM models to SQLite (#40535)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-08 10:22:20 +00:00
Asuka Minato
1c4bcff490 test: migrate console dataset document sessions and ORM models to SQLite (#40522) 2026-09-08 10:17:25 +00:00
Asuka Minato
bcda6b4570 test: migrate workspace snippet sessions and ORM models to SQLite (#40524) 2026-09-08 10:17:08 +00:00
Asuka Minato
e4eb3d0d92 test: migrate provider core entity sessions and ORM models to SQLite (#40532) 2026-09-08 10:17:01 +00:00
Joel
e75b5f6bc9 fix: restore comment marker focus after closing threads (#41978) 2026-09-08 09:44:45 +00:00
yyh
0955b8b810 fix(web): reset goto anything search after dialog closes (#41976) 2026-09-08 09:38:11 +00:00
leilei3167
c9e0118ae9 fix(api): accept CRLF SKILL.md when importing skill zips (#41657)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Crazywoola <100913391+crazywoola@users.noreply.github.com>
2026-09-08 09:34:43 +00:00
Stephen Zhou
2f00e647ef fix(api): correct service api rate limit descriptions (#41975) 2026-09-08 09:33:46 +00:00
Keith
f3e5f41a3c refactor(models): pass session into WorkflowNodeExecutionModel accessors (#41968) 2026-09-08 09:31:08 +00:00
Harsh Kashyap
80428a693a fix(api): handle string and missing params in weekday time tool (#41952) 2026-09-08 09:09:51 +00:00
Sourav Rajvi
f3154e347e fix(rag): preserve CSV cell text during knowledge import (#41922) 2026-09-08 08:47:42 +00:00
Wu Tianwei
2873abd647 fix(i18n): sync agent access point copy (#41969) 2026-09-08 08:47:32 +00:00
Joel
93d6ff3889 feat: support keyboard movement for workflow nodes and comments (#41967) 2026-09-08 08:47:27 +00:00
Harsh Kashyap
67f7a5517a fix(api): anchor JSON extraction on brackets in parse_json_markdown (#41959) 2026-09-08 08:32:14 +00:00
CoralGarden52
2cdfe24c18 fix(dataset): delete segment attachment blobs from storage (#41400) 2026-09-08 08:13:18 +00:00
yyh
12d23062d4 refactor(web): isolate pricing modal state and lifecycle (#41960) 2026-09-08 07:53:39 +00:00
Joel
a233b2f53a fix: make panel and node resizing keyboard accessible (#41963)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-09-08 07:44:41 +00:00
Eddy ZHANG
9e0763e189 refactor(models): remove four unread legacy db.session wrappers (#41964) 2026-09-08 07:27:02 +00:00
Eddy ZHANG
970bbeca8d refactor(models): remove the legacy db.session wrappers on App (#41942) 2026-09-08 07:26:20 +00:00
yyh
8acdb1538b refactor(api): trim workspace features response contract (#41948) 2026-09-08 05:42:22 +00:00
Pranav Agarwal
a67f62e9aa fix(api): enforce RBAC permission on MCP tool provider GET endpoint (#41547) (#41548)
Co-authored-by: yunlu.wen <yunlu.wen@dify.ai>
2026-09-08 04:43:24 +00:00
Joel
efe506a0f5 fix: correct page landmarks and document layout rules (#41938) 2026-09-08 04:22:04 +00:00
yyh
d978fe710f refactor(web): retire provider context (#41944) 2026-09-08 04:16:45 +00:00
Stephen Zhou
7ba2cfb30b fix(api): align service api schemas with runtime behavior (#41943) 2026-09-08 03:59:02 +00:00
非法操作
146b193ef7 refactor(api): extract plugin file upload application service (#41808) 2026-09-08 03:42:32 +00:00
Zhu Lei
c157e59d58 fix(agent): persist Chatflow Agent V2 memory by conversation_id (#41871)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai>
2026-09-08 03:31:01 +00:00
yyh
1006519012 refactor(web): move model lists out of provider context (#41939) 2026-09-08 03:29:56 +00:00
yyh
e26dcf55a9 refactor(web): move provider feature flags to query consumers (#41937) 2026-09-08 02:56:31 +00:00
非法操作
d43a23d8f5 refactor(api): extract tool file download service (#41790) 2026-09-08 02:51:11 +00:00
yyh
22189f69f6 refactor(console): unify environment-aware oRPC clients and query policies (#41932) 2026-09-08 02:04:41 +00:00
非法操作
749d1f8d04 refactor(api): extract upload file delivery service (#41772) 2026-09-08 02:03:24 +00:00
Taranum Wasu
216180c7fd fix(vdb-weaviate): backward-compatible cleanup + preserve positional alignment (#41915)
Co-authored-by: Taranum01 <taranumwasu@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-08 01:55:08 +00:00
Taranum Wasu
eeff2c01b1 fix(dataset_service): refresh session snapshot before UploadFile lookup (#41840)
Co-authored-by: Taranum01 <taranumwasu@users.noreply.github.com>
2026-09-08 01:30:39 +00:00
yyh
4f463b1c46 fix(billing): align zero and unlimited quota semantics (#41927) 2026-09-08 01:14:06 +00:00
yyh
8980506597 refactor(web): remove obsolete agent v2 scaffolding and editor state (#41931) 2026-09-08 01:13:40 +00:00
citizen204
0df092d3c7 refactor: add missing @override decorators to console controller classes (#37113)
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-07 15:56:25 +00:00
Java123456com
d138effca8 style(api): remove unnecessary type conversions in services, repositories and tests (#41856) 2026-09-07 15:54:53 +00:00
ShawYann
a57e04aa49 refactor(models): pass session into InstalledApp.tenant accessor (#41831) 2026-09-07 15:45:39 +00:00
Valor
93287f22a8 refactor: preserve exception context when re-raising in except blocks (#40432)
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
2026-09-07 13:52:29 +00:00
yyh
8fc11b2927 refactor(web): retire provider context plan state (#41925) 2026-09-07 13:28:20 +00:00
WH-2099
a9cb17eb6c fix(api): reject ownerless pending DSL imports (#40107) 2026-09-07 10:59:09 +00:00
yyh
715bc7cd95 fix(dx): repair and restage Python files in pre-commit (#41877) 2026-09-07 10:37:38 +00:00
however-yir
75f867da85 refactor(models): remove legacy db.session wrappers on Message accessors (#41886)
Co-authored-by: however <however-yir@users.noreply.github.com>
2026-09-07 09:28:29 +00:00
Keith
d5be01b253 refactor(models): remove the last standalone legacy db.session properties on Dataset (#41911) 2026-09-07 09:27:04 +00:00
however-yir
b93101459d refactor(models): pass session into ApiToolProvider.user accessor (#41882)
Co-authored-by: however <however-yir@users.noreply.github.com>
2026-09-07 09:25:55 +00:00
非法操作
c17a96f02f refactor(service-api): extract file preview application service (#41732) 2026-09-07 08:59:18 +00:00
FFXN
60656a60cf fix(api): fix tencent summary vector deadlock (#41916)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-07 08:31:08 +00:00
yyh
eba589db89 refactor(api): remove legacy billing enabled state (#41917) 2026-09-07 08:29:57 +00:00
yyh
fc0136647f refactor(web): remove legacy enableBilling consumers (#41913) 2026-09-07 07:56:01 +00:00
Zhu Lei
c7423ffb2d fix(ssrf): apply private allowlist to agent_ssrf_proxy (#41870) 2026-09-07 07:22:12 +00:00
yyh
5c0d3c4393 refactor(web): remove provider plan query flags (#41908) 2026-09-07 06:37:26 +00:00
yyh
3be0cabf0a refactor(web): move app quota refresh out of provider context (#41906) 2026-09-07 06:13:20 +00:00
yyh
40f0b8a2a0 fix(web): use default Code Inspector integration (#41903) 2026-09-07 06:02:40 +00:00
yuki.uix
5e51fe2640 refactor(controllers): adopt with_session in web saved_message (#41859) 2026-09-07 05:43:29 +00:00
Charles
2df92204e9 refactor(models): pass session into CustomizedSnippet accessors (#40379) 2026-09-07 05:37:43 +00:00
github-actions[bot]
5f978fec37 chore(i18n): sync translations with en-US (#41904)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-09-07 05:35:57 +00:00
Keith
11a9627cbb refactor(models): remove remaining legacy db.session property wrappers on Dataset (#41647) 2026-09-07 05:25:51 +00:00
Keith
62a286332a refactor(models): remove legacy db.session property wrappers on DocumentSegment and DatasetQuery (#41794) 2026-09-07 05:24:37 +00:00
Kiveiru3
d91672c6d3 refactor(controllers): inject message request models (#41884) 2026-09-07 05:14:18 +00:00
Eddy ZHANG
56a31bab8d refactor(inner_api): dep-inject workspace payloads with @model_validate (#41540) 2026-09-07 05:13:09 +00:00
however-yir
a1a98e1087 refactor(models): remove legacy db.session wrappers on Message feedback/annotation accessors (#41883)
Co-authored-by: however <however-yir@users.noreply.github.com>
2026-09-07 05:11:29 +00:00
however-yir
80f6f72e8c refactor(models): pass session into AppAnnotationSetting.collection_binding_detail (#41885)
Co-authored-by: however <however-yir@users.noreply.github.com>
2026-09-07 05:00:12 +00:00
Xiyuan Chen
86134ea99e feat(rbac): agent RBAC/ACL scenes and resource-locator check bundles (#41768)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-07 04:39:43 +00:00
dependabot[bot]
60e983c493 chore(deps-dev): bump the dev group in /api with 48 updates (#41896)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-07 04:31:12 +00:00
kenwoodjw
cf34e3ee1a fix(agent): send uploaded images as native multimodal input (#41685)
Co-authored-by: kenwood chan <kenwoodchan@kenwooddeMacBook-Pro.local>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: yunlu.wen <yunlu.wen@dify.ai>
2026-09-07 04:23:03 +00:00
yyh
3e961dbb77 refactor(web): migrate loop variable constant inputs (#41901) 2026-09-07 04:15:41 +00:00
yyh
789cada297 refactor(web): migrate workflow error default inputs (#41900) 2026-09-07 04:15:40 +00:00
SONG ZHIFEI
1f6dadd22c chore: remove unnecessary str() calls in annotation tasks (#41878) 2026-09-07 03:55:20 +00:00
Kiveiru3
e7d34f05ba refactor(models): remove legacy db.session properties from Conversation (#41847) 2026-09-07 03:43:14 +00:00
dependabot[bot]
4cb70b7421 chore(deps): bump anthropics/claude-code-action from 1.0.211 to 1.0.216 in the github-actions-dependencies group (#41892)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-07 03:35:32 +00:00
非法操作
4c124174c9 refactor(api): extract human input file upload persistence (#41716) 2026-09-07 03:21:33 +00:00
Coding On Star
762dc5e8a6 fix(web): open embedded recommend banner plugins in the detail dialog (#41828)
Co-authored-by: fatelei <fatelei@gmail.com>
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: 姜涵煦 <hanxujiang@jianghanxudeMacBook-Pro-2.local>
Co-authored-by: L1nSn0w <l1nsn0w@qq.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-09-07 03:19:07 +00:00
非法操作
4b0e260ac5 refactor(console): decouple app tracing configuration (#41576) 2026-09-07 02:47:00 +00:00
yyh
dde1d500b5 docs: clarify frontend guidance ownership and dependencies (#41848) 2026-09-05 13:28:40 +00:00
chenshiyang
3c55dcc3d1 refactor(models): pass session into AppAnnotationHitHistory accessors (#41832)
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-05 10:32:30 +00:00
chenshiyang
fde602dadf refactor(models): pass session into tool provider accessors (#41830)
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-05 10:31:41 +00:00
Eddy ZHANG
d69b19b567 refactor(web): dep-inject the passport query with @model_validate (#41845) 2026-09-05 10:31:05 +00:00
yyh
00e5786067 fix(select): isolate trigger states and unavailable package selection (#41843) 2026-09-05 03:49:39 +00:00
Stephen Zhou
ad90cb9111 fix: preserve generated API contracts during local development (#41834) 2026-09-04 14:48:46 +00:00
yyh
a2acf62bf1 fix: align select and autocomplete collection contracts (#41829) 2026-09-04 12:26:26 +00:00
Asuka Minato
08c7a2c5f3 test: migrate application generation sessions and ORM models to SQLite (#40509) 2026-09-04 12:16:31 +00:00
yyh
1b4ae3bcd2 fix(button): align loading accessibility contract (#41823) 2026-09-04 11:39:50 +00:00
yyh
2972468bc1 docs(web): align PreviewCard disclosure guidance (#41827) 2026-09-04 11:26:21 +00:00
Coding On Star
d93e5aa444 fix(web): append /api/v1 when SSR reads MARKETPLACE_API_URL (#41824)
Co-authored-by: fatelei <fatelei@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: 姜涵煦 <hanxujiang@jianghanxudeMacBook-Pro-2.local>
Co-authored-by: L1nSn0w <l1nsn0w@qq.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-09-04 10:38:53 +00:00
非法操作
e6c9846726 refactor(api): isolate workflow app log queries (#41509) 2026-09-04 10:06:46 +00:00
Joel
6f37f0661d chore: use semantic tags for interactive elements (#41813) 2026-09-04 10:02:23 +00:00
yyh
dcb7b36ad4 refactor(dify-ui): adopt ID-backed combobox items (#41821) 2026-09-04 09:54:44 +00:00
Pranav Agarwal
067a60e0ac fix(api): enforce admin and RBAC permissions on model provider model credential GET endpoint (#41599) (#41600)
Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai>
2026-09-04 09:17:41 +00:00
非法操作
b15fb30ff5 refactor(api): extract app statistic queries from console controllers (#41557) 2026-09-04 09:15:51 +00:00
yyh
bb7114c2e8 chore: bump base-ui to 1.8.0 (#41820) 2026-09-04 09:15:18 +00:00
Asuka Minato
7128456d57 test: migrate core agent runner sessions and ORM models to SQLite (#40508) 2026-09-04 08:50:33 +00:00
yyh
751023c107 perf(agent-v2): flatten build draft request waterfalls (#41816) 2026-09-04 08:49:05 +00:00
github-actions[bot]
16f5e8738b chore(i18n): sync translations with en-US (#41817)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-09-04 08:47:42 +00:00
yyh
173e92ea24 docs(dify-ui): document combobox chip keyboard hints (#41815) 2026-09-04 08:37:08 +00:00
bucketbase26
b5ee943a63 fix(trigger): sync plugin relationships on publish (#41756) 2026-09-04 08:33:48 +00:00
eux
7919df7ef5 fix(api): skip invalid markdown image URLs (#41343) 2026-09-04 08:17:33 +00:00
非法操作
f8930aba34 refactor(api): extract console workflow run application service (#41336) 2026-09-04 08:16:49 +00:00
Coding On Star
cb6c04637b feat(web): add marketplace creator profiles and homepage redesign (#41538)
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
Co-authored-by: fatelei <fatelei@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: 姜涵煦 <hanxujiang@jianghanxudeMacBook-Pro-2.local>
Co-authored-by: L1nSn0w <l1nsn0w@qq.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-09-04 08:09:45 +00:00
Zhu Lei
949f6c4ad4 fix(api): correct Service API segments has_more pagination (#41776) 2026-09-04 08:00:53 +00:00
yyh
7d7390ddaf fix(web): allow CookieYes consent logging (#41812) 2026-09-04 08:00:34 +00:00
yyh
b816754c06 fix(web): use stable error boundary retry (#41811) 2026-09-04 07:57:35 +00:00
Byron.wang
09f301d969 refactor(api): move OpenAPI account access behind application services (#41308) 2026-09-04 07:38:01 +00:00
Jialong(Bruce) Li
95aeb0c362 fix(skills): tolerate __MACOSX metadata folder when importing a Skill package (#41757) 2026-09-04 07:25:01 +00:00
yyh
336b134a40 refactor(web): migrate runtime app inputs (#41806) 2026-09-04 07:23:52 +00:00
Sourav Rajvi
e0673077a7 fix: close unclosed think tags around agent tool calls (#41764)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-04 07:23:34 +00:00
Zhu Lei
acf18aad13 fix(api): keep segment attachments when attachment_ids omitted (#41777) 2026-09-04 07:21:32 +00:00
Zhu Lei
1942925eb1 fix(api): persist plugin trigger subscription expires_at from provider (#41773) 2026-09-04 07:21:17 +00:00
Crazywoola
f5c14295b4 chore: use emoji issue labels 2026-09-04 15:49:27 +08:00
Zhu Lei
5355edefe3 fix(api): correct dataset and document list has_more pagination (#41784) 2026-09-04 07:19:33 +00:00
Jean-Rémi Larcelet-Prost
b3b92f11af docs(api): Fix reformat script path in api/README.md (#41658) 2026-09-04 15:28:03 +08:00
dependabot[bot]
621ab7d19b chore(deps-dev): bump mkdocs-material from 9.7.6 to 9.7.7 in /dify-agent (#41767)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-04 15:22:59 +08:00
yyh
0d8ad27b14 test: split skill detail page suite (#41799) 2026-09-04 07:06:33 +00:00
yyh
9e68c45058 fix(ci): avoid npm bootstrap in web setup (#41804) 2026-09-04 06:53:54 +00:00
Joel
6208b33e08 chore: correct ARIA roles and preserve control interactions (#41738)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-09-04 06:06:02 +00:00
非法操作
cea89f784f refactor: make explore localization request-driven (#41798) 2026-09-04 06:03:46 +00:00
非法操作
8504aaa0ef fix(api): support Lao as a display language (#41743) 2026-09-04 05:51:34 +00:00
Byron.wang
81abb7322d ci: parallelize change detection and gate style jobs (#41678)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-04 05:05:30 +00:00
Sourav Rajvi
5b13b7a44d refactor(models): use sa.false()/sa.true() for boolean server defaults (#41762)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-04 02:50:27 +00:00
Yunlu Wen
2619d172dc chore: run dify-agent typecheck in python-style (#41701)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-04 02:40:55 +00:00
yyh
d9ff45022b chore(web): enforce query options lint (#41754) 2026-09-04 02:37:21 +00:00
Wu Tianwei
550196e3b8 feat: app deployment v2 (#41444)
Co-authored-by: zhangx1n <zhangxin@dify.ai>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-04 02:01:35 +00:00
非法操作
8e2058a962 refactor: pass notification language explicitly (#41770) 2026-09-04 01:52:30 +00:00
Sourav Rajvi
df0460f876 fix(binding-files): keep UTF-8 text preview when max_bytes splits a character (#41761) 2026-09-04 01:35:32 +00:00
yyh
65b4091402 refactor(query): migrate imperative APIs to query semantics (#41746) 2026-09-03 13:54:48 +00:00
Keith
e6a38fd793 refactor(models): remove legacy db.session property wrappers on Document (#41721) 2026-09-03 13:34:49 +00:00
非法操作
8c98e3fad5 refactor(web): align account preference requests with API contracts (#41748) 2026-09-03 12:55:44 +00:00
非法操作
24f9f07752 fix(web): map pt-BR to pt_BR for tool metadata (#41745) 2026-09-03 12:54:46 +00:00
yyh
4beffa97e0 refactor(web): migrate runtime variable inputs (#41739)
Co-authored-by: Joel <iamjoel007@gmail.com>
2026-09-03 09:38:39 +00:00
Joel
aee0062cb2 chore: remove redundant aria-label attributes (#41724)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-09-03 09:10:16 +00:00
yyh
863930836b chore(dify-ui): enable purity lint in tests (#41736) 2026-09-03 09:00:30 +00:00
zyssyz123
a702b3bb2b fix(agent): surface missing tool credentials (#41726) 2026-09-03 08:53:55 +00:00
Byron.wang
692010f0fc refactor(api): decouple console OAuth flow from legacy account services (#41188)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-03 08:47:14 +00:00
github-actions[bot]
363753a0c5 chore(i18n): sync translations with en-US (#41733)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-09-03 08:46:49 +00:00
yyh
e982a3de33 test(web): remove workspace switcher browser test (#41728) 2026-09-03 08:06:17 +00:00
Stephen Zhou
137eb19de9 chore(web): prune unused i18n keys (#41725) 2026-09-03 08:01:55 +00:00
Zhu Lei
612a929907 fix(web): stringify object editor values before Monaco createModel (#41719) 2026-09-03 07:51:41 +00:00
Stephen Zhou
29acaabc6a chore(web): canonicalize Tailwind classes (#41718) 2026-09-03 07:33:05 +00:00
Jingyi
642ad2fffe fix(web): align workspace switcher states and semantics (#41713) 2026-09-03 07:18:49 +00:00
Benjamin
8f7cca3ae2 fix(web): guard LLM node model_selector against persisted null (#41707) 2026-09-03 06:20:48 +00:00
Byron.wang
b015aa416f refactor(api): extract account login service and consolidate adapters (#41184) 2026-09-03 06:20:21 +00:00
非法操作
ba1d5ba172 refactor(api): centralize remote file handling (#41627) 2026-09-03 06:00:13 +00:00
zyssyz123
688107c3e5 fix(api): bound suggested question generation latency (#41705) 2026-09-03 05:02:09 +00:00
非法操作
d8c3f7c5ee refactor(openapi): inject file upload service (#41606) 2026-09-03 04:42:31 +00:00
非法操作
69484a07e1 refactor(console): inject file service (#41603) 2026-09-03 04:42:30 +00:00
非法操作
5f4b1f867d refactor(web): inject file upload service (#41586) 2026-09-03 04:42:30 +00:00
Eddy ZHANG
fc7193cf04 refactor(service_api): dep-inject the dataset create payload with @model_validate (#41651) 2026-09-03 03:52:41 +00:00
yyh
59021616d4 refactor(web): migrate legacy form controls to Dify UI (#41682) 2026-09-03 03:50:23 +00:00
非法操作
ab0e92b893 refactor(service-api): inject file upload service (#41585) 2026-09-03 03:41:45 +00:00
Wu Tianwei
b0b495d243 feat(web): make markdown form field name length configurable (#41697) 2026-09-03 03:21:10 +00:00
yyh
6536ffc422 docs(web): clarify browser mode focus test boundary (#41692) 2026-09-03 02:48:08 +00:00
Stephen Zhou
b7570ce4b7 chore(dify-ui): migrate class merging to cn (#41693) 2026-09-03 02:31:52 +00:00
dependabot[bot]
907bc2558b chore(deps): bump transformers from 5.5.0 to 5.10.1 in /api (#41609)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-03 01:50:39 +00:00
Eddy ZHANG
95a6a14ed5 refactor(console,web): dep-inject query params with @model_validate (#41646) 2026-09-03 01:50:22 +00:00
Byron.wang
be7d11ca1d refactor(api): extract forgot password application service (#41111)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-03 01:38:59 +00:00
Asuka Minato
8129555642 chore: Site to typebase (#35632) 2026-09-02 14:44:08 +00:00
dependabot[bot]
f28d993607 chore(deps): bump transformers from 5.6.2 to 5.10.1 in /dify-agent (#41608)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-02 14:36:06 +00:00
github-actions[bot]
0fc01fee3f chore(i18n): sync translations with en-US (#41669)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
2026-09-02 11:36:24 +00:00
yyh
bc716020c7 perf(web): load Zendesk on support intent (#41653) 2026-09-02 10:32:47 +00:00
YungLe
3cd1fc098c feat: allow knowledge base API keys to be scoped to a single dataset (#37569)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-02 10:30:42 +00:00
yyh
7664ad7149 fix(web): align avatar file extension with output type (#41662) 2026-09-02 10:22:59 +00:00
Yunlu Wen
b1eafb0d82 fix: validate rule for provider scoped credentials (#41659) 2026-09-02 10:07:44 +00:00
Ingram Z
a1a10f17e3 fix(dify-agent): harden E2B binding lifecycle (#41623)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-02 09:47:24 +00:00
Joel
6ae40b1f15 fix: preserve Swagger tool authorization after refresh (#41656) 2026-09-02 09:13:51 +00:00
yyh
e577c007a0 fix(web): stabilize Home and Studio route titles (#41654) 2026-09-02 09:03:58 +00:00
yyh
02855848ea perf(web): downsample static account avatars (#41655) 2026-09-02 09:02:46 +00:00
dependabot[bot]
98090d60b3 chore(deps): bump pypdf from 6.14.2 to 6.16.1 in /dify-agent (#41604)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-02 08:03:07 +00:00
非法操作
59ee8878f9 refactor(api): require active workspace in request context (#41280) 2026-09-02 06:54:47 +00:00
Wu Tianwei
82d86dcf98 feat: app access point permission (#41631)
Co-authored-by: fatelei <fatelei@gmail.com>
2026-09-02 06:34:33 +00:00
yyh
183741b5f7 refactor(web): migrate webapp email code inputs (#41634) 2026-09-02 06:22:21 +00:00
yyh
de3f3714ae refactor(web): migrate webapp reset password inputs (#41633) 2026-09-02 06:22:20 +00:00
yyh
415e999cef refactor(web): migrate console reset password inputs (#41632) 2026-09-02 06:22:19 +00:00
Joel
c25b439d8b fix: improve snippets list accessibility (#41579)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-09-02 04:39:17 +00:00
QuantumGhost
324ee5f036 fix(api): tolerate workflow pause snapshot cleanup failure (#41581) 2026-09-02 04:33:04 +00:00
yyh
8707d0c82f docs(e2e): refine Playwright and Cucumber skill guidance (#41617) 2026-09-02 03:45:41 +00:00
Eddy ZHANG
4d9bda7422 refactor(inner_api): dep-inject request payloads with @model_validate (#41575) 2026-09-02 03:40:46 +00:00
yyh
88c64a8db9 refactor(web): migrate access point mutations (#41614) 2026-09-02 03:17:03 +00:00
Eddy ZHANG
a5445f2424 refactor(service_api): dep-inject segment payloads with @model_validate (#41571) 2026-09-02 02:59:30 +00:00
dependabot[bot]
334a3043bc chore(deps): bump pypdf from 6.15.0 to 6.16.1 in /api (#41607)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-02 01:56:41 +00:00
Xiyuan Chen
93dae7d8a0 fix(rbac): let agent.manage operate every agent page (#41583)
Co-authored-by: yunlu.wen <yunlu.wen@dify.ai>
2026-09-02 01:45:42 +00:00
yyh
ea98d38c3d refactor(web): migrate webhook table input (#41612) 2026-09-02 01:44:52 +00:00
yyh
18ac946e2b refactor(web): migrate dataset settings name input (#41611) 2026-09-02 01:44:51 +00:00
yyh
baf629dd36 refactor(web): migrate external API selection input (#41610) 2026-09-02 01:44:51 +00:00
dependabot[bot]
a3dbf9293a chore(deps-dev): bump nltk from 3.10.0 to 3.10.3 in /api (#41602)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-02 01:20:41 +00:00
yyh
3f982e3dc8 ci(e2e): shard pull request browser tests (#41596) 2026-09-02 01:19:08 +00:00
yyh
6bc7f01261 fix(web): announce agent draft status (#41595) 2026-09-01 11:53:05 +00:00
Byron.wang
c4ea406586 refactor(api): decouple system features and web adapters (#40772)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-01 11:50:52 +00:00
Asuka Minato
7978352262 test: migrate dataset download sessions and ORM models to SQLite (#40630) 2026-09-01 09:28:50 +00:00
Asuka Minato
980a9d4ed4 test: migrate dataset retrieval sessions and ORM models to SQLite (#40548) 2026-09-01 09:28:33 +00:00
Asuka Minato
d337c59916 test: migrate workspace member sessions and ORM models to SQLite (#40523) 2026-09-01 09:27:46 +00:00
Asuka Minato
c45e5386fb test: migrate agent-v2 workflow sessions and ORM models to SQLite (#40514) 2026-09-01 09:25:46 +00:00
QuantumGhost
d6e4f48526 fix(web): merge workflow tool outputs from all End nodes (#41517)
Co-authored-by: twwu <twwu@dify.ai>
2026-09-01 08:50:37 +00:00
QuantumGhost
27c2f058fe fix(web): publish workflow before creating tool (#41528) 2026-09-01 07:52:07 +00:00
yyh
8e064ffe98 chore: upgrade Node.js runtime to 24 LTS (#41573) 2026-09-01 06:57:38 +00:00
zyssyz123
fe39211aac fix(agent): bound Redis run event streams (#41566)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-01 06:47:16 +00:00
Joel
770b83d9cc fix: handle published workflow request failures in access points (#41572) 2026-09-01 06:37:25 +00:00
Keith
566b8857dd refactor(models): remove legacy db.session property wrappers on Dataset (#41569) 2026-09-01 06:28:24 +00:00
elyar1124
3ed28dce33 refactor(models): pass session into DocumentSegment and AppDatasetJoin accessors (#41568) 2026-09-01 06:24:04 +00:00
Joel
fdca3e2a1a chore: enforce titles on CSS-truncated elements (#41438) 2026-09-01 06:18:53 +00:00
Eddy ZHANG
6411d6235c refactor(service_api): dep-inject query params with @model_validate (#41562) 2026-09-01 05:55:50 +00:00
Eddy ZHANG
1977355dd1 refactor(console): dep-inject model provider payloads with @model_validate (#41539) 2026-09-01 04:24:45 +00:00
yyh
520e5e2beb chore(deps): upgrade workspace dependencies (#41564) 2026-09-01 04:19:06 +00:00
Joel
5cbc42cd38 chore: Revert "feat: use app.acl.access_point_manage to control access point page access" (#41563) 2026-09-01 03:46:03 +00:00
yyh
765e6338d2 fix(plugin): prevent install dialog error overflow (#41561) 2026-09-01 03:34:23 +00:00
yyh
62221bf8b8 refactor(web): align skills card semantics (#41559) 2026-09-01 02:36:21 +00:00
yyh
8f2ff3a8fb refactor(plugins): migrate credential rename input (#41552) 2026-09-01 01:44:44 +00:00
yyh
f92f7e92c3 refactor(workflow): migrate email subject input (#41551) 2026-09-01 01:44:43 +00:00
yyh
72458d5750 refactor(workflow): migrate schedule cron input (#41550) 2026-09-01 01:44:43 +00:00
Xiyuan Chen
ce801f4445 fix(web): hide Open in Explore for SSO-restricted apps (#41518)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-09-01 01:10:03 +00:00
yyh
57144b0094 docs(frontend): clarify transient state ownership (#41541) 2026-08-31 13:37:22 +00:00
Xiyuan Chen
b71dcb8253 fix(web): reset test-email sender state when reopening the modal (#41515)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-08-31 12:15:40 +00:00
zyssyz123
762019a160 fix(workflow): block manual runs for trigger workflows (#41519)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-31 09:30:52 +00:00
Yunlu Wen
39f06b6f05 chore(dify-agent): make local dev work with defaults out of the box (#41533)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-31 08:37:30 +00:00
-LAN-
d0a86cfd4e fix(workflow): allow human input in iteration and loop (#40061) 2026-08-31 08:28:17 +00:00
Joel
336dd0b8c0 fix: hide chat features drawer while opener dialog is active (#41530) 2026-08-31 08:01:55 +00:00
Jingyi
6da1b473e6 fix(web): align tool plugin card layout (#41525) 2026-08-31 07:25:06 +00:00
Joel
f021ee4dc0 fix: prevent the exit versions button from shrinking (#41527) 2026-08-31 07:23:45 +00:00
Joel
280f81757d fix: stabilize node block selectors and restore start tabs (#41523) 2026-08-31 06:58:56 +00:00
Asuka Minato
1952cff091 test: migrate model and agent sessions to SQLite (#40087) 2026-08-31 05:57:40 +00:00
Asuka Minato
f8f71fdc68 test: migrate audit findings sessions and ORM models to SQLite (#40551) 2026-08-31 05:55:38 +00:00
Asuka Minato
9398482b53 test: migrate console dataset sessions and ORM models to SQLite (#40521) 2026-08-31 05:55:12 +00:00
Asuka Minato
5db79a77e4 test: migrate advanced chat generator sessions and ORM models to SQLite (#40585) 2026-08-31 05:51:30 +00:00
Jingyi
02222e8fce fix(web): preserve plugin footer text layout (#41514) 2026-08-31 05:50:29 +00:00
yyh
5a5b9c6fec fix(web): make plugin detail drawer non-modal (#41473) 2026-08-31 05:46:55 +00:00
Byron.wang
d25e2b51fc refactor(api): decouple onboarding and notification services (#40759)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: hjlarry <hjlarry@163.com>
2026-08-31 04:37:07 +00:00
Coding On Star
c6ca395042 fix(web): make registration tracking consent-safe and retryable (#41365)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
2026-08-31 03:39:37 +00:00
Wu Tianwei
7167d7d662 fix(rbac): grant agent access to all legacy roles (#41511) 2026-08-31 03:33:09 +00:00
Byron.wang
9e07c2dac6 refactor(api): extract email registration application service (#41108)
Co-authored-by: hjlarry <hjlarry@163.com>
2026-08-31 03:22:01 +00:00
wangxiaolei
6a00c23e49 feat: use app.acl.access_point_manage to control access point page access (#41462)
Co-authored-by: twwu <twwu@dify.ai>
2026-08-31 03:05:19 +00:00
Xiyuan Chen
750810ea81 fix(api): keep workspace owner consistent on ownership transfer (#41267) 2026-08-31 02:51:19 +00:00
非法操作
b70ad7d424 fix(web): show DSL import warning details (#41502) 2026-08-31 02:36:32 +00:00
zyssyz123
ad86351cc1 fix(agent): reject incompatible session snapshots (#41447) 2026-08-31 02:26:07 +00:00
dependabot[bot]
8b8031710b chore(deps): bump anthropics/claude-code-action from 1.0.199 to 1.0.210 in the github-actions-dependencies group (#41504)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-31 01:25:59 +00:00
yyh
94dff4dddc fix(web): unify tool plugin selection state (#41470) 2026-08-31 01:24:49 +00:00
yyh
17ae89ff22 fix(e2e): target agent roster list items (#41453) 2026-08-31 01:24:04 +00:00
yyh
fce29e6f6a fix(web): refine agent dialog form sessions (#41492) 2026-08-31 01:24:03 +00:00
Eddy ZHANG
6130f081e0 refactor(console): dep-inject account payloads with @model_validate (#41500) 2026-08-31 00:52:10 +00:00
Eddy ZHANG
8894b09c3b refactor(web): dep-inject payloads with @model_validate (#41501) 2026-08-31 00:50:12 +00:00
yyh
bf64390d7c revert(web): revert dataset button a11y changes (#41493) 2026-08-30 16:00:14 +00:00
Eddy ZHANG
884ece570e refactor(service_api): dep-inject dataset payloads with @model_validate (#41490) 2026-08-30 15:06:36 +00:00
LuckTerence
9d396524b9 fix(web): improve a11y of dataset clickable divs with native buttons (#41319) 2026-08-30 13:00:42 +00:00
Eddy ZHANG
8b3c2ee274 refactor(service_api): dep-inject app payloads with @model_validate (#41491) 2026-08-30 12:51:50 +00:00
Weike Zhang
02f9b3ca7c refactor: pass db.session explicitly in instruction_modify_workflow (#37403) (#40827) 2026-08-30 00:48:00 +00:00
jjj-n
09054169c4 refactor(models): pass session into Workflow accessors (#41464) 2026-08-29 14:28:15 +00:00
Asuka Minato
0f3cde8120 test: migrate application DSL sessions and ORM models to SQLite (#40554) 2026-08-29 14:06:07 +00:00
Asuka Minato
2bdcbe96c5 test: enforce centralized config overrides (#40869) 2026-08-29 14:05:35 +00:00
Asuka Minato
3824323fe7 test: centralize core and service config overrides (#40868) 2026-08-29 14:05:34 +00:00
lin
1264d29a4f chore: remove unnecessary type conversions (#41454) 2026-08-29 07:04:18 +00:00
Terminator666666
f8dd276e2c refactor(controllers/console): dep-inject workflow payloads with @model_validate (#41244)
Co-authored-by: Terminator666666 <Terminator666666@users.noreply.github.com>
2026-08-28 14:49:46 +00:00
Eddy ZHANG
37988de678 refactor(service_api): dep-inject payloads with @model_validate (#41374) 2026-08-28 14:47:18 +00:00
Asuka Minato
92110e1982 test: centralize controller and storage config overrides (#40867) 2026-08-28 14:47:17 +00:00
Asuka Minato
d8e7a97b25 test: centralize account and feature config overrides (#40864) 2026-08-28 14:47:16 +00:00
Keith
e6fc0a8ba8 refactor(models): pass session into WorkflowAppLog accessors (#41440) 2026-08-28 14:32:27 +00:00
yyh
28e9a52ea5 feat(web): enhance app and agent card interactions (#41449) 2026-08-28 12:24:56 +00:00
Joel
efd90de70a fix: avoid duplicate error toast when applying agent build drafts (#41442) 2026-08-28 09:18:10 +00:00
Benjamin
aa208d2dd7 fix(web): give the favicon a single owner in root metadata (#41433)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 08:57:04 +00:00
yyh
11bb82c731 fix(web): align agent roster card interactions (#41436) 2026-08-28 08:50:30 +00:00
yyh
78c85ae7c1 fix(agent): add roster publication status contract (#41435)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-28 08:50:30 +00:00
Joel
4c414988ee fix: improve accessibility across the apps list (#41393)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-08-28 07:55:59 +00:00
Xiyuan Chen
3bc4ac44e5 fix(ci): correct difyctl release and e2e workflow gates (#41355) 2026-08-28 06:56:25 +00:00
Jingyi
94fdda18e2 fix(web): prevent tool card footer clipping (#41430) 2026-08-28 05:36:13 +00:00
yyh
0b11d34d4b fix(web): align web app sidebar spacing (#41425) 2026-08-28 05:34:32 +00:00
wangxiaolei
ff5d431efd feat: hide skill_entry when use has no skill.view permission (#41429) 2026-08-28 05:13:38 +00:00
alanhuangyoo
97b5d8092c ci: run the dify-agent test suite (#41327) 2026-08-28 05:09:34 +00:00
Wu Tianwei
f7d6cd1afe fix(web): block resource access for Agent apps (#41424) 2026-08-28 03:43:48 +00:00
Wu Tianwei
1971450ad8 fix: protect workspace owners in resource access (#41423) 2026-08-28 03:03:40 +00:00
Keith
c8606db16b refactor(models): pass session into ChildChunk accessors (#41370)
Co-authored-by: ke-yu <ke-yu@pepabo.com>
2026-08-28 02:30:10 +00:00
chenshiyang
654324c0d9 refactor(models): pass session into PipelineCustomizedTemplate.created_user_name (#41394)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 02:26:41 +00:00
huyua9
650f8880ea docs: correct UUIDv7 helper docstring (#41419)
Co-authored-by: huyua9 <huyua9@users.noreply.github.com>
2026-08-28 02:26:40 +00:00
yyh
f7e71ca9e1 refactor(web): simplify pricing components (#41416) 2026-08-28 01:37:01 +00:00
yyh
290acf1b73 fix(ui): keep scroll area focus indicator visible (#41404) 2026-08-28 01:37:00 +00:00
-LAN-
458c96d5ba fix(api): preserve tracing configuration boundaries (#41403) 2026-08-27 22:11:32 +08:00
Benjamin
2a554ab2d1 perf(web): virtualize the Web Apps sidebar list again (#41397)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-08-27 13:45:50 +00:00
Asuka Minato
3ce262de05 test: migrate agent service sessions and ORM models to SQLite (#40598) 2026-08-27 12:15:36 +00:00
Asuka Minato
3ea5f38e92 test: migrate human input sessions and ORM models to SQLite (#40597) 2026-08-27 12:15:07 +00:00
lei_lei
8c25190713 fix(api): raise Socket.IO max_http_buffer_size for large workflows (#41347) 2026-08-27 11:48:42 +00:00
yyh
9c16c86597 revert(web): restore Agentation devtool (#41387) 2026-08-27 09:09:44 +00:00
wangxiaolei
d84088ef74 feat: new user add automatic add to app / dataset whitelist (#41363) 2026-08-27 08:51:05 +00:00
yyh
a38ddd9d02 fix: align documentation links with stable anchors (#41384) 2026-08-27 08:38:41 +00:00
Joel
d94204ec33 feat: track inline agent and preview mode events (#41373) 2026-08-27 08:09:09 +00:00
HelloSrauker
1db542c706 fix(workflow): correct loop boolean break condition values (#41186) 2026-08-27 08:02:52 +00:00
yyh
15021ad350 chore(web): remove Agentation devtool (#41372) 2026-08-27 07:54:27 +00:00
yyh
59383e88c2 fix(web): refine agent configure section states (#41371) 2026-08-27 07:51:07 +00:00
wangxiaolei
05b00574aa chore: add permission check back (#41369) 2026-08-27 07:45:50 +00:00
yyh
5aac67f570 test(e2e): wait for apps console readiness (#41357) 2026-08-27 07:40:23 +00:00
Joel
1be72af661 fix: allow publishing workflows with legacy Agent nodes (#41368) 2026-08-27 07:26:48 +00:00
Asuka Minato
1ccac4792c test: migrate knowledge file access sessions and ORM models to SQLite (#40596) 2026-08-27 07:19:20 +00:00
Asuka Minato
d67a8323c5 test: migrate agent observability sessions and ORM models to SQLite (#40588) 2026-08-27 07:18:58 +00:00
Asuka Minato
ef20a16a2f test: migrate explore installed app sessions and ORM models to SQLite (#40525) 2026-08-27 07:18:32 +00:00
Asuka Minato
1efc71c624 test: migrate Service API dataset segment sessions and ORM models to SQLite (#40517) 2026-08-27 07:17:41 +00:00
Joel
54298abf2d fix: improve home page accessibility (#41310)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-08-27 07:03:01 +00:00
非法操作
735d814dc0 fix(web): use management provider list for MCP integrations (#41360) 2026-08-27 06:49:47 +00:00
QuantumGhost
3e12f0b9fb fix(api): ignore broken pipe during app stop checks 🤖🤖🤖 (#41356) 2026-08-27 06:39:06 +00:00
yyh
85b4626ac6 fix(web): route Agent V2 documentation links correctly (#41353) 2026-08-27 06:20:03 +00:00
yyh
8266d741e8 refactor(web): migrate tracing inputs (#41341) 2026-08-27 06:13:10 +00:00
yyh
3765a41b8c refactor(web): migrate workflow variable inputs (#41340) 2026-08-27 05:43:10 +00:00
wangxiaolei
6a1b23935f chore: remove permission check (#41349) 2026-08-27 05:29:38 +00:00
非法操作
bf54dd7814 refactor(api): remove unused workflow archived logs endpoint (#41345) 2026-08-27 03:48:57 +00:00
dependabot[bot]
1aa7ee29a1 chore(deps): bump h2 from 4.3.0 to 4.4.1 in /api (#40120)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 03:12:26 +00:00
非法操作
a9319c86ee refactor(api): extract workflow run archive application service (#41253) 2026-08-27 02:41:43 +00:00
非法操作
5a6191b96b refactor(api): extract workflow statistic query service (#41271) 2026-08-27 02:41:02 +00:00
yyh
ce31cdc585 refactor(web): migrate metadata inputs (#41339) 2026-08-27 02:40:08 +00:00
非法操作
32981687dd refactor: remove unused workflow run export endpoint (#41335) 2026-08-27 02:38:27 +00:00
非法操作
58d71bf9c9 fix(web): align model provider access permissions (#41329) 2026-08-27 02:27:57 +00:00
-LAN-
82520c144f fix(api): migrate legacy model types during upgrade (#41326) 2026-08-27 02:00:33 +00:00
Pranav Agarwal
0d05eae517 fix(api): enforce RBAC permission on agent composer GET endpoint (#41324) (#41325) 2026-08-26 20:09:25 +00:00
yyh
c8e29d9a7d fix(web): preserve link semantics in button-styled navigation (#41312) 2026-08-26 10:50:06 +00:00
Xin Zhang
638dfd77a4 fix: update secret environment variable description (#41309) 2026-08-26 10:08:30 +00:00
wangxiaolei
0047522b0d feat: add command cli to migration rbac scope to automatic_include_workspace_members (#41304) 2026-08-26 09:10:22 +00:00
Joel
ac23430aca fix: improve login and signup accessibility (#41301) 2026-08-26 08:16:15 +00:00
yyh
06482af260 test(e2e): stabilize Agent access toggles (#41282) 2026-08-26 07:56:28 +00:00
Byron.wang
30e6069851 refactor(api): thin OAuth controllers and extract application services (#40692) 2026-08-26 07:33:37 +00:00
github-actions[bot]
021d5ceae7 chore(i18n): sync translations with en-US (#41291)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-08-26 07:31:34 +00:00
Wu Tianwei
73dd8739ef feat: optimize resource access config logic (#41164)
Co-authored-by: fatelei <fatelei@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-26 07:18:57 +00:00
林玮 (Jade Lin)
f5aaa50a30 fix(auth): normalize Gmail registration emails (#41249) 2026-08-26 07:14:27 +00:00
林玮 (Jade Lin)
8c9f248520 fix(api): attribute Skill Builder credit usage (#41290) 2026-08-26 07:02:45 +00:00
Taranum Wasu
6e0e32848e fix(human-input): align backend timeout handle with frontend DSL (#40746)
Co-authored-by: Taranum01 <50813317+Taranum01@users.noreply.github.com>
Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com>
2026-08-26 06:47:14 +00:00
非法操作
6ba29b3f0c chore(web): remove retired forum entry (#41289) 2026-08-26 06:47:13 +00:00
lei_lei
119e6af2a9 fix(api): honor DELETE JSON body in model_validate (#41288) 2026-08-26 06:38:13 +00:00
非法操作
c563528a2d refactor(api): standardize compliance downloads (#41057) 2026-08-26 06:32:13 +00:00
非法操作
c4ace15afd refactor(api): extract console app site command boundary (#41216) 2026-08-26 06:31:06 +00:00
ccl125
d5a8ace1c4 fix(rag-pipeline): mark credential-free datasources as authorized (#39772)
Co-authored-by: ccl125 <ccl125@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: FFXN <31929997+FFXN@users.noreply.github.com>
2026-08-26 05:44:10 +00:00
heyszt
86f8b847e9 fix(api): clean multimodal attachments in batch deletion (#41256) 2026-08-26 05:10:28 +00:00
Stephen Zhou
1c6f0a0388 fix(api): restore Service API OpenAPI semantics (#41274) 2026-08-26 05:05:36 +00:00
wangxiaolei
c937fcb21a feat: go to anything check enable skill (#41281) 2026-08-26 04:46:10 +00:00
yyh
0a1ce09dfb refactor(web): migrate external knowledge input (#41278) 2026-08-26 04:44:52 +00:00
yyh
b9ac4d3cb1 refactor(web): migrate dataset settings input (#41277) 2026-08-26 04:44:51 +00:00
yyh
e4cd2c72ad refactor(web): migrate document metadata input (#41276) 2026-08-26 04:44:50 +00:00
Joel
dfe396fb64 fix: separate WebApp analytics from console page tracking (#41251) 2026-08-26 03:02:38 +00:00
Pranav Agarwal
6b5952425d fix(api): enforce RBAC permission on agent app detail GET endpoint (#41264) (#41265) 2026-08-26 02:51:13 +00:00
samzong
1299d99b2d feat(cli): add a compact command map for agent discovery (#41226)
Signed-off-by: samzong <samzong.lu@gmail.com>
2026-08-26 01:13:07 +00:00
yyh
36dfc5abd3 fix(web): upgrade Next.js to 16.3.3 (#41262) 2026-08-26 00:32:30 +00:00
yyh
c7520d7c71 refactor(ci): trim redundant autofix work (#41259) 2026-08-25 13:10:06 +00:00
yyh
798a238e63 fix(web): align goto anything autocomplete behavior (#41258) 2026-08-25 12:39:07 +00:00
yyh
db5121828e fix(agent-v2): refine access card interactions (#41254) 2026-08-25 12:25:49 +00:00
yyh
ed184a6632 fix(web): restore AI credits icon (#41257) 2026-08-25 12:22:49 +00:00
Stephen Zhou
92016aa139 fix(api): align Service API OpenAPI contract (#41248)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-25 10:54:14 +00:00
Yunlu Wen
09a855dcef chore: bump version to 1.17.0 (#41247)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-25 10:52:21 +00:00
wangxiaolei
a6fe278662 feat: hide add library skill in agent ui (#41250)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-25 10:26:06 +00:00
林玮 (Jade Lin)
960018253b feat(api): track credit usage contexts (#41181)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-25 10:21:32 +00:00
yyh
8abff1b0b1 refactor(web): migrate dataset rename form (#41233) 2026-08-25 09:41:37 +00:00
yyh
eb7a1829fe refactor(web): migrate pipeline DSL import form (#41232) 2026-08-25 09:41:36 +00:00
yyh
9ce92d8605 fix(ui): add tabs panel focus treatment (#41231) 2026-08-25 09:41:36 +00:00
wangxiaolei
ddcdbb2d14 feat: add skill enable feature switch (#41246) 2026-08-25 08:42:15 +00:00
Joel
18d3990ae6 fix: show a not-allowed cursor for the prompt editor in build mode (#41243) 2026-08-25 07:43:32 +00:00
zxhlyh
03e4bc50a2 fix(skills): align Japanese Skills page title (#41242) 2026-08-25 07:42:23 +00:00
Joel
370efc493c fix: prevent sidebar footer actions from triggering hover preview (#41230) 2026-08-25 06:53:52 +00:00
Rafli Surya Wijaya
cf236aed8a fix(api): enforce APP_EDIT RBAC permission on MCP server refresh endpoint (#41235) 2026-08-25 06:38:13 +00:00
Asuka Minato
48315be2b1 test: migrate RAG variable controller sessions and ORM models to SQLite (#40609)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-25 06:20:48 +00:00
Asuka Minato
a9035a3264 test: migrate step-by-step tour sessions to SQLite (#40636) 2026-08-25 06:20:13 +00:00
Asuka Minato
39ce9f68fc test(vdb): use real ORM models in provider tests (#40687) 2026-08-25 06:19:59 +00:00
Asuka Minato
ed9d3801ba test: move pure integration cases to unit tests (#40705)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-25 06:19:48 +00:00
Asuka Minato
f3bb73b671 test: centralize app and account config overrides (#40860) 2026-08-25 06:19:35 +00:00
wangxiaolei
bc845ea748 fix: fix skill display name rename conflict (#41229) 2026-08-25 06:13:47 +00:00
非法操作
1afac21690 feat(api): support New Agent Beta for inline Workflow agents (#41215) 2026-08-25 05:39:41 +00:00
samzong
dd2e691ea8 fix(cli): declare write effect for import and context-switch commands (#41225)
Signed-off-by: samzong <samzong.lu@gmail.com>
2026-08-25 05:32:55 +00:00
非法操作
f360a7bf94 fix: allow OAuth trigger builder verification without credentials (#39199)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-25 05:32:27 +00:00
Xiyuan Chen
caf7e90ca5 test(cli): tsconfig gap (#41224) 2026-08-25 05:11:44 +00:00
Alexey Shabalin
1e501cd75b fix(web): stop digits rendering in the emoji font (#41201)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-25 05:04:23 +00:00
盐粒 Yanli
45cb385d77 fix(docker): harden agent compose configuration (#41065) 2026-08-25 04:41:51 +00:00
Yunlu Wen
679514d40a perf(agent): exponential backoff in polling to prevent forking storm (#41222) 2026-08-25 04:36:46 +00:00
Yunlu Wen
9643dcacce perf(agent): polling interval tuning (#41221) 2026-08-25 03:50:22 +00:00
Joel
fcb380044c fix: enforce read-only agent resources in build mode (#41220) 2026-08-25 03:37:02 +00:00
向阳
d4ed92cb0a perf(api): batch recommended app catalog lookups (#41218) 2026-08-25 03:23:52 +00:00
Joel
12f259ecae fix: address Agent UX feedback (#41219) 2026-08-25 03:14:24 +00:00
Aarush Karak
d9ca69d34f fix(types): replace any[] with ThoughtItem[] for agent_thoughts (#41141)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-25 02:08:51 +00:00
Taranum Wasu
ebb0f12cd0 fix(model): re-sign bare/backticked tool file URLs in answer (#40799)
Co-authored-by: Taranum01 <50813317+Taranum01@users.noreply.github.com>
Co-authored-by: Taranum01 <taranumwasu@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Crazywoola <100913391+crazywoola@users.noreply.github.com>
2026-08-25 02:04:21 +00:00
zxhlyh
667d66c4dc fix(skills): align management UI accessibility and localization (#41180)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-08-25 01:53:19 +00:00
非法操作
ef92cb7c65 refactor(api): complete account education boundary (#41153)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-25 01:48:12 +00:00
dependabot[bot]
c9ea15a968 chore(deps): bump boto3 from 1.43.71 to 1.43.76 in /api in the storage group (#41126)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 10:00:23 +08:00
Jingyi
b56d30b366 fix(web): refresh model provider version after update (#41212) 2026-08-25 01:12:01 +00:00
yyh
8d6128957f fix(web): restore model selector popup spacing (#41190) 2026-08-24 15:05:22 +00:00
Joel
e25ca611d8 fix: failed e2e test in agent caused by changed copywriting (#41187) 2026-08-24 13:16:37 +00:00
Joel
44aec257bf fix: avoid copy-sensitive agent file toast assertion (#41179) 2026-08-24 10:33:25 +00:00
Joel
2be3c3d69e fix: refine agent configuration copy (#41177) 2026-08-24 09:58:06 +00:00
yyh
09b199b280 fix: keep pagination skeletons in app grid (#41175) 2026-08-24 09:35:54 +00:00
github-actions[bot]
b9764c4779 chore(i18n): sync translations with en-US (#41173)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
2026-08-24 09:08:26 +00:00
Harsh Kashyap
ff3cf5681f fix(api): attach exc_info=True to 11 logger.debug/info('...: %s', str(e)) sites — sibling of #41066 (DEBUG/INFO level) (#41077)
Co-authored-by: Harsh Kashyap <harsh23kashyap@gmail.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-24 09:08:08 +00:00
Asuka Minato
cfae2515fd test: remove obsolete integration coverage (#40704) 2026-08-24 09:06:29 +00:00
yyh
b5982927de fix: align main navigation card grids (#41167) 2026-08-24 09:04:30 +00:00
Xiyuan Chen
28a4859218 fix(api): respect configured model params in prompt generator (#41165) 2026-08-24 09:02:34 +00:00
QuantumGhost
4f8a4c20fd ci: add HITL IM test environment deployment (#41171) 2026-08-24 08:54:27 +00:00
Crazywoola
21d540f760 feat(web): enhance Go to Anything navigation and discovery (#41160) 2026-08-24 08:48:53 +00:00
Joel
fef28097cc fix: preserve agent task editor focus when inserting commands (#41166) 2026-08-24 08:33:29 +00:00
yyh
3de20e0ba8 fix: keep home intro without banner (#41155) 2026-08-24 08:30:55 +00:00
yyh
703f6d1780 fix(web): align agent v2 monitoring translations (#41161) 2026-08-24 08:21:41 +00:00
-LAN-
2e1620c1ac chore(api): enforce backend layer boundaries (#41159) 2026-08-24 08:17:11 +00:00
zyssyz123
4847447635 fix(agent): allow saving model-less inline Agent drafts (#41147) 2026-08-24 07:57:14 +00:00
Joel
7ce751ffe8 chore: show full agent save time on hover (#41148)
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-08-24 07:53:31 +00:00
Joel
7cb9aa2d3b fix: align duplicate agent name behavior with agent forms (#41156) 2026-08-24 07:48:33 +00:00
yyh
3ab8476d2d fix(web): align agent v2 monitoring copy (#41150) 2026-08-24 07:36:18 +00:00
yyh
643e2d66bf chore(deps): upgrade pnpm workspace dependencies (#41138) 2026-08-24 07:07:47 +00:00
Joel
693e1b0fc5 fix: show checklist errors for inline agents without models (#41146) 2026-08-24 06:57:34 +00:00
林玮 (Jade Lin)
70a513fc62 fix(api): scope hosted credits by tenant plan (#41131)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-24 06:35:42 +00:00
FFXN
bec4ef6ac7 fix: fix upload for plugin (#41133)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-24 05:39:27 +00:00
wangxiaolei
0024a845f7 feat: support agent skill (#39675)
Signed-off-by: kenwoodjw <blackxin55+@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
Co-authored-by: 起岚 <155608604+xiaoyaoqilan@users.noreply.github.com>
Co-authored-by: Parman Mohammadalizadeh <prmma23@gmail.com>
Co-authored-by: Escape0707 <tothesong@gmail.com>
Co-authored-by: Xin Zhang <zhangxin@dify.ai>
Co-authored-by: iridescentWen <66297467+iridescentWen@users.noreply.github.com>
Co-authored-by: Bond Zhu <37842169+MRZHUH@users.noreply.github.com>
Co-authored-by: KVOJJJin <jzongcode@gmail.com>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: 非法操作 <hjlarry@163.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: Chester <superdiao6@gmail.com>
Co-authored-by: WH-2099 <wh2099@pm.me>
Co-authored-by: Jony <619963502@qq.com>
Co-authored-by: Jony <13896935+zyz619963502zyz@users.noreply.github.com>
Co-authored-by: Harsh Kashyap <harsh.kashyap2001@gmail.com>
Co-authored-by: Harsh Kashyap <Harsh23Kashyap@users.noreply.github.com>
Co-authored-by: kenwoodjw <blackxin55+@gmail.com>
Co-authored-by: csurong <csurong1@gmail.com>
Co-authored-by: caosurong <surong.cao@thinkingdata.cn>
Co-authored-by: Taranum Wasu <81034301+Taranum01@users.noreply.github.com>
Co-authored-by: Taranum01 <50813317+Taranum01@users.noreply.github.com>
Co-authored-by: Pranav Agarwal <agarwalpranav0711@gmail.com>
Co-authored-by: Stephen Zhou <hi@hyoban.cc>
Co-authored-by: zl86790 <marshal_li_b@163.com>
Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com>
Co-authored-by: yunlu.wen <yunlu.wen@dify.ai>
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-08-24 05:29:14 +00:00
Crazywoola
0ab88ef36a chore: Replace star image with new asset link (#41136) 2026-08-24 04:33:24 +00:00
yyh
7b3d97cb42 refactor(web): migrate pipeline publishing input form (#41129) 2026-08-24 04:05:39 +00:00
yyh
429edf4d82 refactor(web): migrate pipeline template input form (#41128) 2026-08-24 04:05:38 +00:00
yyh
5a9b6cb239 refactor(web): migrate duplicate app input form (#41127) 2026-08-24 04:05:37 +00:00
Asuka Minato
bda6bf3fee test: migrate trigger and webhook sessions and ORM models to SQLite (#40594) 2026-08-24 04:01:02 +00:00
Joel
24287bcb0c fix: stabilize the agent tool picker placement (#41132) 2026-08-24 03:38:57 +00:00
AsperforMias
3f7e6cdcb4 fix(tts): propagate and validate actual audio MIME (#41043)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: WH-2099 <wh2099@pm.me>
2026-08-24 03:38:09 +00:00
Benjamin
ea6c15b68e fix: fall back to the batch API when the global plugin manifest is empty (#41107)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 03:33:23 +00:00
dependabot[bot]
77998e41e4 chore(deps): bump gunicorn from 26.0.0 to 26.1.0 in /api in the flask group (#41118)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 03:28:14 +00:00
酒诗
aed1672366 refactor: pass session to workflow comment account accessors (#41117)
Co-authored-by: geneachina <geneachina@gmail.com>
2026-08-24 03:20:32 +00:00
dependabot[bot]
11c922b5d4 chore(deps): bump the github-actions-dependencies group with 4 updates (#41120)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 03:17:12 +00:00
XIIRUAN
ccd0f4b15d refactor(web): use shared session factory for JWT decoding (#41007) 2026-08-24 03:16:44 +00:00
lei_lei
ab34f9ae09 fix(workflow): honor stop between non-streaming nodes (#41090)
Co-authored-by: Crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: crazywoola <427733928@qq.com>
2026-08-24 02:51:20 +00:00
ump45nose
9385e583ff fix(agent): avoid empty Bedrock schema fields (#40443)
Co-authored-by: Crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-24 02:08:11 +00:00
非法操作
5018f8aa8a refactor(api): standardize session factory wiring (#41085) 2026-08-24 01:57:17 +00:00
非法操作
ed45016475 refactor(api): standardize console billing portal errors (#41055)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-24 01:55:48 +00:00
陈志谦
9d6c846a49 docs: fix dead anchors in localized READMEs and stale dify-agent link (#41101) 2026-08-24 01:53:59 +00:00
yyh
8bdf702f73 fix(agent-v2): prevent workflow refresh render loop (#41105) 2026-08-23 08:56:02 +00:00
yyh
c62947f1f1 refactor(dify-ui): rename radio family entrypoint (#41104) 2026-08-23 08:08:29 +00:00
Pranav Agarwal
24f5e2ac69 fix(api): enforce admin and RBAC permissions on default model and provider models GET endpoints (#41099) (#41100) 2026-08-23 07:33:57 +00:00
yyh
e18310e52e refactor(dify-ui): simplify overlay content APIs (#41098) 2026-08-22 23:54:08 +00:00
Pranav Agarwal
07119e3f00 fix(api): enforce admin and RBAC permissions on data source integrates GET endpoint (#41079) (#41080) 2026-08-21 17:57:23 +00:00
yyh
a9b8c84e9b refactor(dify-ui): simplify popover content API (#41069) 2026-08-21 13:11:40 +00:00
盐粒 Yanli
35a4533889 refactor(agent): simplify Agent v2 output contract (#41062)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-21 11:55:25 +00:00
Blackoutta
3a8fbcbd02 feat(trace): add provider-neutral unified tracing (#39451)
Co-authored-by: EvoX <evox@evomap.ai>
2026-08-21 11:28:18 +00:00
mesrop2008
23150c2319 fix(api): attach exc_info=True to logger.warning sites that drop the traceback (#41068) 2026-08-21 11:20:42 +00:00
Xiyuan Chen
d6147456e3 feat: Enterprise Home Snapshot — runtime endpoints and dify-agent adapter (#40996) 2026-08-21 10:39:36 +00:00
gegemeimingzi
7d5c68c2e1 fix(web): prevent signin page content from overflowing on mobile (#40921)
Co-authored-by: gegemeimingzi <gegemeimingzi@users.noreply.github.com>
2026-08-21 10:36:31 +00:00
Rafli Surya Wijaya
385e8662bb fix(vdb): add document_id keyword mapping and fix VectorType in elasticsearch-ja adapter (#40400)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-21 10:35:54 +00:00
yyh
6cb96adfbe refactor(dify-ui): compose dialog close controls (#41064) 2026-08-21 10:21:43 +00:00
Yunlu Wen
373f33fd3b fix: add missing metrics back and enhance typing (#40935)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-21 09:46:45 +00:00
yyh
faca558bf5 fix(web): prevent Safari clipping tour recovery prompt (#41060) 2026-08-21 09:19:38 +00:00
Byron.wang
b38d907ede refactor(api): finish account controller application boundary (#40440) 2026-08-21 08:46:06 +00:00
Carson Yang
0c3d08f355 docs: add Sealos deployment option (#41026) 2026-08-21 08:15:35 +00:00
Byron.wang
a52adef203 refactor(api): extract change email state machine (#40439) 2026-08-21 08:13:23 +00:00
Byron.wang
3b05b5e553 refactor(api): move account lifecycle into application services (#40438) 2026-08-21 08:05:43 +00:00
Asuka Minato
92978f6d9f test: migrate console dataset segment sessions and ORM models to SQLite (#40515)
Co-authored-by: Byron.wang <byron@dify.ai>
2026-08-21 06:50:14 +00:00
Asuka Minato
6fe87aaca5 test: migrate residual app generator ORM models to SQLite (#40580)
Co-authored-by: Byron.wang <byron@dify.ai>
2026-08-21 06:49:58 +00:00
Asuka Minato
37a927bf63 test: migrate console agent skill ORM models to SQLite (#40570)
Co-authored-by: Byron.wang <byron@dify.ai>
2026-08-21 06:49:44 +00:00
yyh
44e89d8844 refactor(web): migrate external API input fields (#41052) 2026-08-21 06:38:21 +00:00
yyh
7b1339a156 refactor(web): migrate subscription confirmation input (#41051) 2026-08-21 06:38:21 +00:00
yyh
2ff3f7b533 refactor(web): migrate human input timeout field (#41050) 2026-08-21 06:38:20 +00:00
potoior
34fdac2113 fix: merge system messages and place them first to comply with Qwen/v… (#39136) 2026-08-21 06:24:20 +00:00
Asuka Minato
7d635d9f7e test: migrate auth and access controller ORM models to SQLite (#40572)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-21 06:12:29 +00:00
Asuka Minato
0ce41bfea1 test: migrate console app sessions and ORM models to SQLite (#40559) 2026-08-21 06:12:08 +00:00
Asuka Minato
92b93c9d37 test: migrate remaining controller sessions and ORM models to SQLite (#40538)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-21 06:11:44 +00:00
Asuka Minato
4692741aa0 test: migrate Agent App session store to SQLite ORM (#40638) 2026-08-21 06:10:31 +00:00
Asuka Minato
4ca0a10088 test: migrate vector space admission sessions and ORM models to SQLite (#40640) 2026-08-21 06:10:13 +00:00
Asuka Minato
6ce0976265 test: add tmp_path type (#40724)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-21 06:09:39 +00:00
Asuka Minato
5827ce2adf test: migrate annotation sessions and ORM models to SQLite (#40504) 2026-08-21 06:09:26 +00:00
Byron.wang
1d79b87307 refactor(api): move account access behind application services (#40437) 2026-08-21 05:56:30 +00:00
zl86790
9d8dacd0c8 fix(api): prevent dropped workflow_started events in Redis Streams (#40964)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com>
2026-08-21 03:46:50 +00:00
yyh
720e1fab9b refactor(dify-ui): expose slider anatomy (#41045) 2026-08-21 03:15:45 +00:00
yyh
ca6f4e11f9 docs(dify-ui): establish documentation hierarchy (#41044) 2026-08-21 02:29:41 +00:00
WH-2099
a5e284e6b1 fix(plugin): protect endpoint listings (#40897) 2026-08-21 02:17:58 +00:00
Stephen Zhou
3d08b2f89a fix(web): route enterprise documentation links (#40957) 2026-08-21 02:13:26 +00:00
Pranav Agarwal
43e59d3bc5 fix(api): enforce edit and RBAC permissions on datasource credential …fix(api): enforce edit and RBAC permissions on datasource credential GET endpoint (#41040) (#41041) 2026-08-21 02:11:46 +00:00
WH-2099
edc805c453 fix(api): enforce app-scoped access on indirect resources (#40893) 2026-08-21 00:52:39 +00:00
Ingram Z
0ca99312c1 fix(agent): align Binding file upload timeouts (#41027) 2026-08-20 18:59:44 +00:00
wangxiaolei
785ca4b915 fix: when rbac enabled is true, not check_dataset_permission (#41022) 2026-08-20 14:45:40 +00:00
dependabot[bot]
c7b5a455f5 chore(deps): bump google-cloud-aiplatform from 1.160.0 to 1.164.0 in /api in the google group across 1 directory (#39249)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-20 13:48:44 +00:00
dependabot[bot]
0c698744ab chore(deps): bump the storage group across 1 directory with 2 updates (#40393)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-20 13:47:50 +00:00
hyhyf
e425e18fca fix: add MESSAGE_TRACE task on chat pipline error events (#39129)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-20 13:44:22 +00:00
yyh
f4e50f3c8a refactor(web): migrate empty dataset input (#41030) 2026-08-20 13:38:53 +00:00
yyh
aba3f9c1d6 refactor(web): migrate document rename input (#41029) 2026-08-20 13:38:52 +00:00
yyh
979c8cc34b refactor(web): migrate conversation rename input (#41028) 2026-08-20 13:38:51 +00:00
Asuka Minato
502418f707 test: centralize typed runtime config overrides (#40857)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-20 13:30:04 +00:00
蓝破碎半圆/破碎蓝调
ccfb47d2c5 feat: support ODT document extraction (#39973)
Co-authored-by: Crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
2026-08-20 13:23:03 +00:00
非法操作
1bb9183af9 refactor(api): replace legacy recommended app retrieval stack (#40395) 2026-08-20 13:13:43 +00:00
非法操作
de7e43a7ef refactor(api): decouple recommended app runtime admission (#40257) 2026-08-20 13:13:43 +00:00
非法操作
b09691e5d2 refactor(api): isolate trial app usage writes (#40208) 2026-08-20 13:13:42 +00:00
GGbond
6f70ca8d26 refactor(api): decouple console tag management (#40843)
Co-authored-by: Byron Wang <byron@dify.ai>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-20 12:31:01 +00:00
Fasih Ur Rahman
a7a30b20af fix(api): add missing admin and RBAC permission decorators to model credentials GET endpoints (#40962) 2026-08-20 12:15:13 +00:00
盐粒 Yanli
cbd78c0073 fix(agent): clean legacy Agent Soul files (#41024) 2026-08-20 10:34:11 +00:00
盐粒 Yanli
97a3039a2d refactor(dify-agent): use Workspace as shell temp space (#41023) 2026-08-20 10:16:05 +00:00
盐粒 Yanli
005edb7d47 fix(dify-agent): add reliable shellctl stdio mode (#39997) 2026-08-20 10:10:00 +00:00
盐粒 Yanli
b034368b3b fix(dify-agent): adjust local sandbox home paths (#41021) 2026-08-20 10:00:01 +00:00
Pranav Agarwal
43b6d8e32e fix(api): enforce admin and RBAC permissions on tool OAuth custom client GET endpoint (#40944) (#40945) 2026-08-20 09:10:02 +00:00
FFXN
248df5dd25 fix(snippet): support snippet history version export (#41012)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-20 08:52:45 +00:00
非法操作
f97fc01c2f refactor(api): extract recommended app query service (#40207) 2026-08-20 08:45:47 +00:00
林玮 (Jade Lin)
a6ace29e1a fix(api): handle suspended email domains (#41004)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-20 08:04:05 +00:00
盐粒 Yanli
013b328555 fix(agent): complete ownership-aware deletion lifecycle (#40888)
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-08-20 07:48:02 +00:00
chelsealong
d7b1531431 fix(snippets): implement delete for published snippet workflow versions (#41003)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-20 07:36:17 +00:00
yyh
63acefa6d0 fix(billing): remove misleading tax assurance (#41009) 2026-08-20 07:33:20 +00:00
Stephen Zhou
e2f6418fe4 chore: inline custom ESLint rules (#41008) 2026-08-20 07:32:13 +00:00
yyh
2daa69c92e fix(web): clarify agent deletion consequences (#41002) 2026-08-20 06:45:55 +00:00
yyh
72b6a4e6ed refactor: consolidate frontend browser test infrastructure (#40997) 2026-08-20 06:13:30 +00:00
yyh
b16a24f084 refactor(web): migrate install account fields (#40992) 2026-08-20 05:54:30 +00:00
Asuka Minato
6e890524db test: migrate snippet service ORM models to SQLite (#40567) 2026-08-20 05:12:30 +00:00
Asuka Minato
e27eb51f8a test: migrate media and execution service sessions and ORM models to SQLite (#40552) 2026-08-20 05:11:46 +00:00
Asuka Minato
2155adff5a test: migrate Explore controller sessions and ORM models to SQLite (#40537) 2026-08-20 05:10:49 +00:00
Asuka Minato
c2c1876484 test: migrate document service sessions and ORM models to SQLite (#40536) 2026-08-20 05:10:13 +00:00
非法操作
48f338e649 refactor(api): extract web app runtime bootstrap (#40949) 2026-08-20 05:05:31 +00:00
Byron.wang
b4e13ec83e refactor(api): add account profile application service (#40436)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-20 05:03:08 +00:00
yyh
11f830a17b ci(web): run browser tests on GitHub Ubuntu (#40995) 2026-08-20 04:24:32 +00:00
yyh
5550ec8ebf fix(web): redirect after deleting an agent (#40994) 2026-08-20 04:09:50 +00:00
yyh
ea59256349 refactor(web): migrate signup password fields (#40991) 2026-08-20 03:29:14 +00:00
yyh
0f1536585e refactor(web): migrate signup email field (#40990) 2026-08-20 03:29:13 +00:00
林玮 (Jade Lin)
602a646dbd fix(api): enforce dataset document ownership (#40989) 2026-08-20 03:24:45 +00:00
Amogh Bhat Nagri
54ffbc7f0f fix(logstore): normalize workflow timestamps to naive UTC (#40947)
Co-authored-by: wangxiaolei <fatelei@gmail.com>
2026-08-20 02:58:07 +00:00
WH-2099
ae20d64304 fix(ci): run Playwright E2E on GitHub Ubuntu (#40986) 2026-08-20 02:48:27 +00:00
非法操作
ee997d00fa refactor(api): reuse web app access queries for permissions (#40984) 2026-08-20 02:27:49 +00:00
yyh
98c2ffeec7 fix(ui): align toast swipe direction with viewport (#40980) 2026-08-19 13:16:12 +00:00
yyh
8e1c259a66 fix(web): prevent workflow errors after app deletion (#40977) 2026-08-19 12:24:40 +00:00
盐粒 Yanli
ea3fd4c093 perf(dify-agent): parallelize Agent Stub config pulls (#40973) 2026-08-19 10:35:24 +00:00
盐粒 Yanli
930f4e6c26 fix(dify-agent): persist interrupted run history (#40972) 2026-08-19 10:35:13 +00:00
非法操作
356e3a8ab6 refactor(api): snapshot explore banner feature flag (#40555) 2026-08-19 10:34:37 +00:00
非法操作
532489babd refactor(api): extract web app access queries (#40482) 2026-08-19 10:34:36 +00:00
yyh
6fa9c14490 fix(ui): stabilize toast stack interactions (#40970) 2026-08-19 10:13:43 +00:00
zyssyz123
d0149c3f94 fix(agent): resolve suggested questions from runtime config (#40963) 2026-08-19 07:54:38 +00:00
非法操作
2f6e782215 refactor(api): reuse app definition queries for service API site (#40795) 2026-08-19 07:23:47 +00:00
zyssyz123
414f211cde fix(auth): harden email code login verification (#40960) 2026-08-19 06:57:47 +00:00
Pranav Agarwal
f1b48a93a9 fix(api): enforce edit and RBAC permissions on model provider credential GET endpoints (#40899) (#40900) 2026-08-19 06:48:15 +00:00
盐粒 Yanli
8874f3c80b refactor(agent): remove Agent Drive (#40887)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-19 06:34:36 +00:00
yyh
297fdc7cb8 refactor(web): migrate HTTP timeout inputs (#40953) 2026-08-19 06:30:37 +00:00
yyh
73b15fc562 refactor(web): compose reset password fields (#40952) 2026-08-19 06:30:37 +00:00
yyh
90a6b8c998 refactor(web): simplify debounced filter state (#40951) 2026-08-19 06:30:36 +00:00
Riskey
8d18931125 fix: refine access point copy and remove undeploy state warning (#40886) 2026-08-19 04:11:44 +00:00
Nguyễn Phương
7444f96a1d refactor: clean away unnecessary-type-conversion (bool/str no-op wrappers) (#40822) 2026-08-19 03:48:45 +00:00
Wu Tianwei
8a33a9adb5 fix(web): remove only-me resource access option (#40956) 2026-08-19 02:50:07 +00:00
yyh
b78a3462c9 chore(web): align browser support targets (#40938) 2026-08-18 14:21:18 +00:00
zyssyz123
c38fb16efa fix(agent): retain usage for incomplete runs (#40937) 2026-08-18 13:11:22 +00:00
Asuka Minato
2400c6c59d test: migrate dataset service sessions and ORM models to SQLite (#40534) 2026-08-18 12:35:57 +00:00
yyh
324ddb361d fix(web): gate system features requests by connection (#40939) 2026-08-18 12:34:34 +00:00
Asuka Minato
4f81005662 test: migrate app runtime sessions and ORM models to SQLite (#40590) 2026-08-18 12:27:32 +00:00
Asuka Minato
f2dfa462e1 test: migrate agent runtime sessions and ORM models to SQLite (#40587) 2026-08-18 12:26:58 +00:00
Asuka Minato
14bc28ffbe test: migrate residual controller sessions and ORM models to SQLite (#40579) 2026-08-18 12:26:33 +00:00
Asuka Minato
5afce305e7 test: migrate residual service and task ORM models to SQLite (#40576) 2026-08-18 12:26:01 +00:00
Asuka Minato
beecf5730a test: migrate telemetry and core entity ORM models to SQLite (#40574) 2026-08-18 12:25:42 +00:00
Asuka Minato
eeda983b17 test: migrate service boundary sessions and ORM models to SQLite (#40533)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-18 12:23:33 +00:00
Asuka Minato
e2e3521874 test: migrate residual model sessions and ORM models to SQLite (#40531) 2026-08-18 12:23:02 +00:00
Asuka Minato
303ce70ac4 test: migrate app model sessions and ORM models to SQLite (#40530) 2026-08-18 12:22:33 +00:00
Asuka Minato
5fde1d096e test: eliminate remaining whole config mocks (#40855) 2026-08-18 12:01:49 +00:00
Asuka Minato
3bcd97b643 test: scope plugin and datasource config overrides (#40854) 2026-08-18 12:01:29 +00:00
Asuka Minato
20fdbcf36f test: centralize enterprise config overrides (#40853) 2026-08-18 12:01:01 +00:00
Asuka Minato
c7f4422333 test: keep integration config overrides typed (#40850) 2026-08-18 12:00:32 +00:00
Asuka Minato
da6ca23b50 test: use typed config overrides in service coverage (#40848) 2026-08-18 11:59:58 +00:00
yyh
b064a13aa4 refactor(web): remove legacy embedded API docs (#40929) 2026-08-18 09:53:55 +00:00
yyh
45e2d81be4 fix(ci): align Oxlint suppression formatting (#40934) 2026-08-18 09:41:48 +00:00
yyh
fe41e80f33 refactor(web): remove legacy tag component (#40928) 2026-08-18 07:58:47 +00:00
yyh
a2ac8ad106 refactor(web): migrate clearable search inputs (#40924) 2026-08-18 07:55:08 +00:00
yyh
bd4cfd5a7a refactor(web): migrate retry units to number field (#40923) 2026-08-18 07:55:07 +00:00
chenshiyang
0e975f6305 refactor(models): dep-inject Session on remaining @property accessors (#40797)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-18 07:23:09 +00:00
dependabot[bot]
c94846ee2a chore(deps): bump sqlparse from 0.5.4 to 0.6.0 in /api (#40904)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 06:50:31 +00:00
yyh
9ce463fc24 refactor(web): compose search prefixes with input group (#40922) 2026-08-18 06:04:37 +00:00
zyssyz123
5a0a12cb44 fix: preserve Agent tool-generated file outputs (#40917) 2026-08-18 05:59:43 +00:00
Byron.wang
ba61ab4f9f refactor(api): extract account activation application service (#40421)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-18 05:27:40 +00:00
Byron.wang
891627b210 refactor(api): decouple data source API key auth flow (#40452)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-18 04:30:17 +00:00
蓝破碎半圆/破碎蓝调
ba5e7d76fd fix: accept legacy plugin daemon list responses (#38265) 2026-08-18 04:11:49 +00:00
yyh
809a6cff1e refactor(web): standardize label search inputs (#40914) 2026-08-18 04:05:26 +00:00
yyh
32c11fd06e refactor(web): standardize dataset search inputs (#40913) 2026-08-18 04:05:25 +00:00
yyh
04d2d9ff19 refactor(web): standardize app log search inputs (#40912) 2026-08-18 04:05:25 +00:00
yyh
05499b9011 test(dify-ui): stabilize dropdown menu story (#40916) 2026-08-18 04:05:24 +00:00
Harsh Kashyap
ca32e87683 fix(api): bound connect phase to 5.0s in two api/core/ outbound timeouts (#40807)
Co-authored-by: Harsh Kashyap <Harsh23Kashyap@users.noreply.github.com>
2026-08-18 03:38:59 +00:00
盐粒 Yanli
122368f5e0 chore(dify-agent): disable Landlock for E2B templates (#40892) 2026-08-18 03:30:00 +00:00
盐粒 Yanli
c267e73ca6 fix(dify-agent): use E2B traffic access header (#40891) 2026-08-18 03:29:50 +00:00
盐粒 Yanli
c649b84bf6 fix(dify-agent): isolate cancellation intent observer (#40896) 2026-08-18 03:29:42 +00:00
yyh
09f8f43d20 fix(ci): use default Ubuntu mirror for Playwright (#40909) 2026-08-18 02:55:17 +00:00
mesrop2008
50d6c19ef4 fix(api): clear stale cancellation signals when resuming a paused workflow (#40905) 2026-08-18 02:27:43 +00:00
yyh
6d89557355 feat(dify-ui): add input group composition (#40890) 2026-08-18 02:14:39 +00:00
Amogh Bhat Nagri
30c304c6a7 fix(api): repair two malformed logger format strings (#40901) 2026-08-18 02:10:59 +00:00
yyh
76a91eb433 refactor(dify-ui): expose autocomplete popup anatomy (#40895) 2026-08-18 02:06:03 +00:00
Chengxiao
394b43376a fix: suggest deepseek-v4-flash for Agent mode (#40852) 2026-08-18 01:57:21 +00:00
Asuka Minato
02f0e8cffa test: isolate config inputs from process environment (#40847) 2026-08-17 13:09:17 +00:00
Asuka Minato
3f0a9c5060 test: migrate RAG indexing sessions and ORM models to SQLite (#40513) 2026-08-17 12:13:02 +00:00
Asuka Minato
ce1aed8df5 test: migrate RAG pipeline sessions and ORM models to SQLite (#40511) 2026-08-17 12:12:46 +00:00
Asuka Minato
c3d090fe4e test: migrate Service API dataset metadata sessions and ORM models to SQLite (#40518) 2026-08-17 12:12:06 +00:00
Asuka Minato
d92cf5d198 test: migrate Service API dataset document sessions and ORM models to SQLite (#40519) 2026-08-17 12:11:53 +00:00
Asuka Minato
ccf4d25321 test: migrate console workflow controller sessions and ORM models to SQLite (#40527) 2026-08-17 12:11:34 +00:00
Asuka Minato
4e02075f8d test: migrate console app MCP sessions and ORM models to SQLite (#40526) 2026-08-17 12:11:12 +00:00
Asuka Minato
0f358f5bb3 test: migrate console conversation sessions and ORM models to SQLite (#40528) 2026-08-17 12:10:57 +00:00
Asuka Minato
a575ae5ef2 test: migrate controller auth wrapper sessions and ORM models to SQLite (#40529) 2026-08-17 12:10:44 +00:00
盐粒 Yanli
2aaac4dadb feat(dify-agent): enforce signed upload size limits (#40883) 2026-08-17 11:59:42 +00:00
盐粒 Yanli
bec3890689 fix(dify-agent): enforce short-lived Agent Stub authorization (#40884) 2026-08-17 11:48:12 +00:00
非法操作
e3ab88f6de feat: dispatch New Agent Beta eligibility checks (#40725)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-17 10:33:50 +00:00
盐粒 Yanli
a679ee5644 fix(dify-agent): preserve snapshots for failed and cancelled runs (#40876) 2026-08-17 10:11:22 +00:00
盐粒 Yanli
96809473fc feat(dify-agent): add context-aware history compaction (#40872) 2026-08-17 09:36:21 +00:00
林玮 (Jade Lin)
5ef8becd25 feat: create accounts with client IP (#40875) 2026-08-17 09:29:57 +00:00
盐粒 Yanli
4249a0d052 fix(dify-agent): require E2B traffic authentication (#40871) 2026-08-17 08:35:53 +00:00
zyssyz123
64587eae43 fix(workflow): preserve historical Agent node compatibility (#40859) 2026-08-17 07:31:38 +00:00
yyh
4ce681d42f test(e2e): align agent model selector locators (#40862) 2026-08-17 06:48:00 +00:00
yyh
59c6a470bb fix(web): update agent model request limit copy (#40863) 2026-08-17 06:37:23 +00:00
无能的迪拜巧克力
b02072baee fix(web): let multimodal guidance height fit content (#40846) 2026-08-17 06:10:06 +00:00
Bond Zhu
f6a472d948 feat(ci): sync E2B template on release tag push (#40858) 2026-08-17 06:06:46 +00:00
Weike Zhang
45be4620fe refactor(controllers/web): dep-inject login and forgot-password payloads (#36659) (#40856) 2026-08-17 05:58:40 +00:00
dependabot[bot]
08993dd622 chore(deps): bump the github-actions-dependencies group with 2 updates (#40834)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 02:50:41 +00:00
YungLe
219596c4df feat(plugin-auth): visibility picker for OAuth credentials (#39840)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Wu Tianwei <30284043+WTW0313@users.noreply.github.com>
Co-authored-by: twwu <twwu@dify.ai>
2026-08-17 02:41:26 +00:00
yyh
facb60abfe fix(web): align combobox interaction semantics (#40826) 2026-08-17 02:13:08 +00:00
zy
141cbf9832 fix(api): support audio/x-m4a for ASR (#40776)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-16 02:47:31 +00:00
Asuka Minato
8fb5dd8587 test: migrate agent controller sessions and ORM models to SQLite (#40600)
Co-authored-by: Byron Wang <byron@dify.ai>
2026-08-15 15:15:28 +00:00
Asuka Minato
1651bcc774 test: migrate residual controller sessions and ORM models to SQLite (#40606)
Co-authored-by: Byron Wang <byron@dify.ai>
2026-08-15 15:15:11 +00:00
mesrop2008
c2e68335d4 docs: fix pipeline_generator docstrings to reference pipeline instead of app_model (#40813) 2026-08-15 12:09:16 +00:00
Asuka Minato
177edaa1c6 test: migrate annotation security sessions and ORM models to SQLite (#40604) 2026-08-15 09:54:29 +00:00
Asuka Minato
edb32fba92 test: migrate RAG processor residuals to SQLite ORM models (#40608) 2026-08-15 09:54:09 +00:00
Asuka Minato
959cacdc00 test: migrate agent surface sessions and ORM models to SQLite (#40602) 2026-08-15 09:53:48 +00:00
Asuka Minato
582c73bc3b test: migrate residual user ORM stand-ins to real models (#40625) 2026-08-15 09:52:59 +00:00
Asuka Minato
6eca353804 test: migrate file controller ORM models to real entities (#40632) 2026-08-15 09:52:38 +00:00
Harsh Kashyap
d43685bc79 fix(extension): chain original exception in APIBasedExtensionRequestor per PEP 3134 (#40810)
Co-authored-by: Harsh Kashyap <Harsh23Kashyap@users.noreply.github.com>
2026-08-15 08:48:28 +00:00
Valor
56ebccf14d fix: narrow bare except in storage exists() to ClientError (#40420)
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
2026-08-15 07:12:04 +00:00
chenshiyang
d31a7d47bf refactor(controllers/web): dep-inject payloads with @model_validate (#40796)
Co-authored-by: chenshiyang.10 <chenshiyang.10@jd.com>
2026-08-15 06:55:06 +00:00
Dream
8031ea6d73 fix: replace bare except with except Exception in core agent, storage and vector backends (#40801) 2026-08-15 00:56:09 +00:00
zyssyz123
dfac3e524e fix: reclaim conversation resources on deletion (#40792)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-14 14:30:04 +00:00
WH-2099
fc7b51634d fix(api): protect agent sandbox reads (#40794) 2026-08-14 13:51:41 +00:00
WH-2099
96bfcf20cd fix(api): protect workspace credential mutations (#40790) 2026-08-14 12:03:54 +00:00
Mahbub Souad
557fbec45f refactor: add Mapped type annotations to model fields (#40373) (#40380) 2026-08-14 12:03:17 +00:00
AlexMultiAgent
eaa9d877f1 test(api): make session decorator path assertion platform-agnostic (#39895)
Co-authored-by: -LAN- <laipz8200@outlook.com>
2026-08-14 11:52:40 +00:00
chelsealong
b537fed907 fix(api): point SSRF-blocked error message at the correct dify issue (#40605) 2026-08-14 11:46:50 +00:00
酒诗
b6319c819c chore: dep inject for model in dataset document actions (#40787)
Co-authored-by: geneachina <geneachina@gmail.com>
2026-08-14 11:08:21 +00:00
WH-2099
b6694d462f fix(api): protect custom OAuth client deletion (#40786) 2026-08-14 10:59:49 +00:00
L1nSn0w
ea6770c7a1 feat(oauth): drive silent authorization from oauth_provider_apps.auto_authorize (#40783)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-14 10:29:39 +00:00
Joel
b0c5172e10 fix: show accurate copy for agent run limit errors (#40784) 2026-08-14 10:07:36 +00:00
WH-2099
62a544a146 fix(api): enforce RAG draft variable access boundaries (#40745) 2026-08-14 10:07:22 +00:00
非法操作
eec092f93d refactor(api): reuse app definition queries for service API info (#40476) 2026-08-14 09:38:35 +00:00
非法操作
e0fd3b264a refactor(api): reuse app definition queries for app parameters (#40465) 2026-08-14 09:38:34 +00:00
非法操作
17c8ff6a33 refactor(api): reuse app definition queries in web and service APIs (#40463) 2026-08-14 09:38:33 +00:00
非法操作
c425e4cdd5 refactor(api): extract installed app definition queries (#40462) 2026-08-14 09:38:32 +00:00
yyh
de9b456e82 fix(ci): align staged checks with Vite+ (#40774) 2026-08-14 09:09:33 +00:00
Joel
62d06a3c73 fix: preserve successful last run status after page restore (#40775) 2026-08-14 08:59:18 +00:00
zyssyz123
09f20b7a14 fix(agent): return conflict for duplicate creation (#40771)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-14 08:57:13 +00:00
Asuka Minato
0a1e52598f test: migrate controller identity ORM models to SQLite (#40626) 2026-08-14 08:54:48 +00:00
Asuka Minato
640bf56859 test: migrate workflow tool users to SQLite ORM models (#40628) 2026-08-14 08:54:31 +00:00
Asuka Minato
a958c2cadb test: migrate RAG residual entities to real ORM models (#40629) 2026-08-14 08:53:56 +00:00
Asuka Minato
f058e36084 test: migrate account tenant ORM stand-ins to SQLite (#40627) 2026-08-14 08:53:32 +00:00
Asuka Minato
9f364339cc test: migrate residual namespace ORM entities to real models (#40633) 2026-08-14 08:52:53 +00:00
盐粒 Yanli
3acc9470d4 feat(dify-agent): expand and enforce agent run limits (#40641) 2026-08-14 08:27:19 +00:00
yyh
9ddbc23b16 fix(dify-ui): refine segmented control focus feedback (#40769) 2026-08-14 08:17:14 +00:00
Myshkin451
c903e93e8b fix: preserve file preview content type (#37211)
Co-authored-by: FFXN <31929997+FFXN@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-14 07:45:21 +00:00
盐粒 Yanli
0b36a0bbc0 fix(dify-agent): clarify shell working spaces (#40623)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-14 07:39:32 +00:00
林玮 (Jade Lin)
4e44ade07f fix: handle long text URLs in webscraper (#40757) 2026-08-14 07:34:53 +00:00
Joel
35132d6892 chore: reopen the education application flow (#40760)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-14 07:16:21 +00:00
dependabot[bot]
6edc8ba147 chore(deps): bump pydantic-ai-slim from 1.102.0 to 1.106.0 in /dify-agent (#40736)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-14 06:26:00 +00:00
yyh
12b3b62a7f chore: update workspace dependencies (#40764) 2026-08-14 06:17:11 +00:00
Crazywoola
58653f2cdb feat(workflow-generator): prefer installed tools (#40611) 2026-08-14 05:38:37 +00:00
Asuka Minato
ef176f130b test: migrate workflow event ORM models to real entities (#40634) 2026-08-14 05:35:10 +00:00
Asuka Minato
c36161a73f test: migrate OpenAPI and tool-file ORM entities (#40637) 2026-08-14 05:34:56 +00:00
Asuka Minato
4b189777c4 test: migrate web controller identity ORM models to real entities (#40639) 2026-08-14 05:34:43 +00:00
Asuka Minato
d08dc6162c test: migrate trace workflow run ORM model to real entity (#40642) 2026-08-14 05:34:29 +00:00
Asuka Minato
b5e05516f4 test: migrate annotation controller ORM models to real entities (#40643) 2026-08-14 05:34:16 +00:00
Asuka Minato
9f048cd89b test: migrate load balancing account ORM model to real entity (#40644) 2026-08-14 05:34:00 +00:00
Asuka Minato
78dfce3b63 test: migrate expired log workflow ORM models to real entities (#40645) 2026-08-14 05:33:46 +00:00
Asuka Minato
97d34ae6be test: migrate agent drive end-user ORM models to real entities (#40654) 2026-08-14 05:33:29 +00:00
Asuka Minato
ea0ee680dc test: migrate app service accounts and commit failures to real ORM (#40655) 2026-08-14 05:33:16 +00:00
yyh
9acdcbe2f8 fix(web): skip lifecycle scripts in Docker install (#40763) 2026-08-14 05:12:14 +00:00
yyh
0f63a05d04 chore: upgrade Vite+ to 0.2.9 (#40762) 2026-08-14 04:42:28 +00:00
WH-2099
3cd23d4ef8 fix(agent): authenticate agent control plane (#40737) 2026-08-14 04:32:09 +00:00
Pranav Agarwal
e2852524be fix(api): enforce edit and RBAC permissions on dataset API key GET endpoint (#40740) (#40743) 2026-08-14 04:27:37 +00:00
yyh
f8e05ebfc0 docs(web): consolidate maintainer guidance (#40758) 2026-08-14 03:40:05 +00:00
Joel
0ac005eb98 fix: add semantic document titles across pages (#40396) 2026-08-14 03:29:16 +00:00
林玮 (Jade Lin)
755c154b1f refactor: resolve completion model via manager (#40756) 2026-08-14 02:52:42 +00:00
Jingyi
e3b3165e9a feat(web): embed marketplace in integration categories (#40744) 2026-08-14 01:55:51 +00:00
Xiyuan Chen
bf044cf19c chore(deps): bump wandb to 0.28.2 (go-git, grpc, x/text, Go stdlib CVEs) (#40753) 2026-08-14 01:36:14 +00:00
yyh
1cf8db8395 refactor(web): clarify model selector trigger ownership (#40732)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-14 01:03:50 +00:00
WH-2099
c1bde25bcc fix(api): remove unsafe Bedrock test endpoint (#40742) 2026-08-13 19:47:13 +00:00
Harsh Kashyap
8238684334 fix(api): chain original exception in 13 except blocks per PEP 3134 (#40738)
Co-authored-by: Harsh Kashyap <Harsh23Kashyap@users.noreply.github.com>
2026-08-13 17:50:05 +00:00
zyssyz123
c9c7fb9962 fix(agent): meter internal LLM calls through API (#40613)
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-08-13 12:15:44 +00:00
林玮 (Jade Lin)
b9248690b0 refactor: generalize hosted model quota lifecycle (#40722)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-13 08:10:02 +00:00
yyh
36dfc6c216 refactor(ui): enforce single-choice segmented controls (#40726) 2026-08-13 08:09:10 +00:00
yyh
7a6f380f2c fix(dify-ui): refine pagination navigation semantics (#40723) 2026-08-13 07:54:31 +00:00
Joel
84e348c52b fix: prevent cross-tab chat conversation switching (#40709) 2026-08-13 07:26:38 +00:00
盐粒 Yanli
79b8d7a797 fix(agent): normalize plugin provider identities (#40619)
Co-authored-by: Joel <iamjoel007@gmail.com>
2026-08-13 07:22:41 +00:00
yyh
fb30dd2d58 refactor(web): migrate app card icon controls (#40652) 2026-08-13 07:04:17 +00:00
yyh
a0ca3d2e81 refactor(web): remove legacy action button references (#40651) 2026-08-13 07:04:16 +00:00
yyh
204a906de8 refactor(web): migrate help menu icon trigger (#40650) 2026-08-13 07:04:15 +00:00
yyh
146f402a8c refactor(web): migrate workflow icon commands (#40649) 2026-08-13 06:44:03 +00:00
yyh
d6f32a0267 refactor(web): migrate plugin icon commands (#40648) 2026-08-13 06:44:02 +00:00
yyh
288766171d refactor(web): migrate dataset icon commands (#40647) 2026-08-13 06:44:01 +00:00
yyh
7ab368f4a4 refactor(web): migrate app icon commands (#40646) 2026-08-13 06:44:00 +00:00
yyh
d0e2723e45 refactor(web): migrate semantic icon controls (#40624) 2026-08-13 06:43:59 +00:00
Joel
c085142e66 chore: remove scope selection from the agent environment editor (#40701) 2026-08-13 06:24:51 +00:00
林玮 (Jade Lin)
c647cfd6a3 refactor: resolve summary models through manager (#40696) 2026-08-13 06:22:22 +00:00
yyh
29a4897e73 fix(ci): prevent false failures from superseded runs (#40699) 2026-08-13 06:18:22 +00:00
盐粒 Yanli
8df220c2b3 fix(agent): propagate resource collection failures (#40697) 2026-08-13 06:16:12 +00:00
zyssyz123
6e80f42f38 fix(api): decouple response listeners from execution lifecycle (#39813) 2026-08-13 05:50:43 +00:00
Asuka Minato
211344d0b0 test: migrate conversation lifecycle sessions and ORM models to SQLite (#40656) 2026-08-13 05:20:10 +00:00
Asuka Minato
6b5c04e7d3 test: migrate workflow node trace ORM models to real entities (#40657) 2026-08-13 05:19:52 +00:00
Asuka Minato
6ea89dc7cb test: migrate workflow tool-file ORM model to real entity (#40658) 2026-08-13 05:19:34 +00:00
Asuka Minato
ff46c3d7d7 test: migrate reranker upload-file sessions and ORM models to SQLite (#40659) 2026-08-13 05:19:19 +00:00
Asuka Minato
2f969afaf2 test: migrate app runner transaction sessions and ORM models to SQLite (#40660) 2026-08-13 05:19:00 +00:00
Asuka Minato
fbd38753fa test: migrate residual controller ORM identities to real models (#40661) 2026-08-13 05:18:44 +00:00
Asuka Minato
2d1fb61885 test: migrate RAG dataset ORM contracts to real models (#40662) 2026-08-13 05:18:25 +00:00
Asuka Minato
7e3381e8ad test: migrate OpenTelemetry ORM identities to real models (#40663) 2026-08-13 05:17:53 +00:00
Joel
190780e125 fix(e2e): skip hidden Agent knowledge E2E scenarios (#40691) 2026-08-13 05:03:15 +00:00
yyh
1f96da5e6d fix(ci): always report required style checks (#40684) 2026-08-13 04:05:45 +00:00
Joel
a8d5679a1c fix: update Agent advanced settings copy (#40682) 2026-08-13 03:25:43 +00:00
Xiyuan Chen
5bacb68115 fix(ops): keep the path of self-hosted LangSmith and Weave endpoints (#40584) 2026-08-13 03:18:22 +00:00
Asuka Minato
af2fac75db test: migrate workflow variable ORM model to real entity (#40664) 2026-08-13 03:05:31 +00:00
Asuka Minato
616736e0f1 test: migrate residual session factories to shared SQLite (#40665) 2026-08-13 03:04:59 +00:00
Asuka Minato
94323c0838 test: migrate residual dataset and archive ORM models (#40668) 2026-08-13 03:04:17 +00:00
Asuka Minato
223998bd48 test: migrate residual ORM model stand-ins to real mappings (#40666) 2026-08-13 03:03:46 +00:00
Joel
b3d770a69b fix: hide knowledge retrieval from Agent V2 configuration (#40679) 2026-08-13 03:03:12 +00:00
Asuka Minato
9fb51b7e67 test: migrate API workflow pause ORM models to real entities (#40669) 2026-08-13 03:03:10 +00:00
yyh
67366dd35c feat(ui): expose base toggle composition (#40622) 2026-08-13 02:29:37 +00:00
yyh
71b31e17b0 refactor(web): migrate domain icon commands (#40621) 2026-08-13 02:29:36 +00:00
yyh
312bc4912d refactor(web): migrate shared icon commands (#40620) 2026-08-13 02:29:36 +00:00
yyh
3925b5fa1b refactor(ui): adopt icon button in close controls (#40617) 2026-08-13 02:29:35 +00:00
yyh
d4d30148bd feat(ui): add icon button primitive (#40616) 2026-08-13 02:29:34 +00:00
林玮 (Jade Lin)
12e6838403 fix: meter hosted LLM invocations (#40589) 2026-08-12 12:33:21 +00:00
WH-2099
489772908b fix: enforce API key list permissions (#40516) 2026-08-12 11:44:27 +00:00
Benjamin
6a37e1538a fix(rbac): grant member policies when a dataset is opened to the work… (#39735)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-12 09:02:03 +00:00
wangxiaolei
eeaaf11a51 fix: fix create dataset with document miss rbac operation (#40599)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-12 08:57:43 +00:00
Wu Tianwei
6899c7720f refactor(web): remove deprecated deployments pages (#40593) 2026-08-12 08:49:51 +00:00
Byron.wang
51a007e84e refactor(api): extract init validation into layered application service (#40046) 2026-08-12 08:42:09 +00:00
yyh
6d185b6f40 test(web): remove brittle publisher lock test (#40592) 2026-08-12 07:55:46 +00:00
github-actions[bot]
7358c0f735 chore(i18n): sync translations with en-US (#40573)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-08-12 07:49:39 +00:00
yyh
01460f681e fix(web): render workspace plan badge during SSR (#40581)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-12 07:45:39 +00:00
Crazywoola
750fce7e52 fix(web): restore app deletion autofill and knowledge scope label (#40583) 2026-08-12 07:31:59 +00:00
Asuka Minato
216d528f1b test: migrate agent app sandbox sessions and ORM models to SQLite (#40568) 2026-08-12 06:30:17 +00:00
Xin Zhang
0ee89c1302 fix: restrict app deploy to explicit enterprise permission (#40564) 2026-08-12 06:25:05 +00:00
WH-2099
af382be837 fix(api): bind remaining RAG resources to owners (#40503) 2026-08-12 06:05:16 +00:00
chelsealong
d813edb945 fix(agent-v2): stop dropping non-vision attachments from agent chat (#40179)
Co-authored-by: Crazywoola <100913391+crazywoola@users.noreply.github.com>
2026-08-12 05:57:21 +00:00
无能的迪拜巧克力
9041251da5 feat(web): add multimodal retrieval guidance for Knowledge embedding models (#40566) 2026-08-12 05:54:33 +00:00
Joel
9e04e4f5c2 chore: add Docker configuration for the Turnstile site key (#40569) 2026-08-12 05:54:21 +00:00
yyh
28ba7033fa refactor(web): remove profile bootstrap atom layer (#40474) 2026-08-12 04:05:57 +00:00
Wu Tianwei
fbaf23dc8d fix(web): improve app deployment v2 accessibility (#40550) 2026-08-12 03:43:35 +00:00
github-actions[bot]
9cc1f68364 chore(i18n): sync translations with en-US (#40546)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-08-12 03:25:34 +00:00
zyssyz123
059c9ce0b6 feat: add trunstile (#40494)
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-12 03:08:19 +00:00
yyh
668fb4c14a refactor(web): sync zendesk from profile query (#40473) 2026-08-12 02:57:27 +00:00
yyh
4c06bcf000 refactor(web): sync amplitude from profile query (#40472) 2026-08-12 02:15:24 +00:00
Wu Tianwei
7aba539e82 feat: app deployment v2 (#39829)
Co-authored-by: zhangx1n <zhangxin@dify.ai>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-12 01:55:36 +00:00
WH-2099
ef8544b173 fix(api): bind dataset operations to owners (#40149) 2026-08-11 16:37:14 +00:00
yyh
bee269afe8 refactor(web): align education autocomplete async behavior (#40500) 2026-08-11 14:12:50 +00:00
yyh
324b0f97ff refactor(web): read plugin profile from query cache (#40471) 2026-08-11 11:39:26 +00:00
yyh
7352944dec refactor(web): read account profile from query cache (#40470) 2026-08-11 11:17:29 +00:00
yyh
78c7fb9c5c refactor(web): move education verification to dedicated routes (#40493)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-11 09:56:11 +00:00
Joel
8598af2a17 fix: improve agent working directory tab labels and tooltips (#40496) 2026-08-11 09:36:33 +00:00
yyh
df07a0a1de refactor(web): read workflow profile from query cache (#40469) 2026-08-11 09:03:56 +00:00
Asuka Minato
a64a24b39c test: migrate snippet and vector sessions to SQLite (#40089)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Byron Wang <byron@dify.ai>
2026-08-11 08:51:07 +00:00
yyh
cd0e88c680 refactor(web): read dataset profile from query cache (#40468) 2026-08-11 08:30:49 +00:00
Asuka Minato
08008f6305 test: migrate controller sessions to SQLite (#40083)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-11 08:29:40 +00:00
Valor
e3812cf72b fix: add zh_Hans labels for builtin Audio provider and its tools (#40408) 2026-08-11 08:27:59 +00:00
yyh
4175d062f4 fix(web): make legacy Home redirect permanent (#40492) 2026-08-11 08:27:57 +00:00
Yunlu Wen
84f9b6530e chore: bump cryptography, pypdf, h2 for CVE fixes (#40409)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 08:14:55 +00:00
yyh
3d02a12eb6 refactor(web): read app profile from query cache (#40467) 2026-08-11 08:09:58 +00:00
Byron.wang
c1e94f8ee7 refactor(api): consolidate deployment edition handling (#40142)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-11 07:39:01 +00:00
yyh
7fffd51535 fix(web): preserve workflow canvas after history updates (#40489) 2026-08-11 07:36:21 +00:00
Joel
a68e0bbc6f fix: show a persistent toast for agent run timeouts (#40486) 2026-08-11 07:27:52 +00:00
Asuka Minato
dd3cf53044 test: migrate retention and task sessions to SQLite (#40090)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-11 07:05:36 +00:00
Asuka Minato
500e37e2fd test: migrate core RAG sessions to SQLite (#40085)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-11 07:05:26 +00:00
Joel
44c5f1c011 fix: split sandbox files into saved and temporary tabs (#40478) 2026-08-11 06:46:01 +00:00
Asuka Minato
7162450e83 test: migrate app service sessions to SQLite (#40088)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-11 06:43:15 +00:00
Asuka Minato
3f62f3cd05 test: migrate dataset indexing task sessions to SQLite (#40091) 2026-08-11 06:31:33 +00:00
Asuka Minato
15483f1592 test: use sqlite sessions in rag pipeline coverage (#40093) 2026-08-11 06:19:08 +00:00
wangxiaolei
4c7b870062 fix: _multiple_retrieve_thread not fail fast (#40466) 2026-08-11 05:40:23 +00:00
yyh
48451f5adf fix(web): keep agent ref updates out of render (#40429) 2026-08-11 03:57:58 +00:00
Kecheng Wang
f785af3cec fix(docker): use dynamic DNS resolution for all nginx upstreams (#39155)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 00:48:59 +00:00
Tushar Motwani
aa5e8a5eff chore: suppress pyrefly necessary conversions (#40444) 2026-08-10 16:41:34 +00:00
yyh
6a9efa8ffd fix(web): keep workflow ref updates out of render (#40428) 2026-08-10 14:38:04 +00:00
林玮 (Jade Lin)
4eb9a24997 refactor(workflow): align standalone node execution layers (#40447) 2026-08-10 12:37:34 +00:00
yyh
b206e691ca fix(web): keep shared ref updates out of render (#40427) 2026-08-10 11:33:11 +00:00
yyh
3f354c2b06 docs(web): clarify ref mutation ownership (#40426) 2026-08-10 11:04:18 +00:00
github-actions[bot]
8ae9d0ad8e chore(i18n): sync translations with en-US (#40435)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
2026-08-10 11:00:10 +00:00
Joel
0f583cfe75 fix: widen agent orchestration chat on large displays (#40433) 2026-08-10 09:57:47 +00:00
zyqzyq
df250423d4 feat: add tool plugin param type date and date-picker (#36163)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: hjlarry <hjlarry@163.com>
2026-08-10 09:31:38 +00:00
Asuka Minato
b8934b743c test: migrate core app sessions to SQLite (#40084)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 09:01:02 +00:00
Byron.wang
976d061f9b refactor(api): merge ping and version endpoints into system (#40058)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 08:50:40 +00:00
Asuka Minato
96655436f4 test: migrate RAG index sessions to SQLite (#40080)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 08:43:18 +00:00
非法操作
648806bc9c refactor(api): extract explore banner query service (#40053) 2026-08-10 08:07:54 +00:00
yyh
64ebfe2235 fix(web): name the webapp password visibility action (#40225) 2026-08-10 07:12:24 +00:00
yyh
ca4d7ff1f2 fix(web): make suggested questions native buttons (#40200)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 06:47:19 +00:00
yyh
2917d1d51c refactor(web): migrate webapp password fields to dify ui (#40223) 2026-08-10 06:44:28 +00:00
yyh
c79b9821fa fix(web): name endpoint card actions (#40260) 2026-08-10 06:44:10 +00:00
yyh
6422ef2b57 fix(web): use button for workflow debug close (#40274)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 06:43:23 +00:00
yyh
509ea9afc4 fix(web): name secret key delete action (#40297) 2026-08-10 06:43:02 +00:00
yyh
1828cc6a10 fix(web): name MCP header delete actions (#40300) 2026-08-10 06:42:45 +00:00
yyh
8d5ff67d05 fix(web): name external API card actions (#40304) 2026-08-10 06:42:36 +00:00
yyh
ec63a26511 fix(web): expose chat variable tips state (#40334) 2026-08-10 06:42:03 +00:00
yyh
c2b3b89445 docs(web): add Next.js agent rules (#40398) 2026-08-10 06:39:37 +00:00
Joel
5967bd8e08 chore: tighten education verification and pause discount access (#40402)
Co-authored-by: Yansong Zhang <916125788@qq.com>
2026-08-10 06:33:03 +00:00
非法操作
7e7548cd18 fix(web): allow unlimited text input in trial apps (#40403) 2026-08-10 06:18:38 +00:00
dependabot[bot]
1ba3aa718e chore(deps): bump pymdown-extensions from 11.0 to 11.0.1 in /dify-agent (#40183)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-10 06:16:06 +00:00
dependabot[bot]
c7b1660650 chore(deps): bump gitpython from 3.1.57 to 3.1.58 in /api (#40184)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-10 06:14:40 +00:00
yyh
f5e1f1590f fix(web): use buttons for version filters (#40357)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 04:41:57 +00:00
yyh
09d0cca5b9 fix(web): make tool category filters keyboard accessible (#40243) 2026-08-10 04:18:04 +00:00
yyh
d8736be194 fix(web): name MCP header fields (#40299) 2026-08-10 03:57:24 +00:00
Pranav Agarwal
7f15f506f8 fix(api): end_date filter in feedback export excludes the entire end date (#40114) 2026-08-10 03:27:56 +00:00
chelsealong
6aa3fa5193 fix: catch ValueError instead of bare except in account CLI commands (#40356) 2026-08-10 03:18:33 +00:00
dependabot[bot]
c849be5e52 chore(deps): bump the github-actions-dependencies group with 2 updates (#40391)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-10 03:17:53 +00:00
yyh
b3b3088a17 fix(web): name external API panel close (#40309) 2026-08-10 03:13:36 +00:00
yyh
72fad39913 fix(web): name external API modal close (#40313) 2026-08-10 03:13:35 +00:00
yyh
e12563a4b4 fix(web): name plugin item actions (#40261) 2026-08-10 03:13:33 +00:00
yyh
e8afed13cc fix(web): name plugin detail close action (#40264) 2026-08-10 03:13:33 +00:00
yyh
7a72bb3571 fix(web): name OAuth redirect copy action (#40303) 2026-08-10 03:13:33 +00:00
yyh
08f30717ad fix(web): make verification back actions keyboard accessible (#40196) 2026-08-10 03:13:31 +00:00
yyh
2bdfbb9e61 fix(web): name generated result copy action (#40259) 2026-08-10 03:13:31 +00:00
yyh
06e757a9e9 fix(web): name the snippet info menu trigger (#40251) 2026-08-10 03:13:31 +00:00
yyh
7b84d4daca fix(web): use primary headings on password reset pages (#40193) 2026-08-10 03:13:31 +00:00
yyh
5e0d705a7f fix(web): name the plugin debug trigger (#40252) 2026-08-10 03:13:31 +00:00
yyh
30ca836e03 fix(web): make the citation toggle a button (#40204) 2026-08-10 03:12:25 +00:00
Asuka Minato
b391dc56a0 chore: example to rm db.session (#40370) 2026-08-10 02:58:52 +00:00
yyh
606fac6e1c fix(web): use button for publisher access (#40361)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 02:56:21 +00:00
yyh
ed36ee5133 refactor(web): rely on preview dialog containment (#40363)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 02:56:20 +00:00
yyh
c35242731f fix(web): use button for provider tool item (#40362) 2026-08-10 02:56:20 +00:00
yyh
d2d07d0345 fix(web): use buttons for plugin type switch (#40353)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 02:56:19 +00:00
yyh
8b908d6150 fix(web): use buttons for pricing categories (#40360)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 02:56:18 +00:00
yyh
1017a76b4c fix(web): name assigner add operation (#40337) 2026-08-10 02:56:18 +00:00
yyh
9c5d76c299 refactor(web): rely on QR panel containment (#40352)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 02:56:18 +00:00
yyh
83987b8eec fix(web): name prompt generator action (#40328) 2026-08-10 02:56:16 +00:00
yyh
cbd71699de fix(web): expose SVG preview toggle state (#40332) 2026-08-10 02:56:16 +00:00
yyh
5d58a25e52 fix(web): name human input preview close (#40321) 2026-08-10 02:56:16 +00:00
yyh
764d9b5fbe fix(web): name external retrieval close action (#40317) 2026-08-10 02:56:16 +00:00
yyh
5ffea6d30e fix(web): name code generator action (#40324) 2026-08-10 02:56:16 +00:00
yyh
2796210e18 fix(web): name agent log navigation menu (#40286) 2026-08-10 02:56:14 +00:00
yyh
1b2c71ac75 fix(web): name variable inspect close actions (#40289) 2026-08-10 02:56:14 +00:00
yyh
cc4bf22d07 fix(web): expose workflow parallel toggle state (#40282) 2026-08-10 02:56:14 +00:00
yyh
7295d9306f fix(web): name image previewer actions (#40283) 2026-08-10 02:56:14 +00:00
yyh
2a66b49bc7 fix(web): name human input delete action (#40293) 2026-08-10 02:56:14 +00:00
yyh
c944a25585 fix(web): expose debug toolbar semantics (#40258) 2026-08-10 02:56:12 +00:00
yyh
2067836851 fix(web): name saved item actions (#40256) 2026-08-10 02:56:12 +00:00
yyh
6f95a071b5 fix(web): name snippet stop action (#40278) 2026-08-10 02:56:11 +00:00
yyh
a543fd3656 fix(web): name workflow version history action (#40276) 2026-08-10 02:56:11 +00:00
yyh
d94c4415af fix(web): name annotation reply actions (#40270) 2026-08-10 02:56:11 +00:00
yyh
abbed23c64 fix(web): make dataset sidebar menu items buttons (#40248) 2026-08-10 02:56:09 +00:00
yyh
5de4e5b3c0 fix(web): name dataset configuration actions (#40254) 2026-08-10 02:56:09 +00:00
yyh
1e17a55e79 fix(web): name annotation configuration action (#40255) 2026-08-10 02:56:09 +00:00
yyh
465dab2eed refactor(web): keep copy id events on the button (#40247) 2026-08-10 02:56:09 +00:00
yyh
532e0a77cc fix(web): name conversation menu triggers (#40253) 2026-08-10 02:56:09 +00:00
yyh
47835e6b25 fix(web): expose the voice recording timer (#40205) 2026-08-10 02:56:07 +00:00
yyh
11c537dc1e fix(web): restore webapp email code form submission (#40227) 2026-08-10 02:56:07 +00:00
yyh
e4420227b5 fix(web): make the scroll-to-node action a button (#40214) 2026-08-10 02:56:07 +00:00
yyh
f9e44fe86e fix(web): restore the signup email focus order (#40230) 2026-08-10 02:56:07 +00:00
yyh
b256e211dc fix(web): make the member selector keyboard accessible (#40209) 2026-08-10 02:56:07 +00:00
yyh
2a34c7a714 fix(web): make the human input expander a button (#40202)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 02:55:30 +00:00
yyh
04132b51f0 fix(web): use button for chat variable panel close (#40333)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 02:53:49 +00:00
yyh
908f6ada4e fix(web): restore webapp password form submission (#40222) 2026-08-10 02:53:43 +00:00
yyh
e7da213f93 fix(web): name workflow debug actions (#40273) 2026-08-10 02:53:37 +00:00
yyh
e7f789f185 fix(web): hide citation measurement items from assistive tech (#40203) 2026-08-10 02:34:14 +00:00
yyh
f8d381e6c5 fix(web): name chat settings triggers (#40341) 2026-08-10 02:33:11 +00:00
yyh
c29425c0fc fix(web): name chat history controls (#40344) 2026-08-10 02:33:02 +00:00
yyh
612f9ee669 fix(web): use button for tool detail toggle (#40349)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 02:32:52 +00:00
Souravrajvi0
81a38df814 feat(api): cache remote recommended app template fetches (#40176) 2026-08-10 02:30:15 +00:00
yyh
e9eb130731 fix(web): use a button for unauthorized logout (#40198) 2026-08-10 02:29:46 +00:00
yyh
4edaecc04d fix(web): use a button for the webapp fallback action (#40197) 2026-08-10 02:29:10 +00:00
yyh
fc8d0a0187 fix(web): use a primary heading on the install page (#40194) 2026-08-10 02:26:36 +00:00
yyh
96960499ef fix(web): use primary headings on auth pages (#40192) 2026-08-10 02:26:10 +00:00
yyh
c2687c16b3 refactor(web): unify infinite scroll sentinels (#40188) 2026-08-10 02:22:07 +00:00
Asuka Minato
883944d289 test: Type test (#40371)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-10 02:13:17 +00:00
yyh
a7156a0702 fix(web): remove workflow output title tab stop (#40377) 2026-08-10 01:52:33 +00:00
yyh
dee9d27af8 fix(web): associate app deletion confirmation label (#40375) 2026-08-10 01:52:09 +00:00
yyh
e06f0ee315 fix(web): hide credential icons from assistive tech (#40376) 2026-08-10 01:51:42 +00:00
10155 changed files with 601317 additions and 448986 deletions

View File

@@ -41,6 +41,7 @@
# core/policy/publish_policy.py
from controllers.console.app import request_context
def can_publish() -> bool:
return request_context.current_user.is_admin
```
@@ -50,6 +51,7 @@
def can_publish(role: str) -> bool:
return role == "admin"
# service layer adapts web/user context to domain input
allowed = can_publish(role=current_user.role)
```
@@ -66,6 +68,7 @@
# api/libs/conversation_filter.py
from services.conversation_service import ConversationService
def should_archive_conversation(conversation, tenant_id: str) -> bool:
# Domain policy and service dependency are leaking into libs.
service = ConversationService()
@@ -79,9 +82,11 @@
def older_than_days(idle_days: int, threshold_days: int) -> bool:
return idle_days > threshold_days
# services/conversation_service.py (business logic stays in service/core)
from libs.datetime_utils import older_than_days
def should_archive_conversation(conversation, tenant_id: str) -> bool:
threshold_days = 90 if has_paid_plan(tenant_id) else 30
return older_than_days(conversation.idle_days, threshold_days)

View File

@@ -50,6 +50,7 @@
```python
from sqlalchemy.orm import Mapped
class Dataset(TypeBase):
__tablename__ = "datasets"
id: Mapped[str] = mapped_column(StringUUID, primary_key=True)
@@ -59,6 +60,7 @@
```python
from sqlalchemy.orm import Mapped
class Dataset(TypeBase):
__tablename__ = "datasets"
id: Mapped[str] = mapped_column(StringUUID, primary_key=True)
@@ -101,6 +103,7 @@
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped
class ToolConfig(TypeBase):
__tablename__ = "tool_configs"
config: Mapped[dict] = mapped_column(JSONB, nullable=False)
@@ -111,6 +114,7 @@
from models.types import AdjustedJSON
class ToolConfig(TypeBase):
__tablename__ = "tool_configs"
config: Mapped[dict] = mapped_column(AdjustedJSON(), nullable=False)
@@ -146,7 +150,5 @@
default_expr = sa.text("'database'::character varying") if _is_pg(conn) else sa.text("'database'")
with op.batch_alter_table("dataset_keyword_tables") as batch_op:
batch_op.add_column(
sa.Column("data_source_type", sa.String(255), server_default=default_expr, nullable=False)
)
batch_op.add_column(sa.Column("data_source_type", sa.String(255), server_default=default_expr, nullable=False))
```

View File

@@ -18,9 +18,7 @@
# Existing repository is ignored and service uses ad-hoc table queries.
class AppService:
def archive_app(self, app_id: str, tenant_id: str) -> None:
app = self.session.execute(
select(App).where(App.id == app_id, App.tenant_id == tenant_id)
).scalar_one()
app = self.session.execute(select(App).where(App.id == app_id, App.tenant_id == tenant_id)).scalar_one()
app.archived = True
self.session.commit()
```
@@ -33,6 +31,7 @@
app.archived = True
self.app_repo.save(app)
# If the query is missing, extend the existing abstraction.
active_apps = self.app_repo.list_active_for_tenant(tenant_id=tenant_id)
```
@@ -50,9 +49,10 @@
class ConversationRepository(Protocol):
def list_recent_for_app(self, app_id: str, tenant_id: str, limit: int) -> list[Conversation]: ...
class SqlAlchemyConversationRepository:
def list_recent_for_app(self, app_id: str, tenant_id: str, limit: int) -> list[Conversation]:
...
def list_recent_for_app(self, app_id: str, tenant_id: str, limit: int) -> list[Conversation]: ...
class ConversationService:
def __init__(self, conversation_repo: ConversationRepository):

View File

@@ -126,9 +126,7 @@
# 3) Pessimistic lock with SELECT ... FOR UPDATE (high contention)
run = session.execute(
select(WorkflowRun)
.where(WorkflowRun.id == run_id, WorkflowRun.tenant_id == tenant_id)
.with_for_update()
select(WorkflowRun).where(WorkflowRun.id == run_id, WorkflowRun.tenant_id == tenant_id).with_for_update()
).scalar_one()
run.status = "cancelled"
session.commit()

View File

@@ -7,12 +7,14 @@ description: Use when writing, changing, or reviewing Cucumber and Playwright te
`e2e/AGENTS.md` owns the suite architecture, lifecycle, commands, tags, generated-client boundaries, fixtures, and cleanup contracts. Read the nearest feature-scoped `AGENTS.md` when one exists. This skill adds no parallel package policy.
Cucumber owns scenario and hook execution and reporting. Playwright provides browser automation, contexts, pages, locators, actions, assertions, request, and tracing APIs; protocol exceptions remain with the package-defined owner. Their timeout domains remain separate, and importing from `@playwright/test` does not make Playwright Test runner configuration, fixtures, or retries applicable to this suite.
## Topic Routing
Read only the bundled reference required by the change:
- Locator, assertion, isolation, or waiting decisions: [`references/playwright-best-practices.md`][playwright]
- Scenario wording, step granularity, expressions, or tag design: [`references/cucumber-best-practices.md`][cucumber]
- Scenario wording, step granularity, expressions, World state, hook visibility, or tag design: [`references/cucumber-best-practices.md`][cucumber]
Check current official Playwright or Cucumber documentation before introducing a framework pattern that local code and references do not already establish.
@@ -23,7 +25,7 @@ Check current official Playwright or Cucumber documentation before introducing a
3. Read the target scenario, matching step definitions, and lifecycle files only when session or shared state matters.
4. Reuse an existing step when wording and behavior match; add one coherent scenario or step when they do not.
5. Keep browser actions and assertions at the public user boundary; keep setup, seed, polling, and cleanup at their package-defined owners.
6. Run the narrowest tagged scenario and package checks documented in `e2e/AGENTS.md`; broaden only for shared hooks, tags, or support changes.
6. For changes, run the narrowest tagged scenario and package checks documented in `e2e/AGENTS.md`; broaden only for shared hooks, tags, or support changes.
For review requests, lead with reproducible correctness failures, flake sources, or demonstrated architecture drift. Report the behavior verified and any external-runtime, browser, or environment gap.

View File

@@ -4,15 +4,18 @@ Use this reference when writing or reviewing Gherkin scenarios, step definitions
Official sources:
- https://cucumber.io/docs/guides/10-minute-tutorial
- https://cucumber.io/docs/cucumber/step-definitions
- https://cucumber.io/docs/bdd/better-gherkin
- https://cucumber.io/docs/gherkin/reference
- https://cucumber.io/docs/gherkin/step-organization
- https://cucumber.io/docs/cucumber/state
- https://cucumber.io/docs/cucumber/cucumber-expressions
- https://github.com/cucumber/cucumber-js/blob/main/docs/support_files/world.md
## What Matters Most
### 1. Treat scenarios as executable specifications
Cucumber scenarios should describe examples of behavior, not test implementation recipes.
Cucumber scenarios should usually describe behavior declaratively, not reproduce an interaction script.
Apply it like this:
@@ -20,11 +23,15 @@ Apply it like this:
- avoid UI-internal wording such as selector details, DOM structure, or component names
- keep language concrete enough that the scenario reads like living documentation
Procedural detail is appropriate when the interaction mechanism is the behavior under test, such as keyboard navigation, focus movement, or a required multi-actor sequence.
### 2. Keep scenarios focused
A scenario should usually prove one workflow or business outcome. If a scenario wanders across several unrelated behaviors, split it.
A scenario should prove one business rule or coherent outcome. Cucumber's three-to-five-step recommendation is a review heuristic, not a hard limit: when a scenario is longer, check for multiple outcomes, incidental setup, or UI procedure that can move behind a domain step.
Keep each scenario centered on one coherent outcome. Avoid hidden dependencies on another scenario's side effects, and keep unavoidable setup outside the behavior narrative unless the precondition matters to the specification.
Use `Given` for initial context, `When` for the event, and `Then` for the expected outcome. Restarting those phases is a prompt to review the narrative, not an automatic failure. Avoid hidden dependencies on another scenario's side effects.
Use `Rule` when several examples illustrate one named business rule. Use a short `Background` only for shared context the reader needs to understand; do not hide fixture preparation, runtime readiness, or long setup there.
### 3. Reuse steps, but only when behavior really matches
@@ -45,6 +52,8 @@ Write a new step when:
Do not optimize for a low step count by making vague steps. Optimize for a small set of truthful, domain-owned steps.
Step definitions share one global matching namespace regardless of their directory. Organize them by domain capability and avoid feature-coupled glue or expressions broad enough to overlap unrelated behavior.
### 4. Prefer Cucumber Expressions
Use Cucumber Expressions for parameters unless regex is clearly necessary.
@@ -62,18 +71,24 @@ Use regex for a bounded natural-language alternative only when it keeps Gherkin
### 5. Keep step definitions thin and meaningful
Step definitions are glue between Gherkin and automation, not a second abstraction language.
Step definitions are glue between Gherkin and automation, not a second abstraction language. A step should express one coherent domain action or outcome; it may call several implementation helpers when that keeps UI mechanics out of the specification.
Keep each step to one user-visible action or assertion. In JavaScript and TypeScript, use `async function` when the step reads Cucumber World state because Cucumber binds `this`; do not leak state across scenarios through module globals.
Cucumber creates a new World for each scenario. Keep scenario state there rather than in module globals. Cucumber.js supports a `world` proxy for arrow functions, but this suite consistently uses typed `async function (this: DifyWorld, ...)` so ownership and TypeScript discovery remain explicit.
Hooks are invisible to feature readers. Keep them for low-level browser lifecycle, cleanup, and diagnostics; express business-relevant context in Gherkin.
### 6. Use tags intentionally
Tags should communicate selection or execution intent, not become ad hoc metadata. A tag does not change runtime behavior unless configuration or hooks implement it.
Tags may communicate stable capability grouping, selection, fixture dependency, or conditional-hook intent. A tag changes runtime behavior only when a runner, seed profile, or hook owns that meaning.
## Review Questions
- Does the scenario read like a real example of product behavior?
- Does it prove one outcome, or combine several independent phases?
- Are the steps behavior-oriented instead of implementation-oriented?
- Is procedural wording necessary to the behavior being tested?
- Is a reused step still truthful in this feature?
- Could a new expression overlap an existing step in the global namespace?
- Would `Rule` clarify the business rule, or would `Background` hide important setup?
- Is a new tag documenting real behavior, or inventing semantics that the suite does not implement?
- Would a new reader understand the outcome without opening the step-definition file?

View File

@@ -6,8 +6,12 @@ Official sources:
- https://playwright.dev/docs/best-practices
- https://playwright.dev/docs/locators
- https://playwright.dev/docs/actionability
- https://playwright.dev/docs/test-assertions
- https://playwright.dev/docs/test-timeouts
- https://playwright.dev/docs/browser-contexts
- https://playwright.dev/docs/events
- https://playwright.dev/docs/trace-viewer
## What Matters Most
@@ -21,28 +25,22 @@ Apply it like this:
- keep scenario state in the runner's scenario-owned context rather than module globals
- model special authentication or session setup through explicit per-scenario fixtures rather than shared mutable state
### 2. Prefer user-facing locators
### 2. Select locators by user contract
Playwright recommends built-in locators that reflect what users perceive on the page.
Prefer built-in locators that match how the target is exposed to users:
Preferred order:
- interactive controls: role and accessible name
- form controls: associated label; use placeholder when it is the relevant stable contract, especially when no label exists
- non-interactive content: visible text or the relevant text alternative
- elements without a meaningful user-facing contract: an intentional test id
1. `getByRole`
2. `getByLabel`
3. `getByPlaceholder`
4. `getByText`
5. `getByTestId` when an explicit test contract is the most stable option
This is a semantic choice, not a fixed ranking. Do not add incorrect roles or accessible names solely to satisfy a locator. When the product element should have user-facing semantics, fix that contract instead.
Avoid raw CSS/XPath selectors unless no stable user-facing contract exists and adding one is not practical.
Also remember:
Locators are strict for single-element actions. Scope to a stable region or use `filter({ has, hasText })` to make the intended target unique. Treat `.first()`, `.last()`, or `.nth()` as a review signal: the positional choice should be intentional and stable, not merely silence ambiguity.
- repeated content usually needs scoping to a stable container
- exact text matching is often too brittle when role/name or label already exists
- `getByTestId` is acceptable when semantics are weak but the contract is intentional
- when a real UI region, card, status, or icon lacks an accessible name, prefer adding that semantic contract in product code before falling back to `getByTestId`
### 3. Use web-first assertions
### 3. Use web-first assertions with the right timeout owner
Playwright assertions auto-wait and retry. Prefer them over manual state inspection.
@@ -64,6 +62,8 @@ If a condition genuinely needs custom retry logic, use Playwright's polling/asse
Use `expect.poll` for non-DOM truth such as API state, backend eventual consistency, generated resources, or captured browser events. For DOM state, use locator assertions so Playwright can apply actionability and web-first retry semantics.
Cucumber step and hook timeouts, Playwright locator/action timeouts, and Playwright assertion timeouts are separate budgets. `browserContext.setDefaultTimeout()` does not change the default five-second assertion timeout. Put an explicit longer assertion timeout only on the readiness owner that needs it; do not raise an outer timeout to mask a shorter inner failure.
### 4. Let actions wait for actionability
Locator actions already wait for the element to be actionable. Do not preface every click/fill with extra timing logic unless the action needs a specific visible/ready assertion for clarity.
@@ -77,15 +77,20 @@ Bad pattern:
- stack arbitrary waits before every action
- wait on unstable implementation details instead of the visible state the user cares about
- use `force: true` to bypass a real hit-target, overlay, or disabled-state failure
When synchronizing on a one-shot popup, download, request, or response, create the wait before triggering the action, then await the event. A scenario-owned listener may instead capture events for a later assertion. Do not use `networkidle` as an application-readiness assertion; wait for the user-visible state or owned backend contract.
### 5. Match debugging to the active harness
Playwright supports traces, screenshots, page snapshots, and browser logs. Configure artifact capture at the runner boundary instead of adding parallel diagnostics to individual scenarios.
Playwright supports traces, screenshots, page snapshots, and browser logs. Configure artifact capture in Cucumber hooks instead of adding parallel diagnostics to individual scenarios. If tracing is introduced, use `browserContext.tracing`; Playwright Test options such as `trace: 'on-first-retry'`, projects, workers, fixtures, reporters, and retries do not configure this harness.
## Review Questions
- Would this locator survive DOM refactors that do not change user-visible behavior?
- Is a positional locator expressing product order, or hiding an ambiguous match?
- Is this assertion using Playwright's retrying semantics?
- Is any explicit wait masking a real readiness problem?
- Does an explicit timeout belong to the condition that is actually slow?
- Was an event wait registered before the action that emits it?
- Does this code preserve per-scenario isolation?
- Is a new abstraction really needed, or does it bypass the runner's scenario-owned context and lifecycle?

View File

@@ -5,14 +5,14 @@ description: Use only when the user explicitly requests a review or audit of fro
# Frontend Code Review
Review the requested scope for concrete, reproducible regressions. This skill owns the review phase and routes directly to its bundled rule packs. For a combined review-and-fix request, establish findings before applying implementation or testing guidance.
Review the requested scope for concrete defects and violations of explicit project contracts. This skill owns review decisions; its references route to canonical rules without activating another skill's implementation workflow.
## Evidence First
1. Establish the review scope from the requested files or current diff.
2. Read the changed lines, their behavior owner, and the nearest scoped `AGENTS.md`.
3. Trace public consumers, generated contracts, primitive APIs, or runtime configuration only when they decide correctness.
4. Report only findings tied to an observable failure, violated contract, security boundary, or demonstrated maintenance risk.
4. Report findings tied to an observable failure, violated contract, security boundary, or demonstrated maintenance risk. Explicit team conventions are contracts: establish their scope and exceptions, and do not invent user impact to justify a convention finding.
## Rule Routing
@@ -27,16 +27,16 @@ Read only the packs matched by the diff:
- Stable Dify runtime invariants in the named paths: [`references/dify-invariants.md`][dify-invariants]
- General TypeScript or styling quality not owned above: [`references/code-quality.md`][code-quality]
Read `packages/dify-ui/README.md`, `packages/dify-ui/AGENTS.md`, `web/docs/overlay.md`, or `web/docs/test.md` only when the reviewed code falls under that contract. Check current official documentation when local code and bundled references do not settle a framework, browser, or accessibility behavior.
Read `packages/dify-ui/README.md`, `packages/dify-ui/AGENTS.md`, `packages/dify-ui/docs/overlays.md`, or `web/docs/test.md` only when the reviewed code falls under that contract. Check current official documentation when local code and bundled references do not settle a framework, browser, or accessibility behavior.
## Severity And Output
- **P0**: security or privacy leak, data loss, production crash, or inaccessible critical workflow.
- **P1**: user-visible regression, invalid API or authorization contract, hydration failure, or broken primary interaction.
- **P2**: concrete maintainability, performance, test, or accessibility defect likely to cause incorrect behavior.
- **P2**: concrete maintainability, performance, test, or accessibility defect, or a material violation of an explicit project contract.
- **P3**: minor actionable cleanup; omit unless the user requested a thorough audit.
Lead with findings ordered by severity. Include a tight file and line reference, the failing contract or reproduction path, impact, and a concrete fix direction. If there are no findings, say `No issues found.` and state any material verification gap. Do not add praise sections, speculative risks, or an unsolicited offer to implement fixes.
Lead with findings ordered by severity. Include a tight file and line reference, the observed failure or applicable project rule, and a concrete fix direction. Explain the rule's applicability for convention findings; describe downstream consequences only when supported by evidence. When no findings remain, say so briefly and state any material verification gap. Do not add praise sections, speculative risks, or an unsolicited offer to implement fixes.
[accessibility]: references/accessibility-ui.md
[code-quality]: references/code-quality.md

View File

@@ -2,32 +2,51 @@
Accessibility findings are first-class review findings. Treat broken keyboard access, missing accessible names, focus loss, and unreachable popup content as correctness bugs, not polish.
Before finalizing UI or accessibility findings, fetch the latest Web Interface Guidelines as a required baseline:
## Review Evidence
```text
https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md
```
Do not treat that document as the complete accessibility rule set. Combine it with:
Use the sources relevant to the changed contract:
- `packages/dify-ui/README.md`, `packages/dify-ui/AGENTS.md`, and the relevant primitive implementation when code uses `@langgenius/dify-ui/*`.
- Base UI docs and local `.d.ts` contracts when primitive semantics, focus target, labels, or popup reachability are unclear.
- MDN or relevant WAI-ARIA/browser standards when behavior, compatibility, or deprecation status matters.
- The current feature's product semantics, because an accessible primitive can still be used in an inaccessible workflow.
Consult current official documentation or standards when these sources leave a behavior unresolved. The [Web Interface Guidelines] are an optional broader UI reference, not a required fetch for each review.
## Semantic HTML
Flag:
- Clickable `div` or `span` used for actions.
- Router navigation implemented with button or `onClick` when a `Link` / `<a>` is the real semantic element.
- Icon-only buttons without `aria-label` or `aria-labelledby`.
- Icon-only controls without an accessible name; follow the naming rules below and the Dify UI `IconButton` contract.
- Decorative icons missing `aria-hidden="true"`.
- Images without `alt`; use `alt=""` only when truly decorative.
- Heading levels that skip hierarchy in page-level content.
Prefer semantic HTML before ARIA.
## Page Landmarks
When reviewing Web page layouts, navigation, or landmark changes, read [Page landmarks].
Inspect the composed page with its parent layouts for missing or duplicate main regions,
inappropriate nesting or roles, and ambiguous or broken landmark names. Check affected
unit and E2E locators when semantic elements change. Web owns these composition rules;
use the existing naming contract for label-source decisions.
## Accessible Names And Descriptions
Read [Accessible names and descriptions] when a change affects labels, ARIA naming, help/error relationships, or hidden text. That document owns the shared implementation and review contract.
Flag violations supported by the final rendered behavior:
- Missing or insufficient names, redundant name overrides, or naming attributes prohibited by the element's role.
- Overrides that omit visible label wording or suppress necessary descendant information.
- Broken or stale label/description references, including relationships lost when responsive content or overlays unmount.
- Descriptions used instead of names, repeated help text, or essential structured content available only as a flattened description.
Inspect the computed name and description in the relevant state. Matching an `aria-label` string to nearby text alone does not prove redundancy.
## Keyboard And Focus
Flag:
@@ -48,7 +67,6 @@ Flag:
- Missing stable `name` on form fields that submit or validate.
- Incorrect input `type`, `inputMode`, `autoComplete`, or `spellCheck` for email, token, URL, number, search, code, or username fields.
- Labels that are not clickable.
- Submit buttons disabled before a request starts, preventing normal submit behavior.
- Non-submit buttons inside forms missing `type="button"`.
- Errors not associated with fields or not reachable by screen readers.
- Error recovery that does not focus or expose the first invalid field.
@@ -56,16 +74,20 @@ Flag:
- Placeholder text used as the only label.
- Password managers accidentally triggered on non-auth fields because autocomplete is missing or wrong.
Prefer visible labels. If visible surrounding text already labels the control, use a visually hidden label or a precise `aria-label`.
Prefer visible labels and associate them through the appropriate field primitive, a native `label`, or `aria-labelledby`. Do not duplicate an existing label with hidden text or `aria-label`; follow [Accessible names and descriptions] when no suitable visible label exists.
## Disabled, Loading, And Async States
Flag:
- Loading state without `aria-busy`, `role="status"`, or another accessible update path when it changes user interaction.
- Loading controls whose accessible name disappears, or whose user-relevant progress has no
feature-owned status path. Follow the Dify UI Button contract for focused loading buttons; do not
add `aria-busy` to a button as a generic pending-state substitute.
- The same pending state passed to both Dify UI Button `loading` and `disabled`, which duplicates
state ownership and obscures whether `disabled` expresses independent unavailability.
- Spinner or decorative loading icon exposed to screen readers.
- Disabled controls that hide the reason users cannot proceed.
- `aria-disabled` used without manually blocking click, Space, and Enter.
- Controls marked `aria-disabled` that still activate through supported pointer or keyboard input. Check the primitive's handling before requesting manual event guards.
- Toasts, inline validation, or async status changes that are not announced when users need the update to continue.
- Icon-only loading/error affordances without text or accessible status where the state matters.
@@ -90,7 +112,7 @@ Use Popover for explanatory content, rich help, and infotips. Use Tooltip only a
Flag:
- Text in flex/grid children without `min-w-0` when it can overflow.
- Names, labels, file names, model names, workspace names, or user content lacking `truncate`, `line-clamp`, or `break-words`.
- Long names, labels, or user content that overflow, obscure adjacent controls, or become unreadable in supported layouts.
- Right-side icons, badges, checks, or actions that shrink before the text area.
- Empty arrays or empty strings rendering broken layout instead of an empty state.
- Button, tab, badge, chip, menu item, or card text that can overlap sibling controls at common viewport widths.
@@ -107,3 +129,7 @@ Flag:
- Images without dimensions.
- Loading copy using `...` instead of `…`.
- Hardcoded dates, times, numbers, or currency formats instead of `Intl.*`.
[Accessible names and descriptions]: ../../../../packages/dify-ui/docs/accessible-names-and-descriptions.md
[Page landmarks]: ../../../../web/docs/landmarks.md
[Web Interface Guidelines]: https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md

View File

@@ -26,15 +26,17 @@ Prefer:
## Styling
For Dify UI/Base UI state styling and callback composition, apply the package's [Styling] contract,
including its documented reasons for direct state access.
Flag:
- New CSS modules or ad hoc CSS when Tailwind utilities and Dify tokens cover the need.
- Component-level plain `.css` files or component CSS imported through `globals.css`; use scoped `*.module.css` only when Tailwind and component variants cannot express the style.
- Generic color utilities where Dify semantic tokens exist.
- Hardcoded magic class values for colors, spacing, radius, shadow, z-index, or typography when Dify tokens, component variants, or documented radius mappings exist.
- Hardcoded magic class values for colors, spacing, radius, shadow, z-index, or typography when Dify tokens, component variants, or standard Tailwind radius classes match the required design value. Apply [Styling] when choosing radius classes.
- `!` important modifiers or important CSS overrides without a narrow, documented reason.
- Manual string concatenation, template strings, array `.join(' ')`, or custom ternaries for conditional or multi-line classes.
- JS conditional class branches for primitive visual states already exposed by Dify UI/Base UI `data-*` selectors.
- Manual class-list assembly through string concatenation, template strings, array `.join(' ')`, or a custom conditional combiner instead of `cn(...)`. Conditional values passed to `cn(...)` remain valid.
- Incoming `className` placed before default classes in `cn(...)`, preventing call-site overrides.
- Arbitrary z-index or one-off layering fixes on overlays.
@@ -43,7 +45,6 @@ Use:
- `cn(...)` from the local package or utility already used by the file.
- Dify semantic tokens and Tailwind v4 utilities.
- Existing component variants before one-off class forks.
- Primitive selectors such as `data-disabled:*`, `data-checked:*`, `data-highlighted:*`, `group-data-*`, `peer-data-*`, and `has-[:focus-visible]` before adding React state or boolean props solely for styling.
- Component-level variants, semantic tokens, and normal cascade/order before `!` overrides. Use `!` only for a contained compatibility override that cannot be expressed through the component API or local selector structure.
## Imports
@@ -66,3 +67,5 @@ Flag:
- Error messages that state only the failure and not the next step.
Use feature-local translation keys by default. Alias only when crossing namespaces. `pnpm i18n:check --file <name>` should pass for any touched translation namespace.
[Styling]: ../../../../packages/dify-ui/docs/styling.md

View File

@@ -1,88 +1,31 @@
# Component Architecture Rules
# Component Architecture Review
Use these rules for React component structure, ownership, state, props, effects, and module organization.
Use the canonical reference for the changed concern. These links share rules; they do not activate the implementation skill or its workflow.
## Ownership
| Concern | Canonical rules |
| --- | --- |
| Vertical modules, public entrypoints, data/handler placement, wrappers, Props, and types | [Ownership] |
| Local/Jotai state, form drafts, route identity, URL state, and persistence | [State] |
| Effects, navigation, memoization, and subscriptions | [Runtime] |
| Hotkeys, focus, and secondary surfaces | [Interactions] |
Flag:
## Apply Rules In Their Actual Scope
- State, query, mutation, or handlers hoisted above the lowest component that actually uses them.
- Parent components owning row/item actions that do not coordinate a workflow.
- Prop drilling through multiple pass-through layers.
- A page/tab-level section component becoming the data owner without needing a shared snapshot or shared loading/error/empty UI.
- Feature code promoted to shared only because it appears once or might be reused later.
Explicit team conventions are reviewable contracts, including module organization and public API boundaries. Check the documented exception before reporting a violation. Do not infer an exception solely because the code appears to work, or invent a user-facing failure for a convention finding.
Accept repeated TanStack Query calls in siblings when each component independently consumes the data. Cache deduplication is not a reason to hoist by itself.
- For owner placement, trace the consumers and required lifetime. Establish whether the parent coordinates a snapshot, submission, navigation, shared UI, or persistence before asking to move state or handlers.
- For component boundaries, identify the ownership or encapsulation the proposed extraction would improve; file length alone establishes neither.
- For props and types, check the domain contract and public API. Do not report private props typing style alone; declaration/export syntax matters only for a documented package rule or concrete type, export, or framework defect.
- For state and Effects, trace the source of truth, external synchronization target, and mount/reset boundary. Controlledness alone does not prove that a draft is lifted or persisted; follow the form and overlay contracts linked by [State].
- For navigation, distinguish ordinary links from mutation success, guarded redirects, command flows, and submission side effects.
## Component Boundaries
## Preserve Existing Product Contracts
Flag:
During refactors, trace the interaction being moved through its real consumer. Navigation, sidebar, dropdown, webapp-list, and app-switching changes must preserve expansion controls, hover persistence, pin/delete actions, routing, keyboard/focus handling, and open-state ownership where present.
- React component files over 300 lines when the file mixes multiple responsibilities that can be split into focused colocated components, hooks, or utilities.
- Shallow wrappers that only rename props or hide the real primitive.
- Extra DOM wrappers that do not provide layout, semantics, accessibility, state ownership, or library integration.
- Dialog/dropdown/popover hidden surfaces that obscure the parent flow when they should be extracted into a small local component.
- Business forms, menu bodies, or one-off helpers moved away from their owner without reuse or semantic value.
Check that the changed owner still handles reachable empty, loading, and missing optional-data states, and that primitive wrappers preserve accessible semantics and the public controlled-state contract. Report the actual lost behavior or explicit rule violation; use the package testing policy when assessing regression coverage.
Prefer colocated components split by actual data and state needs.
## Bad Component Design Patterns
Flag:
- Refactors of existing navigation, sidebar, dropdown, webapp list, or app-switching UI that do not preserve behavior-sensitive interactions such as expand/collapse arrows, hover persistence, pin/delete controls, routing, keyboard/focus handling, or open-state ownership.
- Components that mix data fetching, mutation side effects, popup state, form validation, layout, and row rendering without a clear owner.
- Generic components with many boolean props that encode one feature's workflow.
- A shared component that imports feature-specific copy, routes, or API contracts.
- A feature component that accepts pre-rendered fragments only to avoid placing ownership correctly.
- A child component that receives both raw server data and separately derived flags for the same concept.
- A wrapper that changes accessible semantics of the primitive it wraps.
- A component that exposes controlled props but still keeps a competing private state for the same value.
- A component that cannot render empty, loading, or missing optional API fields without caller-side preprocessing.
When existing components already own interaction logic, prefer reusing or extending them. If a refactor is necessary, preserve the old interaction contract and add or update focused tests for changed behavior.
## Props And Types
Flag:
- Declaration or export rewrites made only for stylistic uniformity, without changing an owned behavior or contract.
- Named `Props` types for trivial one-off props where inline typing is clearer.
- Props named by UI implementation instead of domain/API role.
- API data converted too early or under a generic name that breaks traceability.
- Callers duplicating fallback checks that the lowest rendering component already handles.
Do not flag `FC`, `React.FC`, function declarations, arrow functions, named exports, or default exports by syntax alone. Report them only when the chosen form causes a concrete type, lifecycle, export, framework, or enforced package-contract defect.
## Effects
Flag effects that:
- Transform props/state for rendering.
- Copy one state value into another representing the same concept.
- Handle user actions that belong in event handlers.
- Reset state from props when a keyed reset, stable ID, or render-time derivation would work.
- Fetch data that belongs in framework APIs or TanStack Query.
If an effect remains, it must synchronize with a named external system: browser API, subscription, timer, analytics-on-visibility, non-React widget, or imperative DOM integration.
## State Modeling
Flag:
- Storing derived booleans, disabled flags, default tabs, or loading labels that can be calculated from current query/feature state.
- Local state used to fake server data or generated contract fields.
- UI state persisted to localStorage when it is live app state.
- Feature-local mock shells wired to unrelated existing APIs before the real API is confirmed.
Prefer render-time derivation. Keep true local state for user choices, transient input, controlled popups, and feature UI state that has no server source.
## Navigation
Flag:
- Imperative router navigation for ordinary links.
- Button semantics used for navigation.
- Navigation state hidden in component state when URL state is required for shareable filters, tabs, or pagination.
Use `Link` for normal navigation. Use router APIs for mutation success, guarded redirects, command flows, or form submission side effects.
[Interactions]: ../../how-to-write-component/references/interactions.md
[Ownership]: ../../how-to-write-component/references/ownership.md
[Runtime]: ../../how-to-write-component/references/runtime.md
[State]: ../../how-to-write-component/references/state.md

View File

@@ -1,74 +1,28 @@
# Data, Query, And Contract Rules
# Data, Query, And Contract Review
Use these rules for generated contracts, TanStack Query, mutations, auth/SSR boundaries, URL state, and client persistence.
[Data and queries] owns generated-client, Query options, mutation/cache, imperative access, SSR, authentication, and tenant rules. [State ownership] owns URL state and persistence. Read only the reference needed by the diff; reading a shared reference does not activate its implementation skill.
## Generated Contracts
## Generated Contracts And Query Conventions
Flag:
Review explicit team conventions as contracts, including direct generated options, `skipToken` for missing required input, and shared cache policy. A lint rule may enforce part of a convention; neither a green lint result nor a suppression proves the full contract is satisfied.
- New legacy service/helper wrappers around generated `queryOptions()` or `mutationOptions()`.
- Continuing to use deprecated contract operations when a ready generated contract exists.
- Assuming a generated file means an operation is ready without checking deprecated markers, schema shape, and the actual UI consumer.
- Re-declaring API DTOs in components.
- Adding compatibility layers instead of migrating the pointed line and deleting the old layer.
- Establish whether the changed call belongs to a new or migrated surface and whether the generated operation is ready. Check deprecated markers, schema shape, and the real UI consumer before prescribing a migration.
- Distinguish a pass-through wrapper from a feature hook with actual orchestration. Check whether an independent execution condition or Promise composition justifies the documented query/mutation exception.
- Trace generated input and output types through their boundaries. Identify the exact DTO mirror, field widening, placeholder input, or lost intentional empty value when reporting a violation.
- Check whether a local mutation callback owns feature feedback or replaces shared invalidation, retry, or cache defaults. Match optimistic changes to the current list/detail owner.
Backend Pydantic and OpenAPI schemas own API shape. Generated clients and schemas under `packages/contracts/generated/*` are authoritative at frontend boundaries and use the `{ params, query?, body? }` input shape.
## Imperative Access And SSR
## Queries
- Check freshness, projection, retries, and the caller's execution condition separately against [Data and queries]. Trace Promise ownership to distinguish an awaited hard gate from soft prefetching with an explicit failure owner.
- For Server Components, identify who renders the data and who may revalidate it. Check dehydration, the same-key client consumer, error handling, and the intended Suspense/server-rendered-content contract when the diff changes streaming.
- For auth, setup, roles, branding, or availability, trace authoritative data and the loading/fallback path. A static redirect or placeholder value cannot stand in for a request-dependent decision.
Flag:
## Tenant, URL, And Persistence Boundaries
- `enabled` used to hide missing required input instead of `input: skipToken`.
- Fake fallback IDs or placeholder inputs used to force a query to run.
- Query results copied into local state for rendering.
- Shared query behavior such as invalidation, stale defaults, or retry rules reimplemented at call sites.
- `prefetchQuery` treated as a hard gate or as returning data/errors to the caller.
- Trace the current workspace-switch flow and cache lifetime before reporting missing identity in a query key. Verify backend meaning before treating `workspace_id` and `tenant_id` as interchangeable.
- For URL and storage changes, identify whether the value is shareable navigation state, live app state, a one-shot signal, or a low-frequency preference. Apply [State ownership] to that category and verify its write/reset owner.
Use `useQuery(consoleQuery.xxx.queryOptions(...))` or `useQuery(marketplaceQuery.xxx.queryOptions(...))` directly unless a feature hook performs real orchestration.
Report the violated rule and applicable scope or the concrete failing path. Do not invent runtime impact when the finding is a team-convention violation.
## Mutations
Flag:
- Deprecated `useInvalid` or `useReset`.
- `mutateAsync` used without a need for Promise semantics.
- Awaited mutations without `try/catch`.
- Components owning shared cache invalidation that belongs in query defaults.
- Optimistic updates that do not match current list/detail ownership.
Use generated `mutationOptions()` directly when possible. Put shared cache behavior in `createTanstackQueryUtils(...experimental_defaults...)`.
## SSR, Auth, And Route Boundaries
Flag:
- Request-time auth, setup, workspace role, or tenant decisions moved into static `next.config redirects()`.
- Dynamic role gates depending on `workspaces.current` implemented as static path redirects.
- Authorization logic depending on soft `prefetchQuery`.
- Removing a client fallback before server API unavailable behavior is defined.
- Global placeholder query contracts introduced to solve a route-local Suspense issue.
- Branding-sensitive UI reading placeholder defaults without checking pending/placeholder state.
Separate hard gates from soft prefetches. `fetchQuery` can be a server decision boundary; `prefetchQuery` is cache warmup.
## Workspace And Tenant
Flag:
- Treating workspace switch as ordinary CRUD invalidation when the current app flow performs server switch plus full reload.
- Query keys that omit workspace/tenant identity when the query truly varies by workspace and no full reload boundary applies.
- Mixing `workspace_id` and `tenant_id` without tracing the current backend/API contract.
Current Dify workspace switch should be reviewed as a tenant cache boundary first.
## URL State And Local Storage
Flag:
- Shareable filters, tabs, pagination, selected panels, or search state hidden only in component state.
- One-shot navigation signals modeled as subscribed persistent state.
- Live app state stored in localStorage.
- Direct `window.localStorage`, `globalThis.localStorage`, or raw storage calls in app code.
- High-frequency interaction state persisted on every change instead of on commit/settle.
Use URL state for shareable UI state, feature/Jotai/store state for live UI state, and `@/hooks/use-local-storage` only for low-frequency client-only preferences, dismissed notices, and UI defaults.
[Data and queries]: ../../how-to-write-component/references/data.md
[State ownership]: ../../how-to-write-component/references/state.md

View File

@@ -1,141 +1,29 @@
# Dify UI Rules
# Dify UI Review Routing
Use these rules whenever a review touches `packages/dify-ui/` or code consuming `@langgenius/dify-ui/*`.
Use this reference when a review touches `packages/dify-ui/` or consumes
`@langgenius/dify-ui/*`. It routes to owner documentation; it does not redefine package
contracts.
Before finalizing findings for those files, read the current local docs that apply:
Read `packages/dify-ui/AGENTS.md` and the primitive implementation first, then only the matching
owner:
- `packages/dify-ui/README.md`
- `packages/dify-ui/AGENTS.md`
- `web/docs/overlay.md` for floating UI
- `packages/dify-ui/src/<primitive>/index.tsx` for the primitive being changed or consumed
| Review area | Canonical owner |
| ------------------------------------------ | ------------------------------------------------------------------- |
| Package boundary and document routing | `packages/dify-ui/AGENTS.md` |
| Imports, exports, public types, and anatomy | `packages/dify-ui/docs/authoring.md` |
| Button or icon-only actions | `packages/dify-ui/src/button/README.md`, `packages/dify-ui/src/icon-button/README.md` |
| Compound inputs | `packages/dify-ui/src/input-group/README.md` |
| Forms and field semantics | `packages/dify-ui/docs/forms.md` |
| Selection and typed values | `packages/dify-ui/docs/selection.md` |
| Portals, layers, and floating surfaces | `packages/dify-ui/docs/overlays.md` |
| State styling, callbacks, and Tailwind | `packages/dify-ui/docs/styling.md` |
| Package tests and Storybook | `packages/dify-ui/docs/testing.md` |
## Package Boundary
For consumer code under `web/`, also read `web/AGENTS.md` for application-owned reuse policy and
`packages/dify-ui/README.md` for the available public subpaths.
Flag in `packages/dify-ui`:
- Imports from `web/`.
- Dependencies on Next.js, i18n, ky, Jotai, Zustand, TanStack Query, oRPC, or business APIs.
- Business-specific component behavior that belongs in `web/`.
- Multiple unrelated primitives in one component folder.
`packages/dify-ui` is a primitive layer: Base UI headless components + `cva` + `cn` + Dify design tokens.
## Imports And Exports
Flag:
- Consumer imports from `@langgenius/dify-ui` without a subpath.
- Missing `package.json#exports` entry for a new primitive.
- Internal package imports using workspace subpaths instead of relative paths.
- Exported props using internal-only types that consumers cannot import from the component subpath.
- Canonical primitive boundaries or their associated public types using a redundant `Root` suffix when no higher-level convenience component exists in the same subpath.
Consumers use subpath exports such as `@langgenius/dify-ui/button`.
Canonical boundaries use the primitive name and matching public types (`Select` / `SelectProps`). Keep `Root` only to distinguish a low-level anatomy root from a higher-level convenience component (`CheckboxRoot` / `Checkbox`); implementation aliases should still show their Base UI source (`BaseSelect.Root.Props`).
## Props And State
Flag:
- Flattened props where related values need a discriminated union, such as `value` / `defaultValue`, `multiple` / `value`, or `clearable` / `onChange`.
- React state used only to mirror Base UI state for class names.
- JavaScript conditional class logic for visual states that the Dify UI/Base UI primitive already exposes through `data-*` attributes or CSS variables.
- Controlled props added when uncontrolled DOM state or CSS variables would be enough.
- Thin wrappers that rename Base UI parts without adding semantics.
- Generic Base UI selection primitives wrapped without preserving their value generics, such as `Select.Root<Value, Multiple>`, `RadioGroup<Value>`, or `Radio.Root<Value>`.
- Shared select/radio option components that type selected values as `string` while callers pass enums, unions, booleans, numbers, objects, or nullable placeholder values.
Prefer Base UI/Dify UI data attributes and CSS variables for visual state: `data-open`, `data-checked`, `data-disabled`, `data-highlighted`, `data-popup-open`, `group-data-*`, `peer-data-*`, `has-[:focus-visible]`, and primitive CSS variables such as anchor width or transform origin. Use JS conditional classes for product/business state that the primitive does not expose.
For non-string `Select` and `RadioGroup` values, prefer explicit domain generics at the root and at child value carriers. JSX children do not inherit the parent generic, so `RadioGroup<PromptMode>` should compose with `Radio<PromptMode>`, `RadioItem<PromptMode>`, or option values from a typed collection. For `Select`, prefer the Base UI `items` collection pattern for typed value-to-label rendering, and flag string coercion helpers used only to recover display labels.
## Forms
Flag:
- Form-like UI using unrelated `Input` and `Button` pieces without a submit boundary.
- Text-like fields not composed through `Field`, `FieldLabel`, and `FieldControl` when using Dify UI form semantics.
- Select fields using `FieldLabel` instead of `SelectLabel`.
- Slider fields using a generic label instead of `SliderLabel`.
- Checkbox/radio groups missing `Fieldset` and `FieldsetLegend`.
- Field errors or descriptions rendered without `FieldDescription` / `FieldError` relationships.
`Form` is the submit boundary. Dify UI form primitives are not a form state-management framework; business validation and schema-driven behavior belong in `web/`.
## Overlay Contract
Flag:
- Legacy web overlay imports in new or modified code.
- Manual portals around Dify UI overlay primitives.
- Call-site `z-*` overrides on overlays.
- Missing root `isolation: isolate` assumptions when debugging overlay stacking.
- Repeated backdrop, z-index, or portal chrome at call sites.
- Tooltip used for infotips, long text, or interactive content.
All Dify UI body-portalled overlays use `z-50`. Toast uses `z-60`. DOM order handles stacking between overlays.
## Primitive Selection
Flag:
- `Tabs` used for simple mode/filter/view selection where `SegmentedControl` is the semantic primitive.
- `SegmentedControl` used where `tablist` / `tabpanel` semantics are required.
- `Select` used for searchable or free-form input.
- `Combobox` used for unrestricted search text where no selected option is remembered.
- `Autocomplete` used for closed-list selection.
- Tooltip or PreviewCard used for content that must be reachable on touch or by screen readers.
Use:
- `Autocomplete` for free-form text with optional suggestions.
- `Combobox` for searchable selected values from a collection.
- `Select` for closed, scannable option sets.
- `Popover` for infotips, help text, rich content, or interactions.
## Bad Usage Patterns To Flag
Flag:
- Manually recreating UI behavior or chrome already owned by `@langgenius/dify-ui/*` or `web/app/components/base/*`, such as buttons, inputs, toggle groups, popovers, dropdown menus, alert dialogs, switches, avatars, scroll areas, toasts, borders, focus states, disabled states, segmented controls, or existing feature components.
- Styling a raw Base UI primitive directly in `web/` when a Dify UI primitive exists.
- Wrapping a Dify UI primitive in a feature component that hides its label, error, disabled, or focus contract.
- Replacing a semantic primitive with a generic `div` plus classes to match a screenshot.
- Using `Tooltip` because it is visually convenient when the content is actually help text or needs touch access.
- Adding a `z-*` override to make a child popup appear over a parent dialog.
- Adding a new app-level wrapper around Dialog, Drawer, Popover, Select, or Combobox that repeats portal/backdrop/positioner logic.
- Using dify-ui `Input` as a drop-in replacement for legacy inputs that include search, clear, copy, unit, localized placeholder, or number normalization behavior.
- Building a form row from loose text and controls instead of the matching Field/Form primitives.
- Adding component state only to style `data-open`, `data-checked`, `data-disabled`, or highlighted states that Base UI already exposes.
- Passing booleans down only so children can toggle classes already expressible with primitive `data-*` selectors.
## Tokens, Radius, And Styling
Flag:
- `radius-*` class names.
- Custom Tailwind `borderRadius` extension for Figma radius values.
- Generic colors where semantic Dify tokens exist.
- Hardcoded design values where Dify tokens, component variants, or documented Figma radius mappings exist.
- `!` important modifiers used to fight primitive styles instead of fixing the variant, selector, or component composition.
- Manual class strings that duplicate primitive variants.
- `min-w-(--anchor-width)` on picker popups when it defeats viewport clamping.
Use the Figma radius mapping from `packages/dify-ui/README.md`; for example `--radius/sm` maps to `rounded-md`, and `--radius/md` maps to `rounded-lg`.
Use `!` only for a tightly scoped compatibility override after confirming the primitive API, data attributes, and selector structure cannot express the state.
## Focus Details
Flag focus rings attached to the wrong element. For example, Base UI `Slider.Thumb` focuses an internal `input[type=range]`, so the visible thumb wrapper needs `has-[:focus-visible]` rather than direct wrapper `focus-visible`.
## Custom SVG Icons
Flag:
- New generated React icon components or JSON files under `web/app/components/base/icons/src/...` for custom SVG icons.
- Custom SVG icons consumed outside the Tailwind `i-custom-*` icon class pipeline.
- Generated `packages/iconify-collections/custom-*/icons.json` diffs where unrelated existing icons lost or changed intrinsic `width` or `height`.
New custom SVG icons belong in `packages/iconify-collections/assets/...`. Regenerate with `pnpm --filter @dify/iconify-collections generate`, validate with `pnpm --filter @dify/iconify-collections check:dimensions`, and consume the generated icon with Tailwind `i-custom-*` classes.
Treat the implementation, public types, tests, and stories as evidence for the documented
contract. If they disagree, identify the actual owner before reporting a finding. Read current
official Base UI documentation and installed type declarations for upstream-derived behavior.
Report only a reproducible contract violation or observable defect, not a preference inferred from
this routing file.

View File

@@ -71,8 +71,6 @@ Flag:
## React Flow
For workflow React Flow components, keep this Dify-specific rule:
Use [Dify invariants] for React Flow node/edge consumption and provider availability in RAG Pipe template rendering. Callback-only reads or mutations can use `useStoreApi`.
- UI consumption should use React Flow hooks such as `useNodes` / `useEdges`.
- Callback-only reads or mutations can use `useStoreApi`.
- Node components under `web/app/components/workflow/nodes/[nodeName]/node.tsx` must not depend on workflow stores that are absent in RAG Pipe template rendering.
[Dify invariants]: dify-invariants.md

View File

@@ -1,6 +1,6 @@
# Testing Review Rules
`web/docs/test.md` is the canonical frontend testing policy. Use this file only to translate that policy into review findings.
Use `web/docs/test.md` for tests owned by `web/` and `packages/dify-ui/docs/testing.md` for tests owned by Dify UI. These owners define their test boundaries, environments, and checks; this reference only adds review questions.
## Request Missing Tests When Risk Justifies Them
@@ -9,10 +9,11 @@ Flag missing coverage when a change alters a reachable contract such as:
- User interaction, navigation, form submission, validation, or permissions.
- Query or mutation behavior, URL state, persistence, or one-shot signals.
- Loading, error, empty, and recovery states that users can encounter.
- A hidden surface whose close-and-reopen behavior changes whether in-progress state resets or persists.
- Accessibility-critical labels, keyboard flow, focus, disabled state, or overlay behavior.
- A regression-prone business rule or bug fix that can be reproduced through a public boundary.
Do not request tests for mechanical changes, pass-through wrappers, implementation details, or visual-only styling unless they affect behavior. Low coverage alone is not a finding.
Do not request tests for mechanical changes, pass-through wrappers, implementation details, or incidental visual styling. Low coverage alone is not a finding.
## Flag Low-Value or Fragile Tests
@@ -29,8 +30,8 @@ Flag tests that:
- Prefer semantic queries and accessible names.
- Prefer real feature components when integration semantics matter.
- Allow intentional child or provider mocks when setup would dominate the test and that boundary is covered independently.
- Do not accept semantically inaccurate mocks of Dify UI or legacy base primitives.
- Require a real-browser or visual verification plan when `happy-dom` cannot represent the risk.
- Check mocks against the owning package's policy; allowed mocks must preserve the public contract and leave the behavior under review real.
- Apply the owning package's admission policy before requesting new automation. Real-browser verification can be a one-off review; a simulator limitation alone does not justify a permanent test.
- Check that assertions prove the claimed outcome and add evidence beyond existing tests; a browser runtime does not make weak assertions stronger.
Treat test quality, determinism, and regression value as the review criteria. Do not use test count or coverage percentage as a proxy for quality.

View File

@@ -5,12 +5,9 @@ description: Use when writing or changing Vitest or React Testing Library tests
# Frontend Testing
`web/docs/test.md` is the single policy owner. Read it before changing frontend tests; this skill adds no separate requirements.
Read the testing policy for the package that owns the changed tests:
1. Identify the observable contract and regression risk.
2. Choose the smallest boundary that includes the behavior owner.
3. Establish the failing case first when practical, then implement one coherent scenario.
4. Run the focused spec before the affected suite and relevant static checks.
5. Report the behavior verified and any remaining browser, visual, or end-to-end risk.
- `web/`: `web/docs/test.md` owns Web test boundaries, environments, and commands.
- `packages/dify-ui/`: `packages/dify-ui/docs/testing.md` owns primitive test boundaries, environments, Storybook, and commands.
Recommend deleting low-value tests as readily as adding missing behavior coverage. Use `web/docs/test.md` for policy and Web commands; use the `packages/dify-ui/README.md` Development section for Dify UI commands.
This skill adds no parallel policy or check sequence. Follow the selected owner's requirements, including its validation commands. Recommend deleting low-value tests as readily as adding missing behavior coverage, and report the contract verified and any material verification gap.

View File

@@ -7,33 +7,26 @@ description: Use when implementing or refactoring React/TypeScript components an
Use this skill to route component architecture decisions to its bundled references. Read only the references required by the current change.
## First Decisions
| Question | Default | Promote only when |
| --- | --- | --- |
| Where should code live? | In the product workflow, route, or feature owner. | Several verticals need the same stable contract. |
| Who owns state and handlers? | The lowest visual owner that consumes them. | A parent coordinates one workflow or consistent snapshot. |
| Should state enter Jotai? | Keep component and form state local. | Siblings need one source of truth or scoped workflow persistence. |
| Who owns URL state? | Next.js route APIs and `nuqs`. | Atoms require a read-only route-identity bridge. |
| Who owns remote state? | TanStack Query at the lowest consumer. | Atom state drives the query or shared derivations consume it. |
| Is a wrapper needed? | Use the primitive or direct code. | The wrapper owns behavior, validation, state, or semantics. |
| Is an Effect needed? | Derive during render or handle the user action. | A named external system must be synchronized. |
## Topic Routing
- Component moves, module boundaries, props, types, or owner placement: read [`references/ownership.md`][ownership].
- Dify UI wrappers or primitive state styling: read the package's [Public API authoring] and [Styling] contracts.
- Jotai, form drafts, route identity, URL state, or persistence: read [`references/state.md`][state].
- Generated contracts, nullable API data, Query, mutations, SSR, auth, or workspace state: read [`references/data.md`][data].
- Hotkeys, focus, dialogs, menus, popovers, or other secondary surfaces: read [`references/interactions.md`][interactions] and the overlay guide it references when applicable.
- Hotkeys, focus, dialogs, menus, popovers, or other secondary surfaces: read [`references/interactions.md`][interactions] and the overlay guide it references when applicable. Also read [`references/state.md`][state] when the surface owns a draft or other local session state.
- Effects, navigation, memoization, preloading, or render cost: read [`references/runtime.md`][runtime].
## Workflow
## Scope And Verification
1. Identify the behavior owner and the public contract being changed.
2. Read the nearby implementation, tests, and only the routed skill references.
3. Implement one coherent vertical slice. Do not expand into equivalent patterns elsewhere unless the current contract cannot be completed without them.
4. Verify observable behavior at the narrowest sufficient boundary, then run the checks documented by the owning package: `web/docs/test.md` or `web/docs/lint.md` for Web, and the `packages/dify-ui/README.md` Development section for Dify UI.
Identify the behavior owner, state lifetime, and public contract from the nearby implementation and relevant references. Keep the change within that vertical slice unless the contract requires changes elsewhere. Verify the changed behavior and complete the owning package's required checks. For Web, read `web/docs/test.md` for test work and `web/docs/lint.md` for static checks. Dify UI verification is owned by `packages/dify-ui/docs/testing.md`.
## Tailwind CSS
- Write canonical Tailwind v4 classes; prefer canonical utilities over equivalent arbitrary values.
- Common forms include `w-105` instead of `w-[420px]`, `px-2.25` instead of `px-[9px]`, `bg-linear-to-b` instead of `bg-gradient-to-b`, `wrap-break-word` instead of `break-words`, and `field-sizing-content` instead of `[field-sizing:content]`.
[Public API authoring]: ../../../packages/dify-ui/docs/authoring.md
[Styling]: ../../../packages/dify-ui/docs/styling.md
[data]: references/data.md
[interactions]: references/interactions.md
[ownership]: references/ownership.md

View File

@@ -8,9 +8,10 @@ Read this document when a component consumes generated contracts, nullable API v
- Backend Pydantic and OpenAPI schemas own API shape. Follow the generated `{ params, query?, body? }` input shape; when it is wrong, fix the backend schema and regenerate `packages/contracts/generated/*`.
- Do not hand-write DTO mirrors, widen generated fields or enums, edit generated output, or add a parallel frontend status layer unless it models product state absent from the API.
- Check deprecated markers, schema shape, and the actual consumer before assuming that a generated operation is ready to use.
- When a ready generated operation exists for the changed call, migrate deprecated operations and remove the replaced layer instead of adding compatibility wrappers.
- Normalize only at real boundaries such as user input, search, URL params, filenames, DOM IDs, or a required legacy adapter.
- Preserve `null`, `undefined`, and intentional empty strings until the final boundary. Do not use `value || undefined` when an empty string means clearing a field.
- Build required values in the branch that proves them. Avoid `filter(Boolean)`, truthiness filters, non-null assertions after filters, and placeholder values used only to satisfy types.
- Build required values in the branch that proves them. Do not use truthiness filters, non-null assertions, or placeholders to conceal missing required input or discard valid `0`, `false`, or empty-string values.
## Queries
@@ -18,25 +19,40 @@ Read this document when a component consumes generated contracts, nullable API v
- If query input comes from atom state, keep it in `atomWithQuery`; do not unwrap the atom in a component solely to call `useQuery`.
- For missing required input, branch the whole generated input with `skipToken`. Add `enabled` only for an independent execution condition; do not put `skipToken` inside a placeholder payload or coerce IDs to empty strings.
- Return generated `queryOptions()`, `infiniteOptions()`, or `mutationOptions()` directly from TanStack Query atoms. Pass supported options into the generated call instead of spreading into a parallel object.
- Share the exact options between prefetch and render when they represent the same request. Do not extract option helpers merely to reuse input construction.
- For the same logical request, preserve key, input, operation, and result contracts. Reuse exact options only when
transport, context, and cache policy are shared; imperative and observer freshness may differ. Do not extract a helper
merely to reuse input construction.
- Avoid pass-through service hooks that only rename generated options. Keep feature hooks for actual orchestration or shared domain behavior.
## Mutations And Cache
- Use generated `mutationOptions()` directly for owner-local mutations.
- Put shared invalidation, retries, and cache behavior in `createTanstackQueryUtils(...experimental_defaults...)`. Local callbacks may own toast, close, and navigation effects but must not replace shared cache policy.
- Do not introduce deprecated `useInvalid` or `useReset` APIs.
- Put shared invalidation, retries, and cache behavior in `createTanstackQueryUtils(...experimental_defaults...)`. In oRPC v1, caller options override defaults, including callbacks. Local callbacks may own toast, close, and navigation effects but must preserve shared cache behavior.
- Prefer `mutate(...)`. Use `mutateAsync(...)` only when Promise composition is required, and catch awaited failures.
- Preserve intentional empty values and current list/detail ownership when updating data. Do not add optimistic updates without a verified owner contract.
## Prefetch And Hidden Surfaces
- Prefetch expensive secondary content from the trigger or menu-open event when it benefits the visible path. Do not mount hidden subscribers solely to warm the cache.
- `prefetchQuery` is cache warmup, not an authorization or availability gate. Use a hard fetch boundary when the server must decide whether rendering may proceed.
- Use `query` or `infiniteQuery` for imperative access. `staleTime` defines cache acceptance; `select` projects the return
value without replacing cached query-function data. Imperative queries default to no retries when `retry` is not
configured, and observer-only `enabled` does not prevent an imperative call.
- Migrate deprecated `fetchQuery`, `prefetchQuery`, `ensureQueryData`, and their infinite variants when the current `query` or `infiniteQuery` contract applies. A `staleTime` of `0` treats data as stale; a finite value accepts that freshness window; `Infinity` accepts data until invalidation; `'static'` accepts available data even after invalidation.
- `await` blocks, `return` transfers the Promise, and `void` discards its value without handling rejection. Handle
rejection before discarding a potentially rejecting Promise; use `.catch(noop)` only for intentional silence or
feedback owned elsewhere. Hard server gates await the query and preserve rejection.
## SSR, Authentication, And Workspace
- Static configuration owns path-invariant routing. Request-dependent authentication, setup, role, and tenant decisions belong to SSR or runtime decision boundaries.
- Distinguish soft SSR cache warming from authoritative decisions. Prefetched or placeholder data must not grant access or represent successful availability.
- Treat Server Components as query prefetch-and-dehydrate owners by default. Do not render or pass an imperative query
result when a browser observer can revalidate the same data unless ownership and freshness explicitly prevent drift.
- Non-blocking RSC streaming requires pending-query dehydration without redacting Next.js server errors, a
`HydrationBoundary` around the same-key client consumer, and Suspense when that content must be server-rendered.
- Never reuse tenant-scoped state after switching workspaces. Discard it at the switch boundary or isolate it by workspace identity.
- Trace the current switch flow before choosing cache handling: server switch plus full reload is a tenant boundary, not ordinary CRUD invalidation. Include workspace identity in varying query keys when no full-reload boundary applies, and trace the backend contract before interchanging `workspace_id` and `tenant_id`.
- Do not make product or authorization decisions from bootstrap defaults. Wait for authoritative data, or render an explicit loading or error state.
- Preserve an existing client fallback until server API-unavailable behavior has an explicit owner.
- Keep loading and Suspense behavior inside the feature that owns the request. Do not add fake global data merely to bypass that boundary.

View File

@@ -1,6 +1,6 @@
# Component Interactions And Overlays
Read this document when a change involves application hotkeys, focus, dialogs, menus, popovers, or other secondary surfaces. Overlay primitive selection and layering are owned by the [overlay guide].
Read this document when a change involves application hotkeys, focus, dialogs, menus, popovers, or other secondary surfaces. Overlay primitive selection and layering are owned by the [overlay contract].
## Focus And Semantics
@@ -20,12 +20,12 @@ Read this document when a change involves application hotkeys, focus, dialogs, m
## Secondary Surfaces
- Follow `web/docs/overlay.md` for primitive choice. Dify UI primitives are the default, with package-approved Web wrappers such as `Infotip` where the overlay guide allows them.
- Follow the [overlay contract] for primitive choice and shared mechanics. The nearest consumer `AGENTS.md` owns application-specific composite reuse policy.
- Separate behavior ownership from placement ownership: the action may own trigger, open state, and menu content while the caller owns slots, offsets, and alignment.
- Keep menu and dialog surfaces as siblings when a menu command opens a dialog. Mount the dialog outside popup content.
- Mount controlled overlays unconditionally unless unmounting is required for performance or reset semantics. Prefer keyed or owner-local reset over conditional wrappers.
- Put query and mutation work inside dialog or alert-dialog content when it should mount only after opening.
- Prefer uncontrolled roots when the primitive can own open state. Use controlled state only for business coordination, analytics, cleanup, or explicit reset behavior.
- Do not add manual portals or call-site z-index escalation. Fix ownership and stacking structure at the shared boundary.
- Use the [overlay contract] to determine content lifetime and preserve the Root's closing lifecycle. Follow [state ownership] for draft placement and semantic identity; portal placement alone does not locate the state owner.
- Place query subscriptions and mutation observers at the owner whose lifetime matches when they should run. Mounted-session work may belong inside content; work that must start or stop exactly with `open` needs an explicit open-state condition.
- Prefer primitive-owned open state unless another owner must observe or coordinate it. Analytics callbacks and local cleanup alone do not require a controlled root.
[overlay guide]: ../../../../web/docs/overlay.md
[overlay contract]: ../../../../packages/dify-ui/docs/overlays.md
[state ownership]: state.md

View File

@@ -12,28 +12,34 @@ Read this document when adding, moving, splitting, or refactoring React componen
## Component Ownership
- Put state, data access, loading, empty, error, and handlers in the lowest visual owner that uses them.
- Keep coordination in a parent only when it needs one consistent snapshot or coordinates submission, shared selection, batch behavior, navigation, or cross-section loading and errors.
- Repeated TanStack Query calls in siblings are acceptable when each sibling independently consumes the data; the cache already deduplicates requests.
- Put state, data access, loading, empty, error, and handlers in the lowest owner that uses them and whose mounted lifetime matches the required persistence.
- Keep coordination in a parent only when it needs one consistent snapshot, the value must intentionally survive the local owner's unmount, or the parent coordinates submission, shared selection, batch behavior, navigation, or cross-section loading and errors.
- Repeated TanStack Query hooks for the same key and input in Client Component siblings under one QueryClient are
acceptable; shared cache is not a reason to hoist. Separate Server Component QueryClients do not share it, so
request-level deduplication needs an identified request-local cache or verified framework or transport owner.
- Pass stable domain identity across boundaries. Do not pass raw server data together with separately derived flags for the same concept.
- One pass-through prop layer is acceptable. Repeated forwarding means ownership should move closer to the consumer or into feature-scoped shared state.
- Revisit prop forwarding when intermediate components obscure the behavior owner; keep clear data flow rather than introducing shared state merely to avoid passing props.
- Do not replace prop drilling with one large view-model hook. Move each query, derived value, and handler to the concrete owner that consumes it.
- Keep source selection, defaults, validation, dirty checks, and payload shaping beside the workflow that owns submission.
## Boundaries
- Prefer reusing or extending the component that already owns an interaction over rebuilding that behavior in a parallel component.
- State-heavy wizards, drawers, modals, and secondary workflows can form a small vertical surface with an entrypoint, optional feature-local state, and shallow owners matching real visual regions.
- The entrypoint owns route integration, provider wiring, close behavior, and mounting. Composition owners handle workflow branches; the closest visual owner handles section branches.
- The entrypoint owns route integration, provider wiring, placement, and open-state coordination. A content or session owner keeps state scoped to that mounted surface.
- Judge hook lifetime by the component that declares the hook and the primitive's mount contract, not only by where its rendered controls appear in JSX.
- Separate hidden dialogs, dropdowns, and popovers into small local owners when their content obscures the parent flow.
- Keep cohesive forms, menu bodies, and one-off helpers local unless they have their own state, reuse, or semantic boundary.
- Extract components or hooks when they clarify ownership or hide a cohesive implementation; keep logic local when extraction only shortens the file or relocates the same coordination.
- Avoid wrapper components and wrapper DOM that only rename props, pass children through, or hide the real primitive. A wrapper must own behavior, validation, state, accessibility, layout, or library integration.
- Keep feature workflows out of shared components: do not encode one feature through many boolean props or import its copy, routes, and API contracts into a generic component. Do not pass pre-rendered fragments merely to avoid assigning the behavior to its owner.
- Loading states for page sections, cards, lists, tables, forms, and drawers should use skeletons scoped to the loaded content. Reserve spinners for small inline busy indicators.
## Components And Types
- Choose component declaration and export forms from the actual component contract, framework requirements, and enforced package rules. Existing style is context, not authority; do not rewrite unaffected code solely to normalize `FC`, `function`, arrow-function, named-export, or default-export forms.
- Type simple one-off props inline. Name a `Props` type when it is reused, exported, complex, or materially clearer.
- Use API-generated or API-returned types at component boundaries. Keep one-off UI refinements and conversions beside their owner.
- Name props and converted data after their domain/API role, preserving traceability to the original contract.
- Preserve domain value types for selections. Do not widen enums, unions, booleans, numbers, objects, or nullable values to `string` before a real boundary requires it.
- Avoid generic `common.tsx` buckets and aliases that only rename another type. Name files, values, and public types after their domain role.
- Put fallback and invariant checks in the lowest component that already renders that state. Do not extract helpers whose only purpose is hiding missing display data.

View File

@@ -4,9 +4,10 @@ Read this document when a change introduces Effects, navigation side effects, me
## Effects
- Keep render pure: do not read or write `ref.current` during render except for predictable null-guarded lazy initialization. Update interaction-owned refs in event handlers, synchronize external-system refs after commit, and use state or derivation for rendered values.
- Use Effects only to synchronize with a named external system such as a browser API, subscription, timer, analytics integration, non-React widget, or imperative DOM API.
- Do not use Effects to transform render state, handle user actions, copy query data, reset state from props, or fetch data owned by framework APIs or TanStack Query.
- Initialize query-backed forms with keyed remounts or surface-entry hydration instead of copying data through Effects.
- Initialize query-backed form sessions after their defaults are available instead of copying data through Effects. Use a stable semantic identity key when the represented identity changes; use the intended surface lifecycle for per-session reset.
## Navigation

View File

@@ -9,18 +9,21 @@ Read this document when a change involves Jotai, form drafts, route identity, sh
- Keep server and cache state in TanStack Query. Use existing feature stores for complex, high-frequency interaction state such as workflow canvas drag, resize, and runtime panels.
- Use feature-owned storage only for low-frequency client preferences, dismissed notices, and UI defaults. Live application state does not belong in local storage.
## Forms
## Forms And Sessions
- Prefer uncontrolled Dify UI form and field controls when values are only read at submit time. Initialize query-backed defaults with `defaultValue` and keyed remounts.
- Promote form values to atoms only when another owner reacts to in-progress values, the draft must survive scoped unmounting, or several workflow steps edit the same draft.
- Follow the [form contract] for field controlledness and the [overlay contract] for Root/content mounting and state lifetime. These primitive contracts also apply when reviewing a consumer.
- Choose the Web draft owner from the required lifetime. Keep mounted-session drafts in the content owner; start with the lowest shared React owner when another component needs the draft or it must survive unmounting. Use feature-scoped atoms only when their coordination or persistence contract is needed.
- For query-backed defaults, establish the form session after the required defaults are available. `defaultValue` initializes the current mount; a stable semantic identity key may create a fresh snapshot when the represented identity changes. Do not use a generated key as a routine reset command.
- Keep validation, source priority, fallback behavior, dirty checks, and payload assembly in the workflow that owns submission.
- Derive booleans, disabled flags, default tabs, and loading labels from current state. Do not mirror one value into competing prop, default, and local sources; controlled state alone is not a competing-source defect.
- Do not use local state to fake server data or generated contract fields, or connect a feature mock shell to an unrelated API before its actual contract is confirmed.
## Route And URL State
- Treat `useParams`, route arguments, and `nuqs` as the owners of URL identity and updates.
- Hydrate a primitive atom at the route or surface boundary only when query atoms or shared derived atoms require route identity. Keep URL writes in route and query-state APIs.
- Within one route-owned feature, choose one route-identity source. Do not hydrate route identity into atoms while also threading the same ID through multiple component layers.
- Put shareable filters, tabs, pagination, and search state in the URL. Keep one-shot navigation signals and transient UI state out of persistent subscriptions.
- Put shareable filters, tabs, selected panels, pagination, and search state in the URL. Keep one-shot navigation signals and transient UI state out of persistent subscriptions.
## Jotai And Query
@@ -36,3 +39,6 @@ Read this document when a change involves Jotai, form drafts, route identity, sh
- Use feature-owned storage modules built on `createLocalStorageState`; callers should not scatter direct storage access or raw keys.
- Persist high-frequency interaction state only on commit or after updates settle.
- Do not add ad hoc global event listeners for shared state. Centralize subscriptions through the owning atom, store, or subscription hook.
[form contract]: ../../../../packages/dify-ui/docs/forms.md
[overlay contract]: ../../../../packages/dify-ui/docs/overlays.md

View File

@@ -12,7 +12,7 @@
"features": {
"ghcr.io/devcontainers/features/node:1": {
"nodeGypDependencies": true,
"version": "lts"
"version": "24.20.0"
},
"ghcr.io/devcontainers-extra/features/npm-package:1": {
"package": "typescript",
@@ -46,4 +46,4 @@
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
}
}

View File

@@ -37,3 +37,7 @@ indent_size = 2
[package.json]
indent_style = space
indent_size = 2
# Oxlint writes its generated suppression baseline without a final newline.
[oxlint-suppressions.json]
insert_final_newline = false

View File

@@ -1,13 +0,0 @@
have_fun: false
memory_config:
disabled: false
code_review:
disable: true
comment_severity_threshold: MEDIUM
max_review_comments: -1
pull_request_opened:
help: false
summary: false
code_review: false
include_drafts: false
ignore_patterns: []

38
.github/CODEOWNERS vendored
View File

@@ -26,14 +26,34 @@
/cli/ @GareArc
/.github/workflows/cli-tests.yml @GareArc
# Dify Agent
/dify-agent/ @BeautyyuYanli
# E2E
/e2e/ @lyzno1
/.github/workflows/web-e2e.yml @lyzno1
# Backend (default owner, more specific rules below will override)
/api/ @QuantumGhost
# Backend - Tests
# Backend - Tests (default owners, domain-specific rules below will override)
/api/tests/ @laipz8200 @QuantumGhost
/api/tests/*/core/rag/ @JohnJyong
/api/tests/*/services/rag_pipeline/ @JohnJyong
/api/tests/*/services/dataset_service/ @JohnJyong
/api/tests/*/controllers/console/datasets/ @JohnJyong
/api/tests/*/controllers/service_api/dataset/ @JohnJyong
/api/tests/*/vdb/ @JohnJyong
/api/tests/*/core/plugin/ @WH-2099
/api/tests/*/services/plugin/ @WH-2099
/api/tests/*/controllers/inner_api/plugin/ @WH-2099
/api/tests/*/plugin/ @WH-2099
/api/tests/*/controllers/trigger/ @CourTeous33
/api/tests/*/core/trigger/ @CourTeous33
/api/tests/*/trigger/ @CourTeous33
/api/tests/*/controllers/console/billing/ @zyssyz123
/api/tests/*/services/enterprise/ @GareArc
/api/tests/*/migrations/ @snakevash @laipz8200 @MRZHUH
# Backend - RAG (Retrieval Augmented Generation)
/api/core/rag/ @JohnJyong
@@ -108,8 +128,8 @@
/api/tasks/async_workflow_tasks.py @Mairuis
# Backend - Billing
/api/services/billing_service.py @hj24 @zyssyz123
/api/controllers/console/billing/ @hj24 @zyssyz123
/api/services/billing_service.py @zyssyz123
/api/controllers/console/billing/ @zyssyz123
# Backend - Enterprise
/api/configs/enterprise/ @GareArc
@@ -127,6 +147,12 @@
# Frontend
/web/ @iamjoel
# Frontend - Tooling
/lint.config.ts @hyoban @lyzno1
/eslint.config.mjs @hyoban @lyzno1
/knip.config.ts @hyoban @lyzno1
/vite.config.ts @hyoban @lyzno1
# Frontend - Platform and Features
/web/config/ @lyzno1
/web/env.ts @lyzno1
@@ -245,7 +271,7 @@
# Frontend - Billing and Education
/web/app/components/billing/ @iamjoel @zxhlyh
/web/app/education-apply/ @iamjoel @zxhlyh
/web/app/education/ @iamjoel @zxhlyh
# Frontend - Workspace
/web/app/components/header/account-dropdown/workplace-selector/ @iamjoel @zxhlyh
@@ -267,5 +293,9 @@
/web/app/auth/refresh/route.ts @iamjoel @lyzno1
/web/service/server.ts @iamjoel @lyzno1
# Frontend - Browser Mode Tests
/web/app/**/*.browser.spec.ts @lyzno1
/web/app/**/*.browser.spec.tsx @lyzno1
# Docker
/docker/* @laipz8200

View File

@@ -1,7 +1,7 @@
name: '🕷️ Bug report'
description: Report errors or unexpected behavior
labels:
- bug
- 🐞 bug
body:
- type: checkboxes
attributes:

View File

@@ -1,7 +1,7 @@
name: '⭐ Feature or enhancement request'
description: Propose something new.
labels:
- enhancement
- 💪 enhancement
body:
- type: checkboxes
attributes:

View File

@@ -0,0 +1,29 @@
name: Setup Core E2E Dependencies
description: Install API dependencies alongside web dependencies and the selected browser
inputs:
browser:
description: Browser to install (chromium or webkit)
required: true
runs:
using: composite
steps:
- name: Setup web toolchain
uses: ./.github/actions/setup-web
with:
run-install: 'false'
- name: Setup UV and Python
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: '3.12'
cache-dependency-glob: api/uv.lock
- name: Install dependencies in parallel
shell: bash
env:
E2E_INSTALL_BROWSER: ${{ inputs.browser }}
E2E_SETUP_ACTION_PATH: ${{ github.action_path }}
run: bash "$E2E_SETUP_ACTION_PATH/install.sh"

56
.github/actions/setup-core-e2e/install.sh vendored Executable file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -euo pipefail
case "${E2E_INSTALL_BROWSER:-}" in
chromium|webkit) ;;
*) echo "Expected chromium or webkit" >&2; exit 1 ;;
esac
# Keep both branches in this step so failures are collected before tests start.
started=$SECONDS
timing_dir=$(mktemp -d)
trap 'rm -rf "$timing_dir"' EXIT
(
api_started=$SECONDS
uv sync --project api --dev
echo "$((SECONDS - api_started))" > "$timing_dir/api"
) &
api_pid=$!
(
web_started=$SECONDS
vp install --frozen-lockfile
echo "$((SECONDS - web_started))" > "$timing_dir/web"
browser_started=$SECONDS
cd e2e
vp run "e2e:install:ci:$E2E_INSTALL_BROWSER"
echo "$((SECONDS - browser_started))" > "$timing_dir/browser"
) &
web_pid=$!
api_status=0
web_status=0
wait "$api_pid" || api_status=$?
wait "$web_pid" || web_status=$?
{
echo "### E2E dependency preparation ($E2E_INSTALL_BROWSER)"
echo ""
echo "Parallel install wall time: $((SECONDS - started))s"
echo ""
echo "| Phase | Seconds |"
echo "| --- | ---: |"
for phase in api web browser; do
if [[ -f "$timing_dir/$phase" ]]; then
echo "| $phase | $(cat "$timing_dir/$phase") |"
fi
done
echo ""
echo "API exit status: $api_status; Web/browser exit status: $web_status"
} >> "${GITHUB_STEP_SUMMARY:-/dev/null}"
if (( api_status != 0 || web_status != 0 )); then
echo "E2E dependency installation failed (API: $api_status, Web/browser: $web_status)" >&2
exit 1
fi

View File

@@ -1,16 +1,29 @@
name: Setup Web Environment
description: Set up Node.js, Vite+, pnpm, and web dependencies
inputs:
run-install:
description: Install web dependencies after setting up the toolchain
default: 'true'
cache-save:
description: Save the dependency cache after the job completes
default: 'true'
runs:
using: composite
steps:
- name: Setup pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- name: Setup pnpm and Node.js
uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0
with:
run_install: false
install: false
- name: Setup Vite+
uses: voidzero-dev/setup-vp@143f5f385f39b1b753ffed1a01ad443811855c8b # v1.16.1
uses: voidzero-dev/setup-vp@9fd26ff49f9b5e6276c2b493da4454ad4527f7f4 # v1.20.0
with:
node-version-file: .nvmrc
node-manager: false
package-manager: |
pnpm: false
npm: false
cache: true
run-install: true
# Nested composite post actions can evaluate inputs in the outer scope.
cache-save: ${{ inputs.cache-save || 'true' }}
run-install: ${{ inputs.run-install }}

View File

@@ -0,0 +1,21 @@
name: Wait for Core E2E Web Build
description: Wait for the current workflow run's Web build artifact while the producer finishes independently
outputs:
artifact-id:
description: ID of the ready Web build artifact
value: ${{ steps.wait.outputs.result }}
runs:
using: composite
steps:
- name: Wait for build artifact
id: wait
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
E2E_WAIT_ACTION_PATH: ${{ github.action_path }}
with:
result-encoding: string
script: |
const waitForBuild = require(`${process.env.E2E_WAIT_ACTION_PATH}/wait.cjs`)
return await waitForBuild({ github, context, core, runAttempt: Number(process.env.GITHUB_RUN_ATTEMPT) })

View File

@@ -0,0 +1,65 @@
const { setTimeout: delay } = require('node:timers/promises')
module.exports = async function waitForBuild({
github,
context,
core,
runAttempt,
now = Date.now,
sleep = delay,
timeoutMs = 30 * 60 * 1000,
}) {
const started = now()
const run = { ...context.repo, run_id: context.runId, per_page: 100 }
core.info('Waiting for core-e2e-web-build in this workflow run')
while (now() - started < timeoutMs) {
const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRunAttempt, {
...run,
attempt_number: runAttempt,
})
const producer = jobs.find(
(job) =>
job.name === 'Prepare Core E2E Web Build' ||
job.name.endsWith(' / Prepare Core E2E Web Build'),
)
if (producer?.status === 'completed' && producer.conclusion !== 'success') {
throw new Error(
`Web build finished with ${producer.conclusion} before publishing an artifact`,
)
}
if (producer && ['in_progress', 'completed'].includes(producer.status)) {
const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, run)
const upload = producer.steps?.find((step) => step.name === 'Upload Web build')
const artifact = artifacts.find((item) => {
if (item.expired || !/^core-e2e-web-build-[1-9]\d*$/.test(item.name)) return false
if (producer.status === 'in_progress') {
// A rebuilding producer must publish this attempt's immutable artifact.
return item.name === `core-e2e-web-build-${runAttempt}`
}
// GitHub relabels reused successful jobs with the current run_attempt.
// Their upload step keeps its original timestamps, identifying the artifact
// to reuse when only consumers are rerun (including across several attempts).
return (
upload?.conclusion === 'success' &&
item.created_at >= upload.started_at &&
item.created_at <= upload.completed_at
)
})
if (artifact) {
const seconds = Math.round((now() - started) / 1000)
core.info(`Web build artifact ${artifact.name} ready after ${seconds}s`)
await core.summary
.addHeading('E2E Web build wait')
.addRaw(`Artifact wait: ${seconds}s`)
.write()
return String(artifact.id)
}
}
await sleep(Math.min(10000, timeoutMs - (now() - started)))
}
throw new Error('Timed out waiting for core-e2e-web-build in this workflow run')
}

View File

@@ -0,0 +1,164 @@
const assert = require('node:assert/strict')
const { test } = require('node:test')
const waitForBuild = require('./wait.cjs')
function fixture({ artifacts = [], jobs = [producer], error, runAttempt = 2 } = {}) {
let elapsed = 0
const calls = []
const summary = {
addHeading() {
return this
},
addRaw() {
return this
},
async write() {},
}
return {
calls,
options: {
context: { repo: { owner: 'owner', repo: 'repo' }, runId: 123 },
core: { info() {}, summary },
runAttempt,
github: {
rest: {
actions: { listWorkflowRunArtifacts: 'artifacts', listJobsForWorkflowRunAttempt: 'jobs' },
},
async paginate(endpoint, params) {
calls.push({ endpoint, params })
if (error) throw error
const values = endpoint === 'artifacts' ? artifacts : jobs
return typeof values === 'function' ? values(elapsed) : values
},
},
now: () => elapsed,
sleep: async (ms) => {
elapsed += ms
},
timeoutMs: 20000,
},
}
}
const producer = {
name: 'Run Web Full-Stack E2E / Prepare Core E2E Web Build',
status: 'in_progress',
run_attempt: 2,
}
const artifact = {
id: 42,
name: 'core-e2e-web-build-2',
expired: false,
created_at: '2026-09-22T04:08:11Z',
}
const oldArtifact = {
...artifact,
id: 41,
name: 'core-e2e-web-build-1',
created_at: '2026-09-22T03:08:11Z',
}
const successfulProducer = {
...producer,
status: 'completed',
conclusion: 'success',
steps: [
{
name: 'Upload Web build',
conclusion: 'success',
started_at: '2026-09-22T04:08:08Z',
completed_at: '2026-09-22T04:08:12Z',
},
],
}
test('starts consuming the current artifact without waiting for producer cache saves', async () => {
const { options, calls } = fixture({ artifacts: (elapsed) => (elapsed ? [artifact] : []) })
assert.equal(await waitForBuild(options), '42')
assert.equal(options.now(), 10000)
assert.deepEqual(
calls.map((call) => call.endpoint),
['jobs', 'artifacts', 'jobs', 'artifacts'],
)
for (const { endpoint, params } of calls) {
assert.equal(params.run_id, 123)
assert.equal(params.owner, 'owner')
assert.equal(params.repo, 'repo')
if (endpoint === 'jobs') assert.equal(params.attempt_number, 2)
}
})
test('full reruns ignore the old artifact until the rebuilding producer publishes this attempt', async () => {
const { options } = fixture({
artifacts: (elapsed) => (elapsed ? [oldArtifact, artifact] : [oldArtifact]),
})
assert.equal(await waitForBuild(options), '42')
assert.equal(options.now(), 10000)
})
test('does not use an old artifact before the current producer appears or while it is queued', async () => {
const { options } = fixture({
artifacts: [oldArtifact],
jobs: (elapsed) => (elapsed ? [{ ...producer, status: 'queued' }] : []),
})
await assert.rejects(waitForBuild(options), /Timed out/)
})
test('consumer-only reruns reuse the actual successful upload despite a relabeled run_attempt', async () => {
const { options } = fixture({
runAttempt: 4,
artifacts: [oldArtifact, artifact],
jobs: [{ ...successfulProducer, run_attempt: 4 }],
})
assert.equal(await waitForBuild(options), '42')
assert.equal(options.now(), 0)
})
for (const conclusion of ['failure', 'cancelled', 'skipped']) {
test(`does not fall back to an old artifact when the producer finishes with ${conclusion}`, async () => {
const { options } = fixture({
artifacts: [oldArtifact, artifact],
jobs: [{ ...producer, status: 'completed', conclusion }],
})
await assert.rejects(waitForBuild(options), new RegExp(`Web build finished with ${conclusion}`))
assert.equal(options.now(), 0)
})
}
test('ignores unrelated and expired artifacts and stops at the deadline', async () => {
const { options } = fixture({
artifacts: [
{ ...artifact, expired: true },
{ ...artifact, name: 'other' },
],
})
await assert.rejects(waitForBuild(options), /Timed out/)
assert.equal(options.now(), 20000)
})
test('completed rebuilt producers select their upload instead of the old artifact', async () => {
const { options } = fixture({ artifacts: [oldArtifact, artifact], jobs: [successfulProducer] })
assert.equal(await waitForBuild(options), '42')
})
test('allows artifact visibility to catch up after a successful producer finishes', async () => {
const { options } = fixture({
artifacts: (elapsed) => (elapsed ? [oldArtifact, artifact] : [oldArtifact]),
jobs: [successfulProducer],
})
assert.equal(await waitForBuild(options), '42')
assert.equal(options.now(), 10000)
})
test('does not reuse artifacts without a successful matching upload step', async () => {
const { options } = fixture({
artifacts: [artifact],
jobs: [{ ...successfulProducer, steps: [] }],
})
await assert.rejects(waitForBuild(options), /Timed out/)
})
test('reports API errors rather than silently waiting until timeout', async () => {
const { options } = fixture({ error: new Error('Resource not accessible by integration') })
await assert.rejects(waitForBuild(options), /Resource not accessible/)
assert.equal(options.now(), 0)
})

1
.github/labeler.yml vendored
View File

@@ -6,7 +6,6 @@ web:
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- '.nvmrc'
e2e:
- changed-files:

View File

@@ -4,6 +4,8 @@ extends: default
rules:
brackets:
max-spaces-inside: 1
comments:
min-spaces-from-content: 1
comments-indentation: disable
document-start: disable
line-length: disable

View File

@@ -1,7 +1,7 @@
> [!IMPORTANT]
>
> 1. Make sure you have read our [contribution guidelines](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md)
> 1. Ensure there is an associated issue and you have been assigned to it
> 1. Ensure there is an associated issue
> 1. Use the correct syntax to link this PR: `Fixes #<issue number>`.
## Summary
@@ -19,6 +19,6 @@
- [ ] This change requires a documentation update, included: [Dify Document](https://github.com/langgenius/dify-docs)
- [ ] I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!)
- [ ] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
- [ ] I've verified the change and added or updated tests where meaningful regression risk justifies coverage.
- [ ] I've updated the documentation accordingly.
- [ ] I ran `make lint && make type-check` (backend) and `cd web && pnpm exec vp staged` (frontend) to appease the lint gods
- [ ] I ran `make lint && make type-check` (backend) and `vp staged` (frontend) to appease the lint gods

View File

@@ -8,7 +8,8 @@ const headSha = process.env.HEAD_SHA || ''
const files = (process.env.CHANGED_FILES || '').split(/\s+/).filter(Boolean)
const outputPath = process.env.I18N_CHANGES_OUTPUT_PATH || '/tmp/i18n-changes.json'
const englishPath = (fileStem) => path.join(repoRoot, 'web', 'i18n', 'en-US', `${fileStem}.json`)
const englishPath = (fileStem) =>
path.join(repoRoot, 'web', 'i18n', 'locales', 'en-US', `${fileStem}.json`)
const readCurrentJson = (fileStem) => {
const filePath = englishPath(fileStem)
@@ -21,7 +22,7 @@ const readBaseJson = (fileStem) => {
if (!baseSha) return null
try {
const relativePath = `web/i18n/en-US/${fileStem}.json`
const relativePath = `web/i18n/locales/en-US/${fileStem}.json`
const content = execFileSync('git', ['show', `${baseSha}:${relativePath}`], {
encoding: 'utf8',
})

94
.github/scripts/size-labeler.test.cjs vendored Normal file
View File

@@ -0,0 +1,94 @@
const fs = require('node:fs')
const assert = require('node:assert/strict')
const test = require('node:test')
const source = fs.readFileSync(
require('node:path').join(__dirname, '../workflows/size-labeler.yml'),
'utf8',
)
const script = source
.replace(/\r\n/g, '\n')
.split(' script: |\n')[1]
.split('\n')
.map((l) => l.replace(/^ /, ''))
.join('\n')
const run = new (Object.getPrototypeOf(async function () {}).constructor)(
'github',
'context',
'core',
script,
)
async function scenario(files, existing = [], changed = files.length) {
const calls = []
const warnings = []
const api = {
pulls: {
get: async () => ({ data: { changed_files: changed } }),
listFiles: 'files',
},
issues: {
listLabelsOnIssue: 'labels',
addLabels: async (p) => calls.push(['add', p.labels[0]]),
removeLabel: async (p) => calls.push(['remove', p.name]),
},
}
await run(
{
rest: api,
paginate: async (endpoint) =>
endpoint === 'files' ? files : existing.map((name) => ({ name })),
},
{
repo: { owner: 'test', repo: 'repo' },
payload: { pull_request: { number: 1 } },
},
{ warning: (s) => warnings.push(s) },
)
return { calls, warnings }
}
for (const [n, label] of [
[0, 'XS'],
[9, 'XS'],
[10, 'S'],
[29, 'S'],
[30, 'M'],
[99, 'M'],
[100, 'L'],
[499, 'L'],
[500, 'XL'],
[999, 'XL'],
[1000, 'XXL'],
]) {
test(`threshold ${n}`, async () =>
assert.deepEqual((await scenario([{ filename: 'a.py', additions: n, deletions: 0 }])).calls, [
['add', `size:${label}`],
]))
}
test('count deletions', async () =>
assert.deepEqual((await scenario([{ filename: 'a.py', additions: 0, deletions: 30 }])).calls, [
['add', 'size:M'],
]))
test('ignore nested lockfiles', async () =>
assert.deepEqual(
(
await scenario([
{ filename: 'api/uv.lock', additions: 9000, deletions: 5 },
{ filename: 'web/pnpm-lock.yaml', additions: 5000, deletions: 0 },
])
).calls,
[['add', 'size:XS']],
))
test('replace stale size only', async () =>
assert.deepEqual(
(await scenario([{ filename: 'a.py', additions: 10, deletions: 0 }], ['size:L', 'bug', 'web']))
.calls,
[
['add', 'size:S'],
['remove', 'size:L'],
],
))
test('idempotent', async () => assert.deepEqual((await scenario([], ['size:XS', 'bug'])).calls, []))
test('incomplete file list', async () => {
const r = await scenario([], ['size:L'], 3001)
assert.deepEqual(r.calls, [])
assert.equal(r.warnings.length, 1)
})

View File

@@ -36,7 +36,7 @@ concurrency:
jobs:
accessibility:
name: WCAG Level ${{ inputs.level == 'a' && 'A' || 'AA' }} · ${{ inputs.page }}
runs-on: depot-ubuntu-24.04-4
runs-on: ubuntu-24.04
timeout-minutes: 120
defaults:
run:
@@ -52,7 +52,7 @@ jobs:
uses: ./.github/actions/setup-web
- name: Setup UV and Python
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: '3.12'
@@ -80,7 +80,7 @@ jobs:
if [[ "$WCAG_PAGE" == "all" ]]; then
vp run e2e:accessibility:${{ inputs.level }}
else
pnpm exec tsx ./scripts/run-cucumber.ts --full -- --tags "@axe and @wcag-${{ inputs.level }} and @wcag-page-$WCAG_PAGE"
pnpm exec node ./scripts/run-cucumber.ts --full -- --tags "@axe and @wcag-${{ inputs.level }} and @wcag-page-$WCAG_PAGE"
fi
- name: Upload accessibility Cucumber report

View File

@@ -14,28 +14,79 @@ concurrency:
cancel-in-progress: true
jobs:
api-unit-plan:
name: Plan API unit shards
runs-on: depot-ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup UV and Python
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
python-version: '3.12'
enable-cache: false
- name: Download shared timing baseline
# Public statistics are independent of PR refs and require no credentials.
run: |
if ! curl --fail --silent --show-error --location \
--connect-timeout 5 --max-time 15 --retry 2 --retry-max-time 30 \
https://raw.githubusercontent.com/langgenius/dify-test-infra/generated-stats/stats/test-times.json \
--output unit-test-durations.json; then
rm -f unit-test-durations.json
echo '::warning::Timing baseline unavailable; using round-robin file allocation.'
fi
- name: Freeze file assignments for every shard
run: |
uv run --project api --no-sync python api/dev/pytest_sharding.py \
--shard-index 1 --shard-total 2 \
--durations unit-test-durations.json \
--write-plan unit-test-plan.json \
--split-threshold 60 \
--ignore api/tests/unit_tests/controllers \
api/tests/unit_tests \
api/providers/vdb/*/tests/unit_tests \
api/providers/trace/*/tests/unit_tests
- name: Upload shared plan
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: api-unit-plan
path: |
unit-test-plan.json
unit-test-durations.json
retention-days: 7
if-no-files-found: error
api-unit:
name: API Unit Tests
needs: api-unit-plan
name: API Unit Tests (${{ matrix.python-version }}, shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
runs-on: depot-ubuntu-24.04
env:
COVERAGE_FILE: coverage-unit
PYTHONUNBUFFERED: '1'
COVERAGE_FILE: coverage-unit-${{ matrix.python-version }}-${{ matrix.shardIndex }}
defaults:
run:
shell: bash
strategy:
fail-fast: false
matrix:
python-version:
- '3.12'
shardIndex:
- 1
- 2
shardTotal:
- 2
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Setup UV and Python
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: ${{ matrix.python-version }}
@@ -47,34 +98,116 @@ jobs:
- name: Install dependencies
run: uv sync --project api --dev
- name: Download shared plan
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: api-unit-plan
- name: Run Unit Tests
run: |
uv run --project api python api/dev/pytest_sharding.py \
--plan unit-test-plan.json \
--shard-index "${{ matrix.shardIndex }}" \
--shard-total "${{ matrix.shardTotal }}" \
--ignore api/tests/unit_tests/controllers \
api/tests/unit_tests \
api/providers/vdb/*/tests/unit_tests \
api/providers/trace/*/tests/unit_tests > "$RUNNER_TEMP/unit-test-files.txt"
uv run --project api pytest \
-p no:benchmark \
--timeout "${PYTEST_TIMEOUT:-20}" \
-n auto \
api/tests/unit_tests \
api/providers/vdb/*/tests/unit_tests \
api/providers/trace/*/tests/unit_tests \
--ignore=api/tests/unit_tests/controllers
# Controller tests register Flask routes at import time, so keep them out of xdist.
uv run --project api pytest \
--timeout "${PYTEST_TIMEOUT:-20}" \
--cov-append \
api/tests/unit_tests/controllers
-o 'addopts=--cov=./api --cov-branch --import-mode=importlib' \
--cov-report= \
--file-shard-plan unit-test-plan.json \
--shard-index "${{ matrix.shardIndex }}" \
--shard-total "${{ matrix.shardTotal }}" \
--write-test-durations "test-results/durations-${{ matrix.shardIndex }}.json" \
--junitxml=test-results/unit.xml \
"@$RUNNER_TEMP/unit-test-files.txt"
- name: Upload unit test reports
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: api-test-results-unit-${{ matrix.python-version }}-${{ matrix.shardIndex }}
path: test-results/*.xml
if-no-files-found: warn
retention-days: 7
- name: Upload successful timing observations
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: api-unit-durations-${{ matrix.shardIndex }}
path: test-results/durations-*.json
retention-days: 7
if-no-files-found: error
- name: Upload unit coverage data
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: api-coverage-unit
path: coverage-unit
name: api-coverage-unit-${{ matrix.python-version }}-${{ matrix.shardIndex }}
path: ${{ env.COVERAGE_FILE }}
if-no-files-found: error
retention-days: 1
api-controllers:
name: API Controller Tests (3.12)
runs-on: depot-ubuntu-24.04
env:
PYTHONUNBUFFERED: '1'
COVERAGE_FILE: coverage-controllers-3.12
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup UV and Python
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: '3.12'
cache-dependency-glob: api/uv.lock
- name: Check UV lockfile
run: uv lock --project api --check
- name: Install dependencies
run: uv sync --project api --dev
- name: Run Controller Tests
# Controller tests register Flask routes at import time, so keep them out of xdist.
run: |
uv run --project api pytest \
--timeout "${PYTEST_TIMEOUT:-20}" \
-o 'addopts=--cov=./api --cov-branch --import-mode=importlib' \
--cov-report= \
--junitxml=test-results/controllers.xml \
api/tests/unit_tests/controllers
- name: Upload controller test reports
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: api-test-results-controllers-3.12
path: test-results/*.xml
if-no-files-found: warn
retention-days: 7
- name: Upload controller coverage data
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: api-coverage-controllers-3.12
path: ${{ env.COVERAGE_FILE }}
if-no-files-found: error
retention-days: 1
api-integration:
name: API Integration Tests
runs-on: depot-ubuntu-24.04
name: API Integration Tests (${{ matrix.python-version }})
runs-on: depot-ubuntu-24.04-4
env:
COVERAGE_FILE: coverage-integration
COVERAGE_FILE: coverage-integration-${{ matrix.python-version }}
STORAGE_TYPE: opendal
OPENDAL_SCHEME: fs
OPENDAL_FS_ROOT: /tmp/dify-storage
@@ -82,6 +215,7 @@ jobs:
run:
shell: bash
strategy:
fail-fast: false
matrix:
python-version:
- '3.12'
@@ -90,11 +224,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Setup UV and Python
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: ${{ matrix.python-version }}
@@ -111,7 +244,10 @@ jobs:
uv run --project api pytest \
-p no:benchmark \
--start-middleware \
--middleware-stop-timeout 2 \
-n auto \
-o 'addopts=--cov=./api --cov-branch --import-mode=importlib' \
--cov-report= \
--timeout "${PYTEST_TIMEOUT:-180}" \
api/tests/integration_tests/workflow \
api/tests/integration_tests/tools \
@@ -120,8 +256,9 @@ jobs:
- name: Upload integration coverage data
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: api-coverage-integration
path: coverage-integration
name: api-coverage-integration-${{ matrix.python-version }}
path: ${{ env.COVERAGE_FILE }}
if-no-files-found: error
retention-days: 1
api-coverage:
@@ -129,6 +266,7 @@ jobs:
runs-on: depot-ubuntu-24.04
needs:
- api-unit
- api-controllers
- api-integration
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
@@ -141,18 +279,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Setup UV and Python
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: '3.12'
cache-dependency-glob: api/uv.lock
- name: Install dependencies
run: uv sync --project api --dev
- name: Download coverage data
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -164,35 +296,48 @@ jobs:
- name: Combine coverage
run: |
set -euo pipefail
coverage_version=$(python3 -c 'import tomllib; print(next(p["version"] for p in tomllib.load(open("api/uv.lock", "rb"))["package"] if p["name"] == "coverage"))')
echo "### API Coverage" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Merged backend coverage report generated for Codecov project status." >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
mapfile -t unit_coverage < <(find coverage-data -type f -name 'coverage-unit-*' -print | sort)
mapfile -t controller_coverage < <(find coverage-data -type f -name 'coverage-controllers-*' -print | sort)
mapfile -t integration_coverage < <(find coverage-data -type f -name 'coverage-integration-*' -print | sort)
if [[ "${#unit_coverage[@]}" -ne 2 ]]; then
echo "expected 2 unit coverage artifacts, found ${#unit_coverage[@]}" >&2
exit 1
fi
if [[ "${#controller_coverage[@]}" -ne 1 ]]; then
echo "expected 1 controller coverage artifact, found ${#controller_coverage[@]}" >&2
exit 1
fi
if [[ "${#integration_coverage[@]}" -ne 1 ]]; then
echo "expected 1 integration coverage artifact, found ${#integration_coverage[@]}" >&2
exit 1
fi
unit_coverage="$(find coverage-data -type f -name coverage-unit -print -quit)"
integration_coverage="$(find coverage-data -type f -name coverage-integration -print -quit)"
: "${unit_coverage:?coverage-unit artifact not found}"
: "${integration_coverage:?coverage-integration artifact not found}"
uvx --from "coverage==$coverage_version" coverage combine "${unit_coverage[@]}" "${controller_coverage[@]}" "${integration_coverage[@]}"
uvx --from "coverage==$coverage_version" coverage xml -o coverage.xml
report_file="$(mktemp)"
uv run --project api coverage combine "$unit_coverage" "$integration_coverage"
uv run --project api coverage report --show-missing | tee "$report_file"
echo "Summary: \`$(tail -n 1 "$report_file")\`" >> "$GITHUB_STEP_SUMMARY"
{
echo ""
echo "<details><summary>Coverage report</summary>"
echo ""
echo '```'
cat "$report_file"
echo '```'
echo "</details>"
} >> "$GITHUB_STEP_SUMMARY"
uv run --project api coverage xml -o coverage.xml
# Reuse the XML totals instead of analyzing every source file again for a text report.
python3 - <<'PY'
import os
import xml.etree.ElementTree as ET
coverage = ET.parse("coverage.xml").getroot()
with open(os.environ["GITHUB_STEP_SUMMARY"], "a") as summary:
summary.write("### API Coverage\n\n")
summary.write("Merged backend coverage report generated for Codecov project status.\n\n")
summary.write("| Metric | Covered | Total | Coverage |\n")
summary.write("| --- | ---: | ---: | ---: |\n")
for label, metric in (("Lines", "lines"), ("Branches", "branches")):
covered = int(coverage.attrib[f"{metric}-covered"])
total = int(coverage.attrib[f"{metric}-valid"])
percentage = f"{covered / total:.2%}" if total else "N/A"
summary.write(f"| {label} | {covered} | {total} | {percentage} |\n")
summary.write("\nSee Codecov for per-file coverage details.\n")
PY
- name: Report coverage
if: ${{ env.CODECOV_TOKEN != '' }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
files: ./coverage.xml
disable_search: true

View File

@@ -5,8 +5,6 @@ on:
merge_group:
branches: ['main']
types: [checks_requested]
push:
branches: ['main']
permissions:
contents: read
@@ -32,28 +30,6 @@ jobs:
docker/.env.example
docker/docker-compose-template.yaml
docker/docker-compose.yaml
- name: Check web inputs
if: github.event_name != 'merge_group'
id: web-changes
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
with:
files: |
web/**
cli/**
e2e/**
packages/**
sdks/nodejs-client/**
package.json
pnpm-lock.yaml
pnpm-workspace.yaml
.nvmrc
vite.config.ts
lint.config.ts
eslint.config.mjs
oxlint-suppressions.json
eslint-suppressions.json
.vscode/**
.github/**
- name: Check api inputs
if: github.event_name != 'merge_group'
id: api-changes
@@ -82,13 +58,13 @@ jobs:
dify-agent/**/*.py
dify-agent/pyproject.toml
dify-agent/uv.lock
- if: github.event_name != 'merge_group'
- if: github.event_name != 'merge_group' && (steps.docker-compose-changes.outputs.any_changed == 'true' || steps.api-changes.outputs.any_changed == 'true' || steps.frontend-contract-changes.outputs.any_changed == 'true' || steps.dify-agent-changes.outputs.any_changed == 'true')
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.11'
- if: github.event_name != 'merge_group'
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
- if: github.event_name != 'merge_group' && (steps.api-changes.outputs.any_changed == 'true' || steps.frontend-contract-changes.outputs.any_changed == 'true' || steps.dify-agent-changes.outputs.any_changed == 'true')
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
- name: Generate Docker Compose
if: github.event_name != 'merge_group' && steps.docker-compose-changes.outputs.any_changed == 'true'
@@ -118,12 +94,6 @@ jobs:
# Format code
uv run ruff format .
- name: count migration progress
if: github.event_name != 'merge_group' && steps.api-changes.outputs.any_changed == 'true'
run: |
cd api
./cnt_base.sh
- name: ast-grep
if: github.event_name != 'merge_group' && steps.api-changes.outputs.any_changed == 'true'
run: |
@@ -155,7 +125,7 @@ jobs:
find . -name "*.py.bak" -type f -delete
- name: Setup web environment
if: github.event_name != 'merge_group'
if: github.event_name != 'merge_group' && steps.frontend-contract-changes.outputs.any_changed == 'true'
uses: ./.github/actions/setup-web
- name: Generate API docs
@@ -168,13 +138,5 @@ jobs:
if: github.event_name != 'merge_group' && steps.frontend-contract-changes.outputs.any_changed == 'true'
run: pnpm --dir packages/contracts gen-api-contract
- name: ESLint fallback autofix
if: github.event_name != 'merge_group' && steps.web-changes.outputs.any_changed == 'true'
run: pnpm lint:eslint:fix --quiet || true
- name: Vite+ static autofix
if: github.event_name != 'merge_group' && (steps.web-changes.outputs.any_changed == 'true' || steps.frontend-contract-changes.outputs.any_changed == 'true')
run: vp check --fix || true
- if: github.event_name != 'merge_group'
uses: autofix-ci/action@c5b2d67aa2274e7b5a18224e8171550871fc7e4a # v1.3.4

View File

@@ -103,7 +103,7 @@ jobs:
password: ${{ env.DOCKERHUB_TOKEN }}
- name: Set up Depot CLI
uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1
uses: depot/setup-action@91bc8495a33ebfc504ffc89e5674379ccf23c29c # v1.7.2
- name: Extract metadata for Docker
id: meta
@@ -160,10 +160,10 @@ jobs:
file: 'docker/Dockerfile'
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Validate Docker image
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
push: false
context: ${{ matrix.build_context }}
@@ -229,3 +229,38 @@ jobs:
IMAGE_VERSION: ${{ steps.meta.outputs.version }}
run: |
docker buildx imagetools inspect "$IMAGE_NAME:$IMAGE_VERSION"
sync-e2b-template:
needs: create-manifest
runs-on: ubuntu-24.04
if: github.repository == 'langgenius/dify' && startsWith(github.ref, 'refs/tags/')
strategy:
matrix:
include:
- project: dev
api_key_secret: E2B_API_KEY_DEV
- project: prod
api_key_secret: E2B_API_KEY_PROD
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Use Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: package.json
cache: ''
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Login to Docker Hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
username: ${{ env.DOCKERHUB_USER }}
password: ${{ env.DOCKERHUB_TOKEN }}
- name: Sync E2B Template (${{ matrix.project }})
env:
E2B_API_KEY: ${{ secrets[matrix.api_key_secret] }}
run: ./dify-agent-runtime/docker/sync-e2b-template.sh

View File

@@ -87,10 +87,9 @@ jobs:
with:
bun-version: latest
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0
with:
package_json_field: packageManager
run_install: false
install: false
- name: Install CLI dependencies
working-directory: cli
@@ -112,7 +111,7 @@ jobs:
# ════════════════════════════════════════════════════════════════════════════
suite-framework-output-error:
name: 'Suite: framework + output + error-handling'
if: ${{ inputs.suite_framework_output_error != 'false' }}
if: ${{ inputs.suite_framework_output_error }}
needs: provision
runs-on: ubuntu-latest
timeout-minutes: 20
@@ -129,10 +128,8 @@ jobs:
- uses: ./.github/actions/setup-web
- uses: oven-sh/setup-bun@v2
with: { bun-version: latest }
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
with: { package_json_field: packageManager, run_install: false }
- run: pnpm install --frozen-lockfile
with:
bun-version: latest
- run: pnpm tree:gen
- name: Run framework + output + error-handling
@@ -159,7 +156,7 @@ jobs:
# ════════════════════════════════════════════════════════════════════════════
suite-discovery:
name: 'Suite: discovery'
if: ${{ inputs.suite_discovery != 'false' }}
if: ${{ inputs.suite_discovery }}
needs: provision
runs-on: ubuntu-latest
timeout-minutes: 20
@@ -176,10 +173,8 @@ jobs:
- uses: ./.github/actions/setup-web
- uses: oven-sh/setup-bun@v2
with: { bun-version: latest }
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
with: { package_json_field: packageManager, run_install: false }
- run: pnpm install --frozen-lockfile
with:
bun-version: latest
- run: pnpm tree:gen
- name: Run discovery suite
@@ -207,7 +202,7 @@ jobs:
# ════════════════════════════════════════════════════════════════════════════
suite-run:
name: 'Suite: run / ${{ matrix.name }}'
if: ${{ inputs.suite_run != 'false' }}
if: ${{ inputs.suite_run }}
needs: provision
runs-on: ubuntu-latest
timeout-minutes: 20
@@ -239,10 +234,8 @@ jobs:
- uses: ./.github/actions/setup-web
- uses: oven-sh/setup-bun@v2
with: { bun-version: latest }
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
with: { package_json_field: packageManager, run_install: false }
- run: pnpm install --frozen-lockfile
with:
bun-version: latest
- run: pnpm tree:gen
- name: 'Run run/${{ matrix.name }}'
@@ -284,7 +277,7 @@ jobs:
# ════════════════════════════════════════════════════════════════════════════
suite-auth-safe:
name: 'Suite: auth (login / status / whoami)'
if: ${{ inputs.suite_auth != 'false' }}
if: ${{ inputs.suite_auth }}
needs: provision
runs-on: ubuntu-latest
timeout-minutes: 15
@@ -301,10 +294,8 @@ jobs:
- uses: ./.github/actions/setup-web
- uses: oven-sh/setup-bun@v2
with: { bun-version: latest }
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
with: { package_json_field: packageManager, run_install: false }
- run: pnpm install --frozen-lockfile
with:
bun-version: latest
- run: pnpm tree:gen
- name: Run auth/login + status + whoami
@@ -333,7 +324,7 @@ jobs:
suite-last:
name: 'Suite: auth-use + devices + logout + agent (last, serial)'
# Runs when auth is selected; also runs after all parallel jobs finish
if: ${{ inputs.suite_auth != 'false' || inputs.suite_agent != 'false' }}
if: ${{ inputs.suite_auth || inputs.suite_agent }}
needs:
- provision
- suite-framework-output-error
@@ -357,10 +348,8 @@ jobs:
- uses: ./.github/actions/setup-web
- uses: oven-sh/setup-bun@v2
with: { bun-version: latest }
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
with: { package_json_field: packageManager, run_install: false }
- run: pnpm install --frozen-lockfile
with:
bun-version: latest
- run: pnpm tree:gen
- name: Run use / devices / logout / agent (serial)

View File

@@ -7,12 +7,22 @@ on:
description: Dify release tag to attach difyctl assets to (blank = latest stable)
required: false
type: string
dry_run:
description: Build and checksum only — skip asset upload and stale-asset prune
required: false
type: boolean
default: false
workflow_call:
inputs:
release_tag:
description: Dify release tag to attach difyctl assets to (blank = latest stable)
required: false
type: string
dry_run:
description: Build and checksum only — skip asset upload and stale-asset prune
required: false
type: boolean
default: false
release:
types: [released]
@@ -39,11 +49,8 @@ jobs:
with:
persist-credentials: false
- name: Export manifest to env
run: node scripts/release-naming.mjs github-env >> "$GITHUB_ENV"
- name: Validate manifest
run: scripts/release-validate-manifest.sh
run: node scripts/release-naming.mjs validate
- name: Resolve target Dify release
id: resolve
@@ -75,15 +82,6 @@ jobs:
DIFY_TAG: ${{ steps.resolve.outputs.dify_tag }}
run: node scripts/release-naming.mjs compat-check "$DIFY_TAG"
- name: Reject duplicate difyctl version
env:
GH_TOKEN: ${{ github.token }}
run: |
if gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${difyctlTag}" >/dev/null 2>&1; then
echo "::error::difyctl ${version} already released (tag ${difyctlTag} exists); bump cli/package.json version"
exit 1
fi
release:
name: build + attach standalone binaries (all targets)
needs: validate
@@ -120,6 +118,7 @@ jobs:
- name: Compile standalone binaries (all targets)
run: |
CLI_VERSION="$version" \
DIFYCTL_COMMIT="$(git rev-parse HEAD)" \
DIFYCTL_BUILD_DATE="$(git log -1 --format=%cI HEAD)" \
pnpm build:bin
@@ -128,6 +127,7 @@ jobs:
run: scripts/release-write-checksums.sh
- name: Attach difyctl assets to Dify release
if: ${{ !inputs.dry_run }}
env:
GH_TOKEN: ${{ github.token }}
run: |
@@ -135,6 +135,7 @@ jobs:
--repo "$GITHUB_REPOSITORY" --clobber
- name: Prune stale difyctl assets
if: ${{ !inputs.dry_run }}
env:
GH_TOKEN: ${{ github.token }}
run: |
@@ -149,18 +150,3 @@ jobs:
--repo "$GITHUB_REPOSITORY" --yes
fi
done
- name: Create provenance tag
env:
GH_TOKEN: ${{ github.token }}
run: |
ref="refs/tags/${difyctlTag}"
sha="$(git rev-parse HEAD)"
status="$(gh api -X POST "repos/${GITHUB_REPOSITORY}/git/refs" \
-f ref="$ref" -f sha="$sha" --silent --include 2>/dev/null \
| awk 'NR==1 {print $2; exit}' || true)"
case "$status" in
201) echo "::notice::created ${ref}" ;;
422) echo "::notice::tag ${ref} already exists; skipping (immutable)" ;;
*) echo "::error::provenance tag ${ref} not created (HTTP ${status:-unknown})"; exit 1 ;;
esac

View File

@@ -51,7 +51,7 @@ jobs:
- name: Run smoke against live dify
working-directory: ./cli
run: pnpm exec tsx scripts/run-smoke.ts --base-url http://localhost:5001
run: pnpm exec node scripts/run-smoke.ts --base-url http://localhost:5001
- name: Dump dify logs on failure
if: failure()

View File

@@ -44,14 +44,14 @@ jobs:
- name: Validate release manifest
if: matrix.os == 'depot-ubuntu-24.04'
run: scripts/release-validate-manifest.sh
run: node scripts/release-naming.mjs validate
- name: CI pipeline (tree, coverage, build)
run: pnpm run ci
- name: Report coverage
if: ${{ env.CODECOV_TOKEN != '' && matrix.os == 'depot-ubuntu-24.04' }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
directory: cli/coverage
flags: cli

View File

@@ -19,7 +19,7 @@ jobs:
persist-credentials: false
- name: Setup UV and Python
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: '3.12'
@@ -40,7 +40,7 @@ jobs:
cp envs/middleware.env.example middleware.env
- name: Set up Middlewares
uses: hoverkraft-tech/compose-action@11beaa1c2dae4e8ed7b1665aa074723b6cecb0e4 # v3.0.0
uses: hoverkraft-tech/compose-action@b659c744ee9bed25aea2307c017ad849bd104433 # v3.2.0
with:
compose-file: |
docker/docker-compose.middleware.yaml
@@ -69,7 +69,7 @@ jobs:
persist-credentials: false
- name: Setup UV and Python
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: '3.12'
@@ -94,7 +94,7 @@ jobs:
sed -i 's/DB_USERNAME=postgres/DB_USERNAME=mysql/' middleware.env
- name: Set up Middlewares
uses: hoverkraft-tech/compose-action@11beaa1c2dae4e8ed7b1665aa074723b6cecb0e4 # v3.0.0
uses: hoverkraft-tech/compose-action@b659c744ee9bed25aea2307c017ad849bd104433 # v3.2.0
with:
compose-file: |
docker/docker-compose.middleware.yaml

View File

@@ -0,0 +1,25 @@
name: Deploy HITL IM Dev
on:
workflow_run:
workflows: ['Build and Push API & Web']
branches:
- 'build/feat/hitl-im-dev'
types:
- completed
jobs:
deploy:
runs-on: depot-ubuntu-24.04
if: |
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'build/feat/hitl-im-dev'
steps:
- name: Deploy to server
uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1.2.5
with:
host: ${{ secrets.HITL_SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
${{ vars.SSH_SCRIPT || secrets.SSH_SCRIPT }}

View File

@@ -67,5 +67,7 @@ jobs:
host: ${{ secrets.SSH_NEW_RAG_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
# Allow the shared deployment lock and Celery warm shutdown to finish.
command_timeout: 300m
script: |
${{ vars.SSH_SCRIPT || secrets.SSH_SCRIPT }}

47
.github/workflows/dify-agent-tests.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: Dify Agent Tests
on:
workflow_call:
permissions:
contents: read
concurrency:
group: dify-agent-tests-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
dify-agent-unit:
name: Dify Agent Unit Tests
runs-on: depot-ubuntu-24.04
defaults:
run:
shell: bash
working-directory: dify-agent
strategy:
matrix:
python-version:
- '3.12'
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Setup UV and Python
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: ${{ matrix.python-version }}
cache-dependency-glob: dify-agent/uv.lock
- name: Check UV lockfile
run: uv lock --check
- name: Install dependencies
run: uv sync --extra server --dev
- name: Run unit tests
run: uv run pytest

View File

@@ -65,7 +65,7 @@ jobs:
file: 'docker/Dockerfile'
steps:
- name: Set up Depot CLI
uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1
uses: depot/setup-action@91bc8495a33ebfc504ffc89e5674379ccf23c29c # v1.7.2
- name: Build Docker Image
uses: depot/build-push-action@98e78adca7817480b8185f474a400b451d74e287 # v1.18.0
@@ -96,10 +96,10 @@ jobs:
file: 'docker/Dockerfile'
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Build Docker Image
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
push: false
context: ${{ matrix.context }}

39
.github/workflows/image-resources.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Image optimization
on:
pull_request:
permissions:
contents: read
jobs:
image-resources:
name: Image optimization
runs-on: depot-ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha }}
persist-credentials: false
fetch-depth: 0
- name: Set up workspace toolchain
uses: ./.github/actions/setup-web
with:
run-install: 'false'
- name: Install image checker dependencies
run: pnpm --filter @dify/image-resources... install --frozen-lockfile --ignore-scripts
- name: Type-check image checker
run: pnpm --filter @dify/image-resources type-check
- name: Test image checker
run: pnpm --filter @dify/image-resources test
- name: Check image optimization
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
pnpm --filter @dify/image-resources check:images --base "$BASE_SHA"

View File

@@ -15,8 +15,10 @@ permissions:
statuses: write
concurrency:
group: main-ci-${{ github.event.pull_request.number || github.event.merge_group.head_sha || github.run_id }}
cancel-in-progress: true
# Pull request deduplication is owned by skip-duplicate-actions so pre_job can
# publish should_skip before required status finalizers run.
group: main-ci-${{ github.event.merge_group.head_sha || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'merge_group' }}
jobs:
pre_job:
@@ -35,8 +37,6 @@ jobs:
# Check which paths were changed to determine which tests to run
check-changes:
name: Check Changed Files
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
runs-on: depot-ubuntu-24.04
outputs:
api-changed: ${{ steps.changes.outputs.api }}
@@ -46,6 +46,12 @@ jobs:
vdb-changed: ${{ steps.changes.outputs.vdb }}
migration-changed: ${{ steps.changes.outputs.migration }}
sandbox-runtime-changed: ${{ steps.changes.outputs.sandbox-runtime }}
dify-agent-changed: ${{ steps.changes.outputs.dify-agent }}
python-style-changed: ${{ steps.changes.outputs.python-style }}
dify-agent-style-changed: ${{ steps.changes.outputs.dify-agent-style }}
web-style-changed: ${{ steps.changes.outputs.web-style }}
ts-common-style-changed: ${{ steps.changes.outputs.ts-common-style }}
superlinter-changed: ${{ steps.changes.outputs.superlinter }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
@@ -79,7 +85,6 @@ jobs:
- 'pnpm-workspace.yaml'
- 'lint.config.ts'
- '.npmrc'
- '.nvmrc'
- '.github/workflows/cli-tests.yml'
- '.github/actions/setup-web/**'
web:
@@ -88,26 +93,26 @@ jobs:
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- '.nvmrc'
- '.github/workflows/main-ci.yml'
- '.github/workflows/web-tests.yml'
- '.github/actions/setup-web/**'
e2e:
- 'api/**'
- 'api/pyproject.toml'
- 'api/uv.lock'
- 'e2e/**'
- 'web/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- '.nvmrc'
- 'docker/docker-compose.middleware.yaml'
- 'docker/envs/middleware.env.example'
- 'docker/ssrf_proxy/**'
- 'docker/volumes/sandbox/conf/**'
- '.github/workflows/web-e2e.yml'
- '.github/workflows/main-ci.yml'
- '.github/actions/setup-web/**'
- '.github/actions/setup-core-e2e/**'
- '.github/actions/wait-e2e-web-build/**'
vdb:
- 'api/core/rag/datasource/**'
- 'api/tests/integration_tests/vdb/**'
@@ -136,6 +141,9 @@ jobs:
sandbox-runtime:
- 'dify-agent-runtime/**'
- '.github/workflows/sandbox-runtime-tests.yml'
dify-agent:
- 'dify-agent/**'
- '.github/workflows/dify-agent-tests.yml'
migration:
- 'api/migrations/**'
- 'api/.env.example'
@@ -147,6 +155,62 @@ jobs:
- 'docker/generate_docker_compose'
- 'docker/ssrf_proxy/**'
- 'docker/volumes/sandbox/conf/**'
python-style:
- 'api/**'
- 'scripts/ast_grep_guard.py'
- 'scripts/check_no_new_getattr.py'
- 'scripts/check_no_new_controller_sqlalchemy.py'
- 'scripts/lint_controller_sqlalchemy.py'
- 'scripts/ast_grep_rules/no_new_getattr.yml'
- 'scripts/ast_grep_rules/no_new_controller_sqlalchemy.yml'
- '.github/workflows/style.yml'
- '.github/workflows/main-ci.yml'
dify-agent-style:
- 'dify-agent/**'
- '.github/workflows/style.yml'
- '.github/workflows/main-ci.yml'
web-style:
- 'web/**'
- 'e2e/**'
- 'sdks/nodejs-client/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'knip.config.ts'
- 'scripts/check-web-production-unused-after-knip-fix.mjs'
- '.npmrc'
- '.github/workflows/main-ci.yml'
- '.github/workflows/web-bundle-size.yml'
- '.github/workflows/style.yml'
- '.github/actions/setup-web/**'
ts-common-style:
- 'web/**'
- 'cli/**'
- 'e2e/**'
- 'sdks/nodejs-client/**'
- 'packages/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig.json'
- 'vite.config.ts'
- 'lint.config.ts'
- 'eslint.config.mjs'
- 'knip.config.ts'
- 'scripts/check-web-production-unused-after-knip-fix.mjs'
- 'oxlint-suppressions.json'
- 'eslint-suppressions.json'
- '.vscode/**'
- '.github/**'
superlinter:
- '**.sh'
- '**.yaml'
- '**.yml'
- '**Dockerfile'
- 'dev/**'
- '.editorconfig'
- '.vite-hooks/**'
# Run tests in parallel while always emitting stable required checks.
api-tests-run:
@@ -393,11 +457,20 @@ jobs:
style-check:
name: Style Check
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
if: ${{ always() }}
needs:
- pre_job
- check-changes
uses: ./.github/workflows/style.yml
with:
base-rev: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
classification-succeeded: ${{ needs.check-changes.result == 'success' }}
run-python-style: ${{ needs.check-changes.outputs.python-style-changed == 'true' }}
run-dify-agent-style: ${{ needs.check-changes.outputs.dify-agent-style-changed == 'true' }}
run-web-style: ${{ needs.check-changes.outputs.web-style-changed == 'true' }}
run-ts-common-style: ${{ needs.check-changes.outputs.ts-common-style-changed == 'true' }}
run-superlinter: ${{ needs.check-changes.outputs.superlinter-changed == 'true' }}
skip-checks: ${{ needs.pre_job.outputs.should_skip == 'true' }}
vdb-tests-run:
name: Run VDB Tests
@@ -576,3 +649,63 @@ jobs:
echo "Sandbox runtime tests were not required, but the skip job finished with result: $SKIP_RESULT" >&2
exit 1
dify-agent-tests-run:
name: Run Dify Agent Tests
needs:
- pre_job
- check-changes
if: needs.pre_job.outputs.should_skip != 'true' && needs.check-changes.outputs.dify-agent-changed == 'true'
uses: ./.github/workflows/dify-agent-tests.yml
secrets: inherit
dify-agent-tests-skip:
name: Skip Dify Agent Tests
needs:
- pre_job
- check-changes
if: needs.pre_job.outputs.should_skip != 'true' && needs.check-changes.outputs.dify-agent-changed != 'true'
runs-on: depot-ubuntu-24.04
steps:
- name: Report skipped dify agent tests
run: echo "No dify-agent-related changes detected; skipping dify agent tests."
dify-agent-tests:
name: Dify Agent Tests
if: ${{ always() }}
needs:
- pre_job
- check-changes
- dify-agent-tests-run
- dify-agent-tests-skip
runs-on: depot-ubuntu-24.04
steps:
- name: Finalize Dify Agent Tests status
env:
SHOULD_SKIP_WORKFLOW: ${{ needs.pre_job.outputs.should_skip }}
TESTS_CHANGED: ${{ needs.check-changes.outputs.dify-agent-changed }}
RUN_RESULT: ${{ needs.dify-agent-tests-run.result }}
SKIP_RESULT: ${{ needs.dify-agent-tests-skip.result }}
run: |
if [[ "$SHOULD_SKIP_WORKFLOW" == 'true' ]]; then
echo "Dify agent tests were skipped because this workflow run duplicated a successful or newer run."
exit 0
fi
if [[ "$TESTS_CHANGED" == 'true' ]]; then
if [[ "$RUN_RESULT" == 'success' ]]; then
echo "Dify agent tests ran successfully."
exit 0
fi
echo "Dify agent tests were required but finished with result: $RUN_RESULT" >&2
exit 1
fi
if [[ "$SKIP_RESULT" == 'success' ]]; then
echo "Dify agent tests were skipped because no dify-agent-related files changed."
exit 0
fi
echo "Dify agent tests were not required, but the skip job finished with result: $SKIP_RESULT" >&2
exit 1

View File

@@ -36,15 +36,15 @@ jobs:
- 'e2e/package.json'
- 'e2e/test-env.ts'
- 'e2e/tsconfig.json'
- 'e2e/tsx-register.js'
- 'package.json'
- 'pnpm-lock.yaml'
- '.nvmrc'
- '.github/workflows/post-merge.yml'
- '.github/workflows/web-e2e.yml'
- '.github/actions/setup-web/**'
- 'docker/docker-compose.middleware.yaml'
- 'docker/envs/middleware.env.example'
- 'docker/ssrf_proxy/**'
- 'docker/volumes/sandbox/conf/**'
- 'dify-agent/**'
- 'dify-agent-runtime/**'
- 'api/pyproject.toml'
@@ -54,12 +54,30 @@ jobs:
- 'api/core/app/apps/agent_app/**'
- 'api/core/workflow/nodes/agent_v2/**'
- 'api/controllers/console/agent/**'
- 'api/controllers/console/app/agent*.py'
- 'api/controllers/console/app/audio.py'
- 'api/controllers/common/agent_access.py'
- 'api/controllers/inner_api/agent/**'
- 'api/controllers/inner_api/wraps.py'
- 'api/controllers/service_api/app/completion.py'
- 'api/controllers/service_api/wraps.py'
- 'api/controllers/web/completion.py'
- 'api/controllers/web/wraps.py'
- 'api/services/agent_llm_inner_service.py'
- 'api/services/agent_tool_inner_service.py'
- 'api/services/audio_service.py'
- 'api/services/entities/agent_llm_inner.py'
- 'api/services/entities/agent_tool_inner.py'
- 'api/services/errors/agent_tool_inner.py'
- 'api/services/agent/**'
- 'api/core/plugin/**'
- 'api/services/plugin/**'
- 'api/core/tools/**'
- 'api/services/tools/**'
- 'packages/contracts/package.json'
- 'packages/contracts/console.ts'
- 'packages/contracts/generated/api/console/router.gen.ts'
- 'packages/contracts/generated/api/web/**'
- 'packages/contracts/generated/api/console/agent/**'
- 'packages/contracts/generated/api/console/apps/**'
- 'packages/contracts/generated/api/console/datasets/**'
@@ -67,20 +85,27 @@ jobs:
- 'packages/contracts/generated/api/console/workspaces/**'
- 'packages/contracts/generated/api/service/**'
- 'web/features/agent-v2/**'
- 'web/app/(commonLayout)/agents/**'
- 'web/app/(commonLayout)/@detailSidebar/agents/**'
- 'web/app/(commonLayout)/roster/**'
- 'web/app/\(commonLayout\)/agents/**'
- 'web/app/\(commonLayout\)/@detailSidebar/agents/**'
- 'web/app/\(shareLayout\)/chat/**'
- 'web/app/\(shareLayout\)/components/**'
- 'web/app/components/base/chat/chat-with-history/**'
- 'web/app/components/base/chat/chat/**'
- 'web/app/components/base/voice-input/**'
- 'web/app/components/workflow/index.tsx'
- 'web/app/components/workflow/block-selector/**'
- 'web/app/components/workflow/hooks/use-node-keyboard-interactions.ts'
- 'web/app/components/workflow/hooks/use-nodes-interactions.ts'
- 'web/app/components/workflow/hooks/use-nodes-sync-draft.ts'
- 'web/app/components/workflow/nodes/_base/**'
- 'web/app/components/workflow/nodes/agent-v2/**'
- 'web/i18n/en-US/agent-v-2.json'
- 'web/i18n/en-US/common.json'
- 'web/i18n/locales/en-US/agent-v-2.json'
- 'web/i18n/locales/en-US/common.json'
- 'web/service/base.ts'
- 'web/service/client.ts'
- 'web/service/console-link.ts'
- 'web/service/console-openapi-url.ts'
- 'web/service/console-router-loader.ts'
- 'web/service/console/**'
- 'web/service/share.ts'
- 'web/service/use-share.ts'
- 'web/service/webapp-address.ts'
- 'web/package.json'
- 'pnpm-workspace.yaml'
@@ -88,6 +113,9 @@ jobs:
name: External Runtime E2E
needs: check-changes
if: needs.check-changes.outputs.external-e2e-changed == 'true'
permissions:
actions: read
contents: read
uses: ./.github/workflows/web-e2e.yml
with:
run-external-runtime: true

View File

@@ -22,7 +22,7 @@ jobs:
fetch-depth: 0
- name: Setup Python & UV
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true

View File

@@ -24,7 +24,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python & UV
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true

View File

@@ -22,7 +22,7 @@ jobs:
fetch-depth: 0
- name: Setup Python & UV
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true

55
.github/workflows/size-labeler.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: Pull Request Size Labeler
on:
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review, edited]
permissions:
pull-requests: write
concurrency:
group: size-labeler-${{ github.event.pull_request.number }}
cancel-in-progress: false
jobs:
label:
runs-on: depot-ubuntu-24.04
steps:
# Only read PR metadata. Never check out or execute the PR's code here.
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const { owner, repo } = context.repo
const issue_number = context.payload.pull_request.number
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: issue_number })
const files = await github.paginate(github.rest.pulls.listFiles, {
owner, repo, pull_number: issue_number, per_page: 100,
})
// GitHub caps this endpoint at 3,000 files; avoid labeling a partial count.
if (files.length !== pr.changed_files) {
core.warning('Skipping size label because the changed-file list is incomplete')
return
}
const ignoredNames = new Set(['uv.lock', 'pnpm-lock.yaml', 'package-lock.json', 'yarn.lock'])
const changes = files
.filter(file => !ignoredNames.has(file.filename.split('/').pop()))
.reduce((total, file) => total + file.additions + file.deletions, 0)
// Match the descriptions of the repository's existing size labels.
const buckets = [
[10, 'size:XS'], [30, 'size:S'], [100, 'size:M'],
[500, 'size:L'], [1000, 'size:XL'], [Infinity, 'size:XXL'],
]
const label = buckets.find(([limit]) => changes < limit)[1]
const sizeLabels = new Set(buckets.map(([, name]) => name))
const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
owner, repo, issue_number, per_page: 100,
})
// Add first so an API error cannot leave the PR without a size label.
if (!labels.some(item => item.name === label)) {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [label] })
}
for (const item of labels) {
if (sizeLabels.has(item.name) && item.name !== label) {
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: item.name })
}
}

View File

@@ -6,6 +6,35 @@ on:
base-rev:
required: true
type: string
classification-succeeded:
description: Whether the caller successfully classified the changed files.
required: true
type: boolean
run-python-style:
description: Run Python style checks for the changed files.
required: true
type: boolean
run-dify-agent-style:
description: Run Dify Agent style checks for the changed files.
required: true
type: boolean
run-web-style:
description: Run Web style checks for the changed files.
required: true
type: boolean
run-ts-common-style:
description: Run shared TypeScript style checks for the changed files.
required: true
type: boolean
run-superlinter:
description: Run SuperLinter for the changed files.
required: true
type: boolean
skip-checks:
description: Create the required check runs without repeating previously successful work.
required: false
type: boolean
default: false
permissions:
checks: write
@@ -15,70 +44,82 @@ permissions:
jobs:
python-style:
name: Python Style
if: ${{ !inputs.skip-checks && (!inputs.classification-succeeded || inputs.run-python-style || inputs.run-dify-agent-style) }}
runs-on: depot-ubuntu-24.04
steps:
- name: Verify changed-file classification
if: ${{ !inputs.classification-succeeded }}
run: |
echo "Changed-file classification failed; refusing to skip Python style checks." >&2
exit 1
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
- name: Check changed files
id: changed-files
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
with:
files: |
api/**
scripts/ast_grep_guard.py
scripts/check_no_new_getattr.py
scripts/check_no_new_controller_sqlalchemy.py
scripts/lint_controller_sqlalchemy.py
scripts/ast_grep_rules/no_new_getattr.yml
scripts/ast_grep_rules/no_new_controller_sqlalchemy.yml
.github/workflows/style.yml
.github/workflows/main-ci.yml
- name: Setup UV and Python
if: steps.changed-files.outputs.any_changed == 'true'
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: false
python-version: '3.12'
cache-dependency-glob: api/uv.lock
cache-dependency-glob: |
api/uv.lock
dify-agent/uv.lock
- name: Install dependencies
if: steps.changed-files.outputs.any_changed == 'true'
if: inputs.run-python-style
run: uv sync --project api --dev
- name: Run Import Linter
if: steps.changed-files.outputs.any_changed == 'true'
if: inputs.run-python-style
run: uv run --directory api --dev lint-imports
- name: Run Response Contract Linter
if: steps.changed-files.outputs.any_changed == 'true'
if: inputs.run-python-style
run: uv run --project api --dev python api/dev/lint_response_contracts.py --fail-on-mismatch
- name: Run No New Getattr Guard
if: steps.changed-files.outputs.any_changed == 'true'
if: inputs.run-python-style
run: uv run --project api python scripts/check_no_new_getattr.py --base-rev "${{ inputs.base-rev }}"
- name: Run No New Controller SQLAlchemy Guard
if: steps.changed-files.outputs.any_changed == 'true'
if: inputs.run-python-style
run: uv run --project api python scripts/check_no_new_controller_sqlalchemy.py --base-rev "${{ inputs.base-rev }}"
- name: Run Type Checks
if: steps.changed-files.outputs.any_changed == 'true'
if: inputs.run-python-style
env:
PYREFLY_OUTPUT_FORMAT: github
run: make type-check-core
- name: Run Dify Agent Ruff
if: inputs.run-dify-agent-style
run: make -C dify-agent check
- name: Run Dify Agent Type Checks
if: inputs.run-dify-agent-style
run: make -C dify-agent typecheck
- name: Dotenv check
if: steps.changed-files.outputs.any_changed == 'true'
if: inputs.run-python-style
run: uv run --project api dotenv-linter ./api/.env.example ./web/.env.example
web-style:
name: Web Style
web-build:
name: Web Build and Bundle Analysis
if: ${{ !inputs.skip-checks && inputs.classification-succeeded && inputs.run-web-style }}
permissions:
contents: read
pull-requests: write
uses: ./.github/workflows/web-bundle-size.yml
with:
base-rev: ${{ inputs.base-rev }}
web-style-checks:
name: Web Static Checks
if: ${{ !inputs.skip-checks && (!inputs.classification-succeeded || inputs.run-web-style) }}
runs-on: depot-ubuntu-24.04
defaults:
run:
@@ -88,131 +129,114 @@ jobs:
pull-requests: read
steps:
- name: Verify changed-file classification
if: ${{ !inputs.classification-succeeded }}
working-directory: .
run: |
echo "Changed-file classification failed; refusing to skip Web style checks." >&2
exit 1
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check changed files
id: changed-files
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
with:
files: |
web/**
e2e/**
sdks/nodejs-client/**
packages/**
package.json
pnpm-lock.yaml
pnpm-workspace.yaml
knip.config.ts
scripts/check-web-production-unused-after-knip-fix.mjs
.nvmrc
.github/workflows/style.yml
.github/actions/setup-web/**
- name: Setup web environment
if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/actions/setup-web
- name: Check generated i18n types
run: vp run i18n:check-types
- name: Web tsslint
if: steps.changed-files.outputs.any_changed == 'true'
env:
NODE_OPTIONS: --max-old-space-size=4096
run: vp run lint:tss
- name: Web dead code check
if: steps.changed-files.outputs.any_changed == 'true'
working-directory: .
run: vp run knip
- name: Web dead code check production
if: steps.changed-files.outputs.any_changed == 'true'
working-directory: .
run: vp run knip:production
- name: Web production unused declarations check
if: steps.changed-files.outputs.any_changed == 'true'
working-directory: .
run: vp run knip:production-unused-check
# Preserve the existing required check while build and static checks run in parallel.
web-style:
name: Web Style
if: ${{ always() && !inputs.skip-checks && (!inputs.classification-succeeded || inputs.run-web-style) }}
needs: [web-build, web-style-checks]
runs-on: ubuntu-24.04
permissions: {}
steps:
- name: Verify web checks
env:
BUILD_RESULT: ${{ needs.web-build.result }}
STYLE_RESULT: ${{ needs.web-style-checks.result }}
run: |
if [[ "$BUILD_RESULT" != 'success' || "$STYLE_RESULT" != 'success' ]]; then
echo "Web checks failed: build=$BUILD_RESULT, static checks=$STYLE_RESULT" >&2
exit 1
fi
ts-common-style:
name: TS Common
if: ${{ !inputs.skip-checks && (!inputs.classification-succeeded || inputs.run-ts-common-style) }}
runs-on: depot-ubuntu-24.04-4
permissions:
checks: write
pull-requests: read
steps:
- name: Verify changed-file classification
if: ${{ !inputs.classification-succeeded }}
run: |
echo "Changed-file classification failed; refusing to skip TypeScript style checks." >&2
exit 1
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check changed files
id: changed-files
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
with:
files: |
web/**
cli/**
e2e/**
sdks/nodejs-client/**
packages/**
package.json
pnpm-lock.yaml
pnpm-workspace.yaml
.nvmrc
vite.config.ts
lint.config.ts
eslint.config.mjs
oxlint-suppressions.json
eslint-suppressions.json
.vscode/**
.github/**
- name: Setup web environment
if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/actions/setup-web
- name: Static check
if: steps.changed-files.outputs.any_changed == 'true'
run: pnpm -w check
run: vp run -w check
superlinter:
name: SuperLinter
if: ${{ !inputs.skip-checks && (!inputs.classification-succeeded || inputs.run-superlinter) }}
runs-on: depot-ubuntu-24.04
steps:
- name: Verify changed-file classification
if: ${{ !inputs.classification-succeeded }}
run: |
echo "Changed-file classification failed; refusing to skip SuperLinter." >&2
exit 1
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Check changed files
id: changed-files
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
with:
files: |
**.sh
**.yaml
**.yml
**Dockerfile
dev/**
.editorconfig
- name: Super-linter
uses: super-linter/super-linter/slim@4ce20838b8ab83717e78138c5b3a1407148e0918 # v8.7.0
if: steps.changed-files.outputs.any_changed == 'true'
env:
BASH_SEVERITY: warning
DEFAULT_BRANCH: origin/main
EDITORCONFIG_FILE_NAME: editorconfig-checker.json
FILTER_REGEX_INCLUDE: pnpm-lock.yaml
ENABLE_GITHUB_PULL_REQUEST_SUMMARY_COMMENT: false
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
IGNORE_GENERATED_FILES: true
IGNORE_GITIGNORED_FILES: true
VALIDATE_ALL_CODEBASE: false
VALIDATE_BASH: true
VALIDATE_BASH_EXEC: true
# FIXME: temporarily disabled until api-docker.yaml's run script is fixed for shellcheck

View File

@@ -31,7 +31,7 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
node-version-file: package.json
cache: ''
cache-dependency-path: 'pnpm-lock.yaml'

View File

@@ -8,15 +8,19 @@ on:
workflow_dispatch:
inputs:
files:
description: 'Specific files to translate (space-separated, e.g., "app common"). Required for full mode; leave empty in incremental mode to use en-US files changed since HEAD~1.'
description: 'Specific files to translate (space-separated, e.g., "app common").
Required for full mode; leave empty in incremental mode to use en-US
files changed since HEAD~1.'
required: false
type: string
languages:
description: 'Specific languages to translate (space-separated, e.g., "zh-Hans ja-JP"). Leave empty for all supported target languages except en-US.'
description: 'Specific languages to translate (space-separated, e.g., "zh-Hans
ja-JP"). Leave empty for all supported target languages except en-US.'
required: false
type: string
mode:
description: 'Sync mode: incremental (compare with previous en-US revision) or full (sync all keys in scope)'
description: 'Sync mode: incremental (compare with previous en-US revision) or
full (sync all keys in scope)'
required: false
default: incremental
type: choice
@@ -65,7 +69,7 @@ jobs:
/supported: true/ && value != \"en-US\" {
printf \"%s \", value
}
" web/i18n-config/languages.ts | sed 's/[[:space:]]*$//')
" web/i18n/languages.ts | sed 's/[[:space:]]*$//')
generate_changes_json() {
node .github/scripts/generate-i18n-changes.mjs
@@ -106,9 +110,9 @@ jobs:
elif [ "$SYNC_MODE" = "incremental" ]; then
BASE_SHA=$(git rev-parse HEAD~1 2>/dev/null || true)
if [ -n "$BASE_SHA" ]; then
CHANGED_FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- 'web/i18n/en-US/*.json' 2>/dev/null | sed -n 's@^.*/@@p' | sed 's/\.json$//' | tr '\n' ' ' | sed 's/[[:space:]]*$//')
CHANGED_FILES=$(git diff --name-only --find-renames=100% --diff-filter=AMDT "$BASE_SHA" "$HEAD_SHA" -- 'web/i18n/' 2>/dev/null | sed -n '\@^web/i18n/locales/en-US/[^/]*\.json$@p' | sed -n 's@^.*/@@p' | sed 's/\.json$//' | tr '\n' ' ' | sed 's/[[:space:]]*$//')
else
CHANGED_FILES=$(find web/i18n/en-US -maxdepth 1 -type f -name '*.json' -print | sed -n 's@^.*/@@p' | sed 's/\.json$//' | sort | tr '\n' ' ' | sed 's/[[:space:]]*$//')
CHANGED_FILES=$(find web/i18n/locales/en-US -maxdepth 1 -type f -name '*.json' -print | sed -n 's@^.*/@@p' | sed 's/\.json$//' | sort | tr '\n' ' ' | sed 's/[[:space:]]*$//')
fi
elif [ "$SYNC_MODE" = "full" ]; then
echo "workflow_dispatch full mode requires the files input to stay within CI limits." >&2
@@ -158,7 +162,7 @@ jobs:
- name: Run Claude Code for Translation Sync
if: steps.context.outputs.CHANGED_FILES != ''
uses: anthropics/claude-code-action@c038e4dcdedfbbca18dfb17df35a17e40ded4ddc # v1.0.186
uses: anthropics/claude-code-action@4036a180cf690f49529f5d8c79c998855287f590 # v1.0.230
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
@@ -166,12 +170,12 @@ jobs:
show_full_output: ${{ github.event_name == 'workflow_dispatch' }}
prompt: |
You are the i18n sync agent for the Dify repository.
Your job is to keep translations synchronized with the English source files under `${{ github.workspace }}/web/i18n/en-US/`.
Your job is to keep translations synchronized with the English source files under `${{ github.workspace }}/web/i18n/locales/en-US/`.
Use absolute paths at all times:
- Repo root: `${{ github.workspace }}`
- Web directory: `${{ github.workspace }}/web`
- Language config: `${{ github.workspace }}/web/i18n-config/languages.ts`
- Language config: `${{ github.workspace }}/web/i18n/languages.ts`
Inputs:
- Files in scope: `${{ steps.context.outputs.CHANGED_FILES }}`
@@ -194,12 +198,12 @@ jobs:
Required execution plan:
1. Resolve target languages.
- Use the provided `Target languages` value as the source of truth.
- If it is unexpectedly empty, read `${{ github.workspace }}/web/i18n-config/languages.ts` and use every language with `supported: true` except `en-US`.
- If it is unexpectedly empty, read `${{ github.workspace }}/web/i18n/languages.ts` and use every language with `supported: true` except `en-US`.
2. Stay strictly in scope.
- Only process the files listed in `Files in scope`.
- Only process the resolved target languages, never `en-US`.
- Do not touch unrelated i18n files.
- Do not modify `${{ github.workspace }}/web/i18n/en-US/`.
- Do not modify `${{ github.workspace }}/web/i18n/locales/en-US/`.
3. Resolve source changes.
- If `Structured change set available` is `true`, read `/tmp/i18n-changes.json` and use it as the source of truth for file-level and key-level changes.
- For each file entry:
@@ -223,7 +227,7 @@ jobs:
- Match the existing terminology and register used by each locale.
- Prefer one Edit per file when stable, but prioritize correctness over batching.
6. Verify only the edited files.
- Run `vp fmt <repo-relative edited paths such as web/i18n/...>`
- Run `vp fmt <repo-relative edited paths such as web/i18n/locales/...>`
- Run `vp run dify-web#i18n:check ${{ steps.context.outputs.FILE_ARGS }} ${{ steps.context.outputs.LANG_ARGS }}`
- If verification fails, fix the remaining problems before continuing.
7. Stop after the scoped locale files are updated and verification passes.
@@ -237,7 +241,7 @@ jobs:
if: steps.context.outputs.CHANGED_FILES != ''
shell: bash
run: |
if [ -z "$(git -C "${{ github.workspace }}" status --porcelain -- web/i18n/)" ]; then
if [ -z "$(git -C "${{ github.workspace }}" status --porcelain -- web/i18n/locales/)" ]; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
exit 0
fi
@@ -256,7 +260,7 @@ jobs:
shell: bash
run: |
git -C "${{ github.workspace }}" checkout -B "${{ steps.pr_meta.outputs.branch_name }}"
git -C "${{ github.workspace }}" add web/i18n/
git -C "${{ github.workspace }}" add web/i18n/locales/
git -C "${{ github.workspace }}" commit -m "chore(i18n): sync translations with en-US"
- name: Push translation branch
@@ -322,7 +326,7 @@ jobs:
'## Verification',
'',
`- \`vp run dify-web#i18n:check --file ${process.env.FILES_IN_SCOPE} --lang ${process.env.TARGET_LANGS}\``,
`- \`vp fmt <repo-relative edited paths such as web/i18n/...>\``,
`- \`vp fmt <repo-relative edited paths such as web/i18n/locales/...>\``,
'',
'## Notes',
'',

View File

@@ -4,7 +4,7 @@ on:
push:
branches: [main]
paths:
- 'web/i18n/en-US/*.json'
- 'web/i18n/locales/en-US/*.json'
permissions:
contents: write
@@ -36,9 +36,9 @@ jobs:
HEAD_SHA="${{ github.sha }}"
if [ -n "$BASE_SHA" ]; then
CHANGED_FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- 'web/i18n/en-US/*.json' 2>/dev/null | sed -n 's@^.*/@@p' | sed 's/\.json$//' | tr '\n' ' ' | sed 's/[[:space:]]*$//')
CHANGED_FILES=$(git diff --name-only --find-renames=100% --diff-filter=AMDT "$BASE_SHA" "$HEAD_SHA" -- 'web/i18n/' 2>/dev/null | sed -n '\@^web/i18n/locales/en-US/[^/]*\.json$@p' | sed -n 's@^.*/@@p' | sed 's/\.json$//' | tr '\n' ' ' | sed 's/[[:space:]]*$//')
else
CHANGED_FILES=$(find web/i18n/en-US -maxdepth 1 -type f -name '*.json' -print | sed -n 's@^.*/@@p' | sed 's/\.json$//' | sort | tr '\n' ' ' | sed 's/[[:space:]]*$//')
CHANGED_FILES=$(find web/i18n/locales/en-US -maxdepth 1 -type f -name '*.json' -print | sed -n 's@^.*/@@p' | sed 's/\.json$//' | sort | tr '\n' ' ' | sed 's/[[:space:]]*$//')
fi
export BASE_SHA HEAD_SHA CHANGED_FILES

View File

@@ -29,14 +29,14 @@ jobs:
persist-credentials: false
- name: Free Disk Space
uses: endersonmenezes/free-disk-space@7901478139cff6e9d44df5972fd8ab8fcade4db1 # v3.2.2
uses: endersonmenezes/free-disk-space@2a22f8c59cae9cddae7194b33e6b92d023b2b4b8 # v4.0.0
with:
remove_dotnet: true
remove_haskell: true
remove_tool_cache: true
- name: Setup UV and Python
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: ${{ matrix.python-version }}

View File

@@ -26,14 +26,14 @@ jobs:
persist-credentials: false
- name: Free Disk Space
uses: endersonmenezes/free-disk-space@7901478139cff6e9d44df5972fd8ab8fcade4db1 # v3.2.2
uses: endersonmenezes/free-disk-space@2a22f8c59cae9cddae7194b33e6b92d023b2b4b8 # v4.0.0
with:
remove_dotnet: true
remove_haskell: true
remove_tool_cache: true
- name: Setup UV and Python
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: ${{ matrix.python-version }}

162
.github/workflows/web-bundle-size.yml vendored Normal file
View File

@@ -0,0 +1,162 @@
name: Web Bundle Analysis
on:
workflow_call:
inputs:
base-rev:
description: Exact base revision selected by the main CI event.
required: true
type: string
permissions:
contents: read
pull-requests: write
jobs:
measure:
name: Measure ${{ matrix.revision }}
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- revision: base
sha: ${{ inputs.base-rev }}
- revision: merge
sha: ${{ github.sha }}
env:
NEXT_TELEMETRY_DISABLED: '1'
NODE_OPTIONS: --max-old-space-size=6144
steps:
# Keep one collector for both revisions, including when it does not exist on base yet.
- name: Checkout reporting tools
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Preserve collector
run: cp web/scripts/bundle-analysis.ts "$RUNNER_TEMP/bundle-analysis.ts"
- name: Checkout measured revision
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ matrix.sha }}
persist-credentials: false
- name: Setup web environment
uses: ./.github/actions/setup-web
- name: Instrument selected revision
run: node "$RUNNER_TEMP/bundle-analysis.ts" instrument web
- name: Full Vinext build with official Rolldown analysis
working-directory: web
shell: bash
run: |
set -o pipefail
started=$SECONDS
pnpm exec vinext build 2>&1 | tee "$RUNNER_TEMP/vinext-build.log"
printf 'Full Vinext build: **%s seconds**.\n' "$((SECONDS - started))" >> "$GITHUB_STEP_SUMMARY"
- name: Collect sizes
env:
COMMIT_SHA: ${{ matrix.sha }}
run: node "$RUNNER_TEMP/bundle-analysis.ts" collect web "$COMMIT_SHA" "$RUNNER_TEMP/bundle-size/summary.json"
- name: Upload size snapshot
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: web-bundle-summary-${{ matrix.revision }}
path: ${{ runner.temp }}/bundle-size/summary.json
if-no-files-found: error
retention-days: 14
- name: Upload official Rolldown analysis
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: web-bundle-analyzer-${{ matrix.revision }}
path: |
web/dist/client/bundle-analysis.json
web/dist/client/bundle-analysis.md
${{ runner.temp }}/vinext-build.log
include-hidden-files: true
if-no-files-found: error
retention-days: 14
report:
name: Compare bundle sizes
needs: measure
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout reporting tools
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup Node.js
uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0
with:
install: false
- name: Download size snapshots
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: web-bundle-summary-*
path: bundle-results
- name: Compare and summarize
id: compare
env:
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
node web/scripts/bundle-analysis.ts compare \
bundle-results/web-bundle-summary-base/summary.json \
bundle-results/web-bundle-summary-merge/summary.json \
bundle-report.md
printf '\n[Download base/merge snapshots and official Rolldown analysis reports](%s#artifacts).\n' "$RUN_URL" >> bundle-report.md
printf '\nComparison: event base SHA vs the tested merge commit at GITHUB_SHA (PR or merge queue).\n' >> bundle-report.md
cat bundle-report.md >> "$GITHUB_STEP_SUMMARY"
# Forks still receive the job summary and artifacts, without a privileged PR build.
- name: Update PR report
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
SHOULD_COMMENT: ${{ steps.compare.outputs.should-comment }}
with:
script: |
const fs = require('node:fs');
const { owner, repo } = context.repo;
const pull_number = context.payload.pull_request.number;
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number });
if (pr.state !== 'open' || pr.head.sha !== context.payload.pull_request.head.sha ||
pr.base.sha !== context.payload.pull_request.base.sha) {
core.info('Skipping a superseded bundle report.');
return;
}
const body = fs.readFileSync('bundle-report.md', 'utf8');
const comments = await github.paginate(github.rest.issues.listComments, {
owner, repo, issue_number: pull_number, per_page: 100,
});
const existing = comments.find(comment =>
comment.user?.login === 'github-actions[bot]' &&
comment.body?.startsWith('<!-- dify-vinext-bundle-size -->'));
if (process.env.SHOULD_COMMENT !== 'true') {
if (existing) {
await github.rest.issues.deleteComment({ owner, repo, comment_id: existing.id });
}
core.info('Bundle changes are below the PR comment thresholds. See the job summary.');
return;
}
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number: pull_number, body });
}

View File

@@ -9,16 +9,15 @@ on:
type: boolean
permissions:
actions: read
contents: read
jobs:
test:
name: Web Full-Stack E2E
core-build:
name: Prepare Core E2E Web Build
if: ${{ !inputs.run-external-runtime }}
runs-on: depot-ubuntu-24.04-4
timeout-minutes: 120
defaults:
run:
shell: bash
timeout-minutes: 30
steps:
- name: Checkout code
@@ -26,16 +25,221 @@ jobs:
with:
persist-credentials: false
- name: Switch Ubuntu mirror
- name: Test Web build synchronization
run: node --test .github/actions/wait-e2e-web-build/wait.test.cjs
- name: Compute build source hash
id: build-source
shell: bash
run: |
sudo sed -i 's|http://us-east-1.ec2.archive.ubuntu.com/ubuntu|http://azure.archive.ubuntu.com/ubuntu|g' /etc/apt/sources.list.d/ubuntu.sources
sudo apt-get update
# Hash tracked build inputs, excluding generated files and backend-only changes.
source_hash=$(git ls-tree -r HEAD -- web packages e2e/scripts e2e/package.json e2e/test-env.ts package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc .github/actions/setup-web .github/workflows/web-e2e.yml | sha256sum | cut -d ' ' -f 1)
echo "hash=$source_hash" >> "$GITHUB_OUTPUT"
- name: Setup web dependencies
uses: ./.github/actions/setup-web
with:
# Browser jobs already save this dependency cache; avoid duplicate uploads.
cache-save: 'false'
- name: Restore Next.js build cache
id: next-cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: web/.next/cache
key: e2e-next-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml') }}-${{ steps.build-source.outputs.hash }}
restore-keys: |
e2e-next-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml') }}-
- name: Run E2E support unit tests
working-directory: ./e2e
run: vp run test:unit
- name: Build production Web app
working-directory: ./e2e
env:
E2E_FORCE_WEB_BUILD: '1'
CACHE_HIT: ${{ steps.next-cache.outputs.cache-hit || 'false' }}
run: |
started=$SECONDS
vp run e2e:web:build
{
echo "### E2E Web build"
echo "Build: $((SECONDS - started))s"
echo "Exact cache hit: $CACHE_HIT (false can include a compatible fallback)"
} >> "$GITHUB_STEP_SUMMARY"
- name: Package Web build
run: >-
tar -cf e2e-web-build.tar -C web
.next/BUILD_ID
.next/e2e-web-build.sha256
.next/standalone
.next/static
- name: Upload Web build
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: core-e2e-web-build-${{ github.run_attempt }}
path: e2e-web-build.tar
if-no-files-found: error
retention-days: 7
- name: Save Next.js build cache
if: github.event_name != 'merge_group' && steps.next-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: web/.next/cache
key: ${{ steps.next-cache.outputs.cache-primary-key }}
chromium-full:
name: Chromium Full (${{ matrix.shard }}/3)
if: ${{ !inputs.run-external-runtime }}
runs-on: ubuntu-24.04
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3]
env:
E2E_ADMIN_EMAIL: e2e-admin@example.com
E2E_ADMIN_NAME: E2E Admin
E2E_ADMIN_PASSWORD: E2eAdmin12345
E2E_INIT_PASSWORD: E2eInit12345
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Prepare Chromium E2E dependencies
timeout-minutes: 15
uses: ./.github/actions/setup-core-e2e
with:
browser: chromium
# Install dependencies while core-build runs, then wait only for its artifact.
- name: Wait for Web build
id: web-build
timeout-minutes: 35
uses: ./.github/actions/wait-e2e-web-build
- name: Download Web build
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
artifact-ids: ${{ steps.web-build.outputs.artifact-id }}
github-token: ${{ github.token }}
- name: Extract Web build
run: tar -xf e2e-web-build.tar -C web
- name: Run Chromium full shard
working-directory: ./e2e
run: vp run e2e:full -- --shard ${{ matrix.shard }}/3
- name: Upload Cucumber report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: cucumber-report-chromium-${{ matrix.shard }}
path: e2e/cucumber-report
if-no-files-found: ignore
overwrite: true
retention-days: 7
- name: Upload E2E logs
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-logs-chromium-${{ matrix.shard }}
path: e2e/.logs/*.log
if-no-files-found: ignore
include-hidden-files: true
overwrite: true
retention-days: 7
webkit-smoke:
name: WebKit Browser Smoke
if: ${{ !inputs.run-external-runtime }}
runs-on: ubuntu-24.04
timeout-minutes: 45
env:
E2E_ADMIN_EMAIL: e2e-admin@example.com
E2E_ADMIN_NAME: E2E Admin
E2E_ADMIN_PASSWORD: E2eAdmin12345
E2E_BROWSER: webkit
E2E_INIT_PASSWORD: E2eInit12345
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Prepare WebKit E2E dependencies
timeout-minutes: 15
uses: ./.github/actions/setup-core-e2e
with:
browser: webkit
# Install dependencies while core-build runs, then wait only for its artifact.
- name: Wait for Web build
id: web-build
timeout-minutes: 35
uses: ./.github/actions/wait-e2e-web-build
- name: Download Web build
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
artifact-ids: ${{ steps.web-build.outputs.artifact-id }}
github-token: ${{ github.token }}
- name: Extract Web build
run: tar -xf e2e-web-build.tar -C web
- name: Run WebKit browser smoke
working-directory: ./e2e
run: vp run e2e:full -- --tags '@browser-smoke'
- name: Upload Cucumber report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: cucumber-report-webkit
path: e2e/cucumber-report
if-no-files-found: ignore
overwrite: true
retention-days: 7
- name: Upload E2E logs
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-logs-webkit
path: e2e/.logs/*.log
if-no-files-found: ignore
include-hidden-files: true
overwrite: true
retention-days: 7
external-runtime:
name: Web Full-Stack E2E
if: ${{ inputs.run-external-runtime }}
runs-on: ubuntu-24.04
timeout-minutes: 120
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup web dependencies
uses: ./.github/actions/setup-web
- name: Setup UV and Python
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true
python-version: '3.12'
@@ -46,82 +250,12 @@ jobs:
- name: Install API dependencies
run: uv sync --project api --dev
- name: Run E2E support unit tests
if: ${{ !inputs.run-external-runtime }}
working-directory: ./e2e
run: vp run test:unit
- name: Install Playwright browsers for core E2E
if: ${{ !inputs.run-external-runtime }}
timeout-minutes: 15
working-directory: ./e2e
run: vp run e2e:install:ci
- name: Install Chromium for external runtime E2E
if: ${{ inputs.run-external-runtime }}
- name: Install Chromium
timeout-minutes: 15
working-directory: ./e2e
run: vp run e2e:install:ci:chromium
- name: Run isolated source-api and built-web Cucumber E2E tests
if: ${{ !inputs.run-external-runtime }}
working-directory: ./e2e
env:
E2E_ADMIN_EMAIL: e2e-admin@example.com
E2E_ADMIN_NAME: E2E Admin
E2E_ADMIN_PASSWORD: E2eAdmin12345
E2E_FORCE_WEB_BUILD: '1'
E2E_INIT_PASSWORD: E2eInit12345
run: vp run e2e:full
- name: Preserve Chromium E2E report and logs
if: ${{ !cancelled() && !inputs.run-external-runtime }}
run: |
if [[ -d e2e/cucumber-report ]]; then
mv e2e/cucumber-report e2e/cucumber-report-non-external
fi
if [[ -d e2e/.logs ]]; then
mv e2e/.logs e2e/.logs-non-external
fi
- name: Run WebKit keyboard and browser smoke tests
if: ${{ !inputs.run-external-runtime }}
working-directory: ./e2e
env:
E2E_ADMIN_EMAIL: e2e-admin@example.com
E2E_ADMIN_NAME: E2E Admin
E2E_ADMIN_PASSWORD: E2eAdmin12345
E2E_BROWSER: webkit
E2E_INIT_PASSWORD: E2eInit12345
run: |
teardown_webkit_smoke() {
local run_status=$?
trap - EXIT
if ! vp run e2e:middleware:down; then
echo "::error title=E2E teardown failed::WebKit smoke middleware did not shut down cleanly."
if [[ "$run_status" -eq 0 ]]; then
run_status=1
fi
fi
exit "$run_status"
}
trap teardown_webkit_smoke EXIT
vp run e2e:middleware:up
vp run e2e -- --tags '@browser-smoke'
- name: Preserve WebKit E2E report and logs
if: ${{ !cancelled() && !inputs.run-external-runtime }}
run: |
if [[ -d e2e/cucumber-report ]]; then
mv e2e/cucumber-report e2e/cucumber-report-webkit
fi
if [[ -d e2e/.logs ]]; then
mv e2e/.logs e2e/.logs-webkit
fi
- name: Run prepared and external runtime E2E tests
if: ${{ inputs.run-external-runtime }}
working-directory: ./e2e
env:
E2E_ADMIN_EMAIL: e2e-admin@example.com
@@ -154,10 +288,8 @@ jobs:
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: cucumber-report
path: |
e2e/cucumber-report
e2e/cucumber-report-non-external
e2e/cucumber-report-webkit
path: e2e/cucumber-report
overwrite: true
retention-days: 7
- name: Upload E2E logs
@@ -165,18 +297,17 @@ jobs:
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-logs
path: |
e2e/.logs/*.log
e2e/.logs-non-external/*.log
e2e/.logs-webkit/*.log
path: e2e/.logs/*.log
include-hidden-files: true
overwrite: true
retention-days: 7
- name: Upload E2E seed report
if: ${{ !cancelled() && inputs.run-external-runtime }}
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-seed-report
path: e2e/seed-report
if-no-files-found: ignore
overwrite: true
retention-days: 7

View File

@@ -33,17 +33,52 @@ jobs:
uses: ./.github/actions/setup-web
- name: Run tests
run: vp test run --reporter=blob --reporter=minimal --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --coverage
run: vp test run --project unit --reporter=blob --reporter=minimal --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --coverage
- name: Upload blob report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: blob-report-${{ matrix.shardIndex }}
path: web/.vitest-reports/*
path: web/.vitest/blob/*
include-hidden-files: true
retention-days: 1
browser-test:
name: Web Browser Tests
runs-on: ubuntu-24.04
timeout-minutes: 20
defaults:
run:
shell: bash
working-directory: ./web
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup web environment
uses: ./.github/actions/setup-web
- name: Install Chromium for Browser Mode
timeout-minutes: 15
run: vp exec playwright install --with-deps --only-shell chromium
- name: Run browser tests
run: vp test run --project browser --silent=passed-only
- name: Upload browser failure artifacts
if: ${{ failure() && !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: browser-test-failures
path: web/.vitest-browser/
if-no-files-found: ignore
include-hidden-files: true
retention-days: 7
merge-reports:
name: Merge Test Reports
if: ${{ !cancelled() }}
@@ -68,7 +103,7 @@ jobs:
- name: Download blob reports
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: web/.vitest-reports
path: web/.vitest/blob
pattern: blob-report-*
merge-multiple: true
@@ -76,8 +111,7 @@ jobs:
run: vp test --merge-reports --coverage --silent=passed-only
- name: Report coverage
if: ${{ env.CODECOV_TOKEN != '' }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
directory: web/coverage
flags: web
@@ -86,7 +120,7 @@ jobs:
dify-ui-test:
name: dify-ui Tests
runs-on: depot-ubuntu-24.04-4
runs-on: ubuntu-24.04
timeout-minutes: 20
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
@@ -101,11 +135,6 @@ jobs:
with:
persist-credentials: false
- name: Switch Ubuntu mirror
run: |
sudo sed -i 's|http://us-east-1.ec2.archive.ubuntu.com/ubuntu|http://azure.archive.ubuntu.com/ubuntu|g' /etc/apt/sources.list.d/ubuntu.sources
sudo apt-get update
- name: Setup web environment
uses: ./.github/actions/setup-web
@@ -116,9 +145,18 @@ jobs:
- name: Run dify-ui tests
run: vp test run --project unit --coverage --silent=passed-only
- name: Upload dify-ui test failure artifacts
if: ${{ failure() && !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dify-ui-test-failures
path: packages/dify-ui/.vitest-browser/
if-no-files-found: ignore
include-hidden-files: true
retention-days: 7
- name: Report coverage
if: ${{ env.CODECOV_TOKEN != '' }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
directory: packages/dify-ui/coverage
flags: dify-ui
@@ -127,7 +165,7 @@ jobs:
dify-ui-storybook-test:
name: dify-ui Storybook Tests
runs-on: depot-ubuntu-24.04-4
runs-on: ubuntu-24.04
timeout-minutes: 20
defaults:
run:
@@ -140,11 +178,6 @@ jobs:
with:
persist-credentials: false
- name: Switch Ubuntu mirror
run: |
sudo sed -i 's|http://us-east-1.ec2.archive.ubuntu.com/ubuntu|http://azure.archive.ubuntu.com/ubuntu|g' /etc/apt/sources.list.d/ubuntu.sources
sudo apt-get update
- name: Setup web environment
uses: ./.github/actions/setup-web
@@ -154,3 +187,13 @@ jobs:
- name: Run dify-ui Storybook tests
run: vp run test:storybook
- name: Upload dify-ui Storybook test failure artifacts
if: ${{ failure() && !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dify-ui-storybook-test-failures
path: packages/dify-ui/.vitest-browser/
if-no-files-found: ignore
include-hidden-files: true
retention-days: 7

2
.gitignore vendored
View File

@@ -185,6 +185,7 @@ docker/volumes/minio/*
docker/volumes/milvus/*
docker/volumes/chroma/*
docker/volumes/opensearch/data/*
docker/volumes/openshell-mtls/*
docker/volumes/myscale/data/*
docker/volumes/myscale/log/*
docker/volumes/unstructured/*
@@ -263,6 +264,7 @@ scripts/stress-test/reports/
.qoder/*
.context/
# Vitest local reports
web/.vitest-browser/
web/.vitest-reports/
# dify-agent-runtime

1
.nvmrc
View File

@@ -1 +0,0 @@
22

View File

@@ -1,12 +1,5 @@
#!/bin/sh
# get the list of modified files
files=$(git diff --cached --name-only)
# check if api or web directory is modified
api_modified=false
web_modified=false
skip_web_checks=false
set -eu
git_path() {
git rev-parse --git-path "$1"
@@ -18,46 +11,7 @@ if [ -f "$(git_path MERGE_HEAD)" ] || \
[ -f "$(git_path SQUASH_MSG)" ] || \
[ -d "$(git_path rebase-merge)" ] || \
[ -d "$(git_path rebase-apply)" ]; then
skip_web_checks=true
fi
for file in $files
do
# Use POSIX compliant pattern matching
case "$file" in
api/*.py)
# set api_modified flag to true
api_modified=true
;;
web/*)
# set web_modified flag to true
web_modified=true
;;
esac
done
# run linters based on the modified modules
if $api_modified; then
echo "Running Ruff linter on api module"
# run Ruff linter auto-fixing
uv run --project api --dev ruff check --fix ./api
# run Ruff linter checks
uv run --project api --dev ruff check ./api || status=$?
status=${status:-0}
if [ $status -ne 0 ]; then
echo "Ruff linter on api module error, exit code: $status"
echo "Please run 'dev/reformat' to fix the fixable linting errors."
exit 1
fi
fi
if $skip_web_checks; then
echo "Git operation in progress, skipping web checks"
echo "Git operation in progress, skipping staged checks; CI validates the result."
exit 0
fi

View File

@@ -4,7 +4,6 @@
"tailwindCSS.experimental.configFile": "web/app/styles/globals.css",
// Format
"[javascript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
"[javascriptreact]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
"[typescript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
@@ -25,9 +24,9 @@
"[handlebars]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file",
"oxc.fmt.configPath": "./vite.config.ts",
"oxc.disableNestedConfig": true,
"oxc.fmt.disableNestedConfig": true,
// Lint fix
"eslint.useFlatConfig": true,
"eslint.validate": ["json", "jsonc", "markdown", "yaml", "toml"],
"editor.codeActionsOnSave": {

View File

@@ -1,6 +1,6 @@
# AGENTS.md
Dify is an open-source platform for building LLM applications, agentic workflows, and RAG pipelines. This monorepo contains the backend API (`api/`), frontend application (`web/`), deployment assets (`docker/`), standalone agent backend (`dify-agent/`), CLI (`cli/`), and end-to-end suite (`e2e/`). Follow the nearest scoped `AGENTS.md` for the files being changed.
Dify is an open-source platform for building LLM applications, agentic workflows, and RAG pipelines. This monorepo contains the backend API (`api/`), frontend application (`web/`), deployment assets (`docker/`), standalone agent backend (`dify-agent/`), CLI (`cli/`), and end-to-end suite (`e2e/`). Follow the nearest scoped `AGENTS.md` for the files being changed. Apply its guidance within the user's requested scope; explicit user instructions take precedence over workflow defaults.
## Repository Gotchas

View File

@@ -1 +0,0 @@
AGENTS.md

View File

@@ -22,6 +22,8 @@ Don't forget to link an existing issue or open a new issue in the PR's descripti
### Bug reports
For security vulnerabilities, follow our [security policy](./SECURITY.md) and report privately instead of opening a public issue.
> [!IMPORTANT]
> Please make sure to include the following information when submitting a bug report:
@@ -36,7 +38,7 @@ How we prioritize:
| Issue Type | Priority |
| ------------------------------------------------------------ | --------------- |
| Bugs in core functions (cloud service, cannot login, applications not working, security loopholes) | Critical |
| Bugs in core functions (cloud service, cannot login, applications not working) | Critical |
| Non-critical bugs, performance boosts | Medium Priority |
| Minor fixes (typos, confusing but working UI) | Low Priority |
@@ -71,6 +73,10 @@ How we prioritize:
1. Please link the issue in the PR description, `fixes #<issue_number>`
1. Get merged!
### AI-assisted contributions
AI tools are welcome, but you are responsible for understanding and verifying every change you submit. Describe the problem, your solution, and actual test results in your own words. Unreviewed or repetitive low-quality submissions may be closed.
### Setup the project
#### Frontend

View File

@@ -58,6 +58,7 @@
<a href="./docs/sl-SI/README.md"><img alt="README Slovenščina" src="https://img.shields.io/badge/Sloven%C5%A1%C4%8Dina-d9d9d9"></a>
<a href="./docs/bn-BD/README.md"><img alt="README in বাংলা" src="https://img.shields.io/badge/বাংলা-d9d9d9"></a>
<a href="./docs/hi-IN/README.md"><img alt="README in हिन्दी" src="https://img.shields.io/badge/Hindi-d9d9d9"></a>
<a href="./docs/id-ID/README.md"><img alt="README dalam Bahasa Indonesia" src="https://img.shields.io/badge/Bahasa%20Indonesia-d9d9d9"></a>
</p>
Dify is an open-source LLM app development platform. Its intuitive interface combines AI workflow, RAG pipeline, agent capabilities, model management, observability features (including [Opik](https://www.comet.com/docs/opik/integrations/dify), [Langfuse](https://docs.langfuse.com), and [Arize Phoenix](https://docs.arize.com/phoenix)) and more, letting you quickly go from prototype to production. Here's a list of the core features:
@@ -104,8 +105,7 @@ Intuitive interface for crafting prompts, comparing model performance, and addin
**4. RAG Pipeline**:
Extensive RAG capabilities that cover everything from document ingestion to retrieval, with out-of-box support for text extraction from PDFs, PPTs, and other common document formats.
**5. Agent capabilities**:
You can define agents based on LLM Function Calling or ReAct, and add pre-built or custom tools for the agent. Dify provides 50+ built-in tools for AI agents, such as Google Search, DALL·E, Stable Diffusion and WolframAlpha.
**5. Agent**: Autonomous agents with a sandbox of their own: they run commands, install software, and handle files to get open-ended tasks done. Describe the agent you want and it builds itself. Give it skills, connect tools from [Dify Marketplace](https://marketplace.dify.ai/?category=tool), MCP servers, or your own APIs, and put it to work as a chat app or as a step in any workflow.
**6. LLMOps**:
Monitor and analyze application logs and performance over time. You could continuously improve prompts, datasets, and models based on production data and annotations.
@@ -122,90 +122,42 @@ All of Dify's offerings come with corresponding APIs, so you could effortlessly
Quickly get Dify running in your environment with this [starter guide](#quick-start).
Use our [documentation](https://docs.dify.ai) for further references and more in-depth instructions.
- **Dify for enterprise / organizations<br/>**
We provide additional enterprise-centric features. [Send us an email](mailto:business@dify.ai?subject=%5BGitHub%5DBusiness%20License%20Inquiry) to discuss your enterprise needs. <br/>
- **[Dify Enterprise](https://dify.ai/dify-enterprise)<br/>**
For organizations requiring self-hosting, SSO, RBAC Security and Enterprise Support SLAs, please fill out [this form](https://share-na2.hsforms.com/14-09ff5HS92Sh4m3f4yrcw40s9fk) to speak to a solution representative. <br/>
## Staying ahead
Star Dify on GitHub and be instantly notified of new releases.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Advanced Setup
### Custom configurations
If you need to customize the configuration, edit `docker/.env`. The essential startup defaults live in [`docker/.env.example`](docker/.env.example), and optional advanced variables are split under `docker/envs/` by theme. After making any changes, re-run `docker compose up -d` from the `docker` directory. You can find the full list of available environment variables [here](https://docs.dify.ai/getting-started/install-self-hosted/environments).
### Metrics Monitoring with Grafana
Import the dashboard to Grafana, using Dify's PostgreSQL database as data source, to monitor metrics in granularity of apps, tenants, messages, and more.
- [Grafana Dashboard by @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
### Deployment with Kubernetes
If you'd like to configure a highly available setup, there are community-contributed [Helm Charts](https://helm.sh/) and YAML files which allow Dify to be deployed on Kubernetes.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Using Terraform for Deployment
Deploy Dify to Cloud Platform with a single click using [terraform](https://www.terraform.io/)
##### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### Using AWS CDK for Deployment
Deploy Dify to AWS with [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK by @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Using Alibaba Cloud Computing Nest
Quickly deploy Dify to Alibaba cloud with [Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Using Alibaba Cloud Data Management
One-Click deploy Dify to Alibaba Cloud with [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### Deploy to AKS with Azure Devops Pipeline
One-Click deploy Dify to AKS with [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
For custom configuration, observability, and deployment options, see [Advanced Setup](docs/ADVANCED_SETUP.md).
## Contributing
For those who'd like to contribute code, see our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
At the same time, please consider supporting Dify by sharing it on social media and at events and conferences.
Dify welcomes contributions of all kinds:
> We are looking for contributors to help translate Dify into languages other than Mandarin or English. If you are interested in helping, please see the [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/8Tpq4AcN9c).
- **Code**: Read the [Contribution Guide](CONTRIBUTING.md), then browse [good first issues](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22).
- **Ideas and feedback**: Start or join a [GitHub Discussion](https://github.com/langgenius/dify/discussions).
- **Translations**: Follow the [internationalization guide](web/i18n/README.md) to add or update a locale.
- **Community**: Share the apps you build, help other users, and spread the word about Dify.
### Contributors
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Community & contact
- [GitHub Discussion](https://github.com/langgenius/dify/discussions). Best for: sharing feedback and asking questions.
- [GitHub Issues](https://github.com/langgenius/dify/issues). Best for: bugs you encounter using Dify.AI, and feature proposals. See our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Best for: sharing your applications and hanging out with the community.
- [X(Twitter)](https://twitter.com/dify_ai). Best for: sharing your applications and hanging out with the community.
Choose the channel that best fits your question:
**Contributors**
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): Get help, share feedback, and propose ideas.
- [GitHub Issues](https://github.com/langgenius/dify/issues): Report reproducible bugs and track engineering work. Read the [Contribution Guide](CONTRIBUTING.md) before opening one.
- [Discord](https://discord.gg/FngNHpbcY7): Chat in real time, share your apps, and connect with other Dify users.
- [X](https://x.com/dify_ai): Follow Dify for release news and project updates.
## Star History

View File

@@ -98,9 +98,11 @@ REDIS_KEEPALIVE=true
CELERY_BROKER_URL=redis://:difyai123456@localhost:${REDIS_PORT}/1
CELERY_BACKEND=redis
# Ops trace retry configuration
OPS_TRACE_RETRYABLE_DISPATCH_MAX_RETRIES=60
# Ops trace configuration
OPS_TRACE_UNIFIED_ENABLED=false
OPS_TRACE_RETRYABLE_DISPATCH_MAX_RETRIES=780
OPS_TRACE_RETRYABLE_DISPATCH_DELAY_SECONDS=5
OPS_TRACE_PARENT_CONTEXT_TTL_SECONDS=3900
# Database configuration
DB_TYPE=postgresql
@@ -220,6 +222,7 @@ SUPABASE_URL=your-server-url
# CORS configuration
WEB_API_CORS_ALLOW_ORIGINS=http://localhost:3000,*
CONSOLE_CORS_ALLOW_ORIGINS=http://localhost:3000,*
WEBSOCKET_MAX_HTTP_BUFFER_SIZE=10485760
# When the frontend and backend run on different subdomains, set COOKIE_DOMAIN to the site’s top-level domain (e.g., `example.com`). Leading dots are optional.
COOKIE_DOMAIN=
@@ -231,6 +234,7 @@ VECTOR_INDEX_NAME_PREFIX=Vector_index
# Weaviate configuration
WEAVIATE_ENDPOINT=http://localhost:8080
WEAVIATE_GRPC_ENDPOINT=grpc://localhost:50051
WEAVIATE_API_KEY=WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih
WEAVIATE_BATCH_SIZE=100
WEAVIATE_TOKENIZATION=word
@@ -456,6 +460,12 @@ UPLOAD_IMAGE_FILE_SIZE_LIMIT=10
UPLOAD_VIDEO_FILE_SIZE_LIMIT=100
UPLOAD_AUDIO_FILE_SIZE_LIMIT=50
UPLOAD_SKILL_FILE_SIZE_LIMIT=50
SKILL_PACKAGE_MAX_UNCOMPRESSED_BYTES=209715200
SKILL_PACKAGE_MAX_ENTRIES=5000
SKILL_PACKAGE_MAX_SKILL_MD_BYTES=1048576
AGENT_PACKAGE_MAX_BYTES=536870912
AGENT_PACKAGE_MAX_MANIFEST_BYTES=5242880
AGENT_PACKAGE_MAX_ENTRIES=5000
# Comma-separated list of file extensions blocked from upload for security reasons.
# Extensions should be lowercase without dots (e.g., exe,bat,sh,dll).
@@ -488,6 +498,13 @@ SENDGRID_API_KEY=
# Sentry configuration
SENTRY_DSN=
# Cloudflare Turnstile server-side verification for Dify Cloud sign-in
TURNSTILE_SECRET_KEY=
# Comma-separated parent or exact hostnames, for example: dify.ai,staging.dify.dev
TURNSTILE_ALLOWED_HOSTNAMES=
# Enable only after the compatible web client has been fully deployed.
TURNSTILE_EMAIL_CODE_VERIFY_REQUIRED=false
# DEBUG
DEBUG=false
ENABLE_REQUEST_LOGGING=False
@@ -578,7 +595,7 @@ INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=4000
# Workflow runtime configuration
WORKFLOW_MAX_EXECUTION_STEPS=500
WORKFLOW_MAX_EXECUTION_TIME=1200
WORKFLOW_MAX_EXECUTION_TIME=3600
WORKFLOW_CALL_MAX_DEPTH=5
MAX_VARIABLE_SIZE=204800
# Maximum concurrent node-builder LLM calls per workflow generation request
@@ -623,7 +640,7 @@ WORKFLOW_LOG_CLEANUP_BATCH_SIZE=100
WORKFLOW_LOG_CLEANUP_SPECIFIC_WORKFLOW_IDS=
# App configuration
APP_MAX_EXECUTION_TIME=1200
APP_MAX_EXECUTION_TIME=3600
APP_DEFAULT_ACTIVE_REQUESTS=0
APP_MAX_ACTIVE_REQUESTS=0
@@ -698,11 +715,12 @@ INNER_API_KEY_FOR_PLUGIN=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y
# Dify Agent backend
AGENT_BACKEND_BASE_URL=http://localhost:5050
# Bearer token sent to the Agent backend /runs API. Must match DIFY_AGENT_API_TOKEN on the server side.
# Bearer token sent to the Agent backend control-plane API. Must match DIFY_AGENT_API_TOKEN on the server side.
AGENT_BACKEND_API_TOKEN=dify-agent-run-token-for-dev-only
AGENT_BACKEND_STREAM_READ_TIMEOUT_SECONDS=30
AGENT_BACKEND_STREAM_MAX_RECONNECTS=3
AGENT_BACKEND_RUN_TIMEOUT_SECONDS=1200
# Client deadline for converting a Binding file to a ToolFile through the Agent backend.
AGENT_BACKEND_BINDING_FILE_DOWNLOAD_TIMEOUT_SECONDS=240
# KnowledgeFS (Dataset 2.0)
KNOWLEDGE_FS_ENABLED=false
@@ -729,6 +747,8 @@ RESET_PASSWORD_TOKEN_EXPIRY_MINUTES=5
EMAIL_REGISTER_TOKEN_EXPIRY_MINUTES=5
CHANGE_EMAIL_TOKEN_EXPIRY_MINUTES=5
OWNER_TRANSFER_TOKEN_EXPIRY_MINUTES=5
EMAIL_CODE_LOGIN_TOKEN_EXPIRY_MINUTES=5
EMAIL_CODE_LOGIN_MAX_ATTEMPTS=5
CREATE_TIDB_SERVICE_JOB_ENABLED=false
@@ -825,6 +845,12 @@ ENABLE_HUMAN_INPUT_TIMEOUT_TASK=true
# Human input timeout check interval in minutes
HUMAN_INPUT_TIMEOUT_TASK_INTERVAL=1
# Whether to recover soft-deleted conversation cleanup jobs periodically
ENABLE_CONVERSATION_CLEANUP_TASK=true
# Recovery interval in minutes and maximum conversations dispatched per sweep
CONVERSATION_CLEANUP_TASK_INTERVAL=5
CONVERSATION_CLEANUP_BATCH_SIZE=100
# Nacos remote settings source HTTP timeouts (seconds).
# Bound how long requests to the Nacos endpoint wait before failing, so a slow or
# unresponsive Nacos server cannot stall API startup or token refresh.
@@ -832,3 +858,6 @@ HUMAN_INPUT_TIMEOUT_TASK_INTERVAL=1
DIFY_ENV_NACOS_REQUEST_TIMEOUT=10.0
# Connect timeout for Nacos requests (default: 3.0)
DIFY_ENV_NACOS_CONNECT_TIMEOUT=3.0
# skill entry
ENABLE_SKILL=true

View File

@@ -15,6 +15,109 @@ root_packages =
services
include_external_packages = True
[importlinter:contract:backend-layers]
name = Backend modules follow the transport, application, domain, and shared-library layers
type = layers
layers =
controllers
services
core
libs
# Migration baseline: keep every exception exact. The default unmatched-import
# error makes this list shrink whenever a legacy dependency is removed.
unmatched_ignore_imports_alerting = error
ignore_imports =
core.app.app_config.easy_ui_based_app.dataset.manager -> services.dataset_service
core.app.apps.advanced_chat.app_generator -> controllers.console.app.workflow
core.app.apps.advanced_chat.app_generator -> services.conversation_service
core.app.apps.advanced_chat.app_generator -> services.errors.conversation
core.app.apps.advanced_chat.app_generator -> services.workflow_draft_variable_service
core.app.apps.advanced_chat.app_runner -> services.conversation_variable_updater
core.app.apps.agent_app.app_generator -> services.agent.composer_service
core.app.apps.agent_app.app_generator -> services.agent.workspace_service
core.app.apps.agent_app.app_generator -> services.conversation_service
core.app.apps.agent_app.runtime_request_builder -> services.agent.prompt_mentions
core.app.apps.agent_app.session_store -> services.agent.workspace_service
core.app.apps.agent_chat.app_generator -> services.conversation_service
core.app.apps.base_app_generator -> services.workflow_draft_variable_service
core.app.apps.chat.app_generator -> services.conversation_service
core.app.apps.common.workflow_response_converter -> services.variable_truncator
core.app.apps.completion.app_generator -> services.errors.app
core.app.apps.completion.app_generator -> services.errors.message
core.app.apps.message_based_app_generator -> services.errors.app_model_config
core.app.apps.message_based_app_generator -> services.errors.conversation
core.app.apps.message_based_app_generator -> services.errors.message
core.app.apps.pipeline.pipeline_generator -> services
core.app.apps.pipeline.pipeline_generator -> services.dataset_service
core.app.apps.pipeline.pipeline_generator -> services.datasource_provider_service
core.app.apps.pipeline.pipeline_generator -> services.feature_service
core.app.apps.pipeline.pipeline_generator -> services.workflow_draft_variable_service
core.app.apps.pipeline.pipeline_runner -> services.dataset_ref_service
core.app.apps.workflow.app_generator -> controllers.console.app.workflow
core.app.apps.workflow.app_generator -> services.snippet_generate_service
core.app.apps.workflow.app_generator -> services.workflow_draft_variable_service
core.app.apps.workflow_app_runner -> tasks.mail_human_input_delivery_task
core.app.features.annotation_reply.annotation_reply -> services.annotation_service
core.app.features.annotation_reply.annotation_reply -> services.dataset_service
core.app.llm.quota -> services.credit_pool_service
core.app.task_pipeline.message_cycle_manager -> services.account_service
core.app.task_pipeline.message_cycle_manager -> services.annotation_service
core.app.workflow.layers.persistence -> services.workflow.inspector_events
core.datasource.datasource_manager -> services.datasource_provider_service
core.helper.credential_utils -> services.enterprise.plugin_manager_service
core.helper.credential_utils -> services.system_feature_service
core.indexing_runner -> services.vector_space_admission_service
core.mcp.auth_client -> services
core.provider_manager -> services.credential_permission_service
core.provider_manager -> services.credit_pool_service
core.provider_manager -> services.feature_service
core.rag.datasource.retrieval_service -> services.external_knowledge_service
core.rag.index_processor.index_processor -> services.vector_space_admission_service
core.rag.index_processor.index_processor_base -> services.file_service
core.rag.index_processor.processor.paragraph_index_processor -> services.account_service
core.rag.index_processor.processor.paragraph_index_processor -> services.summary_index_service
core.rag.index_processor.processor.parent_child_index_processor -> services.account_service
core.rag.index_processor.processor.parent_child_index_processor -> services.summary_index_service
core.rag.index_processor.processor.qa_index_processor -> services.summary_index_service
core.rag.retrieval.dataset_retrieval -> services.external_knowledge_service
core.rag.retrieval.dataset_retrieval -> services.feature_service
core.rag.summary_index.summary_index -> services.summary_index_service
core.rag.summary_index.summary_index -> tasks.generate_summary_index_task
core.repositories.sqlalchemy_workflow_node_execution_repository -> services.file_service
core.repositories.sqlalchemy_workflow_node_execution_repository -> services.variable_truncator
core.tools.tool_manager -> services
core.workflow.node_runtime -> services
core.workflow.nodes.agent.message_transformer -> services
core.workflow.nodes.agent_v2.agent_node -> services.agent.prompt_mentions
core.workflow.nodes.agent_v2.agent_node -> services.agent.workspace_service
core.workflow.nodes.agent_v2.dify_tools_builder -> services
core.workflow.nodes.agent_v2.runtime_feature_manifest -> services.agent.knowledge_datasets
core.workflow.nodes.agent_v2.runtime_request_builder -> services.agent.prompt_mentions
core.workflow.nodes.agent_v2.runtime_request_builder -> services.skill_management_service
core.workflow.nodes.agent_v2.session_store -> services.agent.workspace_service
core.workflow.nodes.agent_v2.validators -> services.agent.knowledge_datasets
core.workflow.nodes.agent_v2.workspace_retirement_layer -> tasks.collect_agent_resources_task
libs.email_i18n -> services.entities.feature_entities
libs.email_i18n -> services.system_feature_service
libs.external_api -> core
libs.external_api -> core.errors.error
libs.external_api -> extensions.ext_logging
libs.flask_utils -> models
libs.helper -> core.app.features.rate_limiting.rate_limit
libs.helper -> models
libs.helper -> models.model
libs.json_in_md_parser -> core.llm_generator.output_parser.errors
libs.login -> extensions.ext_login
libs.login -> models
libs.login -> models.model
libs.oauth -> core.helper.http_client_pooling
libs.oauth_bearer -> models
libs.rsa -> extensions.ext_storage
libs.workspace_permission -> services.enterprise.enterprise_service
services.account_service -> controllers
services.account_service -> controllers.console.error
services.app_generate_service -> controllers.console.app.workflow
[importlinter:contract:no-direct-rsa-imports]
# Note: `libs` itself is deliberately excluded from source_modules -- import-linter's
# `forbidden` contract cannot check a package against its own descendant (libs.rsa lives
@@ -65,6 +168,299 @@ forbidden_modules =
sqlalchemy
werkzeug
[importlinter:contract:init-validation-service-boundary]
name = Initialization validation application service is framework and persistence neutral
type = forbidden
source_modules =
services.init_validation_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:explore-banner-query-service-boundary]
name = Explore banner query application service is framework and persistence neutral
type = forbidden
source_modules =
services.explore_banner_query_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:installed-app-access-boundary]
name = Installed app access application service is framework and persistence neutral
type = forbidden
source_modules =
services.installed_app_access_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:installed-app-management-boundary]
name = Installed app management service is framework and persistence neutral
type = forbidden
source_modules =
services.installed_app_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:installed-app-conversation-boundary]
name = Installed app conversation service is framework and persistence neutral
type = forbidden
source_modules =
services.installed_app_conversation_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:installed-app-message-boundary]
name = Installed app message service is framework and persistence neutral
type = forbidden
source_modules =
services.installed_app_message_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:installed-app-message-persistence-boundary]
name = Installed app message persistence does not call the legacy message service
type = forbidden
source_modules =
repositories.installed_app_message_repository
forbidden_modules =
services.message_service
allow_indirect_imports = True
[importlinter:contract:conversation-persistence-boundary]
name = Conversation persistence does not call legacy conversation or workspace services
type = forbidden
source_modules =
repositories.installed_app_conversation_repository
repositories.conversation_lifecycle
repositories.agent_workspace_repository
forbidden_modules =
services.conversation_service
services.agent.workspace_service
allow_indirect_imports = True
[importlinter:contract:installed-app-generation-boundary]
name = Installed app generation service is framework and persistence neutral
type = forbidden
source_modules =
services.installed_app_generation_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:account-application-boundary]
name = Account application services and contracts are framework and persistence neutral
type = forbidden
source_modules =
services.account_access_service
services.account_avatar_service
services.account_change_email_ports
services.account_change_email_service
services.account_email_registration_service
services.account_deletion_service
services.account_deletion_feedback_service
services.account_education_service
services.account_errors
services.account_forgot_password_service
services.account_initialization_service
services.account_integration_service
services.account_login_service
services.account_oauth_service
services.account_password_service
services.account_ports
services.account_profile_service
services.entities.account_access_entities
services.entities.account_entities
services.entities.account_login_entities
services.entities.account_oauth_entities
services.entities.auth_audit_entities
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:app-definition-query-service-boundary]
name = App definition query application service is framework and persistence neutral
type = forbidden
source_modules =
services.app_definition_query_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:app-site-service-boundary]
name = App site application service is framework and persistence neutral
type = forbidden
source_modules =
services.app_site_service
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:end-user-query-service-boundary]
name = App-scoped end user query service is framework and persistence neutral
type = forbidden
source_modules =
services.app_scoped_end_user_query_service
services.entities.app_scoped_end_user_entities
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:end-user-command-service-boundary]
name = App-scoped end user command service is framework and persistence neutral
type = forbidden
source_modules =
services.app_scoped_end_user_service
forbidden_modules =
configs
controllers
extensions
flask
models.model
repositories
sqlalchemy
werkzeug
[importlinter:contract:app-tracing-config-service-boundary]
name = App tracing configuration service is framework and persistence neutral
type = forbidden
source_modules =
services.app_tracing_config_service
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:message-file-preview-service-boundary]
name = Message file preview application service is framework and persistence neutral
type = forbidden
source_modules =
services.message_file_preview_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:upload-file-delivery-service-boundary]
name = Upload file delivery application service is framework and persistence neutral
type = forbidden
source_modules =
services.upload_file_delivery_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:webapp-access-query-service-boundary]
name = Web app access query application service is framework and persistence neutral
type = forbidden
source_modules =
services.webapp_access_query_service
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
services.enterprise
services.feature_service
sqlalchemy
werkzeug
[importlinter:contract:web-app-runtime-query-service-boundary]
name = Web app runtime query application service does not directly depend on configuration, feature implementation, transport, or ORM modules
type = forbidden
source_modules =
services.web_app_runtime_query_service
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
services.feature_service
services.feature_service_gateway
sqlalchemy
werkzeug
allow_indirect_imports = True
[importlinter:contract:feature-query-service-boundary]
name = Feature query application service is framework and persistence neutral
type = forbidden
@@ -79,6 +475,58 @@ forbidden_modules =
sqlalchemy
werkzeug
[importlinter:contract:data-source-oauth-service-boundary]
name = Data source OAuth application service is framework and persistence neutral
type = forbidden
source_modules =
services.data_source_oauth_service
forbidden_modules =
configs
controllers
extensions
flask
httpx
libs
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:oauth-server-application-service-boundary]
name = OAuth server application service is framework and persistence neutral
type = forbidden
source_modules =
services.oauth_server_service
forbidden_modules =
configs
controllers
extensions
flask
httpx
libs
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:oauth-device-application-service-boundary]
name = OAuth device application service and contracts are framework and persistence neutral
type = forbidden
source_modules =
services.oauth_device_application_service
services.oauth_device_contracts
forbidden_modules =
configs
controllers
extensions
flask
httpx
libs
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:workspace-member-query-service-boundary]
name = Workspace member query application service is framework and persistence neutral
type = forbidden
@@ -125,3 +573,231 @@ forbidden_modules =
repositories
sqlalchemy
werkzeug
[importlinter:contract:inner-mail-service-boundary]
name = Inner mail application service is framework and implementation neutral
type = forbidden
source_modules =
services.inner_mail_service
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
sqlalchemy
tasks
werkzeug
[importlinter:contract:web-passport-service-boundary]
name = Web passport application service is framework and persistence neutral
type = forbidden
source_modules =
services.web_passport_service
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:file-grant-service-boundary]
name = File grant application service is framework and persistence neutral
type = forbidden
source_modules =
services.file_grant_service
services.entities.file_grant_entities
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:plugin-file-upload-service-boundary]
name = Plugin file upload application service is framework and persistence neutral
type = forbidden
source_modules =
services.plugin_file_upload_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:account-activation-service-boundary]
name = Account activation application service is framework and persistence neutral
type = forbidden
source_modules =
services.account_activation_service
services.entities.account_activation_entities
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:tag-application-service-boundary]
name = Tag application service is framework and persistence neutral
type = forbidden
source_modules =
services.tag_application_service
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:tool-file-download-service-boundary]
name = Tool file download application service is framework and persistence neutral
type = forbidden
source_modules =
services.tool_file_download_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:recommended-app-query-service-boundary]
name = Recommended app query application service is framework and persistence neutral
type = forbidden
source_modules =
services.recommended_app_query_service
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
services.feature_service
services.recommended_app_catalog_gateway
sqlalchemy
werkzeug
[importlinter:contract:recommended-app-catalog-gateway-boundary]
name = Recommended app catalog gateway does not depend on Flask
type = forbidden
source_modules =
services.recommended_app_catalog_gateway
forbidden_modules =
flask
[importlinter:contract:trial-app-application-boundary]
name = Trial app access and generation services are framework and persistence neutral
type = forbidden
source_modules =
services.trial_app_access_service
services.trial_app_generation_service
forbidden_modules =
controllers
extensions
flask
models
repositories
services.app_generate_service
sqlalchemy
werkzeug
[importlinter:contract:app-preview-query-boundary]
name = App preview queries are framework and persistence neutral
type = forbidden
source_modules =
services.app_preview_query_service
services.app_preview_details_service
forbidden_modules =
controllers
extensions
flask
models
repositories
sqlalchemy
werkzeug
[importlinter:contract:message-suggested-questions-boundary]
name = Message suggested questions port is framework and persistence neutral
type = forbidden
source_modules =
services.message_suggested_questions_service
forbidden_modules =
controllers
extensions
flask
models
repositories
services.message_service
sqlalchemy
werkzeug
[importlinter:contract:billing-application-boundary]
name = Billing application services and ports are framework and infrastructure neutral
type = forbidden
source_modules =
services.billing_portal_service
services.compliance_download_service
services.partner_tenant_binding_service
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
services.billing_service
sqlalchemy
werkzeug
[importlinter:contract:workflow-run-archive-application-boundary]
name = Workflow run archive application service and task domain are framework and persistence neutral
type = forbidden
source_modules =
services.retention.workflow_run.archive_download_task
services.retention.workflow_run.archive_log_service
forbidden_modules =
configs
controllers
extensions
flask
models
repositories
sqlalchemy
tasks
werkzeug
[importlinter:contract:app-audio-boundary]
name = App audio operations and values are framework and persistence neutral
type = forbidden
source_modules =
services.app_audio_service
services.audio_types
forbidden_modules =
controllers
extensions
flask
models
repositories
services.audio_service
sqlalchemy
werkzeug

View File

@@ -23,3 +23,8 @@ Run direct Python commands through `uv run --project api`. Docker-backed integra
- Use Pydantic v2 for request and response models. Reuse domain-specific exceptions and translate them at the controller boundary.
- Use existing Celery task and queue owners for asynchronous work; do not route unrelated jobs through workflow-specific services.
- Celery tasks that may be retried or redelivered must keep side effects idempotent and log affected resource identifiers.
## Typing Best Practises
- Treat nullable types (`T | None`) strictly as union types; document the semantic meaning for each branch.
- Treat default parameter values as introducing distinct function overloads; verify that every added default has demonstrable practical utility and avoids masking unsafe runtime states.

View File

@@ -33,7 +33,6 @@ RUN uv sync --frozen --no-dev --no-editable
FROM base AS production
ENV FLASK_APP=app.py
ENV EDITION=SELF_HOSTED
ENV DEPLOY_ENV=PRODUCTION
ENV CONSOLE_API_URL=http://127.0.0.1:5001
ENV CONSOLE_WEB_URL=http://127.0.0.1:3000
@@ -54,15 +53,14 @@ WORKDIR /app/api
# Create non-root user
ARG dify_uid=1001
ARG NODE_MAJOR=22
ARG NODE_PACKAGE_VERSION=22.21.0-1nodesource1
ARG NODE_PACKAGE_VERSION=24.20.0-1nodesource1
ARG NODESOURCE_KEY_FPR=6F71F525282841EEDAF851B42F59B5F99B1BE0B4
RUN groupadd -r -g ${dify_uid} dify && \
useradd -r -u ${dify_uid} -g ${dify_uid} -s /bin/bash dify && \
chown -R dify:dify /app
RUN \
apt-get update \
RUN NODE_MAJOR="${NODE_PACKAGE_VERSION%%.*}" \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
curl \

View File

@@ -87,16 +87,17 @@ The scripts resolve paths relative to their location, so you can run them from a
uv sync --group dev
```
1. Run the tests locally with mocked system environment variables in `tool.pytest_env` section in `pyproject.toml`, more can check [Claude.md](../CLAUDE.md)
1. Run the tests locally with mocked system environment variables in `tool.pytest_env` section in `pyproject.toml`, more can check [AGENTS.md](../AGENTS.md)
Continue in the `api` directory from the previous step.
```bash
cd api
uv run pytest # Run all tests
uv run pytest tests/unit_tests/ # Unit tests only
uv run pytest tests/integration_tests/ # Integration tests
# Code quality
./dev/reformat # Run all formatters and linters
../dev/reformat # Run all formatters and linters
uv run ruff check --fix ./ # Fix linting issues
uv run ruff format ./ # Format code
uv run pyrefly check # Type checking

View File

@@ -14,6 +14,7 @@ from contexts.wrapper import RecyclableContextVar
from controllers.console.error import UnauthorizedAndForceLogout
from core.logging.context import init_request_context
from dify_app import DifyApp
from enums import DeploymentEdition
from extensions.ext_socketio import sio
from services.enterprise.enterprise_service import EnterpriseService
from services.entities.feature_entities import LicenseStatus
@@ -104,6 +105,10 @@ def create_flask_app_with_configs() -> DifyApp:
dify_app = DifyApp(__name__)
dify_app.config.from_mapping(dify_config.model_dump())
dify_app.config["RESTX_INCLUDE_ALL_MODELS"] = True
# flask-restx appends url-map suggestions to 404 bodies: they enumerate routes to
# anonymous callers, and a policy 404 on an existing route (edition admission)
# ends up suggesting the very path that was just requested.
dify_app.config["RESTX_ERROR_404_HELP"] = False
# add before request hook
@dify_app.before_request
@@ -112,7 +117,7 @@ def create_flask_app_with_configs() -> DifyApp:
init_request_context()
RecyclableContextVar.increment_thread_recycles()
if dify_config.ENTERPRISE_ENABLED:
if dify_config.DEPLOYMENT_EDITION == DeploymentEdition.ENTERPRISE:
surface = _match_license_gated_surface(request.path)
if surface is not None:
try:

View File

@@ -23,6 +23,7 @@ from dify_agent.protocol import (
CancelRunResponse,
CreateRunRequest,
CreateRunResponse,
RunCancelledEvent,
RunEvent,
RunStatusResponse,
)
@@ -45,6 +46,15 @@ class AgentBackendRunClient(Protocol):
def cancel_run(self, run_id: str, request: CancelRunRequest | None = None) -> CancelRunResponse:
"""Request explicit cancellation for one Agent backend run."""
def cancel_run_and_wait(
self,
run_id: str,
request: CancelRunRequest | None = None,
*,
after: str | None = None,
) -> RunCancelledEvent:
"""Request cancellation and wait for runner cleanup to finish."""
def stream_events(
self,
run_id: str,
@@ -67,6 +77,15 @@ class _DifyAgentSyncClient(Protocol):
def cancel_run_sync(self, run_id: str, request: CancelRunRequest | None = None) -> CancelRunResponse:
"""Cancel one run synchronously."""
def cancel_run_and_wait_sync(
self,
run_id: str,
request: CancelRunRequest | None = None,
*,
after: str | None = None,
) -> RunCancelledEvent:
"""Cancel one run and wait for its terminal event synchronously."""
def stream_events_sync(
self,
run_id: str,
@@ -92,11 +111,9 @@ class DifyAgentBackendRunClient:
client: _DifyAgentSyncClient,
*,
stream_max_reconnects: int = 3,
stream_timeout_seconds: float = 1200,
) -> None:
self.client = client
self._stream_max_reconnects = stream_max_reconnects
self._stream_timeout_seconds = stream_timeout_seconds
def create_run(self, request: CreateRunRequest) -> CreateRunResponse:
"""Create one run through ``POST /runs`` and normalize client exceptions."""
@@ -112,6 +129,19 @@ class DifyAgentBackendRunClient:
except Exception as exc:
raise _normalize_dify_agent_error(exc) from exc
def cancel_run_and_wait(
self,
run_id: str,
request: CancelRunRequest | None = None,
*,
after: str | None = None,
) -> RunCancelledEvent:
"""Cancel one run, then wait for the cleanup-complete terminal event."""
try:
return self.client.cancel_run_and_wait_sync(run_id, request=request, after=after)
except Exception as exc:
raise _normalize_dify_agent_error(exc) from exc
def stream_events(
self,
run_id: str,
@@ -125,7 +155,6 @@ class DifyAgentBackendRunClient:
run_id,
after=after,
max_reconnects=self._stream_max_reconnects,
timeout_seconds=self._stream_timeout_seconds,
should_stop=should_stop,
)
except Exception as exc:

View File

@@ -8,11 +8,40 @@ classes.
from __future__ import annotations
from http import HTTPStatus
from typing import Any
from dify_agent.client import DifyAgentHTTPError
from dify_agent.protocol import RunFailureType
def backend_reported_failure(exc: DifyAgentHTTPError) -> bool:
"""Whether the backend itself reported the failure.
``DifyAgentValidationError`` is a ``DifyAgentHTTPError`` that the client also
raises when a *successful* response fails DTO validation. Those carry a 2xx
status and a Pydantic error list, not a backend-authored error envelope.
"""
return exc.status_code >= HTTPStatus.BAD_REQUEST
def backend_error_detail(
exc: DifyAgentHTTPError,
*,
default_code: str = "agent_backend_error",
) -> tuple[str, str]:
"""Split an Agent backend HTTP error into the code and message the backend itself sent."""
detail = exc.detail
if isinstance(detail, dict):
code = detail.get("code")
message = detail.get("message")
return (
code if isinstance(code, str) and code else default_code,
message if isinstance(message, str) and message else str(exc),
)
return default_code, str(detail)
class AgentBackendError(Exception):
"""Base error for API-side Agent backend integration failures."""

View File

@@ -99,6 +99,8 @@ class AgentBackendRunFailedInternalEvent(AgentBackendInternalEventBase):
error: str
error_type: RunFailureType | None = None
reason: str | None = None
session_snapshot: CompositorSessionSnapshot | None = None
usage: dict[str, JsonValue] | None = None
class AgentBackendRunCancelledInternalEvent(AgentBackendInternalEventBase):
@@ -107,6 +109,8 @@ class AgentBackendRunCancelledInternalEvent(AgentBackendInternalEventBase):
type: Literal[AgentBackendInternalEventType.RUN_CANCELLED] = AgentBackendInternalEventType.RUN_CANCELLED
reason: str | None = None
message: str | None = None
session_snapshot: CompositorSessionSnapshot | None = None
usage: dict[str, JsonValue] | None = None
type AgentBackendInternalEvent = Annotated[
@@ -184,6 +188,8 @@ class AgentBackendRunEventAdapter:
error=event.data.error,
error_type=event.data.error_type,
reason=event.data.reason,
session_snapshot=event.data.session_snapshot,
usage=_agent_run_usage(event.data.usage),
)
]
case RunCancelledEvent():
@@ -193,6 +199,8 @@ class AgentBackendRunEventAdapter:
source_event_id=event.id,
reason=event.data.reason,
message=event.data.message,
session_snapshot=event.data.session_snapshot,
usage=_agent_run_usage(event.data.usage),
)
]
raise TypeError(f"unsupported agent backend run event: {type(event).__name__}")

View File

@@ -8,6 +8,25 @@ from clients.agent_backend.client import AgentBackendRunClient, DifyAgentBackend
from clients.agent_backend.fake_client import FakeAgentBackendRunClient, FakeAgentBackendScenario
def create_agent_backend_client(
*,
base_url: str,
api_token: str | None = None,
stream_timeout: float = 30,
timeout: float = 30.0,
binding_file_download_timeout: float = 240,
) -> Client:
api_token = api_token.strip() if api_token else None
headers = {"Authorization": f"Bearer {api_token}"} if api_token else None
return Client(
base_url=base_url,
stream_timeout=stream_timeout,
timeout=timeout,
binding_file_download_timeout=binding_file_download_timeout,
headers=headers,
)
def create_agent_backend_run_client(
*,
base_url: str | None = None,
@@ -16,18 +35,17 @@ def create_agent_backend_run_client(
fake_scenario: str | FakeAgentBackendScenario = FakeAgentBackendScenario.SUCCESS,
stream_read_timeout_seconds: float = 30,
stream_max_reconnects: int = 3,
stream_run_timeout_seconds: float = 1200,
) -> AgentBackendRunClient:
"""Create the API-side run client without hiding the ``dify-agent`` protocol."""
if use_fake:
return FakeAgentBackendRunClient(scenario=FakeAgentBackendScenario(fake_scenario))
if base_url is None:
raise ValueError("base_url is required when creating a real Agent backend client")
headers: dict[str, str] = {}
if api_token:
headers["Authorization"] = f"Bearer {api_token}"
return DifyAgentBackendRunClient(
Client(base_url=base_url, stream_timeout=stream_read_timeout_seconds, headers=headers),
create_agent_backend_client(
base_url=base_url,
api_token=api_token,
stream_timeout=stream_read_timeout_seconds,
),
stream_max_reconnects=stream_max_reconnects,
stream_timeout_seconds=stream_run_timeout_seconds,
)

View File

@@ -18,6 +18,8 @@ from dify_agent.protocol import (
CreateRunRequest,
CreateRunResponse,
DeferredToolCallPayload,
RunCancelledEvent,
RunCancelledEventData,
RunEvent,
RunFailedEvent,
RunFailedEventData,
@@ -69,6 +71,28 @@ class FakeAgentBackendRunClient:
del request
return CancelRunResponse(run_id=run_id, status="cancelled")
def cancel_run_and_wait(
self,
run_id: str,
request: CancelRunRequest | None = None,
*,
after: str | None = None,
) -> RunCancelledEvent:
"""Return a deterministic cleanup-complete cancellation event."""
del after
request = request or CancelRunRequest()
_ = self.cancel_run(run_id, request)
return RunCancelledEvent(
id="cancel-0",
run_id=run_id,
created_at=_FIXED_TIME,
data=RunCancelledEventData(
reason=request.reason,
message=request.message,
session_snapshot=CompositorSessionSnapshot(layers=[]),
),
)
def stream_events(
self,
run_id: str,
@@ -133,7 +157,11 @@ class FakeAgentBackendRunClient:
id="2-0",
run_id=run_id,
created_at=_FIXED_TIME,
data=RunFailedEventData(error="fake failure", reason="unit_test"),
data=RunFailedEventData(
error="fake failure",
reason="unit_test",
session_snapshot=CompositorSessionSnapshot(layers=[]),
),
),
)
case FakeAgentBackendScenario.PAUSED:

View File

@@ -25,11 +25,9 @@ from dify_agent.layers.dify_core_tools import DIFY_CORE_TOOLS_LAYER_TYPE_ID, Dif
from dify_agent.layers.dify_plugin import (
DIFY_PLUGIN_LLM_LAYER_TYPE_ID,
DIFY_PLUGIN_TOOLS_LAYER_TYPE_ID,
DifyPluginCredentialValue,
DifyPluginLLMLayerConfig,
DifyPluginToolsLayerConfig,
)
from dify_agent.layers.drive import DIFY_DRIVE_LAYER_TYPE_ID, DifyDriveLayerConfig
from dify_agent.layers.execution_context import (
DIFY_EXECUTION_CONTEXT_LAYER_TYPE_ID,
DifyExecutionContextLayerConfig,
@@ -38,6 +36,11 @@ from dify_agent.layers.knowledge import DIFY_KNOWLEDGE_BASE_LAYER_TYPE_ID, DifyK
from dify_agent.layers.output import DIFY_OUTPUT_LAYER_TYPE_ID, DifyOutputLayerConfig
from dify_agent.layers.runtime import DIFY_RUNTIME_LAYER_TYPE_ID, DifyRuntimeLayerConfig
from dify_agent.layers.shell import DIFY_SHELL_LAYER_TYPE_ID, DifyShellLayerConfig
from dify_agent.layers.user_prompt import (
DIFY_USER_PROMPT_LAYER_TYPE_ID,
DifyUserPromptFileConfig,
DifyUserPromptLayerConfig,
)
from dify_agent.protocol import (
DIFY_AGENT_HISTORY_LAYER_ID,
DIFY_AGENT_MODEL_LAYER_ID,
@@ -57,7 +60,6 @@ AGENT_APP_USER_PROMPT_LAYER_ID = "agent_app_user_prompt"
DIFY_EXECUTION_CONTEXT_LAYER_ID = "execution_context"
DIFY_RUNTIME_LAYER_ID = "runtime"
DIFY_CONFIG_LAYER_ID = "config"
DIFY_DRIVE_LAYER_ID = "drive"
DIFY_PLUGIN_TOOLS_LAYER_ID = "tools"
DIFY_CORE_TOOLS_LAYER_ID = "core_tools"
DIFY_KNOWLEDGE_BASE_LAYER_ID = "knowledge"
@@ -73,24 +75,10 @@ def _shell_layer_deps() -> dict[str, str]:
}
def _drive_layer_deps() -> dict[str, str]:
return {"shell": DIFY_SHELL_LAYER_ID}
def _config_layer_deps() -> dict[str, str]:
return {"shell": DIFY_SHELL_LAYER_ID}
def _shell_config_with_drive_ref(
shell_config: DifyShellLayerConfig | None,
drive_config: DifyDriveLayerConfig | None,
) -> DifyShellLayerConfig:
config = shell_config or DifyShellLayerConfig()
if drive_config is None:
return config
return config.model_copy(update={"agent_stub_drive_ref": drive_config.drive_ref})
def _markdown_backtick_fence(text: str) -> str:
"""Choose a fence that will not terminate inside the prompt body."""
longest_backtick_run = max((len(match.group(0)) for match in re.finditer(r"`+", text)), default=0)
@@ -150,8 +138,8 @@ class AgentBackendModelConfig(BaseModel):
plugin_id: str
model_provider: str
model: str
credentials: dict[str, DifyPluginCredentialValue] = Field(default_factory=dict)
model_settings: dict[str, JsonValue] = Field(default_factory=dict)
context_window_tokens: int | None = Field(default=None, gt=0)
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
@@ -225,9 +213,6 @@ class AgentBackendWorkflowNodeRunInput(BaseModel):
core_tools: DifyCoreToolsLayerConfig | None = None
knowledge: DifyKnowledgeBaseLayerConfig | None = None
config_layer_config: DifyConfigLayerConfig | None = None
# Drive Skills & Files declaration (dify.drive) — an index the agent pulls
# through the back proxy, never inline content.
drive_config: DifyDriveLayerConfig | None = None
# Human-in-the-loop ask_human deferred tool (dify.ask_human). Present only when
# the Agent Soul configures human involvement; a deferred call ends the run and
# the workflow pauses via the existing HITL form mechanism (ENG-635).
@@ -266,6 +251,7 @@ class AgentBackendAgentAppRunInput(BaseModel):
execution_context: DifyExecutionContextLayerConfig
backend_binding_ref: str = Field(min_length=1)
user_prompt: str
user_files: list[DifyUserPromptFileConfig] = Field(default_factory=list)
agent_soul_prompt: str | None = None
agent_config_version_kind: AgentConfigVersionKind = "snapshot"
idempotency_key: str | None = None
@@ -274,9 +260,6 @@ class AgentBackendAgentAppRunInput(BaseModel):
core_tools: DifyCoreToolsLayerConfig | None = None
knowledge: DifyKnowledgeBaseLayerConfig | None = None
config_layer_config: DifyConfigLayerConfig | None = None
# Drive Skills & Files declaration (dify.drive) — an index the agent pulls
# through the back proxy, never inline content.
drive_config: DifyDriveLayerConfig | None = None
# Human-in-the-loop ask_human deferred tool (dify.ask_human). Present only when
# the Agent Soul configures human involvement (ENG-635).
ask_human_config: DifyAskHumanLayerConfig | None = None
@@ -308,7 +291,7 @@ class AgentBackendRunRequestBuilder:
"""Build an Agent App conversation-turn run request.
Layer graph: optional Agent Soul system prompt → user prompt →
execution context → optional shell / config / drive / history
execution context → optional shell / config / history
(multi-turn) → LLM → optional plugin-direct tools / core-routed tools /
knowledge search / ask_human / structured output. Mirrors the
workflow-node layer ordering minus the workflow-job / previous-node
@@ -333,9 +316,9 @@ class AgentBackendRunRequestBuilder:
[
RunLayerSpec(
name=AGENT_APP_USER_PROMPT_LAYER_ID,
type=PLAIN_PROMPT_LAYER_TYPE_ID,
type=DIFY_USER_PROMPT_LAYER_TYPE_ID,
metadata={**run_input.metadata, "origin": "agent_app_user_prompt"},
config=PromptLayerConfig(user=run_input.user_prompt),
config=DifyUserPromptLayerConfig(text=run_input.user_prompt, files=run_input.user_files),
),
RunLayerSpec(
name=DIFY_EXECUTION_CONTEXT_LAYER_ID,
@@ -346,9 +329,7 @@ class AgentBackendRunRequestBuilder:
]
)
include_shell = (
run_input.include_shell or run_input.config_layer_config is not None or run_input.drive_config is not None
)
include_shell = run_input.include_shell or run_input.config_layer_config is not None
if include_shell:
layers.append(
RunLayerSpec(
@@ -358,16 +339,15 @@ class AgentBackendRunRequestBuilder:
config=DifyRuntimeLayerConfig(backend_binding_ref=run_input.backend_binding_ref),
)
)
# Sandboxed bash workspace (dify.shell). It enters before config/drive
# so eager pulls materialize content in the same filesystem used by
# model commands.
# Sandboxed bash workspace (dify.shell). It enters before config so
# eager pulls materialize content in the same filesystem used by model commands.
layers.append(
RunLayerSpec(
name=DIFY_SHELL_LAYER_ID,
type=DIFY_SHELL_LAYER_TYPE_ID,
deps=_shell_layer_deps(),
metadata=run_input.metadata,
config=_shell_config_with_drive_ref(run_input.shell_config, run_input.drive_config),
config=run_input.shell_config or DifyShellLayerConfig(),
)
)
@@ -382,19 +362,6 @@ class AgentBackendRunRequestBuilder:
)
)
if run_input.drive_config is not None:
# Drive Skills & Files declaration (dify.drive): the catalog plus
# prompt-mentioned entries eagerly pulled through the shell layer.
layers.append(
RunLayerSpec(
name=DIFY_DRIVE_LAYER_ID,
type=DIFY_DRIVE_LAYER_TYPE_ID,
deps=_drive_layer_deps(),
metadata=run_input.metadata,
config=run_input.drive_config,
)
)
if run_input.include_history:
layers.append(
RunLayerSpec(
@@ -414,8 +381,8 @@ class AgentBackendRunRequestBuilder:
plugin_id=run_input.model.plugin_id,
model_provider=run_input.model.model_provider,
model=run_input.model.model,
credentials=run_input.model.credentials,
model_settings=_agent_model_settings(run_input.model.model_settings),
context_window_tokens=run_input.model.context_window_tokens,
),
)
)
@@ -496,7 +463,7 @@ class AgentBackendRunRequestBuilder:
"""Build a workflow Agent Node run request without defining another wire schema.
Layer graph mirrors the workflow surface: prompts → execution context →
optional shell / config / drive / history → LLM → optional
optional shell / config / history → LLM → optional
plugin-direct tools / core-routed tools / knowledge search /
ask_human / structured output.
"""
@@ -538,9 +505,7 @@ class AgentBackendRunRequestBuilder:
]
)
include_shell = (
run_input.include_shell or run_input.config_layer_config is not None or run_input.drive_config is not None
)
include_shell = run_input.include_shell or run_input.config_layer_config is not None
if include_shell:
layers.append(
RunLayerSpec(
@@ -550,16 +515,15 @@ class AgentBackendRunRequestBuilder:
config=DifyRuntimeLayerConfig(backend_binding_ref=run_input.backend_binding_ref),
)
)
# Sandboxed bash workspace (dify.shell). It enters before drive so
# drive can materialize mentioned targets with `dify-agent drive pull`
# in the same shell-visible filesystem used by model commands.
# Sandboxed bash workspace (dify.shell). It enters before config so
# eager pulls materialize content in the same filesystem used by model commands.
layers.append(
RunLayerSpec(
name=DIFY_SHELL_LAYER_ID,
type=DIFY_SHELL_LAYER_TYPE_ID,
deps=_shell_layer_deps(),
metadata=run_input.metadata,
config=_shell_config_with_drive_ref(run_input.shell_config, run_input.drive_config),
config=run_input.shell_config or DifyShellLayerConfig(),
)
)
@@ -574,19 +538,6 @@ class AgentBackendRunRequestBuilder:
)
)
if run_input.drive_config is not None:
# Drive Skills & Files declaration (dify.drive): the catalog plus
# prompt-mentioned entries eagerly pulled through the shell layer.
layers.append(
RunLayerSpec(
name=DIFY_DRIVE_LAYER_ID,
type=DIFY_DRIVE_LAYER_TYPE_ID,
deps=_drive_layer_deps(),
metadata=run_input.metadata,
config=run_input.drive_config,
)
)
if run_input.include_history:
layers.append(
RunLayerSpec(
@@ -607,8 +558,8 @@ class AgentBackendRunRequestBuilder:
plugin_id=run_input.model.plugin_id,
model_provider=run_input.model.model_provider,
model=run_input.model.model,
credentials=run_input.model.credentials,
model_settings=_agent_model_settings(run_input.model.model_settings),
context_window_tokens=run_input.model.context_window_tokens,
),
),
]
@@ -687,7 +638,7 @@ class AgentBackendRunRequestBuilder:
)
_SENSITIVE_KEY_PARTS = ("secret", "credential", "token", "password", "api_key")
_SENSITIVE_KEY_PARTS = ("secret", "credential", "token", "password", "api_key", "base64_data")
def redact_for_agent_backend_log(value: object) -> object:
@@ -696,9 +647,10 @@ def redact_for_agent_backend_log(value: object) -> object:
return redact_for_agent_backend_log(value.model_dump(mode="json", warnings=False))
if isinstance(value, dict):
redacted: dict[object, object] = {}
is_multimodal_file = value.get("type") == "image" and "filename" in value and "mime_type" in value
for key, item in value.items():
key_text = str(key).lower()
if any(part in key_text for part in _SENSITIVE_KEY_PARTS):
if any(part in key_text for part in _SENSITIVE_KEY_PARTS) or (is_multimodal_file and key_text == "url"):
redacted[key] = "[REDACTED]"
else:
redacted[key] = redact_for_agent_backend_log(item)

View File

@@ -22,7 +22,13 @@ from .plugin import (
setup_system_trigger_oauth_client,
transform_datasource_credentials,
)
from .rbac import migrate_dataset_permissions_to_rbac, migrate_member_roles_to_rbac
from .rbac import (
migrate_agent_permissions_to_rbac,
migrate_dataset_permissions_to_rbac,
migrate_member_roles_to_rbac,
migrate_only_me_resource_whitelist_scopes_to_automatic_include,
migrate_resource_whitelist_scopes_to_automatic_include,
)
from .retention import (
archive_workflow_runs,
archive_workflow_runs_plan,
@@ -76,12 +82,15 @@ __all__ = [
"install_plugins",
"install_rag_pipeline_plugins",
"legacy_model_types",
"migrate_agent_permissions_to_rbac",
"migrate_annotation_vector_database",
"migrate_data_for_plugin",
"migrate_dataset_permissions_to_rbac",
"migrate_knowledge_vector_database",
"migrate_member_roles_to_rbac",
"migrate_only_me_resource_whitelist_scopes_to_automatic_include",
"migrate_oss",
"migrate_resource_whitelist_scopes_to_automatic_include",
"migration_data_wizard",
"old_metadata_migration",
"remove_orphaned_files_on_storage",

View File

@@ -8,6 +8,7 @@ from constants.languages import languages
from extensions.ext_database import db
from libs.helper import email as email_validate
from libs.password import hash_password, password_pattern, valid_password
from services.account_email import normalize_email
from services.account_service import AccountService, RegisterService, TenantService
@@ -20,7 +21,7 @@ def reset_password(email, new_password, password_confirm):
Reset password of owner account
Only available in SELF_HOSTED mode
"""
if str(new_password).strip() != str(password_confirm).strip():
if new_password.strip() != password_confirm.strip():
click.echo(click.style("Passwords do not match.", fg="red"))
return
normalized_email = email.strip().lower()
@@ -33,7 +34,7 @@ def reset_password(email, new_password, password_confirm):
try:
valid_password(new_password)
except:
except ValueError:
click.echo(click.style(f"Invalid password. Must match {password_pattern}", fg="red"))
return
@@ -62,7 +63,7 @@ def reset_email(email, new_email, email_confirm):
Replace account email
:return:
"""
if str(new_email).strip() != str(email_confirm).strip():
if new_email.strip() != email_confirm.strip():
click.echo(click.style("New emails do not match.", fg="red"))
return
normalized_new_email = new_email.strip().lower()
@@ -75,13 +76,14 @@ def reset_email(email, new_email, email_confirm):
try:
email_validate(normalized_new_email)
except:
except ValueError:
click.echo(click.style(f"Invalid email: {new_email}", fg="red"))
return
with Session(db.engine) as session:
account = session.merge(account)
account.email = normalized_new_email
account.normalized_email = normalize_email(normalized_new_email)
session.commit()
click.echo(click.style("Email updated successfully.", fg="green"))

View File

@@ -7,7 +7,11 @@ from typing import cast
import click
from commands.rbac import migrate_dataset_permissions_to_rbac
from commands.rbac import (
migrate_dataset_permissions_to_rbac,
migrate_only_me_resource_whitelist_scopes_to_automatic_include,
migrate_resource_whitelist_scopes_to_automatic_include, # noqa: F401
)
from extensions.ext_database import db
from graphon.model_runtime.entities.model_entities import ModelType
from services.legacy_model_type_migration import (
@@ -179,3 +183,4 @@ def legacy_model_types(
data_migrate.add_command(legacy_model_types)
data_migrate.add_command(migrate_dataset_permissions_to_rbac)
data_migrate.add_command(migrate_only_me_resource_whitelist_scopes_to_automatic_include)

View File

@@ -32,7 +32,7 @@ logger = logging.getLogger(__name__)
@click.command("setup-system-tool-oauth-client", help="Setup system tool oauth client.")
@click.option("--provider", prompt=True, help="Provider name")
@click.option("--client-params", prompt=True, help="Client Params")
def setup_system_tool_oauth_client(provider, client_params):
def setup_system_tool_oauth_client(provider: str, client_params: str):
"""
Setup system tool oauth client
"""
@@ -79,7 +79,7 @@ def setup_system_tool_oauth_client(provider, client_params):
@click.command("setup-system-trigger-oauth-client", help="Setup system trigger oauth client.")
@click.option("--provider", prompt=True, help="Provider name")
@click.option("--client-params", prompt=True, help="Client Params")
def setup_system_trigger_oauth_client(provider, client_params):
def setup_system_trigger_oauth_client(provider: str, client_params: str):
"""
Setup system trigger oauth client
"""
@@ -129,7 +129,7 @@ def setup_system_trigger_oauth_client(provider, client_params):
@click.command("setup-datasource-oauth-client", help="Setup datasource oauth client.")
@click.option("--provider", prompt=True, help="Provider name")
@click.option("--client-params", prompt=True, help="Client Params")
def setup_datasource_oauth_client(provider, client_params):
def setup_datasource_oauth_client(provider: str, client_params: str):
"""
Setup datasource oauth client
"""

View File

@@ -3,6 +3,8 @@ from __future__ import annotations
import json
from collections.abc import Iterator
from concurrent.futures import ThreadPoolExecutor, as_completed
from dataclasses import dataclass
from enum import StrEnum
import click
from sqlalchemy import select
@@ -11,10 +13,33 @@ from sqlalchemy.orm import Session
from configs import dify_config
from core.db.session_factory import session_factory
from core.rbac import RBACResourceWhitelistScope
from models import Dataset, DatasetPermission, DatasetPermissionEnum, TenantAccountJoin, TenantAccountRole
from services.enterprise.rbac_service import ListOption, RBACService, ReplaceMemberBindings, ReplaceUserAccessPolicies
from models import (
Agent,
AgentStatus,
App,
Dataset,
DatasetPermission,
DatasetPermissionEnum,
Tenant,
TenantAccountJoin,
TenantAccountRole,
)
from services.enterprise.rbac_service import (
LegacyAgentRoleMigration,
ListOption,
RBACResourceType,
RBACService,
ReplaceMemberBindings,
ReplaceUserAccessPolicies,
)
_RBAC_DEFAULT_ACCESS_POLICY_ID = "default"
_RBAC_RESOURCE_ACCESS_POLICY_BATCH_SIZE = 500
_AGENT_MIGRATION_STATE_LABEL = {
False: "would change",
True: "changed",
}
_LEGACY_ROLE_TO_BUILTIN_TAG = {
TenantAccountRole.OWNER.value: "owner",
@@ -66,6 +91,88 @@ def _resolve_builtin_role_id(tenant_id: str, operator_account_id: str, legacy_ro
return _resolve_builtin_role_ids(tenant_id, operator_account_id)[legacy_role]
def _iter_tenant_ids(tenant_id: str | None, *, batch_size: int) -> Iterator[str]:
if tenant_id:
yield tenant_id
return
last_id: str | None = None
while True:
with session_factory.create_session() as session:
stmt = select(Tenant.id).order_by(Tenant.id.asc()).limit(batch_size)
if last_id is not None:
stmt = stmt.where(Tenant.id > last_id)
rows = session.execute(stmt).scalars().all()
if not rows:
return
for row in rows:
yield str(row)
last_id = str(rows[-1])
def _emit_agent_migration_event(payload: dict[str, object]) -> None:
click.echo(json.dumps(payload, sort_keys=True))
@dataclass(frozen=True)
class _AgentMigrationEventKind:
event_stem: str
include_tenant_id: bool = True
@property
def skipped(self) -> str:
return f"{self.event_stem}_skipped"
@property
def failed(self) -> str:
return f"{self.event_stem}_failed"
def outcome(self, *, apply: bool) -> str:
return f"{self.event_stem}_applied" if apply else f"{self.event_stem}_proposed_change"
_AGENT_ROLE_MIGRATION_EVENT_KIND = _AgentMigrationEventKind(event_stem="agent_manage_role_migration")
_AGENT_ROLE_TEMPLATE_MIGRATION_EVENT_KIND = _AgentMigrationEventKind(
event_stem="agent_manage_role_template_migration", include_tenant_id=False
)
_AGENT_ACCESS_BOOTSTRAP_EVENT_KIND = _AgentMigrationEventKind(event_stem="agent_access_bootstrap")
_AGENT_BACKING_APP_SPECIFIC_WHITELIST_EVENT = "agent_backing_app_has_specific_whitelist"
_AGENT_ACCESS_BOOTSTRAP_MEMBER_SOURCE = "workspace_members"
class _AgentAccessBootstrapReason(StrEnum):
ALREADY_INITIALIZED = "already_initialized"
MISSING_WHITELIST_SCOPE = "missing_whitelist_scope"
NO_CREATOR = "no_creator"
def _agent_manage_role_event(
tenant_id: str | None,
entry: LegacyAgentRoleMigration,
*,
apply: bool,
kind: _AgentMigrationEventKind,
) -> dict[str, object]:
base: dict[str, object] = {
"dry_run": not apply,
"role_id": entry.role_id,
"role_name": entry.role_name,
}
if kind.include_tenant_id:
base["tenant_id"] = tenant_id
if entry.skipped:
return {**base, "event": kind.skipped, "reason": entry.skipped}
return {
**base,
"event": kind.outcome(apply=apply),
"after": {
"added_keys": entry.added_keys,
"removed_keys": entry.removed_keys,
"bound_policies": entry.bound_policies,
},
}
def _iter_tenant_member_batches(
tenant_id: str | None,
*,
@@ -310,6 +417,499 @@ def _emit_dataset_permission_migration_event(payload: dict[str, object]) -> None
click.echo(json.dumps(payload, sort_keys=True))
def _emit_resource_whitelist_scope_migration_event(payload: dict[str, object]) -> None:
click.echo(json.dumps(payload, sort_keys=True))
def _normalize_rbac_whitelist_scope(scope: object) -> RBACResourceWhitelistScope | None:
if scope is None:
return None
try:
return RBACResourceWhitelistScope(str(scope))
except ValueError as exc:
raise ValueError(f"Unsupported RBAC whitelist scope: {scope}") from exc
def _owner_account_id(tenant_id: str, *, session: Session) -> str:
account_id = session.scalar(
select(TenantAccountJoin.account_id)
.where(TenantAccountJoin.tenant_id == tenant_id, TenantAccountJoin.role == TenantAccountRole.OWNER)
.order_by(TenantAccountJoin.id.asc())
.limit(1)
)
if not account_id:
raise ValueError(f"Workspace owner not found for tenant={tenant_id}")
return str(account_id)
def _workspace_member_account_id_batches(tenant_id: str, batch_size: int) -> Iterator[list[str]]:
last_join_id: str | None = None
while True:
with session_factory.create_session() as session:
stmt = (
select(TenantAccountJoin.id, TenantAccountJoin.account_id)
.where(TenantAccountJoin.tenant_id == tenant_id)
.order_by(TenantAccountJoin.id.asc())
.limit(batch_size)
)
if last_join_id:
stmt = stmt.where(TenantAccountJoin.id > last_join_id)
rows = list(session.execute(stmt).all())
if not rows:
return
yield [str(row.account_id) for row in rows]
last_join_id = str(rows[-1].id)
def _replace_resource_whitelist(
resource_type: str,
*,
tenant_id: str,
operator_account_id: str,
resource_id: str,
automatic_include_workspace_members: bool,
) -> None:
payload = ReplaceMemberBindings(automatic_include_workspace_members=automatic_include_workspace_members)
if resource_type == "app":
RBACService.AppAccess.replace_whitelist(
tenant_id=tenant_id,
account_id=operator_account_id,
app_id=resource_id,
payload=payload,
)
return
RBACService.DatasetAccess.replace_whitelist(
tenant_id=tenant_id,
account_id=operator_account_id,
dataset_id=resource_id,
payload=payload,
)
def _replace_resource_default_access_policies(
resource_type: str,
*,
tenant_id: str,
operator_account_id: str,
resource_id: str,
account_ids: list[str],
) -> None:
if not account_ids:
return
payload = ReplaceUserAccessPolicies(
access_policy_ids=[_RBAC_DEFAULT_ACCESS_POLICY_ID],
account_ids=account_ids,
)
if resource_type == "app":
RBACService.AppAccess.replace_user_access_policies(
tenant_id=tenant_id,
account_id=operator_account_id,
app_id=resource_id,
target_account_id=None,
payload=payload,
)
return
RBACService.DatasetAccess.replace_user_access_policies(
tenant_id=tenant_id,
account_id=operator_account_id,
dataset_id=resource_id,
target_account_id=None,
payload=payload,
)
def _resource_legacy_whitelist_config(
resource_type: str,
*,
tenant_id: str,
operator_account_id: str,
resource_id: str,
):
if resource_type == "app":
return RBACService.AppAccess.legacy_whitelist_config(
tenant_id=tenant_id,
account_id=operator_account_id,
app_id=resource_id,
)
return RBACService.DatasetAccess.legacy_whitelist_config(
tenant_id=tenant_id,
account_id=operator_account_id,
dataset_id=resource_id,
)
def _iter_rbac_resource_rows(
resource_type: str,
*,
tenant_id: str | None,
resource_id: str | None,
batch_size: int,
) -> Iterator[tuple[str, str, str, str | None]]:
model = App if resource_type == "app" else Dataset
last_resource_id: str | None = None
while True:
with session_factory.create_session() as session:
stmt = select(model.id, model.tenant_id, model.maintainer).order_by(model.id.asc()).limit(batch_size)
if tenant_id:
stmt = stmt.where(model.tenant_id == tenant_id)
if resource_id:
stmt = stmt.where(model.id == resource_id)
if last_resource_id:
stmt = stmt.where(model.id > last_resource_id)
rows = list(session.execute(stmt).all())
if not rows:
return
for row in rows:
yield resource_type, str(row.tenant_id), str(row.id), str(row.maintainer) if row.maintainer else None
last_resource_id = str(rows[-1].id)
if resource_id:
return
def _iter_selected_rbac_resource_rows(
resource_type: str,
*,
tenant_id: str | None,
resource_id: str | None,
batch_size: int,
) -> Iterator[tuple[str, str, str, str | None]]:
if resource_type in {"app", "all"}:
yield from _iter_rbac_resource_rows(
"app",
tenant_id=tenant_id,
resource_id=resource_id if resource_type == "app" else None,
batch_size=batch_size,
)
if resource_type in {"dataset", "all"}:
yield from _iter_rbac_resource_rows(
"dataset",
tenant_id=tenant_id,
resource_id=resource_id if resource_type == "dataset" else None,
batch_size=batch_size,
)
@click.command(
"rbac-migrate-resource-whitelist-scopes",
help=(
"Migrate RBAC app/dataset whitelist scope values to automatic_include_workspace_members. "
"Old scope all becomes true; specific and only_me become false."
),
)
@click.option("--tenant-id", help="Only migrate resources in a single workspace.")
@click.option(
"--resource-type",
type=click.Choice(["app", "dataset", "all"]),
default="all",
show_default=True,
help="Resource type to migrate.",
)
@click.option("--resource-id", help="Only migrate a single resource. Requires --resource-type app or dataset.")
@click.option("--batch-size", default=500, show_default=True, type=click.IntRange(min=1))
@click.option(
"--member-batch-size",
default=_RBAC_RESOURCE_ACCESS_POLICY_BATCH_SIZE,
show_default=True,
type=click.IntRange(min=1),
help="Workspace members written per default-policy call when migrating old scope all.",
)
@click.option(
"--dry-run/--apply",
default=True,
show_default=True,
help="Preview the migration without writing RBAC bindings. Use --apply to write changes.",
)
def migrate_resource_whitelist_scopes_to_automatic_include(
tenant_id: str | None,
resource_type: str,
resource_id: str | None,
batch_size: int,
member_batch_size: int,
dry_run: bool,
) -> None:
"""Backfill RBAC app/dataset automatic workspace-member inclusion from old RBAC scope."""
if resource_id and resource_type == "all":
raise click.BadParameter("--resource-id requires --resource-type app or dataset", param_hint="--resource-id")
click.echo(click.style("Starting RBAC resource whitelist scope migration.", fg="green"))
scanned_count = 0
migrated_count = 0
member_policy_batch_count = 0
owner_account_ids_by_tenant_id: dict[str, str] = {}
for (
current_resource_type,
workspace_id,
current_resource_id,
maintainer_account_id,
) in _iter_selected_rbac_resource_rows(
resource_type,
tenant_id=tenant_id,
resource_id=resource_id,
batch_size=batch_size,
):
scanned_count += 1
with session_factory.create_session() as session:
operator_account_id = maintainer_account_id or owner_account_ids_by_tenant_id.get(workspace_id)
if not operator_account_id:
operator_account_id = _owner_account_id(workspace_id, session=session)
owner_account_ids_by_tenant_id[workspace_id] = operator_account_id
legacy_config = _resource_legacy_whitelist_config(
current_resource_type,
tenant_id=workspace_id,
operator_account_id=operator_account_id,
resource_id=current_resource_id,
)
scope = _normalize_rbac_whitelist_scope(legacy_config.rbac_whitelist_scope)
if scope is None:
_emit_resource_whitelist_scope_migration_event(
{
"event": "resource_whitelist_scope_migration_skipped",
"reason": "missing_legacy_scope",
"dry_run": dry_run,
"tenant_id": workspace_id,
"resource_type": current_resource_type,
"resource_id": current_resource_id,
}
)
continue
automatic_include_workspace_members = scope is RBACResourceWhitelistScope.ALL
if scope is RBACResourceWhitelistScope.SPECIFIC:
member_account_ids = sorted(set(legacy_config.account_ids))
member_source = "legacy_specific_members"
elif scope is RBACResourceWhitelistScope.ONLY_ME:
member_account_ids = [maintainer_account_id] if maintainer_account_id else []
member_source = "resource_maintainer"
else:
member_account_ids = []
member_source = "workspace_members"
_emit_resource_whitelist_scope_migration_event(
{
"event": "resource_whitelist_scope_migration_proposed_change",
"dry_run": dry_run,
"tenant_id": workspace_id,
"operator_account_id": operator_account_id,
"resource_type": current_resource_type,
"resource_id": current_resource_id,
"before": {
"rbac_whitelist_scope": scope.value,
"legacy_account_ids": sorted(set(legacy_config.account_ids)),
},
"after": {
"automatic_include_workspace_members": automatic_include_workspace_members,
"default_policy_member_source": member_source,
"default_policy_account_ids": member_account_ids,
},
}
)
if dry_run:
continue
_replace_resource_whitelist(
current_resource_type,
tenant_id=workspace_id,
operator_account_id=operator_account_id,
resource_id=current_resource_id,
automatic_include_workspace_members=automatic_include_workspace_members,
)
migrated_count += 1
if scope is RBACResourceWhitelistScope.ALL:
for batch in _workspace_member_account_id_batches(workspace_id, member_batch_size):
_replace_resource_default_access_policies(
current_resource_type,
tenant_id=workspace_id,
operator_account_id=operator_account_id,
resource_id=current_resource_id,
account_ids=batch,
)
member_policy_batch_count += 1
else:
_replace_resource_default_access_policies(
current_resource_type,
tenant_id=workspace_id,
operator_account_id=operator_account_id,
resource_id=current_resource_id,
account_ids=member_account_ids,
)
if member_account_ids:
member_policy_batch_count += 1
if scanned_count == 0:
click.echo(click.style("No RBAC resources found for migration.", fg="yellow"))
return
if dry_run:
click.echo(
click.style(
f"Dry run completed. Scanned {scanned_count} RBAC resources. No RBAC bindings were written.",
fg="yellow",
)
)
else:
click.echo(
click.style(
"RBAC resource whitelist scope migration completed. "
f"Scanned {scanned_count} resources, migrated {migrated_count}, "
f"wrote {member_policy_batch_count} default-policy batches.",
fg="green",
)
)
@click.command(
"rbac-migrate-resource-whitelist-scopes",
help=(
"Migrate RBAC app/dataset whitelist configs whose old scope is only_me to "
"automatic_include_workspace_members=true and sync workspace members into the whitelist."
),
)
@click.option("--tenant-id", help="Only migrate resources in a single workspace.")
@click.option(
"--resource-type",
type=click.Choice(["app", "dataset", "all"]),
default="all",
show_default=True,
help="Resource type to migrate.",
)
@click.option("--resource-id", help="Only migrate a single resource. Requires --resource-type app or dataset.")
@click.option("--batch-size", default=500, show_default=True, type=click.IntRange(min=1))
@click.option(
"--member-batch-size",
default=_RBAC_RESOURCE_ACCESS_POLICY_BATCH_SIZE,
show_default=True,
type=click.IntRange(min=1),
help="Workspace members written per default-policy call.",
)
@click.option(
"--dry-run/--apply",
default=True,
show_default=True,
help="Preview the migration without writing RBAC bindings. Use --apply to write changes.",
)
def migrate_only_me_resource_whitelist_scopes_to_automatic_include(
tenant_id: str | None,
resource_type: str,
resource_id: str | None,
batch_size: int,
member_batch_size: int,
dry_run: bool,
) -> None:
"""Backfill automatic workspace-member inclusion for old RBAC only_me resource scopes."""
if resource_id and resource_type == "all":
raise click.BadParameter("--resource-id requires --resource-type app or dataset", param_hint="--resource-id")
click.echo(click.style("Starting RBAC only_me resource whitelist scope migration.", fg="green"))
scanned_count = 0
only_me_count = 0
migrated_count = 0
member_policy_batch_count = 0
owner_account_ids_by_tenant_id: dict[str, str] = {}
for (
current_resource_type,
workspace_id,
current_resource_id,
maintainer_account_id,
) in _iter_selected_rbac_resource_rows(
resource_type,
tenant_id=tenant_id,
resource_id=resource_id,
batch_size=batch_size,
):
scanned_count += 1
with session_factory.create_session() as session:
operator_account_id = maintainer_account_id or owner_account_ids_by_tenant_id.get(workspace_id)
if not operator_account_id:
operator_account_id = _owner_account_id(workspace_id, session=session)
owner_account_ids_by_tenant_id[workspace_id] = operator_account_id
legacy_config = _resource_legacy_whitelist_config(
current_resource_type,
tenant_id=workspace_id,
operator_account_id=operator_account_id,
resource_id=current_resource_id,
)
scope = _normalize_rbac_whitelist_scope(legacy_config.rbac_whitelist_scope)
if scope is not RBACResourceWhitelistScope.ONLY_ME:
continue
only_me_count += 1
_emit_resource_whitelist_scope_migration_event(
{
"event": "only_me_resource_whitelist_scope_migration_proposed_change",
"dry_run": dry_run,
"tenant_id": workspace_id,
"operator_account_id": operator_account_id,
"resource_type": current_resource_type,
"resource_id": current_resource_id,
"before": {
"rbac_whitelist_scope": scope.value,
"legacy_account_ids": sorted(set(legacy_config.account_ids)),
},
"after": {
"automatic_include_workspace_members": True,
"default_policy_member_source": "workspace_members",
},
}
)
if dry_run:
continue
_replace_resource_whitelist(
current_resource_type,
tenant_id=workspace_id,
operator_account_id=operator_account_id,
resource_id=current_resource_id,
automatic_include_workspace_members=True,
)
migrated_count += 1
for batch in _workspace_member_account_id_batches(workspace_id, member_batch_size):
_replace_resource_default_access_policies(
current_resource_type,
tenant_id=workspace_id,
operator_account_id=operator_account_id,
resource_id=current_resource_id,
account_ids=batch,
)
member_policy_batch_count += 1
if scanned_count == 0:
click.echo(click.style("No RBAC resources found for migration.", fg="yellow"))
return
if dry_run:
click.echo(
click.style(
f"Dry run completed. Scanned {scanned_count} RBAC resources, found {only_me_count} only_me resources. "
"No RBAC bindings were written.",
fg="yellow",
)
)
else:
click.echo(
click.style(
"RBAC only_me resource whitelist scope migration completed. "
f"Scanned {scanned_count} resources, migrated {migrated_count}, "
f"wrote {member_policy_batch_count} default-policy batches.",
fg="green",
)
)
@click.command(
"rbac-migrate-dataset-permissions",
help=(
@@ -399,7 +999,9 @@ def migrate_dataset_permissions_to_rbac(
)
scanned_count += 1
replace_whitelist_payload = ReplaceMemberBindings(scope=scope)
replace_whitelist_payload = ReplaceMemberBindings(
automatic_include_workspace_members=scope is RBACResourceWhitelistScope.ALL
)
if dry_run:
_emit_dataset_permission_migration_event(
{
@@ -509,3 +1111,302 @@ def migrate_dataset_permissions_to_rbac(
fg="green",
)
)
_AgentRow = tuple[str, str | None, str | None]
def _iter_agent_row_batches(tenant_id: str, batch_size: int) -> Iterator[list[_AgentRow]]:
last_agent_id: str | None = None
while True:
with session_factory.create_session() as session:
stmt = (
select(Agent.id, Agent.created_by, Agent.backing_app_id)
.where(Agent.tenant_id == tenant_id, Agent.status != AgentStatus.ARCHIVED)
.order_by(Agent.id.asc())
.limit(batch_size)
)
if last_agent_id:
stmt = stmt.where(Agent.id > last_agent_id)
rows = list(session.execute(stmt).all())
if not rows:
return
yield [
(
str(row.id),
str(row.created_by) if row.created_by else None,
str(row.backing_app_id) if row.backing_app_id else None,
)
for row in rows
]
last_agent_id = str(rows[-1].id)
@dataclass
class _AgentAccessBootstrapCounts:
changed: int = 0
already_initialized: int = 0
@dataclass(frozen=True)
class _AgentAccessBootstrapOptions:
tenant_id: str
agent_id: str
creator_account_id: str | None
backing_app_id: str | None
operator_account_id: str
member_batch_size: int
apply: bool
initialized: bool
def _report_backing_app_specific_whitelist(options: _AgentAccessBootstrapOptions) -> None:
if not options.backing_app_id:
return
app_config = _resource_legacy_whitelist_config(
"app",
tenant_id=options.tenant_id,
operator_account_id=options.operator_account_id,
resource_id=options.backing_app_id,
)
if _normalize_rbac_whitelist_scope(app_config.rbac_whitelist_scope) is not RBACResourceWhitelistScope.SPECIFIC:
return
_emit_agent_migration_event(
{
"event": _AGENT_BACKING_APP_SPECIFIC_WHITELIST_EVENT,
"dry_run": not options.apply,
"tenant_id": options.tenant_id,
"agent_id": options.agent_id,
"app_id": options.backing_app_id,
"backing_app_account_ids": sorted(set(app_config.account_ids)),
}
)
def _write_agent_access_rows(options: _AgentAccessBootstrapOptions) -> None:
for batch in _workspace_member_account_id_batches(options.tenant_id, options.member_batch_size):
RBACService.AgentAccess.replace_user_access_policies(
tenant_id=options.tenant_id,
account_id=options.operator_account_id,
agent_id=options.agent_id,
target_account_id=None,
payload=ReplaceUserAccessPolicies(
access_policy_ids=[_RBAC_DEFAULT_ACCESS_POLICY_ID],
account_ids=batch,
),
)
if options.creator_account_id:
RBACService.AccessPolicies.sync_creator_access_policy_member_bindings(
tenant_id=options.tenant_id,
account_id=options.creator_account_id,
resource_type=RBACResourceType.AGENT,
resource_id=options.agent_id,
)
RBACService.AgentAccess.replace_whitelist(
tenant_id=options.tenant_id,
account_id=options.operator_account_id,
agent_id=options.agent_id,
payload=ReplaceMemberBindings(automatic_include_workspace_members=True),
)
def _emit_agent_access_bootstrap_skipped(
options: _AgentAccessBootstrapOptions,
reason: _AgentAccessBootstrapReason,
) -> None:
_emit_agent_migration_event(
{
"event": _AGENT_ACCESS_BOOTSTRAP_EVENT_KIND.skipped,
"reason": reason.value,
"dry_run": not options.apply,
"tenant_id": options.tenant_id,
"agent_id": options.agent_id,
"operator_account_id": options.operator_account_id,
}
)
def _agent_access_bootstrap_failure(
options: _AgentAccessBootstrapOptions,
exc: Exception,
) -> click.ClickException:
_emit_agent_migration_event(
{
"event": _AGENT_ACCESS_BOOTSTRAP_EVENT_KIND.failed,
"tenant_id": options.tenant_id,
"agent_id": options.agent_id,
"error": str(exc),
}
)
return click.ClickException(f"tenant {options.tenant_id} agent {options.agent_id}: {exc}")
def _bootstrap_agent_access(options: _AgentAccessBootstrapOptions, counts: _AgentAccessBootstrapCounts) -> None:
if options.initialized:
counts.already_initialized += 1
_emit_agent_access_bootstrap_skipped(options, _AgentAccessBootstrapReason.ALREADY_INITIALIZED)
return
config = RBACService.AgentAccess.legacy_whitelist_config(
tenant_id=options.tenant_id,
account_id=options.operator_account_id,
agent_id=options.agent_id,
)
try:
scope = _normalize_rbac_whitelist_scope(config.rbac_whitelist_scope)
except ValueError as exc:
raise _agent_access_bootstrap_failure(options, exc) from exc
if scope is None:
_emit_agent_access_bootstrap_skipped(options, _AgentAccessBootstrapReason.MISSING_WHITELIST_SCOPE)
return
account_ids = sorted(set(config.account_ids))
_report_backing_app_specific_whitelist(options)
if options.apply:
try:
_write_agent_access_rows(options)
except Exception as exc:
raise _agent_access_bootstrap_failure(options, exc) from exc
counts.changed += 1
event: dict[str, object] = {
"event": _AGENT_ACCESS_BOOTSTRAP_EVENT_KIND.outcome(apply=options.apply),
"dry_run": not options.apply,
"tenant_id": options.tenant_id,
"agent_id": options.agent_id,
"operator_account_id": options.operator_account_id,
"before": {"rbac_whitelist_scope": scope.value, "whitelist_account_ids": account_ids},
"after": {
"automatic_include_workspace_members": True,
"default_policy_member_source": _AGENT_ACCESS_BOOTSTRAP_MEMBER_SOURCE,
"creator_access_policy_synced": bool(options.creator_account_id),
},
}
if not options.creator_account_id:
event["reason"] = _AgentAccessBootstrapReason.NO_CREATOR.value
_emit_agent_migration_event(event)
def _bootstrap_tenant_agent_access(
tenant_id: str,
*,
agent_batch_size: int,
member_batch_size: int,
apply: bool,
counts: _AgentAccessBootstrapCounts,
) -> None:
owner_account_id: str | None = None
for rows in _iter_agent_row_batches(tenant_id, agent_batch_size):
initialized_agent_ids = set(
RBACService.Migrations.list_configured_agent_ids(tenant_id, [agent_id for agent_id, _, _ in rows])
)
for agent_id, creator_account_id, backing_app_id in rows:
if creator_account_id:
operator_account_id = creator_account_id
else:
if owner_account_id is None:
with session_factory.create_session() as session:
owner_account_id = _owner_account_id(tenant_id, session=session)
operator_account_id = owner_account_id
_bootstrap_agent_access(
_AgentAccessBootstrapOptions(
tenant_id=tenant_id,
agent_id=agent_id,
creator_account_id=creator_account_id,
backing_app_id=backing_app_id,
operator_account_id=operator_account_id,
member_batch_size=member_batch_size,
apply=apply,
initialized=agent_id in initialized_agent_ids,
),
counts,
)
@click.command(
"rbac-migrate-agent-permissions",
help=(
"Upgrade step for agent RBAC. Phase 1 asks the RBAC service to replace agent.manage on every "
"custom role with agent.create plus the agent.full_access binding. Phase 2 bootstraps the access "
"rows pre-existing agents never got, so they stay visible to workspace members. Agents that "
"already have a whitelist scope row are skipped. Dry run by default."
),
)
@click.option("--tenant-id", help="Only migrate a single workspace.")
@click.option(
"--batch-size",
default=500,
show_default=True,
type=click.IntRange(min=1),
help="Tenants fetched per database batch.",
)
@click.option(
"--agent-batch-size",
default=500,
show_default=True,
type=click.IntRange(min=1),
help="Agents fetched per database batch.",
)
@click.option(
"--member-batch-size",
default=_RBAC_RESOURCE_ACCESS_POLICY_BATCH_SIZE,
show_default=True,
type=click.IntRange(min=1),
help="Workspace members written per default-policy call when bootstrapping an agent.",
)
@click.option("--apply", is_flag=True, default=False, help="Write changes. Without it nothing is written.")
def migrate_agent_permissions_to_rbac(
tenant_id: str | None,
batch_size: int,
agent_batch_size: int,
member_batch_size: int,
apply: bool,
) -> None:
click.echo(click.style("Starting agent RBAC migration: custom roles holding agent.manage.", fg="green"))
tenant_count = 0
role_count = 0
skipped_count = 0
seen_template_ids: set[str] = set()
template_count = 0
agent_counts = _AgentAccessBootstrapCounts()
for workspace_id in _iter_tenant_ids(tenant_id, batch_size=batch_size):
tenant_count += 1
try:
report = RBACService.Migrations.migrate_agent_manage_roles(workspace_id, apply=apply)
except Exception as exc:
raise click.ClickException(f"tenant {workspace_id}: {exc}") from exc
for entry in report.roles:
role_count += 1
if entry.skipped:
skipped_count += 1
_emit_agent_migration_event(
_agent_manage_role_event(workspace_id, entry, apply=apply, kind=_AGENT_ROLE_MIGRATION_EVENT_KIND)
)
for template in report.role_templates:
if template.role_id in seen_template_ids:
continue
seen_template_ids.add(template.role_id)
template_count += 1
_emit_agent_migration_event(
_agent_manage_role_event(None, template, apply=apply, kind=_AGENT_ROLE_TEMPLATE_MIGRATION_EVENT_KIND)
)
_bootstrap_tenant_agent_access(
workspace_id,
agent_batch_size=agent_batch_size,
member_batch_size=member_batch_size,
apply=apply,
counts=agent_counts,
)
role_state = _AGENT_MIGRATION_STATE_LABEL[apply]
click.echo(
f"{tenant_count} tenant(s), {role_count} role(s) {role_state}, {skipped_count} skipped, "
f"{template_count} template(s) {role_state}, "
f"{agent_counts.changed} agent(s) {role_state}, "
f"{agent_counts.already_initialized} already initialised"
)
if not apply:
click.echo(click.style("Dry run: no changes written. Re-run with --apply.", fg="yellow"))

View File

@@ -10,6 +10,8 @@ import click
import sqlalchemy as sa
from sqlalchemy.orm import Session, sessionmaker
from configs import dify_config
from enums import CloudPlan, DeploymentEdition
from extensions.ext_database import db
from libs.datetime_utils import naive_utc_now
from services.clear_free_plan_tenant_expired_logs import ClearFreePlanTenantExpiredLogs
@@ -126,14 +128,12 @@ def _get_archive_candidate_tenant_ids_by_prefix(
def _filter_paid_workflow_archive_tenant_ids(tenant_ids: list[str]) -> tuple[list[str], list[str]]:
from configs import dify_config
from enums.cloud_plan import CloudPlan
from services.billing_service import BillingService
tenant_ids = sorted(set(tenant_ids))
if not tenant_ids:
return [], []
if not dify_config.BILLING_ENABLED:
if dify_config.DEPLOYMENT_EDITION != DeploymentEdition.CLOUD:
return tenant_ids, []
plans = BillingService.get_plan_bulk_with_cache(tenant_ids)
@@ -1462,7 +1462,7 @@ def cleanup_orphaned_draft_variables(
"--graceful-period",
default=21,
show_default=True,
help="Graceful period in days after subscription expiration, will be ignored when billing is disabled.",
help="Graceful period in days after subscription expiration; ignored outside the Cloud edition.",
)
@click.option("--dry-run", is_flag=True, default=False, help="Show messages logs would be cleaned without deleting")
def clean_expired_messages(
@@ -1514,8 +1514,8 @@ def clean_expired_messages(
if from_days_ago <= before_days:
raise click.UsageError("--from-days-ago must be greater than --before-days.")
# Create policy based on billing configuration
# NOTE: graceful_period will be ignored when billing is disabled.
# Create the policy for the configured deployment edition.
# NOTE: graceful_period is ignored outside the Cloud edition.
policy = create_message_clean_policy(graceful_period_days=graceful_period)
if from_days_ago is not None and before_days is not None:

View File

@@ -751,7 +751,7 @@ def migrate_oss(
UploadFile.storage_type == source_storage_type,
UploadFile.key.in_(copied_upload_file_keys),
)
.values(storage_type=dify_config.STORAGE_TYPE)
.values(storage_type=StorageType(dify_config.STORAGE_TYPE))
),
).rowcount
db.session.commit()

View File

@@ -6,7 +6,7 @@ from sqlalchemy import delete, select, update
from sqlalchemy.orm import sessionmaker
from configs import dify_config
from enums.deployment_edition import DeploymentEdition
from enums import DeploymentEdition
from events.app_event import app_was_created
from extensions.ext_database import db
from extensions.ext_redis import redis_client

View File

@@ -5,7 +5,6 @@ from typing import Any, override
from pydantic.fields import FieldInfo
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict, TomlConfigSettingsSource
from enums.deployment_edition import DeploymentEdition
from libs.file_utils import search_file_upwards
from .deploy import DeploymentConfig
@@ -117,11 +116,3 @@ class DifyConfig(
),
),
)
@property
def DEPLOYMENT_EDITION(self) -> DeploymentEdition:
if self.EDITION == "CLOUD":
return DeploymentEdition.CLOUD
if self.ENTERPRISE_ENABLED:
return DeploymentEdition.ENTERPRISE
return DeploymentEdition.COMMUNITY

View File

@@ -1,8 +1,8 @@
from typing import Literal
from pydantic import Field
from pydantic_settings import BaseSettings
from enums import DeploymentEdition
class DeploymentConfig(BaseSettings):
"""
@@ -25,9 +25,14 @@ class DeploymentConfig(BaseSettings):
default=False,
)
EDITION: Literal["SELF_HOSTED", "CLOUD"] = Field(
description="Deployment edition of the application (e.g., 'SELF_HOSTED', 'CLOUD')",
default="SELF_HOSTED",
DEPLOYMENT_EDITION: DeploymentEdition = Field(
description="Product edition of the application.",
default=DeploymentEdition.COMMUNITY,
)
INIT_PASSWORD: str = Field(
description="Password required before initializing a self-hosted deployment",
default="",
)
DEPLOY_ENV: str = Field(

View File

@@ -8,12 +8,6 @@ class EnterpriseFeatureConfig(BaseSettings):
**Before using, please contact business@dify.ai by email to inquire about licensing matters.**
"""
ENTERPRISE_ENABLED: bool = Field(
description="Enable or disable enterprise-level features."
"Before using, please contact business@dify.ai by email to inquire about licensing matters.",
default=False,
)
WEBAPP_PUBLIC_ACCESS_ENABLED: bool = Field(
description="Whether admins are allowed to set a webapp's access mode to public (anyone with the link, "
"no auth). Disable in security-sensitive on-prem deployments.",
@@ -59,7 +53,7 @@ class EnterpriseTelemetryConfig(BaseSettings):
"""
ENTERPRISE_TELEMETRY_ENABLED: bool = Field(
description="Enable enterprise telemetry collection (also requires ENTERPRISE_ENABLED=true).",
description="Enable enterprise telemetry collection for enterprise deployments.",
default=False,
)
@@ -89,11 +83,6 @@ class EnterpriseTelemetryConfig(BaseSettings):
default=False,
)
ENTERPRISE_SERVICE_NAME: str = Field(
description="Service name for OTEL resource.",
default="dify",
)
ENTERPRISE_OTEL_SAMPLING_RATE: float = Field(
description="Sampling rate for enterprise traces (0.0 to 1.0, default 1.0 = 100%).",
default=1.0,

View File

@@ -1,8 +1,10 @@
from configs.extra.agent_backend_config import AgentBackendConfig
from configs.extra.archive_config import ArchiveStorageConfig
from configs.extra.knowledge_fs_config import KnowledgeFSConfig
from configs.extra.logstore_config import LogStoreConfig
from configs.extra.notion_config import NotionConfig
from configs.extra.sentry_config import SentryConfig
from configs.extra.turnstile_config import TurnstileConfig
class ExtraServiceConfig(
@@ -10,7 +12,9 @@ class ExtraServiceConfig(
AgentBackendConfig,
ArchiveStorageConfig,
KnowledgeFSConfig,
LogStoreConfig,
NotionConfig,
SentryConfig,
TurnstileConfig,
):
pass

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