Compare commits

..

1049 Commits

Author SHA1 Message Date
8cd97eba32 fix: not show straegy type 2025-01-09 20:50:54 +08:00
ab66f30e1d feat: add parallel test case 2025-01-09 15:31:44 +08:00
566dc9b4a8 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-09 15:17:31 +08:00
641395b3ec feat: agent strategy form support required mark 2025-01-09 15:17:27 +08:00
db60150f30 chore: change use new log struct generator 2025-01-09 15:08:05 +08:00
b964c713bd chore: add tooltip props to agent strategy select 2025-01-09 14:56:46 +08:00
7b4d49a79c Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-09 14:53:07 +08:00
0ad08f523b chore: add tooltip props to agent strategy select 2025-01-09 14:52:59 +08:00
Yi
f4ca5938ec Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-09 14:47:17 +08:00
Yi
38437efec0 fix: change "upgrade" to "install" for generality in plugin version switch 2025-01-09 14:47:08 +08:00
52553a66ba fix style of agent strategy 2025-01-09 14:40:58 +08:00
a87c2d7e20 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-09 14:31:49 +08:00
08cff0045d feat: form not installed indicator 2025-01-09 14:31:43 +08:00
195a507b40 dark mode of api 2025-01-09 14:14:08 +08:00
d7ec013665 modals of app overview 2025-01-09 14:14:08 +08:00
dc9675d2d8 fix: plugin tool width in agent app 2025-01-09 13:53:07 +08:00
06fe17c93e chore: enchance fold into anim 2025-01-09 13:45:30 +08:00
5ec4695e4a feat: tool icon check plugin status 2025-01-09 13:29:23 +08:00
bb60db7078 feat: tool selector get icon from marketplace 2025-01-09 13:00:43 +08:00
e3e8eafd32 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-09 11:53:30 +08:00
eff6a55960 fix: install button 2025-01-09 11:53:24 +08:00
Yi
a7f0933e55 chore: add checks in the url fetch step of the installFromGitHub 2025-01-09 11:52:44 +08:00
eaf1177cd4 dark mode of overview card view 2025-01-09 11:35:37 +08:00
297b5280f0 tracing modal & config 2025-01-09 11:35:37 +08:00
4aac48ed61 feat: github install add anim 2025-01-09 11:29:48 +08:00
192af8df9f feat: local and bundle support hide anim 2025-01-09 11:16:33 +08:00
9822445c1e chore: all hide logic to hooks 2025-01-09 11:04:23 +08:00
018e32e355 fix: installed not clear countdown 2025-01-09 10:49:22 +08:00
41b720502f merge main 2025-01-09 10:40:46 +08:00
a157af724f fix: marketplace submit button 2025-01-09 10:35:47 +08:00
cc0d864599 fix: agent node tool list header width 2025-01-09 09:55:56 +08:00
4a6a1b9855 chore: fix request form data 2025-01-08 20:01:01 +08:00
7ccc268ced fix: stop anim 2025-01-08 19:34:41 +08:00
6483d20f56 feat: install countdown 2025-01-08 19:25:01 +08:00
Yi
61237e8037 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-08 17:55:41 +08:00
Yi
aae601f233 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-08 17:55:19 +08:00
701520ffd0 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-08 17:54:31 +08:00
596e3409b7 fix: agent prompt 2025-01-08 17:54:24 +08:00
Yi
38dd285656 fix: long model name display issue 2025-01-08 17:53:25 +08:00
0248c8cb8c fix: agent key 2025-01-08 17:48:26 +08:00
88b3c0b7a6 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-08 17:36:32 +08:00
3ae7787011 fix: number not supported 2025-01-08 17:36:26 +08:00
Yi
c357ec0f7c chore: add loading to the model selector trigger 2025-01-08 17:27:03 +08:00
Yi
763f91fcea Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-08 17:12:01 +08:00
Yi
62d53399ae fix: add bg color to the top section in plugins page 2025-01-08 17:11:55 +08:00
c3215a8f94 feat: fold into animation 2025-01-08 17:02:47 +08:00
d65c1218ae Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-08 16:57:01 +08:00
83723212af fix: agent node getNodeUsedVars 2025-01-08 16:56:56 +08:00
Yi
84c2a9dc3b Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-08 16:51:14 +08:00
Yi
87d7df3ed4 fix: make the status indicator component compatible with the switch plugin version component 2025-01-08 16:51:06 +08:00
586f9d8d89 fix: agent log 2025-01-08 16:46:35 +08:00
b656b7a0dd Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-08 16:41:22 +08:00
0fdf7b23c6 feat: add 'agent' tag to plugin constants and translations 2025-01-08 16:41:16 +08:00
Yi
369e3eb97f feat: add version switch in model selector 2025-01-08 16:39:56 +08:00
f58eef74b3 feat: switch plugin version changelog 2025-01-08 16:19:41 +08:00
2fd083629d feat: new switch plugin version 2025-01-08 15:58:05 +08:00
Yi
755685a89a Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-08 15:54:21 +08:00
Yi
23b29b1d21 fix: check-dependencies typo and fetch model list typo 2025-01-08 15:54:04 +08:00
4b01ef2b48 fix: chat log 2025-01-08 15:42:18 +08:00
05d273d643 fix tracing dropdown 2025-01-08 15:40:13 +08:00
5481b42257 dark mode for tracing dropdown 2025-01-08 15:28:45 +08:00
fa83f2e2fa fix: switch version plugin 2025-01-08 15:13:19 +08:00
5e4d2f9501 Revert "chore: remove switch version component in strategy select"
This reverts commit f63cc9e069.
2025-01-08 14:55:55 +08:00
Yi
fdfe724438 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-08 14:50:25 +08:00
Yi
b0b373d2fb fix: revert the modelInList check in the model icon component 2025-01-08 14:50:14 +08:00
f63cc9e069 chore: remove switch version component in strategy select 2025-01-08 14:48:36 +08:00
Yi
8c9c336b64 fix: isInModelList default value 2025-01-08 14:47:01 +08:00
Yi
2dac103463 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-08 14:45:10 +08:00
Yi
17dfb3654f feat: add version incompatible case 2025-01-08 14:45:03 +08:00
98ad18ba81 fix: agent log 2025-01-08 14:20:08 +08:00
53bb1bb937 fix: agent node cannot output var 2025-01-08 14:09:35 +08:00
93806148cd chore: plugin mutation modal component 2025-01-08 12:37:33 +08:00
adf0d94a9a unsupported actions 2025-01-08 11:51:59 +08:00
d17932d723 fix: install task icon 2025-01-08 10:12:55 +08:00
Yi
3cbb288a25 feat: add version check i18n 2025-01-08 10:09:11 +08:00
99e2eaa6ee fix: switch plugin version 2025-01-08 10:03:00 +08:00
6006380723 fix: tool select ui problem in workflow 2025-01-07 17:10:27 +08:00
55ad1fe1f0 chore: tool selelct use the same tools data 2025-01-07 16:58:31 +08:00
Yi
0383fce821 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-07 16:57:09 +08:00
Yi
85610651c0 node ui fix 2025-01-07 16:57:01 +08:00
768e1b9da3 fix: wrong tooltip content for switch plugin version 2025-01-07 16:43:36 +08:00
a6d172f111 feat: github refresh plugin list 2025-01-07 16:42:40 +08:00
5817c07db6 feat: local install refresh 2025-01-07 16:34:08 +08:00
Yi
2f5da1f6aa fix: update model icon 2025-01-07 16:31:15 +08:00
130cfaf2a4 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-07 15:20:00 +08:00
f840b7db0c fix: type for tool list 2025-01-07 15:19:46 +08:00
aac2184069 chore: use install button in tool item 2025-01-07 15:18:07 +08:00
f03631ff9e feat: init agent strategy install from marketplace 2025-01-07 15:08:36 +08:00
Yi
e207894d7e Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-07 14:45:00 +08:00
Yi
8512a7d3ad chore: update the onOpenModel function to a hook 2025-01-07 14:43:54 +08:00
46614cc6f5 fix badge & refresh state of tool install state 2025-01-07 14:41:48 +08:00
b39acd0bad Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-07 14:03:53 +08:00
48b88b90f5 feat: i18n text in agent strategy selector 2025-01-07 14:03:40 +08:00
851fe246a7 fix: installing info in plugin nav 2025-01-07 14:00:29 +08:00
Yi
ff61c22f36 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-07 12:48:42 +08:00
Yi
e6eae8568f chore: update the model install button 2025-01-07 12:48:36 +08:00
6d5c34564b fix qrcode z-index 2025-01-07 12:16:34 +08:00
4c5fa00c50 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-07 11:57:40 +08:00
910e6d17d3 feat: adjust strategy selector height 2025-01-07 11:57:35 +08:00
275696edba fix system model selector 2025-01-07 11:53:34 +08:00
1348e32015 fix balance model z-index 2025-01-07 11:53:34 +08:00
f5b2735dd5 theme default light 2025-01-07 11:53:34 +08:00
8d39ec1da5 chart drak mode 2025-01-07 11:53:34 +08:00
Yi
0f6cbf25d3 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-07 11:34:09 +08:00
0e98794d49 feat: use all refresh plugin tools to hooks 2025-01-07 11:31:22 +08:00
1419430015 chore: upd 2025-01-07 11:25:06 +08:00
Yi
5d25643f54 fix: group icon style 2025-01-07 11:18:01 +08:00
e24b04b30f refactor: switch plugin version component to not accept version 2025-01-07 10:58:53 +08:00
a8c4870349 refactor: switch plugin version component to not accept version 2025-01-07 10:55:42 +08:00
bdb9d676b1 chore: update switch plugin i18n 2025-01-07 10:49:26 +08:00
0beebab605 fix: workflow store agent strategy not up to date 2025-01-07 09:57:35 +08:00
15f3e46c49 refactor: some field name in strategy status 2025-01-07 09:30:35 +08:00
61d2f70927 feat: add transform node to node data 2025-01-06 18:36:45 +08:00
228cd1cdbe feat: add iteration id 2025-01-06 16:48:24 +08:00
e0ed17a2e6 chore: can generator middle struct 2025-01-06 15:31:25 +08:00
07aa2ca9cf fix: single run log 2025-01-03 16:34:23 +08:00
5fdfba6b00 feat: make iteration 2025-01-03 15:41:04 +08:00
fbf9984d85 refactor: strategy status 2025-01-03 15:25:10 +08:00
2296bb162b Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-03 13:52:43 +08:00
06f0c3c886 refactor: strategy status 2025-01-03 13:52:38 +08:00
483890b207 fix install in tool item 2025-01-03 13:39:23 +08:00
390107f97e Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-03 10:35:11 +08:00
5ba0b85738 feat: install plugin button 2025-01-03 10:35:06 +08:00
39335b8038 refactor I18n render in plugin detail 2025-01-03 10:16:44 +08:00
5fb356fd33 refactor: rename renderI18nObject to getValueFromI18nObject for clarity 2025-01-02 18:07:44 +08:00
c458c28c62 feat: enhance plugin item localization with i18n support 2025-01-02 17:35:11 +08:00
055fb22b9b Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-02 16:36:03 +08:00
f2eb095960 feat: strategy install status 2025-01-02 16:35:58 +08:00
d5cfb26db6 feat: support make retry data 2025-01-02 16:29:12 +08:00
a514bde428 fix: marketplace page size 2025-01-02 15:50:40 +08:00
87ce813175 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-02 15:03:23 +08:00
336b26569b feat: agent form string type support var 2025-01-02 15:03:18 +08:00
a432fcfd5e agent node single run 2025-01-02 14:56:15 +08:00
0ed4ec5cd0 fix: agent strategy selector show workflow 2025-01-02 14:48:40 +08:00
add6dff789 feat: agent node check install strategy 2025-01-02 14:27:19 +08:00
c469da2020 fix: handle install title and descript may caused i18n problem 2025-01-02 14:22:04 +08:00
55aad3718d fix: multi loop nodes remove children error 2025-01-02 14:09:57 +08:00
c6c388fbda feat: agent checklist i18n 2025-01-02 13:58:30 +08:00
d3d606799d Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2025-01-02 11:46:52 +08:00
c1ae681b6c feat: agent checklist i18n 2025-01-02 11:46:47 +08:00
f11ea5ae97 fix: title not show all if space is enough 2025-01-02 11:37:31 +08:00
e112357e91 chore: temp 2025-01-02 11:37:31 +08:00
1b8ec6710a feat: agent node checklist 2025-01-02 11:29:10 +08:00
4663af8a60 tool setting schema 2025-01-02 08:43:09 +08:00
91ac660f81 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-31 16:33:22 +08:00
3c829a1c28 fix: some style 2024-12-31 16:33:16 +08:00
Yi
f9ce6b6a6e Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-31 16:13:13 +08:00
Yi
afe8c85b99 model feature icons 2024-12-31 16:13:06 +08:00
bc5cd4405b agent node single run 2024-12-31 16:08:09 +08:00
Yi
be36aedac1 agent node: fix installed model ui 2024-12-31 15:59:43 +08:00
9e6f3e9161 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-31 15:53:45 +08:00
52537c9e6b fix: model deprecated in model list 2024-12-31 15:53:38 +08:00
8d229f26d0 fix: card name too long 2024-12-31 15:47:48 +08:00
Yi
504c794e8a add icons and fix ui typos 2024-12-31 15:47:14 +08:00
cab8c6c5a7 feat: support scroll into letter 2024-12-31 15:41:29 +08:00
Yi
5bba422c0b fix model icon size in llm node 2024-12-31 15:13:56 +08:00
870779534f Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-31 15:04:17 +08:00
f956c2edcd chore: update style 2024-12-31 15:04:13 +08:00
2a29dd3534 fix: letter list not right 2024-12-31 15:01:31 +08:00
Yi
b1831bc582 fix the model icon ui 2024-12-31 14:35:11 +08:00
Yi
3e979a5ded fix: model selector's trigger ui 2024-12-31 14:23:13 +08:00
7011a5029e merge main 2024-12-31 14:12:35 +08:00
d7cbbbca8e fix: tool single run 2024-12-31 14:08:40 +08:00
21d082f3da feat: tool single run 2024-12-31 14:05:56 +08:00
Yi
2cdf2b2e05 fix the model icon size in the agent node 2024-12-31 14:04:03 +08:00
Yi
ec2dd750f1 fix: set a fixed height to the model selection in agent node 2024-12-31 13:59:55 +08:00
066595f3aa chore: remove unused code 2024-12-31 13:50:31 +08:00
f81bbee6b6 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-31 13:46:12 +08:00
4855e87876 fix: editor 2024-12-31 13:46:06 +08:00
e47aaad396 remove test codes 2024-12-31 12:42:49 +08:00
df421796bb add model info in model selector 2024-12-31 12:41:08 +08:00
fe5702784e Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-31 11:52:20 +08:00
eba4042a62 wip: instruction field 2024-12-31 11:52:15 +08:00
3bed0346d7 fix: choose tools 2024-12-31 11:51:50 +08:00
Yi
3a09f43f70 feat: update the install logic 2024-12-31 11:46:36 +08:00
Yi
232fb66edd ui fixes in model selector 2024-12-31 11:08:53 +08:00
a3f736f6e5 feat: multi steps circle 2024-12-30 18:39:33 +08:00
d9d42b2d8c feat: multi step circle 2024-12-30 18:38:07 +08:00
0a30018330 chore: one step circle check 2024-12-30 18:27:59 +08:00
128410902c temp 2024-12-30 18:27:59 +08:00
Yi
9b6f580365 add model install in model configuration for the agent node 2024-12-30 17:55:46 +08:00
afb3548e45 refactor: agent parameters 2024-12-30 17:40:56 +08:00
e1cb85cee1 fix label of tool picker 2024-12-30 15:43:24 +08:00
2007828404 installation state of tool 2024-12-30 15:28:15 +08:00
f5b4366bd8 fix: agent node 2024-12-30 14:49:12 +08:00
cd7e6ca010 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-30 13:31:02 +08:00
2ac6f00efb fix: agent node toolbox 2024-12-30 13:30:57 +08:00
Yi
f461f56886 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-30 12:55:42 +08:00
Yi
ba16cbccf0 feat: add install options for model configuration 2024-12-30 12:55:28 +08:00
08a1f241ca Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-30 11:28:57 +08:00
a5509fbe5a feat: agent node toolbox 2024-12-30 11:28:51 +08:00
98a03b0593 fix: agent log structure 2024-12-30 11:25:28 +08:00
f72818bed5 fix: agent log structure 2024-12-30 11:14:59 +08:00
0c5101fb3c fix default value for multiple tool selector 2024-12-30 10:38:45 +08:00
84febd5e94 fix: agent log structure 2024-12-30 10:23:48 +08:00
b5ad9a58f7 default value for multiple tool selector 2024-12-30 10:23:00 +08:00
8a90a9bd5d fix: in steam mode trigger the error in change list 2024-12-27 18:28:52 +08:00
6f8e217580 refact workflow run log 2024-12-27 18:10:44 +08:00
891a76f2fa enabled count 2024-12-27 17:37:18 +08:00
3c85363392 multiple tool selector 2024-12-27 17:29:46 +08:00
a863e9f674 refact workflow run log 2024-12-27 17:22:11 +08:00
6a2a7aca9b chore: tool node support agent logs 2024-12-27 16:52:22 +08:00
18393d2e27 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-27 16:48:32 +08:00
de48a1c7e9 fix: type error 2024-12-27 16:48:16 +08:00
3d5620dfb3 fix: parallel start node not insert into the right place 2024-12-27 16:47:05 +08:00
da2982ba98 feat: model list on agent node 2024-12-27 16:45:46 +08:00
0108b28305 feat: model list on agent node 2024-12-27 16:39:18 +08:00
573c8f909c add multiple tool selector 2024-12-27 16:30:34 +08:00
3a5170716b fix: pararllel title 2024-12-27 16:29:15 +08:00
e4cc8f7010 refact workflow run log 2024-12-27 16:18:04 +08:00
23913b8640 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-27 16:04:53 +08:00
0d0a4cfaa1 fix: select var 2024-12-27 16:04:48 +08:00
04d8d16d8a chore: fix retry not work in iteration 2024-12-27 15:54:56 +08:00
0d2a74b8cb feat: output var 2024-12-27 15:34:54 +08:00
ae42edb8d7 remove test code 2024-12-27 14:43:07 +08:00
e903cd8073 fix: llm node 2024-12-27 14:31:50 +08:00
31cca291b7 chore: remove test page 2024-12-27 14:22:00 +08:00
ed6c9625e8 fix scope features 2024-12-27 14:20:46 +08:00
08c517dd99 feat: iteration support parallel 2024-12-27 14:20:18 +08:00
69a6556f52 tool item 2024-12-27 14:09:52 +08:00
Yi
e34eebfb0b Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-27 13:58:05 +08:00
Yi
ef3e904839 feat: model-selector in Agent node (case: installed models) 2024-12-27 13:57:54 +08:00
df5fb6dca9 feat: agent strategy max iter slider 2024-12-27 13:30:39 +08:00
2f65d0439c fix: not parall not group by branch 2024-12-27 13:08:05 +08:00
5cdca9cafe fix: marketplace i18n 2024-12-27 11:54:42 +08:00
Yi
67019d128b Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-27 11:49:03 +08:00
697ee496b7 chore: upd mock form for dev 2024-12-27 11:43:29 +08:00
7f5e27d001 chore: extend form component for override 2024-12-27 11:30:43 +08:00
1f128729f4 fix: sub iteration would crash page 2024-12-27 11:24:47 +08:00
Yi
a562e6db89 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-27 10:47:07 +08:00
c23fe3b67f chore: format 2024-12-27 10:25:15 +08:00
95da3a4cf1 tool credential panel 2024-12-26 18:58:01 +08:00
bfecc73de9 support extra for tool selector 2024-12-26 17:58:34 +08:00
Yi
b4e9dddbca Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-26 17:40:42 +08:00
10218cfe8d add tool selector in agent node 2024-12-26 17:33:06 +08:00
e00da7a1d8 tool setting form 2024-12-26 17:06:15 +08:00
469ce0f23d add tool description 2024-12-26 17:06:15 +08:00
23bf0a6812 tool selector support scope 2024-12-26 17:06:11 +08:00
605085bddf chore: remove max_iterations for agent node 2024-12-26 16:42:34 +08:00
Yi
dbc45f0d74 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-26 16:36:09 +08:00
6175f8c16f merge main 2024-12-26 16:35:11 +08:00
7e94056507 refact workflow run log 2024-12-26 15:44:59 +08:00
1429b30e84 chore: upd 2024-12-26 15:27:15 +08:00
5ef9156242 feat: custom credential form 2024-12-26 14:44:05 +08:00
6f865b96a2 refact workflow run log 2024-12-26 14:41:00 +08:00
b7e56a23a0 feat: custom credential form 2024-12-26 14:32:21 +08:00
Yi
5b8ad3ac4b Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-26 13:19:32 +08:00
e0b7f48b6b chore: add i18n str 2024-12-26 13:02:00 +08:00
Yi
ff70e14de2 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-26 12:44:41 +08:00
244517e6e7 fix: no icon in tool select 2024-12-26 12:41:44 +08:00
cd08f98bff chore: upd app icon 2024-12-26 12:32:02 +08:00
Yi
b752c0654e Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-26 10:59:33 +08:00
e07d7ee4fc chore: remove console 2024-12-26 10:55:30 +08:00
Yi
c476f06388 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-26 10:53:02 +08:00
5f13402c6e Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-26 10:17:21 +08:00
8dcd82290c feat: select strategy 2024-12-26 10:16:52 +08:00
86eb618b84 refact workflow run log 2024-12-26 10:16:12 +08:00
e1ea82475d feat: format agent 2024-12-25 18:37:05 +08:00
08515957f1 refact workflow run log 2024-12-25 16:57:22 +08:00
8503099d65 feat: integration 2024-12-25 16:46:32 +08:00
Yi
c04a89d5b1 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-25 16:39:22 +08:00
8d225264fa feat: retry 2024-12-25 16:25:19 +08:00
26901b2c87 refact workflow run log 2024-12-25 16:15:20 +08:00
b56acb825f fix org info in tool detail panel 2024-12-25 15:34:07 +08:00
Yi
f1e17ad134 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-25 15:24:59 +08:00
b8e4580074 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-25 15:23:50 +08:00
935c126abd strategy detail panel 2024-12-25 15:15:55 +08:00
b4105fcc9c strategy list 2024-12-25 15:15:55 +08:00
5f65fb1b62 chore: add model select mock 2024-12-25 15:12:33 +08:00
Yi
986d6eed36 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-25 15:10:05 +08:00
e0f83d06d8 chore: add model select mock 2024-12-25 15:06:58 +08:00
Yi
f42cfe8075 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-25 14:54:22 +08:00
a7b2f9aef0 chore: add i18n 2024-12-25 14:52:11 +08:00
c867584049 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-25 14:24:02 +08:00
ac2c8344f2 chore: add i18n 2024-12-25 14:23:57 +08:00
8c662e04e0 feat: add iteration error 2024-12-25 14:20:39 +08:00
e1d0c29711 chore: add icon for agent 2024-12-25 14:13:20 +08:00
Yi
50c77ad405 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-25 14:12:54 +08:00
880496db0b chore: add more tests 2024-12-25 14:12:53 +08:00
057da6c31b chore: gen agent icon 2024-12-25 14:07:05 +08:00
c0a8b89e93 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-25 14:06:08 +08:00
a8a956b5f1 chore: add i18n 2024-12-25 14:05:49 +08:00
ff02e1cb8f chore: enchance test output struct 2024-12-25 14:04:46 +08:00
Yi
d78719a6f8 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-25 13:41:06 +08:00
da6d65b072 fix style of account setting 2024-12-25 12:35:27 +08:00
8d1a8eac51 add use-strategy 2024-12-25 11:55:59 +08:00
e34fe3d10a feat: support iteration handle 2024-12-25 11:47:31 +08:00
Yi
8d79b24883 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-12-25 11:43:00 +08:00
03520a5a81 feat: strategy form init 2024-12-25 11:24:24 +08:00
Yi
d43b2c62f5 Merge branch 'main' into feat/plugins 2024-12-24 17:44:12 +08:00
07d7965e3b support custom output schema in tool node 2024-12-24 16:26:09 +08:00
e4d72f3442 chore: hide log 2024-12-24 16:10:37 +08:00
c93c264c5a feat: format to tracing 2024-12-24 16:08:48 +08:00
77017522b8 chore: init tool select component 2024-12-24 16:01:14 +08:00
aa309964e5 chore: init tool select component 2024-12-24 15:55:45 +08:00
754baf8d02 refact workflow run 2024-12-24 15:38:54 +08:00
b98dd22491 refact workflow run 2024-12-24 15:31:46 +08:00
7c460eb6e7 chore: add warning ui for agentic stragey 2024-12-24 14:20:15 +08:00
e2e2090e0c support model params change 2024-12-24 14:15:18 +08:00
c8fc1deca6 support & operator of model-selector scope 2024-12-24 12:18:03 +08:00
Yi
6dd14ca2be chore: add agent strategy option apart from bundle 2024-12-24 12:05:35 +08:00
ec6f4ee9df Merge branch 'main' into feat/plugins 2024-12-24 11:35:47 +08:00
9d117fa2f9 param rules panel 2024-12-24 11:00:31 +08:00
e8319f01e0 fix: crash 2024-12-24 10:33:56 +08:00
c3f3b79b79 merge main 2024-12-23 15:33:08 +08:00
327eac09e7 fix: scope value 2024-12-20 16:37:30 +08:00
cf75e2f053 model selector support scope 2024-12-20 16:36:22 +08:00
12c47d80af support llm tool_parameters 2024-12-20 12:46:07 +08:00
5c6916354e app selector support scope 2024-12-20 11:59:18 +08:00
6d2b2d7810 fix number of tool actions 2024-12-20 11:27:52 +08:00
1fa5b1755f dark mode for model provider 2024-12-20 10:44:39 +08:00
5dad4793e6 fix: Fix context selector usage in plugin page components 2024-12-20 09:56:06 +08:00
04054954c5 Merge branch 'main' into feat/plugins 2024-12-16 17:55:52 +08:00
672843dcab feat: marketplace list more link 2024-12-16 17:39:07 +08:00
4f8cdabef0 dark mode of model provider 2024-12-16 16:29:45 +08:00
e792e91777 fix: model page 2024-12-16 14:56:55 +08:00
d5cb9f0193 add help doc link of endpoint 2024-12-16 14:20:22 +08:00
171cc88a0d merge main 2024-12-16 11:09:13 +08:00
031c2ad899 chore: update pnpm lock 2024-12-13 17:27:04 +08:00
463f5a34c6 dark mode for plugins 2024-12-13 14:06:07 +08:00
5c98f1a5aa dark mode for tools 2024-12-13 13:10:29 +08:00
a0e999e438 feat: plugin add agent-strategy type 2024-12-12 17:29:25 +08:00
e0cc990fa5 fix: tool parameter form change 2024-12-12 15:12:41 +08:00
5b3112a137 fix: model page empty 2024-12-11 14:10:28 +08:00
35fb9099e3 fix: model page 2024-12-10 15:51:55 +08:00
c51c032334 fix: dsl check 2024-12-10 14:11:34 +08:00
5825c101c9 fix: marketplace exclude 2024-12-10 13:37:02 +08:00
4c1bf96b14 fix: node build out of memory 2024-12-09 18:26:09 +08:00
69daf4a027 merge main 2024-12-06 16:56:42 +08:00
3bc9ddb006 fix mutation of model credentials update 2024-12-06 16:05:06 +08:00
ea2862e435 fix style of boolean form of model parameters 2024-12-06 16:05:06 +08:00
ca3d96e5f4 fix: tool list 2024-12-06 15:42:02 +08:00
Yi
d6dea67947 apply the skeleton component to the Plugin loading card 2024-12-05 15:27:47 +08:00
Yi
b8f9747849 Merge branch "main" into feat/plugins 2024-12-05 15:08:09 +08:00
0e70e72594 fix: marketplace list 2024-12-05 14:54:20 +08:00
Yi
0e419a7a16 feat: move linear gradient bg to common config 2024-12-05 14:44:45 +08:00
0ac5e53c2e fix: refactor GitHub releases fetching to improve error handling and format response 2024-12-04 18:08:25 +08:00
44b0039e8b fix: install plugin type show error 2024-12-04 18:06:00 +08:00
becdca24df fix: enhance GitHub releases fetching with optional authentication 2024-12-04 16:49:49 +08:00
f634a4488f merge main 2024-12-04 11:06:00 +08:00
Yi
fc2656b4b7 fix: workspace selector UI 2024-12-03 18:34:18 +08:00
d6a4cbc6cc fix: marketplace list 2024-12-03 18:03:39 +08:00
Yi
1e2ee61f6a feat: add version check before Plugin install from GitHub 2024-12-03 15:47:45 +08:00
ab9a177c90 fix: marketplace list 2024-12-03 14:34:44 +08:00
8b28ed589c feat: update translation for bundle 2024-12-03 13:43:13 +08:00
3b1211d6bc chore: bump cross-spawn from 7.0.3 to 7.0.6 2024-12-02 17:54:39 +08:00
7bd3f2b932 feat: update description for API endpoints configuration 2024-12-02 17:41:52 +08:00
b78ab0bd69 fix: other install error 2024-11-29 15:18:07 +08:00
84cad5969e fix: not handleinstall error 2024-11-29 15:09:23 +08:00
35ef874867 fix: detect is same packege use uniqid 2024-11-29 10:56:58 +08:00
47579c86e6 fix: from marketplace install and update bundle 2024-11-28 18:47:06 +08:00
32619bd05e fix: install bunlde support update 2024-11-28 18:26:18 +08:00
6a500edf4d fix: loading of credential form 2024-11-28 17:48:28 +08:00
be7fa93ffc fix: modify tip of default embedding model 2024-11-28 17:27:21 +08:00
3bcd470ec6 fix: authorization in debugging plugin 2024-11-28 17:27:21 +08:00
73825c59e4 fix: crash of tool authorization in agent 2024-11-28 17:27:21 +08:00
5f76975e12 fix: icon of tool provider in agent 2024-11-28 17:27:21 +08:00
3ddb3d2bff fix: deleted tools in agent 2024-11-28 17:27:21 +08:00
a70eda6c45 add loading for auth modal 2024-11-28 17:27:21 +08:00
42cdc55db1 fix: do not show error tooltip if http status code is 401 2024-11-28 16:04:19 +08:00
a76d0a09b6 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-28 16:03:05 +08:00
51f0f21a47 chore: not load updateInfo can not install 2024-11-28 14:30:31 +08:00
073e847524 fix: installed 2024-11-28 14:13:43 +08:00
3e601c4ef5 feat: local support upgrade 2024-11-28 14:09:05 +08:00
fba468e8ad fix: handle install the same version 2024-11-28 13:50:04 +08:00
f40b212b04 feat: support update if installed from marketplace 2024-11-28 12:00:12 +08:00
37eee7be24 fix: model provider page marketplace 2024-11-28 11:26:45 +08:00
99942b26e6 chore: support install hide button 2024-11-28 10:40:33 +08:00
d4cda69b0e feat: marketplace add exclude 2024-11-27 16:14:15 +08:00
e145dba487 fix: dsl check 2024-11-27 14:30:47 +08:00
e908ecab8f fix: add ui 2024-11-27 10:50:39 +08:00
b8af4aead1 fix: update version show problem 2024-11-26 14:38:43 +08:00
d40f0e645c fix: install marketplace bundle title 2024-11-26 14:11:21 +08:00
4b77ced4ad fix: not show from market bundle package icon 2024-11-26 14:07:45 +08:00
b84b0c8ba8 merge main 2024-11-26 11:38:37 +08:00
f47b32b26d fix: dsl 2024-11-26 11:10:53 +08:00
102a42d24c Merge branch 'main' into feat/plugins 2024-11-26 11:08:30 +08:00
00eb47384a merge main 2024-11-26 10:31:39 +08:00
2041650cca merge main 2024-11-26 10:29:58 +08:00
a0873a956f merge main 2024-11-26 10:27:48 +08:00
df049564e8 feat: support install bundle from marketplace 2024-11-25 18:01:30 +08:00
b93be49530 chore: support update show version 2024-11-25 18:01:29 +08:00
57756b18e4 fix: plugin task 2024-11-25 17:54:28 +08:00
8ad9ab40df fix: plugin task 2024-11-25 17:32:57 +08:00
4f54ac6ed6 fix: marketplace collection condition 2024-11-25 12:03:49 +08:00
3263a6a5f5 fix: marketplace plugin tags i18n 2024-11-22 15:26:25 +08:00
506e5e0bc8 fix: plugin type 2024-11-22 14:53:20 +08:00
c768f8fdd1 fix: path of tool provider 2024-11-22 11:41:36 +08:00
d45ce48932 fix: update theme imports in globals.css 2024-11-22 11:02:45 +08:00
3e3ae989f0 chore: in tool file and files all support file and files 2024-11-22 10:54:22 +08:00
786f2d9bf6 chore: fix categoriesMap[category] undefined 2024-11-22 10:39:22 +08:00
85377d13fc Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-22 10:22:39 +08:00
51e04b45ec fix: update error icon z-index and upgrade react-query dependencies 2024-11-22 10:22:33 +08:00
bd6c2e519c fix: corner mark 2024-11-22 09:09:35 +08:00
3f0b35d72e feat: install bundle from marketplace code 2024-11-21 18:22:11 +08:00
78c867b9a3 use plugin detail for builtin tool 2024-11-21 18:11:36 +08:00
f213c8f393 fix: version switch 2024-11-21 18:11:36 +08:00
1b3f4f1f2a feat: support icon size 2024-11-21 18:00:13 +08:00
c59c696df2 chore: add version type in marketplace return 2024-11-21 17:43:41 +08:00
8993a91f12 fix: upgrade sussces auto hide 2024-11-21 17:38:23 +08:00
02c5989612 recover api tool create card 2024-11-21 17:29:28 +08:00
ac42ba880a fix: install handle of list refresh 2024-11-21 17:11:22 +08:00
56f573ecfb fix: update plugin handle 2024-11-21 15:35:39 +08:00
2560d3edae fix: bundle install title 2024-11-21 15:22:19 +08:00
351615fb98 fix: not the first time upload bundle error 2024-11-21 15:14:21 +08:00
11ed86f2a8 feat: download package 2024-11-21 14:25:58 +08:00
b01c18ae7f fix: version badge 2024-11-21 14:16:02 +08:00
84a3fe85c8 fix: marketplace card link 2024-11-21 13:50:26 +08:00
3efc6c5050 fix: github link 2024-11-21 13:42:06 +08:00
42a0534299 fix: locale of model provider 2024-11-21 13:18:33 +08:00
eec193488d fix: detail link of plugin 2024-11-21 13:09:10 +08:00
021bc57cd4 empty of tool list 2024-11-21 12:43:18 +08:00
022eda9e8b fix: tool providers 2024-11-21 12:03:53 +08:00
2067092f52 feat: detail link in tools 2024-11-21 11:33:21 +08:00
000db07d29 feat: in tool install plugin 2024-11-21 11:16:05 +08:00
98ae34acd5 chore: support tags filter 2024-11-21 10:51:36 +08:00
698e94856e fix: plugin task zindex 2024-11-21 08:42:36 +08:00
99ffe43e91 merge main 2024-11-20 18:24:03 +08:00
498222371c chore: parse marketplace bundle new api 2024-11-20 17:52:35 +08:00
26288e71d3 fix: local bundle install 2024-11-20 16:32:20 +08:00
19fb466074 fix: tool icon in app configure 2024-11-20 16:22:51 +08:00
cd4eb9c3f1 fix: icon in model list 2024-11-20 16:22:51 +08:00
4ef0a3818f feat: can install bundle from local 2024-11-20 16:21:09 +08:00
77d6dbb3d0 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-20 15:31:03 +08:00
e3b8926aef fix: correct fetched releases variable in version check and update dev script for debugging 2024-11-20 15:30:57 +08:00
efb84ff03d fix: github install fix 2024-11-20 15:05:17 +08:00
1846a7de73 fix: handle github item from data struct diffe 2024-11-20 14:48:38 +08:00
40d025052d fix: load package dsl error 2024-11-20 14:27:46 +08:00
bb2914652a feat: finsh loading error comp 2024-11-20 13:56:32 +08:00
e6a03f7a58 feat: loading error struct 2024-11-20 11:50:46 +08:00
386ee7b07b update model provider list after plugin installed 2024-11-20 11:49:20 +08:00
788f9de843 update model provider list after deleting plugin 2024-11-20 10:44:20 +08:00
5947e38ea0 feat: install by local bundle change 2024-11-19 18:32:17 +08:00
e151c2ee8c fix: when error can not install 2024-11-19 17:29:15 +08:00
a093a48675 feat: show package item in bundle 2024-11-19 17:29:14 +08:00
27b1a51572 fix: marketplace collection label description 2024-11-19 16:43:13 +08:00
a867040b88 fix icon of model provider 2024-11-19 16:26:23 +08:00
2e3442f74c feat: support local installed package 2024-11-19 16:04:31 +08:00
43d7a538dc feat: marketplace list url support search & tags 2024-11-19 15:08:54 +08:00
c5c06c18f1 feat: can upload and parse bundle 2024-11-19 15:05:15 +08:00
972eaa5948 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-19 11:20:23 +08:00
87b23a1fac feat: refactor GitHub releases fetching and update handling with improved error notifications 2024-11-19 11:20:17 +08:00
07174cf52c Revert "fix: dataset details"
This reverts commit 812fbab57b.
2024-11-18 15:41:31 +08:00
d3fe6fd303 fix: display version from GitHub metadata if available 2024-11-18 11:57:31 +08:00
87ca20c047 feat: add version checking for GitHub releases and improve error handling 2024-11-18 11:54:51 +08:00
07494459cc Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-18 11:23:49 +08:00
812fbab57b fix: dataset details 2024-11-18 11:23:44 +08:00
afdfc8c609 chore: enabled for useQuery 2024-11-18 11:02:37 +08:00
66f0e1209a switch version 2024-11-17 12:57:34 +08:00
f0e4885926 tooltip of endpoints 2024-11-17 10:54:40 +08:00
1dc2d7f4a2 chore: fix jump url 2024-11-15 16:49:12 +08:00
9193bc3143 fix: marketplace link 2024-11-15 16:46:23 +08:00
3b032f086d fix: market icon not show 2024-11-15 16:37:51 +08:00
746838e276 fix: marketplace link 2024-11-15 16:32:49 +08:00
Yi
5ea306850e fix: copy tooltip shows in wrong place 2024-11-15 16:23:11 +08:00
Yi
a6be3fdcd0 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-15 16:09:21 +08:00
Yi
a7a5c6f4cb fix: update the upgrade button text content in the header 2024-11-15 16:09:04 +08:00
f53b658964 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-15 16:07:41 +08:00
70bf321fd7 fix: add plugin type to card payload and integrate categories hook 2024-11-15 16:07:34 +08:00
35eafd239d fix: plugin task 2024-11-15 15:50:14 +08:00
3f2baf0131 fix: plugin task 2024-11-15 15:46:29 +08:00
a5a6969db3 fix 2024-11-15 15:36:32 +08:00
c176494405 fix: search box style 2024-11-15 15:26:29 +08:00
6fbff78b9c merge 2024-11-15 15:16:53 +08:00
0738c2ef54 fix: can not get url 2024-11-15 15:03:18 +08:00
984e4564f8 fix: dsl check plugin 2024-11-15 14:54:17 +08:00
bc927868f4 fix: plugin task 2024-11-15 14:54:17 +08:00
a0758dc2fc feat: finish github install 2024-11-15 14:40:04 +08:00
6b759795d5 feat: can install github 2024-11-15 13:09:29 +08:00
bba80f465b just add 2024-11-15 13:09:28 +08:00
Yi
735e47f5e5 chore: update the styling in the "members setting" 2024-11-15 11:29:48 +08:00
d354c69493 chore: update translations to include 'Marketplace' terminology in Chinese localization 2024-11-15 11:05:20 +08:00
8f14881aff app parameters 2024-11-15 10:43:21 +08:00
e53c4fc0ad empty inputs form of app selector 2024-11-15 10:43:21 +08:00
f9f2e68bd8 app selector in form 2024-11-15 10:43:21 +08:00
7b4d67d72f app list filter 2024-11-15 10:43:21 +08:00
7446244147 app picker 2024-11-15 10:43:21 +08:00
c723bd2c96 app selector trigger 2024-11-15 10:43:21 +08:00
73ce8a17a5 feat: add loading 2024-11-15 10:39:57 +08:00
6f5e010db5 chore: handle uploaded selected 2024-11-14 18:31:22 +08:00
Yi
495d86fd96 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-14 18:28:18 +08:00
Yi
42ba4e4f0e chore: update the plugins tab button 2024-11-14 18:26:16 +08:00
6af51701de fix: card link 2024-11-14 18:18:10 +08:00
76104d811c feat: can show install plugins 2024-11-14 16:54:36 +08:00
1877433f20 fix: marketplace 2024-11-14 16:37:53 +08:00
Yi
a403fb565d chore: update the plan tags 2024-11-14 16:21:28 +08:00
f58b88f319 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-14 15:47:27 +08:00
cd2860deb4 feat: add i18n support for plugin installation and empty states 2024-11-14 15:47:22 +08:00
070968a048 style: add lint rule for tailwind 2024-11-14 15:39:21 +08:00
ceb18d160f fix: marketplace i18n 2024-11-14 14:56:15 +08:00
6a63a03cb2 feat: tool list use query 2024-11-14 14:48:13 +08:00
a0a62db6ad refactor: Split linear-gradient and color 2024-11-14 14:36:30 +08:00
253abaf1a3 fix: marketplace empty page 2024-11-14 14:27:34 +08:00
44cc6157f1 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-14 11:35:53 +08:00
Yi
028fc00be6 chore: update i18n & setting window 2024-11-14 11:19:39 +08:00
1aed0fe5d6 chore: use mitt hook 2024-11-14 11:04:41 +08:00
300cd675c6 update tool list after tool selector 2024-11-14 00:04:06 +08:00
ff1d42bd66 add tool selector in endpoint modal 2024-11-14 00:02:04 +08:00
194a99220b authorization of tool selector 2024-11-14 00:02:04 +08:00
926f85ce4f tool selector trigger 2024-11-14 00:02:04 +08:00
Yi
6c6e1e90cd Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-13 18:41:27 +08:00
2fbc0c2261 revert: revert category label handling in plugin card component 2024-11-13 18:40:23 +08:00
Yi
bac1e99557 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-13 18:40:20 +08:00
Yi
93e3077f77 chore: update the esc button and the upgrade button 2024-11-13 18:40:11 +08:00
fa6858090b refactor: remove version prop from plugin item components and update version comparison logic 2024-11-13 18:34:04 +08:00
61eb655823 feat: update plugin category labels and improve internationalization support 2024-11-13 18:30:38 +08:00
6298332950 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-13 16:55:49 +08:00
a1719c49b7 feat: add internationalization support for plugin categories and update translations 2024-11-13 16:55:43 +08:00
Yi
b10a1cd325 chore: update the theme selector setting in globals.css 2024-11-13 16:43:00 +08:00
aa88028564 fix: marketplace list 2024-11-13 16:28:38 +08:00
3e314843db chore: add missing file 2024-11-13 16:23:33 +08:00
0d607a8c90 feat: add update workflow to update use query 2024-11-13 16:23:31 +08:00
577a948f42 feat: dsl check plugin 2024-11-13 15:48:25 +08:00
edbfe27eb1 chore: add invalid all built in tools 2024-11-13 15:17:22 +08:00
5c98d80fdf chore: more filter text content 2024-11-13 15:12:58 +08:00
89b470d0d5 fix: toggle tool 2024-11-13 15:04:23 +08:00
19dc983d30 feat: support toggle set tool when the texts changes 2024-11-13 14:54:37 +08:00
e842a46fe2 fix: resolve issue with dark mode 2024-11-13 14:43:37 +08:00
1573f6f6aa feat: add tag and q filter 2024-11-13 14:08:16 +08:00
910d5df513 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-13 13:33:47 +08:00
fa01360498 fix: Fix theme initialization issue 2024-11-13 13:11:47 +08:00
1fe5be532d fix: new workflow init draft failed 2024-11-13 12:59:52 +08:00
d67eb907dd fix: show index letter problem 2024-11-13 11:56:33 +08:00
83dae7e5bc fix: can not select action in workflow and tools 2024-11-13 11:38:44 +08:00
c6d1b7869d fix: plugin task 2024-11-13 11:33:58 +08:00
Yi
601d267b7a Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-13 10:39:07 +08:00
Yi
327940a120 chore: update the update plugin from GitHub 2024-11-13 10:38:56 +08:00
582c7ce348 fix: plugin task 2024-11-12 18:28:34 +08:00
13d3f67746 feat: plugin task use query 2024-11-12 17:58:32 +08:00
9c6aafd415 feat: install bundle ui 2024-11-12 17:33:10 +08:00
3716ea46b5 chore: bump elliptic from 6.5.7 to 6.6.0 2024-11-12 17:19:02 +08:00
3c89b8a698 confirm model selector schema in agent and workflow 2024-11-12 16:57:22 +08:00
b188800f16 model selector in endpoint modal 2024-11-12 16:57:22 +08:00
75a037bc2a update style of model & parameter selector 2024-11-12 16:57:22 +08:00
5efcdd6fa7 model parameters 2024-11-12 16:57:22 +08:00
06c4627abb tool selecting in configure 2024-11-12 16:57:22 +08:00
f5267d317e fix quota of model provider 2024-11-12 16:57:22 +08:00
2573950f88 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-12 16:48:28 +08:00
2ba38c6c45 feat: add marketplace-divider-bg color variable for dark and light themes 2024-11-12 16:48:24 +08:00
07edda8a85 feat: add new marketplace env 2024-11-12 15:56:03 +08:00
7791d290c7 refactor: update AudioPlayer styles with CSS variables and improve VideoGallery rendering 2024-11-12 15:49:00 +08:00
8203b23df2 feat: install bundle struct 2024-11-12 15:48:00 +08:00
d4c9c76454 chore: mask the debug key 2024-11-12 14:31:15 +08:00
a059660ed8 fix: sse post no token 2024-11-12 14:24:18 +08:00
Yi
20357beda4 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-12 13:32:26 +08:00
7e39565fd2 feat: handle on update show update modal 2024-11-12 11:48:55 +08:00
27f794e197 feat: plugin task 2024-11-11 18:17:58 +08:00
Yi
a8e8e36756 chore: update the upgrade button 2024-11-11 18:00:44 +08:00
Yi
dbc10425c8 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-11 17:21:37 +08:00
Yi
810443c511 chore: update the upgrade button and add premium badge component 2024-11-11 17:21:25 +08:00
f47b5ce63a chore: install plugin by local use use query 2024-11-11 14:58:17 +08:00
822c18cb76 fix: credentials: 2024-11-11 12:27:58 +08:00
66b08e653e fix: credentials: 2024-11-11 12:27:13 +08:00
5e81150b22 useQuery for endpoints 2024-11-09 14:44:48 +08:00
1e62768eed useQuery in action list 2024-11-09 12:51:10 +08:00
59a9235041 useQuery for model list 2024-11-09 12:12:10 +08:00
f2bf2e4470 fix style of provider added card 2024-11-08 21:32:46 +08:00
c77b38b97d chore: install from marketplace 2024-11-08 18:29:54 +08:00
33349191e9 marketplace usemutation 2024-11-08 18:21:58 +08:00
d4f7ebfd2e feat: refactor http client 2024-11-08 17:21:55 +08:00
ebdf72fffc check update 2024-11-08 17:01:49 +08:00
1f1c61541e install from settings 2024-11-08 16:33:00 +08:00
f70c23dd7a endpoints api 2024-11-08 16:19:00 +08:00
f498686c3a model list style fix 2024-11-08 16:19:00 +08:00
d13169934d fix: marketplace i18n 2024-11-08 16:14:29 +08:00
324437b3f1 feat: mutation permersions 2024-11-08 16:12:04 +08:00
c6a6c53084 chore: update theme var define 2024-11-08 15:26:59 +08:00
edc2fe050a chore: debug info use query 2024-11-08 15:10:06 +08:00
6fcebf3ecd chore: remove log 2024-11-08 14:26:52 +08:00
b754bf80ae fix: custom tools not show 2024-11-08 14:24:28 +08:00
a9de7f24a2 merge main 2024-11-08 13:55:39 +08:00
6759c6d5e6 Revert "feat: add gray label color variables for dark and light themes"
This reverts commit a75cef2c3b.
2024-11-08 13:42:38 +08:00
920d6d6882 fix credentials of action list 2024-11-08 12:32:02 +08:00
2511968cb4 fix action list 2024-11-08 12:32:02 +08:00
f684e1c12e provider compatible in logs 2024-11-08 12:32:02 +08:00
be75a1e432 provider compatible in moderation 2024-11-08 12:32:02 +08:00
5a679ed396 provider compatible in model_config 2024-11-08 12:32:02 +08:00
0cfd676fd6 provider compatible 2024-11-08 12:32:02 +08:00
d997499ecf Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-08 11:33:42 +08:00
4d3ffbb6f0 fix: update repository URL format in plugin action component 2024-11-08 11:33:35 +08:00
a710858d09 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-08 11:32:34 +08:00
a75cef2c3b feat: add gray label color variables for dark and light themes 2024-11-08 11:31:48 +08:00
Yi
392db19ea2 chore: update the update plugin steps 2024-11-08 11:08:40 +08:00
Yi
7cb6039833 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-07 18:22:08 +08:00
Yi
e041a9e418 chore: upgrade button styling 2024-11-07 18:21:32 +08:00
281c6dc337 merge 2024-11-07 17:44:54 +08:00
7d7ade26ce fix(remote-files): fallback to get when remote server not support head method (#10370) 2024-11-07 17:39:52 +08:00
6aa2af215b refactor(iteration): introduce specific exceptions for iteration errors (#10366) 2024-11-07 17:39:52 +08:00
598d307afd refactor(knowledge-retrieval): improve error handling with custom exceptions (#10385) 2024-11-07 17:39:52 +08:00
39fdcfd7e9 refactor(tool-node): introduce specific exceptions for tool node errors (#10357) 2024-11-07 17:39:51 +08:00
47f638e5aa refactor(question_classifier): improve error handling with custom exceptions (#10365) 2024-11-07 17:39:51 +08:00
03b57d1f0a fixed: web api remote urls error (#10383)
Co-authored-by: hobo.l <hobo.l@binance.com>
2024-11-07 17:39:51 +08:00
99c84c423e Adjusted docker manifests and environment variables for OceanBase vector database (#10395) 2024-11-07 17:39:51 +08:00
omr
cc2cc56f25 fix typo: mMaximum -> Maximum (#10389) 2024-11-07 17:39:50 +08:00
393885ee16 fix: remove duplicated category “recommended” (#10375) 2024-11-07 17:39:50 +08:00
9143d460fa fix(model_runtime): remove vision from features for Claude 3.5 Haiku (#10360) 2024-11-07 17:39:49 +08:00
c0ff0cf7cf fix: remove unsupported vision in OpenRouter Haiku 3.5 (#10364) 2024-11-07 17:39:49 +08:00
9c90d98027 chore(ci): bring back poetry cache to speed up CI jobs (#10347) 2024-11-07 17:39:48 +08:00
0d74466f45 chore: lazy import sagemaker (#10342) 2024-11-07 17:39:48 +08:00
d2e293b9be chore: update translation for 'account' from '계좌' to '계정' (#10350) 2024-11-07 17:39:48 +08:00
8fae321b6a chore(ci): separate vector store tests into new workflow (#10354) 2024-11-07 17:39:47 +08:00
a2b42c9431 fix(api): remove fixed source attribute from FileApi (#10353) 2024-11-07 17:39:47 +08:00
0a4b256b5a feat: support png, gif, webp (#7947)
Co-authored-by: xuanson9699 <84961581+xuanson9699@users.noreply.github.com>
2024-11-07 17:39:47 +08:00
9f7124a79d Update README.md (#10332) 2024-11-07 17:39:46 +08:00
7a217534d1 Gitee AI tools (#10314) 2024-11-07 17:39:46 +08:00
a9ed0f0b42 feat: The SSRF request timeout configuration item is added (#10292) 2024-11-07 17:39:46 +08:00
545d2b2622 feat: add support for anthropic/claude-3-5-haiku through OpenRouter (#10331) 2024-11-07 17:39:45 +08:00
f114da4e81 feat(vannaai): add base_url configuration (#10294) 2024-11-07 17:39:45 +08:00
f4e3e3fc19 docs: remove the TOC part (#10324) 2024-11-07 17:39:45 +08:00
1277941821 fix: special prompt not work for comfyUI tool (#10307) 2024-11-07 17:39:45 +08:00
f500e6cf5b chore: update version to 0.11.0 across all relevant files (#10278) 2024-11-07 17:39:44 +08:00
a3b71830d0 fix: iteration none output error (#10295) 2024-11-07 17:39:44 +08:00
13b7e18a50 fix(http_request): improve parameter initialization and reorganize tests (#10297) 2024-11-07 17:39:44 +08:00
5f4bb12a1a fix typo: writeOpner to writeOpener (#10290) 2024-11-07 17:39:43 +08:00
781e8e1a4a fix: handle KeyError when accessing rules in CleanProcessor.clean (#10258) 2024-11-07 17:39:43 +08:00
eux
c721617e19 fix: borken faq url in CONTRIBUTING.md (#10275) 2024-11-07 17:39:43 +08:00
6b51e81de1 feat: add xAI model provider (#10272) 2024-11-07 17:39:43 +08:00
736719745c feat(model_runtime): add new model 'claude-3-5-haiku-20241022' (#10285) 2024-11-07 17:39:43 +08:00
e0e4a6f819 fix(model_runtime): fix wrong max_tokens for Claude 3.5 Haiku on Amazon Bedrock (#10286) 2024-11-07 17:39:42 +08:00
6ab6b9cc40 feat(model): add validation for custom disclaimer length (#10287) 2024-11-07 17:39:42 +08:00
52eb18937e fix(node): correct file property name in function switch (#10284) 2024-11-07 17:39:42 +08:00
aab1ab692a refactor the logic of refreshing access_token (#10068) 2024-11-07 17:39:40 +08:00
3e7f38d904 chore: translate i18n files (#10273)
Co-authored-by: laipz8200 <16485841+laipz8200@users.noreply.github.com>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
2024-11-07 17:39:15 +08:00
94c5e36334 Updates: Add mplfonts library for customizing matplotlib fonts and Va… (#9903) 2024-11-07 17:39:14 +08:00
baaa3ae02c feat: Iteration node support parallel mode (#9493) 2024-11-07 17:39:09 +08:00
623b27583b fix(workflow): handle else condition branch addition error in if-else node (#10257) 2024-11-07 17:38:39 +08:00
93e9aeb4e9 feat(document_extractor): support tool file in document extractor (#10217) 2024-11-07 17:38:39 +08:00
391ad7734e feat: support Claude 3.5 Haiku on Amazon Bedrock (#10265) 2024-11-07 17:38:38 +08:00
c5422af400 refactor(parameter_extractor): implement custom error classes (#10260) 2024-11-07 17:38:37 +08:00
65a04ee0be fix: buitin tool aippt (#10234)
Co-authored-by: jinqi.guo <jinqi.guo@ubtrobot.com>
2024-11-07 17:38:37 +08:00
84c35aef6c fix(validation): allow to use 0 in the inputs form (#10255) 2024-11-07 17:38:37 +08:00
c2b4845719 chore(Dockerfile): upgrade zlib arm64 (#10244) 2024-11-07 17:38:37 +08:00
cd0f10567f Using a dedicated interface to obtain the token credential for the gitee.ai provider (#10243) 2024-11-07 17:38:36 +08:00
454b755c6b feat(workflow): add configurable workflow file upload limit (#10176)
Co-authored-by: JzoNg <jzongcode@gmail.com>
2024-11-07 17:38:35 +08:00
352c1fc370 fix: missing working directory parameter in script (#10226) 2024-11-07 17:38:35 +08:00
181eb6038f refactor(list_operator): replace ValueError with InvalidKeyError (#10222) 2024-11-07 17:38:34 +08:00
c32cbeb29a refactor(workflow): introduce specific error handling for LLM nodes (#10221) 2024-11-07 17:38:34 +08:00
62f8c875c8 refactor(http_request): add custom exception handling for HTTP request nodes (#10219) 2024-11-07 17:38:33 +08:00
baed53bbfa refactor(workflow): introduce specific exceptions for code validation (#10218) 2024-11-07 17:38:32 +08:00
565a0d992a chore(llm_node): remove unnecessary type ignore for context assignment (#10216) 2024-11-07 17:38:32 +08:00
e90a06a7b7 fix the ssrf of docx file extractor external images (#10237) 2024-11-07 17:38:31 +08:00
5a0b22dbd4 Modify translation (#10213) 2024-11-07 17:38:30 +08:00
31445c3782 Add Lindorm as a VDB choice (#10202)
Co-authored-by: jiangzhijie <jiangzhijie.jzj@alibaba-inc.com>
2024-11-07 17:38:30 +08:00
8e6f5f4bb0 Fix/10199 application error a client side exception has occurred see the browser console for more information (#10211) 2024-11-07 17:38:29 +08:00
e259b360c2 refactor(validation): improve input validation logic (#10175) 2024-11-07 17:38:29 +08:00
762dec2dc4 chore(list_operator): refine exception handling for error specificity (#10206) 2024-11-07 17:38:28 +08:00
ada7f5c30f fix(document_extractor): update base exception class (#10208) 2024-11-07 17:38:28 +08:00
6b965eaea3 Feat : add LLM model indicator in prompt generator (#10187) 2024-11-07 17:38:27 +08:00
ea67bc1166 chore: enable vision support for models in OpenRouter that should have supported vision (#10191) 2024-11-07 17:38:26 +08:00
01e8f6066a chore : code generator preview hint (#10188) 2024-11-07 17:38:26 +08:00
39effd350e fix: webapp upload file (#10195) 2024-11-07 17:38:25 +08:00
00bfb35759 fix(api): replace current_user with end_user in file upload (#10194) 2024-11-07 17:38:25 +08:00
101d9798f0 feat(document_extractor): integrate unstructured API for PPTX extraction (#10180) 2024-11-07 17:38:24 +08:00
ba48754be6 fix(tools): suppress RuntimeWarnings in podcast audio generator (#10182) 2024-11-07 17:38:24 +08:00
d963df32b9 [fix] fix the bug that modify document name not effective (#10154) 2024-11-07 17:38:23 +08:00
67ce763377 fix(workflow model): ensure consistent timestamp updating (#10172) 2024-11-07 17:38:23 +08:00
a32c0ef43c fix: Cannot find declaration to go to CLEAN_DAY_SETTING (#10157)
Co-authored-by: 刘江波 <liujiangbo1@xiaomi.com>
2024-11-07 17:38:22 +08:00
6e03c10285 feat: add gpustack model provider (#10158) 2024-11-07 17:38:21 +08:00
8f14c422a7 refactor(tools): Avoid warnings. (#10161) 2024-11-07 17:38:20 +08:00
07787366cd refactor(migration/model): update column types for workflow schema (#10160) 2024-11-07 17:38:20 +08:00
8f2d3b6743 Feat/add-remote-file-upload-api (#9906) 2024-11-07 17:38:19 +08:00
e7bc863f26 fix: upload remote image preview (#9952) 2024-11-07 17:38:19 +08:00
1d411e195a clean un-allowed special charters when doing indexing estimate (#10153) 2024-11-07 17:38:19 +08:00
2a7ae6b0df refactor(service): handle unsupported DSL version with warning (#10151) 2024-11-07 17:38:18 +08:00
8f8a3f4318 Add VESSL AI OpenAI API-compatible model provider and LLM model (#9474)
Co-authored-by: moon <moon@vessl.ai>
2024-11-07 17:38:18 +08:00
4b89dba3a5 feat: synchronize input/output variables in the panel with generated code by the code generator (#10150) 2024-11-07 17:38:18 +08:00
19c0d1fbf8 Refined README for better reading experience. (#10143) 2024-11-07 17:38:17 +08:00
94cd4912e1 add llm: ernie-4.0-turbo-128k of wenxin (#10135)
Co-authored-by: Pigeon姚宏锋 <pigeon.yhf@galaxyoversea.com>
2024-11-07 17:38:17 +08:00
602f75bb30 fix: avoid unexpected error when create knowledge base with baidu vector database and wenxin embedding model (#10130) 2024-11-07 17:38:15 +08:00
c2810de952 fix: bar chart issue with duplicate x-axis labels being incorrectly ignored (#10134)
Co-authored-by: liusurong.lsr <liusurong.lsr@alibaba-inc.com>
2024-11-07 17:38:14 +08:00
1b645c1cc9 fix issue: query is none when doing retrieval (#10129) 2024-11-07 17:38:14 +08:00
ae3482e0b4 Fix/rerank validation issue (#10131)
Co-authored-by: Yi <yxiaoisme@gmail.com>
2024-11-07 17:38:14 +08:00
76c265f781 Feat/update knowledge api url (#10102)
Co-authored-by: nite-knite <nkCoding@gmail.com>
2024-11-07 17:38:12 +08:00
omr
b1946c60d8 fix: optimize unique document filtering with set (#10082) 2024-11-07 17:38:12 +08:00
c1c13cf828 fix(Dockerfile): conditionally install zlib1g based on architecture (#10118) 2024-11-07 17:38:11 +08:00
e4a48e28e5 fix: log detail panel not showing any message when total count greate… (#10119) 2024-11-07 17:38:11 +08:00
67efcbd6bb fix issue: update document segment setting failed (#10107) 2024-11-07 17:38:11 +08:00
7466061e5a fix: Version '1:1.3.dfsg+really1.3.1-1' for 'zlib1g' was not found (#10096) 2024-11-07 17:38:10 +08:00
b7534b764d feat(app_dsl_service): enhance error handling and DSL version management (#10108) 2024-11-07 17:38:09 +08:00
eb335ed464 chore: save uploaded file extension as lower case (#10111) 2024-11-07 17:38:09 +08:00
4d9e7c1884 refactor(version): simplify version comparison logic (#10109) 2024-11-07 17:38:09 +08:00
8f7cac6bde fix: view logs in prompt, no response when clicked (#10093)
Co-authored-by: zhanganguo <zhanganguo@lixiang.com>
2024-11-07 17:38:08 +08:00
d4608f0571 chore: remove an unnecessary link (#10088) 2024-11-07 17:38:08 +08:00
9f27b5bb12 feat: enhance comfyui workflow (#10085) 2024-11-07 17:38:07 +08:00
fb9c54e35f build: update docker login action (#10050) 2024-11-07 17:38:07 +08:00
dea45682bc chore: update type definition to resolve lint error in Base usage at text-editor.tsx (#10083) 2024-11-07 17:38:06 +08:00
a0abd5d077 improve: significantly speed up the server launching time by async preloading tool providers (#9146) 2024-11-07 17:38:05 +08:00
924dbc128d fix azure chatgpt o1 parameter error (#10067) 2024-11-07 17:38:05 +08:00
b76aa11919 Revert "chore: improve validation and handler of logging timezone with TimezoneName" (#10077) 2024-11-07 17:38:05 +08:00
7971efd23e fix the typos in the hit testing template (#10072) 2024-11-07 17:38:04 +08:00
b8c2e5359b Fixed the issue where recall the knowledge base in the iteration of the workflow and report errors when executing (#10060) 2024-11-07 17:38:04 +08:00
952847ed29 chore: Set file size limits for video and audio uploads from docker env (#10063) 2024-11-07 17:38:03 +08:00
4e4a8a327b fix: prevent onChange during IME composition (#10059) 2024-11-07 17:38:03 +08:00
8d1591e5d5 fix: fix poetry install command in devcontainer (#9507) 2024-11-07 17:38:03 +08:00
4b8896e034 fix(workflow): refine variable type checks in LLMNode (#10051) 2024-11-07 17:38:02 +08:00
c2d3464a17 chore: mount config file of sandbox (#8576) 2024-11-07 17:38:02 +08:00
0886c6f224 fix: resolve the incorrect model name of hunyuan-standard-256k (#10052) 2024-11-07 17:38:01 +08:00
1ee4c13758 chore: use dify_config.TIDB_SPEND_LIMIT instead of constant value (#10038) 2024-11-07 17:38:01 +08:00
c293aceec1 feat: /conversations api response add 'update_at' field,and update api docs add sort_by parameter (#10043) 2024-11-07 17:38:01 +08:00
4d5752fc94 feat: add YAML type in document extractor node (#9997) 2024-11-07 17:38:00 +08:00
c615ed57b9 add PROMPT_GENERATION_MAX_TOKENS and CODE_GENERATION_MAX_TOKENS in docker enviromment (#10040) 2024-11-07 17:38:00 +08:00
020d4baf92 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-07 16:52:26 +08:00
3f8a10613d refactor: remove unused fetchInstalledPluginList function and integrate useInstalledPluginList hook 2024-11-07 16:52:22 +08:00
53fa13f007 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-07 16:44:46 +08:00
d00e1067bf fix: develop page docs style 2024-11-07 16:44:05 +08:00
2dd9c64d34 chore: use query 2024-11-07 16:42:44 +08:00
b7c40579b2 build: update tailwind 2024-11-07 16:41:35 +08:00
c7f8a0fc7b Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-07 16:06:13 +08:00
de24d9c145 fix: handle empty fetched releases and update locale usage in plugin item 2024-11-07 16:06:08 +08:00
6357e1516e build: update react-hook-form 2024-11-07 15:47:07 +08:00
b83dc5ab99 fix: marketplace 2024-11-07 15:37:39 +08:00
Yi
65285965b6 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-07 15:31:42 +08:00
Yi
035c9eb147 fix: propagation from closing tag in filters 2024-11-07 15:31:11 +08:00
91b3aec292 feat: add use tools 2024-11-07 15:24:17 +08:00
d3a9747bbd build: update @floating-ui/react 2024-11-07 15:22:00 +08:00
6a99fab92f fix: marketplace 2024-11-07 15:07:07 +08:00
Yi
2a590f6d2b Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-07 13:42:45 +08:00
Yi
117b0f20dd chore: update the plugin install dropdown styling 2024-11-07 13:42:34 +08:00
c445f747b7 fix: prompt editor 2024-11-07 12:11:26 +08:00
8ed0963c6b Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-07 11:46:32 +08:00
Yi
6c47e0b5d1 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-07 11:37:21 +08:00
Yi
e99e87269e chore: update the selectPackage component 2024-11-07 11:37:05 +08:00
0a1319548a build: update react markdown
build: update react markdown

build: update react markdown
2024-11-07 11:34:55 +08:00
b1242ba1ac build: init @tanstack/react-query 2024-11-07 09:44:36 +08:00
fde0e6c7f9 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-06 18:43:31 +08:00
92153328ea feat: add loading state for installed plugin list in context and update PluginsPanel to display loading indicator 2024-11-06 18:43:27 +08:00
e66ba6ffdd chore: fill repo url 2024-11-06 18:13:15 +08:00
a7e320dc25 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-06 16:58:56 +08:00
5008d9f4a0 feat: add onSuccess callback to InstallFromGitHub and update plugin list on install actions 2024-11-06 16:58:52 +08:00
Yi
912f84777b Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-06 16:49:29 +08:00
Yi
4c0e4e490a chore: update the 'Update plugin from GitHub' 2024-11-06 16:49:16 +08:00
306843fe6a chore: plugin info 2024-11-06 16:42:32 +08:00
b6a4af4041 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-06 16:18:25 +08:00
cce39b85e9 feat: enhance plugin filtering with tags support 2024-11-06 16:18:18 +08:00
057d380119 feat: add install model plugin 2024-11-06 15:57:50 +08:00
4cf9ff6132 chore: remove log 2024-11-06 15:44:49 +08:00
4cfbcd9c79 feat: add install 2024-11-06 15:42:48 +08:00
fcde5b5c9e fix: detail 2024-11-06 15:37:12 +08:00
21b3703bd8 fix: i18n 2024-11-06 15:23:38 +08:00
9025e85ca5 locale 2024-11-06 15:00:37 +08:00
bc43e3a9fe card locale 2024-11-06 14:52:47 +08:00
319a54aa2f i18n 2024-11-06 14:37:20 +08:00
7c2ab21c9c i18n 2024-11-06 11:55:19 +08:00
0d08b6cf51 fix: enhance plugin installation and update handling 2024-11-06 09:50:05 +08:00
Yi
61a70e7a71 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-05 17:53:03 +08:00
Yi
bde1261b8c chore: update installFromGitHub component 2024-11-05 17:52:47 +08:00
08bb6bf858 fix: tags 2024-11-05 17:35:47 +08:00
1003190dc0 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-05 16:54:57 +08:00
4c516a50b8 fix: update type from InstalledPlugin to PluginDetail in detail-header component 2024-11-05 16:53:54 +08:00
8058a1dbe4 feat: handle update 2024-11-05 16:47:26 +08:00
Yi
8533ded335 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-05 16:36:55 +08:00
1a547b0db9 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-05 16:25:38 +08:00
0b90625e57 feat: integrate GitHub API for plugin version check and add access token support 2024-11-05 16:25:20 +08:00
e989c1f3aa feat: fill update install payload 2024-11-05 16:04:52 +08:00
da15a25cf5 feat: add update from github modal content 2024-11-05 15:27:08 +08:00
43254ceeb0 chore: temp types 2024-11-05 15:22:48 +08:00
Yi
faf550164d Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-05 15:21:54 +08:00
Yi
1a4234347a chore: update the installFromGitHub logic 2024-11-05 15:21:45 +08:00
85947efcfa temp: change payload types 2024-11-05 15:21:07 +08:00
474ea97fc7 feat: add update to modal context 2024-11-05 15:11:55 +08:00
a3becde6d8 feat: plugin tags 2024-11-05 15:07:24 +08:00
Yi
c40544a134 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-05 11:41:03 +08:00
Yi
4028bb4f58 chore: update installPluginFromGitHub component 2024-11-05 11:40:52 +08:00
52268460a1 fix: update dependency in Empty component to improve rendering logic 2024-11-05 11:19:04 +08:00
b64298c458 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-11-05 11:05:20 +08:00
16dee11589 feat: add Empty component to installed plugin list 2024-11-05 11:05:14 +08:00
Yi
691dbf9d17 chore: update plugin from GitHub 2024-11-05 11:02:57 +08:00
ebebbb684b endpoints list 2024-11-02 15:36:38 +08:00
cee51ac084 need author judgement 2024-11-02 15:36:38 +08:00
ef00ad0417 action list 2024-11-02 15:36:38 +08:00
c37615cd33 plugin detail delete handler 2024-11-02 15:36:38 +08:00
a387cfbc9a model list in plugin detail 2024-11-02 15:36:38 +08:00
04fdb4af0f plugin header operation 2024-11-02 15:36:38 +08:00
57f9a41e7f plugin detail selecting 2024-11-02 15:36:38 +08:00
eb8b827906 verified tag 2024-11-02 15:36:38 +08:00
99a9bf6d56 feat: support search from marketplace list 2024-11-01 18:36:24 +08:00
Yi
ceae69b773 chore: update the workspace selector 2024-11-01 16:48:40 +08:00
3c8548c562 feat: create tool model 2024-11-01 16:14:01 +08:00
8874837dc3 feat: plugin tasks 2024-11-01 16:08:31 +08:00
Yi
40e171c2c6 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-01 15:40:06 +08:00
Yi
824ed7d6c2 chore: plugin button 2024-11-01 15:39:55 +08:00
4caa8f38bc hide plugin detail 2024-11-01 15:33:15 +08:00
5d5db7c6c1 fix: key too long breaks ui 2024-11-01 15:28:10 +08:00
ca50522f80 feat: set tool params 2024-11-01 15:21:38 +08:00
Yi
2fd4b6e6d2 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-11-01 14:56:11 +08:00
Yi
c503e8ebc9 chore: install package from GitHub 2024-11-01 14:55:56 +08:00
245bb02c88 chore: add on start to install 2024-11-01 14:51:35 +08:00
9f08206503 feat: can choose tool in agent page 2024-11-01 14:41:27 +08:00
66be03f622 fix: search tools ui and some ui problem 2024-11-01 14:02:10 +08:00
1a92064260 remove unused components 2024-11-01 12:09:17 +08:00
7752f374e5 Revert "update model provider api responses"
This reverts commit c8dc5e4849.
2024-11-01 12:00:54 +08:00
e7dcc53b55 Revert "load balance"
This reverts commit 378a9dd850.
2024-11-01 12:00:54 +08:00
22766c27c7 Revert "model list of provider"
This reverts commit 766ac3e255.
2024-11-01 12:00:54 +08:00
c82b641357 Revert "get credentials of provider"
This reverts commit 72ef04d3e4.
2024-11-01 12:00:54 +08:00
1d871dae0d Revert "credentials of models"
This reverts commit 2ed73b763d.
2024-11-01 12:00:54 +08:00
c39be7852f Revert "parameters and rules"
This reverts commit 22696fa75b.
2024-11-01 12:00:54 +08:00
f2a5da918b Revert "other providers"
This reverts commit 339dfe5e02.
2024-11-01 12:00:54 +08:00
930425b896 Revert "annotation config"
This reverts commit 633768cd2a.
2024-11-01 12:00:54 +08:00
32e4efb524 Revert "logs"
This reverts commit 06729f6d9d.
2024-11-01 12:00:54 +08:00
96c3ec91af click handle of provider card 2024-11-01 12:00:54 +08:00
f257184b00 provider card & link 2024-11-01 12:00:54 +08:00
581d09895e search model in provider settings 2024-11-01 12:00:54 +08:00
197f1b3957 feat: search box 2024-11-01 11:26:36 +08:00
207b589458 chore: toolpicker add trigger 2024-11-01 11:22:18 +08:00
b5be6bacef fix: marketplace list 2024-10-31 18:54:33 +08:00
bca99cf4f8 feat: tool picker 2024-10-31 18:37:50 +08:00
0e873223d7 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-10-31 18:32:32 +08:00
133b487566 feat: Enhance plugin item links and increase value display width 2024-10-31 18:32:25 +08:00
a9e8eb1c7e fix: marketplace list 2024-10-31 18:31:36 +08:00
bf2b9bb898 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-10-31 16:44:08 +08:00
837990cb80 feat: Add loading to delete confirmation dialog 2024-10-31 16:44:04 +08:00
4be2edd934 feat: plugin list tasks 2024-10-31 16:27:24 +08:00
7d4f8e0082 Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-10-31 16:20:31 +08:00
36ab121b87 feat: plugin uninstall & plugin list filtering 2024-10-31 16:20:25 +08:00
7f2980fbc0 chore: fix index show 2024-10-31 16:10:48 +08:00
ae21d48132 feat: list flat view 2024-10-31 15:59:39 +08:00
8af8a0f46d fix: tool list 2024-10-31 15:41:02 +08:00
4adb61d6c7 refactor: Update marketplace API prefix in layout.tsx 2024-10-31 13:45:46 +08:00
7dd7f06f7d chore: handle icon toggle fold 2024-10-31 12:00:11 +08:00
074e660a67 feat: can add tree view 2024-10-31 11:37:55 +08:00
06729f6d9d logs 2024-10-31 09:06:10 +08:00
633768cd2a annotation config 2024-10-31 08:43:12 +08:00
339dfe5e02 other providers 2024-10-31 08:22:14 +08:00
22696fa75b parameters and rules 2024-10-31 08:22:14 +08:00
2ed73b763d credentials of models 2024-10-31 08:22:14 +08:00
72ef04d3e4 get credentials of provider 2024-10-31 08:22:14 +08:00
766ac3e255 model list of provider 2024-10-31 08:22:14 +08:00
378a9dd850 load balance 2024-10-31 08:22:14 +08:00
c8dc5e4849 update model provider api responses 2024-10-31 08:22:14 +08:00
1387c6bd1c update style of agent tool list 2024-10-31 08:22:14 +08:00
fda21f6b05 fix size of icon 2024-10-31 08:22:14 +08:00
8c25915d2b action panel tabs 2024-10-31 08:22:14 +08:00
aba48749da action panel header 2024-10-31 08:22:14 +08:00
35a66ffe9f tool actions 2024-10-31 08:22:14 +08:00
3db9174f55 fix: marketplace list 2024-10-30 17:24:46 +08:00
02854b273f fix: marketplace 2024-10-30 16:36:08 +08:00
fd0a830816 feat: marketplace list 2024-10-30 15:27:38 +08:00
e7fb92e169 feat: marketplace list 2024-10-30 15:16:13 +08:00
9c4e809799 chore: tiny css and i18n 2024-10-30 14:27:39 +08:00
169eb32662 chore: toolpicker ui 2024-10-30 14:24:00 +08:00
0f4291bd36 merge main 2024-10-30 11:20:20 +08:00
14b641557a feat: marketplace sort 2024-10-30 11:07:13 +08:00
9c963d6f69 build: echarts update 2024-10-30 10:29:10 +08:00
96abeda2e7 build: echarts update 2024-10-30 10:28:22 +08:00
62fa90e30e feat: marketplace sort 2024-10-29 18:14:05 +08:00
7e378e219c chore: add missing i18n 2024-10-29 18:11:09 +08:00
34652010f5 feat: add market url prefix 2024-10-29 18:09:59 +08:00
082f6f6a5f build: update deps 2024-10-29 17:47:06 +08:00
6f52edb157 feat: plugin permission 2024-10-29 17:18:23 +08:00
0dcbb34cab feat: handle from market install 2024-10-29 16:33:46 +08:00
0886d7bb8b feat: marketplace list 2024-10-29 15:23:29 +08:00
e65a47cff7 feat: marketplace list 2024-10-29 15:23:29 +08:00
36c01d89c9 fix: cros promblem 2024-10-29 15:19:47 +08:00
6726ca102e feat: add get marketpalce get 2024-10-29 14:53:14 +08:00
e135707f88 chore: plugin icon not show 2024-10-29 11:57:21 +08:00
f2765b9d31 feat: fetch plugin icon 2024-10-29 11:47:23 +08:00
1e0877dcbf feat: marketplace list 2024-10-29 11:33:52 +08:00
9a65c3391b feat: marketplace list 2024-10-29 10:51:41 +08:00
ca9e23d6ea fix: check status 2024-10-28 18:36:23 +08:00
c4d6f9e179 build: update react-easy-crop 2024-10-28 16:46:35 +08:00
0cec6195a3 test 2024-10-28 16:33:02 +08:00
966d42a4db Merge branch 'main' into feat/plugins 2024-10-28 15:53:39 +08:00
15c33ba7f3 build: update lexical deps 2024-10-28 15:33:22 +08:00
ed7f74c99c build: update i18n pkg 2024-10-28 15:32:39 +08:00
1db4139b5a build: update deps
build: update classnames

build: update types

build: update uuid

build: update emoji-mart

build: update use-context-selector

build: update ui component deps
2024-10-28 15:24:44 +08:00
0d85d44de5 feat: marketplace list 2024-10-28 11:50:45 +08:00
f0f1bfa5d9 build: fix eslint 2024-10-28 11:03:43 +08:00
9161ce481e Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins 2024-10-28 10:59:39 +08:00
c760902e72 chore: remove useless rule 2024-10-28 10:56:52 +08:00
7c8c15ef1a test no use rule 2024-10-28 10:56:31 +08:00
765eb282f3 merge main 2024-10-28 10:51:02 +08:00
0e2b38dddc build: update qrcode.react 2024-10-25 18:03:25 +08:00
92e4b3304c build: update deps
build: update sass

build: update lodash-es

build: update qs & js-cookie
2024-10-25 17:58:19 +08:00
c777d55a1c feat: marketplace install 2024-10-25 16:46:02 +08:00
cd27ae4319 build: update testing-library 2024-10-25 13:01:39 +08:00
bd82c7edac build: update storybook 2024-10-25 13:01:39 +08:00
d5043c6628 build: update husky & lint-staged 2024-10-25 13:01:39 +08:00
d9a0584052 build: update eslint & fix some case 2024-10-25 12:51:11 +08:00
3e011109ad merge main 2024-10-25 11:26:49 +08:00
bdb990eb90 merge main 2024-10-25 11:26:48 +08:00
ae00211691 feat: marketplace types 2024-10-25 11:15:32 +08:00
0ef35a0ee0 fix: enable_marketplace 2024-10-25 10:41:25 +08:00
9a9d90ad7f feat: can install 2024-10-24 17:24:46 +08:00
606fc7be0c feat: support upload pkg 2024-10-24 17:14:17 +08:00
d7def41acc feat: fetch debug key api 2024-10-24 16:07:46 +08:00
b6a560ce86 style: lint 2024-10-24 14:52:07 +08:00
25f34f6703 fix: marketplace plugin type icon 2024-10-24 14:21:12 +08:00
7daa365564 chore: to common install comp 2024-10-24 11:10:44 +08:00
13ccd294cb fix: install error title not update 2024-10-23 17:55:25 +08:00
d357f359ab feat: support install failed 2024-10-23 17:52:39 +08:00
474cedf653 feat: debug info api 2024-10-23 17:19:54 +08:00
5d3c88a0b3 chore: bump mermaid from 10.4.0 to 10.9.3 2024-10-23 17:02:38 +08:00
ae2c76bda2 feat: install from marketplace 2024-10-23 16:42:24 +08:00
c46b5f2fd0 feat: handle install search params and hide 2024-10-23 15:26:31 +08:00
2cb7b73ee7 feat: handle import from marketplace 2024-10-23 15:00:31 +08:00
8d8d5b5235 chore: handle verified 2024-10-23 11:45:36 +08:00
15acfffd60 chore: some ui and mock data 2024-10-23 11:40:56 +08:00
Yi
18a266eac2 chore: i18n for install from GitHub section 2024-10-22 18:39:23 +08:00
7751070da8 chore: install from local i18n 2024-10-22 18:11:54 +08:00
Yi
fa8c3d0d7b Resolved merge conflicts by removing conflicting files 2024-10-22 17:58:41 +08:00
Yi
a567cff809 chore: update the install from GitHub component 2024-10-22 17:51:14 +08:00
583b0e9f97 chore: remove replicated types 2024-10-22 17:29:58 +08:00
5fddb23516 feat: install progress 2024-10-22 17:21:44 +08:00
Yi
0e52971997 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-22 16:40:50 +08:00
Yi
1387f406a3 fix: log format 2024-10-22 16:40:27 +08:00
ff31f0540a style: lint 2024-10-22 15:32:58 +08:00
4873e6e2a1 build: fix eslint stylistic 2024-10-22 15:32:48 +08:00
510ce057f7 chore: add package manager setting to vscode setting example 2024-10-22 14:18:55 +08:00
0e53cc0e8c fix: eslint indent 2024-10-22 14:15:22 +08:00
Yi
0b8c896481 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-22 13:43:15 +08:00
Yi
15fe635465 chore: install package from GitHub 2024-10-22 13:43:01 +08:00
f8c3189f4d build: fix eslint undef 2024-10-22 11:43:23 +08:00
f215db87e3 build: fix eslint undef 2024-10-22 11:36:42 +08:00
67d02212b4 chore: pnpm 2024-10-22 11:18:30 +08:00
cff9adaf8e chore: tools ts problems 2024-10-22 11:06:28 +08:00
cdd2a40086 style: minimium codemod 2024-10-22 02:24:59 +00:00
024028bc52 build: sync eslint rule 2024-10-22 10:24:10 +08:00
0ae085b48a build: add eslint common rule 2024-10-22 10:24:10 +08:00
2094c54951 build: update eslint config antfu 2024-10-22 10:24:10 +08:00
f4f11135d3 build: using eslint flat config 2024-10-22 10:24:10 +08:00
8e9d7a229d feat: scroll to view and fix action hidden 2024-10-21 18:21:45 +08:00
8f49572f85 chore: from marketplace tilte ui 2024-10-21 15:07:18 +08:00
5aa7696cc3 update style of action list 2024-10-21 11:34:21 +08:00
15dd79e822 provider detail data binding 2024-10-21 11:34:21 +08:00
4651ab4195 new style of provider detail 2024-10-21 11:34:21 +08:00
5e3160e6f6 fix title & description of tool provider 2024-10-21 11:34:21 +08:00
973cd126bb create & update endpoint 2024-10-21 11:34:21 +08:00
ebaf8766ef endpoint form 2024-10-21 11:34:21 +08:00
d2190e9c3a remove endpoint 2024-10-21 11:34:21 +08:00
37f55098fe switch endpoint service state 2024-10-21 11:34:21 +08:00
b1771194cc servise of endpoints 2024-10-21 11:34:21 +08:00
0279bd8c75 endpoint card databing 2024-10-21 11:34:21 +08:00
5e077e4ce8 endpoints data binding 2024-10-21 11:34:21 +08:00
64067e1f20 plugin detail header operations 2024-10-21 11:34:21 +08:00
5295c72ca1 endpoints mock data 2024-10-21 11:34:21 +08:00
1ecea62052 add verified tag 2024-10-21 11:34:21 +08:00
307af29b65 add plugin description 2024-10-21 11:34:21 +08:00
10190a9aa5 plugin detail header data binding 2024-10-21 11:34:21 +08:00
7c5c35600c plugin detail type 2024-10-21 11:34:21 +08:00
63b333cdb1 modify plugin detail panel 2024-10-21 11:34:21 +08:00
a6776190bd chore: update remix icon 2024-10-21 11:34:19 +08:00
9577cbac27 build: docker use pnpm 2024-10-21 11:27:01 +08:00
f6ae13abad ci: migrate to pnpm 2024-10-21 11:14:30 +08:00
f3d501e7d5 fix: gen-icon script phantom deps 2024-10-21 10:43:52 +08:00
2eab8fcc33 build: switch to pnpm 2024-10-21 10:43:52 +08:00
bdb81fe20d feat: choose tool sticky 2024-10-18 18:18:59 +08:00
Yi
0f60fe7f2a chore: update workspace name (truncated for long name) 2024-10-18 14:17:53 +08:00
Yi
425f624de5 chore: add plugin panel 2024-10-18 14:02:40 +08:00
Yi
b1919745e2 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-18 14:01:34 +08:00
9a242bcac9 Merge branch 'main' into feat/plugins 2024-10-18 09:59:43 +08:00
Yi
a6109a60b8 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-17 15:22:08 +08:00
Yi
28f7bbf83a chore: installation progress bar 2024-10-17 15:21:56 +08:00
cac04c5f3c refactor: chagne card to client component 2024-10-17 15:06:06 +08:00
18f5f9cc37 feat: plugin upgrade 2024-10-16 18:05:55 +08:00
1787c5c93f chore: handle tag name too long 2024-10-16 16:39:47 +08:00
f981494613 feat: plugin support emoji icon 2024-10-16 16:28:19 +08:00
fbc853af92 feat: remove config 2024-10-16 16:28:18 +08:00
1a64c660ba enable marketplace 2024-10-16 15:50:18 +08:00
846555af1b fix: action buttion ui 2024-10-16 11:45:25 +08:00
bca94854f7 feat: plugin info 2024-10-16 11:30:04 +08:00
1bd70bd8bf chore: copy button 2024-10-16 11:05:51 +08:00
d1dcd39191 feat: add debug info i18n and extract common to components 2024-10-16 10:52:16 +08:00
35384bda41 chore: refactor card loading 2024-10-15 22:52:57 +08:00
89fb6eb648 chore: set hideCornerMark to optional 2024-10-15 21:45:30 +08:00
aa61a890b2 chore: change downloadCount to optional 2024-10-15 21:43:20 +08:00
31ece363c3 chore: chagne mangament attr name 2024-10-15 21:35:56 +08:00
70a5d78cc5 chore: priviege i18n 2024-10-15 21:31:06 +08:00
57f4dfdb6f feat: add permission data logic 2024-10-15 19:20:59 +08:00
Yi
aa9028a607 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-15 15:48:05 +08:00
Yi
d83f94c55c fix: set constrain for uploading packages only works in the Plugins tab 2024-10-15 15:47:54 +08:00
a8c5e0b0b0 tool list item click 2024-10-15 15:13:00 +08:00
Yi
177e8cbf73 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-15 14:57:37 +08:00
Yi
23828fd15a Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-15 14:57:11 +08:00
2cc37ac8e5 tool list 2024-10-15 14:57:00 +08:00
Yi
c9ee1e9ff2 feat: install difypkg ui 2024-10-15 14:56:59 +08:00
4f10f5d5f4 chore: hover show action 2024-10-15 11:57:44 +08:00
Yi
c48c84674e Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-15 11:02:45 +08:00
Yi
1e9fbbf41b fix: dynamic sub header color 2024-10-15 11:02:25 +08:00
4dd144ce43 tools list 2024-10-15 10:41:10 +08:00
Yi
a387ff1c38 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-14 18:43:24 +08:00
Yi
a9e367e6de feat: use-uploader hook 2024-10-14 18:43:08 +08:00
e2fec587f8 feat: from marketplace 2024-10-14 18:35:13 +08:00
39a6f0943d marketplace 2024-10-14 12:38:53 +08:00
684896d100 merge main 2024-10-14 10:29:52 +08:00
54f911f6cd endpoints 2024-10-13 10:49:55 +08:00
0e5c16d0c2 feat: view to ui and fix some ui promblem 2024-10-12 18:15:11 +08:00
b8cd6ea478 feat: support view choose 2024-10-12 17:36:35 +08:00
fc61fd0f50 action list 2024-10-12 17:08:45 +08:00
2fbfc988c4 plugin panel detail 2024-10-12 16:37:57 +08:00
99f5fea001 plugin detail panel header 2024-10-12 16:37:57 +08:00
ecd2a1be9f marketplace 2024-10-12 16:34:18 +08:00
49ee9ca5f1 feat: tool item support action 2024-10-12 16:04:16 +08:00
6d0eef12b1 feat: split tools data to out and add demo 2024-10-12 14:30:46 +08:00
c1e0a939b0 marketplace 2024-10-12 12:46:49 +08:00
060a894bd1 interaction of plugin detail panel 2024-10-12 12:36:29 +08:00
c75e02b5b2 update provider card 2024-10-12 12:36:29 +08:00
fcf43ee845 plugin page context 2024-10-12 11:33:12 +08:00
466f61d044 relocate file 2024-10-12 11:05:03 +08:00
27ae74af50 hook 2024-10-12 11:03:00 +08:00
8dd941e3d2 chore: instal plug add tag 2024-10-11 18:18:32 +08:00
dec4bf6b98 fix: install modal item server 2024-10-11 18:06:23 +08:00
e2c33fc40f fix: plugin item i18n 2024-10-11 18:05:45 +08:00
Yi
c74e59d1f4 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-11 17:28:27 +08:00
Yi
1fcb902715 feat: add cards to "install from marketplace" 2024-10-11 17:25:33 +08:00
c08f98218c hide search in other pages 2024-10-11 17:13:28 +08:00
Yi
c6377f6e38 fix: naming styles 2024-10-11 16:29:07 +08:00
Yi
3cb0a5bd68 Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-11 16:28:04 +08:00
Yi
95777d23e0 fix: naming styles 2024-10-11 16:27:31 +08:00
e7dc16fd08 provider card 2024-10-11 16:21:19 +08:00
495dec143c unconfigured provider 2024-10-11 16:21:19 +08:00
4cc6dfa232 new style of provider card 2024-10-11 16:21:18 +08:00
d1452d4af4 no provider installed 2024-10-11 16:21:18 +08:00
d68ca56b3a system default model 2024-10-11 16:21:18 +08:00
49856d8d17 setting content header & close button 2024-10-11 16:21:18 +08:00
902de72cc0 new style of settings 2024-10-11 16:21:18 +08:00
76f6b8d104 marketplace 2024-10-11 16:15:24 +08:00
f111e605c4 marketplace 2024-10-11 15:27:14 +08:00
b358ed3a5b fix: init workflow image crash 2024-10-11 14:31:14 +08:00
88dbf639e0 chore: enchance locale props 2024-10-11 14:24:43 +08:00
aa8b525b48 fix: icon tsx to router problem 2024-10-11 14:10:24 +08:00
Yi
c990bc61db feat: install plugins (partial) 2024-10-11 12:39:27 +08:00
6d7588f236 chore: fix ui 2024-10-10 17:53:13 +08:00
8257c7bf02 chore: remove useless data 2024-10-10 17:49:23 +08:00
946068967b feat: finish card components 2024-10-10 17:47:04 +08:00
19f5684960 feat: add label and fix some ui problem 2024-10-10 11:29:11 +08:00
6d62840aff chore: split card component 2024-10-10 10:40:26 +08:00
ab868ac979 fix: error igm 2024-10-10 10:25:25 +08:00
1d74e693ea chore: fix imge name 2024-10-10 10:22:18 +08:00
fa43d4202f feat: plugin item 2024-10-09 18:36:15 +08:00
6b29860788 feat: add installed 2024-10-09 18:12:14 +08:00
36800eeaba feat: base card component 2024-10-09 17:51:54 +08:00
Yi
67acd174ac add different styles to plugins and discover 2024-10-09 15:55:33 +08:00
b5edc64b2a plugin type switch 2024-10-09 15:06:09 +08:00
Yi
d00b2724cc Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins 2024-10-09 12:53:56 +08:00
Yi
43f87c0b86 make the drop plugin only appears when the user selects "plugins" 2024-10-09 12:53:43 +08:00
7a43f48c95 chore: add test page 2024-10-09 11:35:16 +08:00
58a913b09d marketplace 2024-10-08 17:58:05 +08:00
cd03795f2c chore: add endpoint types 2024-10-08 15:44:52 +08:00
36f8b5711d feat: plugin types 2024-09-30 15:29:53 +08:00
f9c48e9ea9 fix: eslint to find top dir 2024-09-29 18:27:36 +08:00
3b48f8c98e Merge branch 'main' into tp 2024-09-27 16:47:57 +08:00
cef1010cb5 style update 2024-09-27 16:47:01 +08:00
cb4875a3a7 chore: split the common tailwind config 2024-09-26 15:06:36 +08:00
bbca708832 add marketplace card 2024-09-24 11:18:34 +08:00
05aec43ee3 Merge branch 'main' into tp 2024-09-23 11:39:46 +08:00
Yi
e8127756e0 Merge branch 'main' of github.com:langgenius/dify into feat/plugins 2024-09-18 20:57:52 +08:00
Yi
792595a46f update page header 2024-09-18 18:32:33 +08:00
Yi
d7d7281c93 feat: plugin homepage 2024-09-16 18:58:39 +08:00
Yi
21193c2fbf update the plugins page 2024-09-14 17:09:25 +08:00
3258 changed files with 287979 additions and 36995 deletions

View File

@ -8,7 +8,7 @@ inputs:
poetry-version:
description: Poetry version to set up
required: true
default: '2.0.1'
default: '1.8.4'
poetry-lockfile:
description: Path to the Poetry lockfile to restore cache from
required: true

View File

@ -26,9 +26,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Poetry and Python ${{ matrix.python-version }}
uses: ./.github/actions/setup-poetry
@ -45,17 +42,25 @@ jobs:
run: poetry install -C api --with dev
- name: Check dependencies in pyproject.toml
run: poetry run -P api bash dev/pytest/pytest_artifacts.sh
run: poetry run -C api bash dev/pytest/pytest_artifacts.sh
- name: Run Unit tests
run: poetry run -P api bash dev/pytest/pytest_unit_tests.sh
run: poetry run -C api bash dev/pytest/pytest_unit_tests.sh
- name: Run ModelRuntime
run: poetry run -C api bash dev/pytest/pytest_model_runtime.sh
- name: Run dify config tests
run: poetry run -P api python dev/pytest/pytest_config_tests.py
run: poetry run -C api python dev/pytest/pytest_config_tests.py
- name: Run Tool
run: poetry run -C api bash dev/pytest/pytest_tools.sh
- name: Run mypy
run: |
poetry run -C api python -m mypy --install-types --non-interactive .
pushd api
poetry run python -m mypy --install-types --non-interactive .
popd
- name: Set up dotenvs
run: |
@ -75,4 +80,4 @@ jobs:
ssrf_proxy
- name: Run Workflow
run: poetry run -P api bash dev/pytest/pytest_workflow.sh
run: poetry run -C api bash dev/pytest/pytest_workflow.sh

View File

@ -5,7 +5,6 @@ on:
branches:
- "main"
- "deploy/dev"
- "dev/plugin-deploy"
release:
types: [published]
@ -80,12 +79,10 @@ jobs:
cache-to: type=gha,mode=max,scope=${{ matrix.service_name }}
- name: Export digest
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
mkdir -p /tmp/digests
sanitized_digest=${DIGEST#sha256:}
touch "/tmp/digests/${sanitized_digest}"
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
@ -135,15 +132,10 @@ jobs:
- name: Create manifest list and push
working-directory: /tmp/digests
env:
IMAGE_NAME: ${{ env[matrix.image_name_env] }}
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf "$IMAGE_NAME@sha256:%s " *)
$(printf '${{ env[matrix.image_name_env] }}@sha256:%s ' *)
- name: Inspect image
env:
IMAGE_NAME: ${{ env[matrix.image_name_env] }}
IMAGE_VERSION: ${{ steps.meta.outputs.version }}
run: |
docker buildx imagetools inspect "$IMAGE_NAME:$IMAGE_VERSION"
docker buildx imagetools inspect ${{ env[matrix.image_name_env] }}:${{ steps.meta.outputs.version }}

View File

@ -4,7 +4,6 @@ on:
pull_request:
branches:
- main
- plugins/beta
paths:
- api/migrations/**
- .github/workflows/db-migration-test.yml
@ -20,9 +19,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Poetry and Python
uses: ./.github/actions/setup-poetry

View File

@ -1,47 +0,0 @@
name: Build docker image
on:
pull_request:
branches:
- "main"
paths:
- api/Dockerfile
- web/Dockerfile
concurrency:
group: docker-build-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build-docker:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- service_name: "api-amd64"
platform: linux/amd64
context: "api"
- service_name: "api-arm64"
platform: linux/arm64
context: "api"
- service_name: "web-amd64"
platform: linux/amd64
context: "web"
- service_name: "web-arm64"
platform: linux/arm64
context: "web"
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker Image
uses: docker/build-push-action@v6
with:
push: false
context: "{{defaultContext}}:${{ matrix.context }}"
platforms: ${{ matrix.platform }}
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@ -9,6 +9,6 @@ yq eval '.services["pgvecto-rs"].ports += ["5431:5432"]' -i docker/docker-compos
yq eval '.services["elasticsearch"].ports += ["9200:9200"]' -i docker/docker-compose.yaml
yq eval '.services.couchbase-server.ports += ["8091-8096:8091-8096"]' -i docker/docker-compose.yaml
yq eval '.services.couchbase-server.ports += ["11210:11210"]' -i docker/docker-compose.yaml
yq eval '.services.tidb.ports += ["4000:4000"]' -i docker/tidb/docker-compose.yaml
yq eval '.services.tidb.ports += ["4000:4000"]' -i docker/docker-compose.yaml
echo "Ports exposed for sandbox, weaviate, tidb, qdrant, chroma, milvus, pgvector, pgvecto-rs, elasticsearch, couchbase"

View File

@ -17,9 +17,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Check changed files
id: changed-files
@ -41,12 +38,12 @@ jobs:
if: steps.changed-files.outputs.any_changed == 'true'
run: |
poetry run -C api ruff --version
poetry run -C api ruff check ./
poetry run -C api ruff format --check ./
poetry run -C api ruff check ./api
poetry run -C api ruff format --check ./api
- name: Dotenv check
if: steps.changed-files.outputs.any_changed == 'true'
run: poetry run -P api dotenv-linter ./api/.env.example ./web/.env.example
run: poetry run -C api dotenv-linter ./api/.env.example ./web/.env.example
- name: Lint hints
if: failure()
@ -62,9 +59,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Check changed files
id: changed-files
@ -72,12 +66,6 @@ jobs:
with:
files: web/**
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
- name: Setup NodeJS
uses: actions/setup-node@v4
if: steps.changed-files.outputs.any_changed == 'true'
@ -101,9 +89,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Check changed files
id: changed-files
@ -132,9 +117,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Check changed files
id: changed-files

View File

@ -26,9 +26,6 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
@ -38,7 +35,7 @@ jobs:
cache-dependency-path: 'pnpm-lock.yaml'
- name: Install Dependencies
run: pnpm install --frozen-lockfile
run: pnpm install
- name: Test
run: pnpm test

View File

@ -16,7 +16,6 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # last 2 commits
persist-credentials: false
- name: Check for file changes in i18n/en-US
id: check_files

View File

@ -28,9 +28,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Poetry and Python ${{ matrix.python-version }}
uses: ./.github/actions/setup-poetry
@ -54,15 +51,7 @@ jobs:
- name: Expose Service Ports
run: sh .github/workflows/expose_service_ports.sh
- name: Set up Vector Store (TiDB)
uses: hoverkraft-tech/compose-action@v2.0.2
with:
compose-file: docker/tidb/docker-compose.yaml
services: |
tidb
tiflash
- name: Set up Vector Stores (Weaviate, Qdrant, PGVector, Milvus, PgVecto-RS, Chroma, MyScale, ElasticSearch, Couchbase)
- name: Set up Vector Stores (TiDB, Weaviate, Qdrant, PGVector, Milvus, PgVecto-RS, Chroma, MyScale, ElasticSearch, Couchbase)
uses: hoverkraft-tech/compose-action@v2.0.2
with:
compose-file: |
@ -78,9 +67,7 @@ jobs:
pgvector
chroma
elasticsearch
- name: Check TiDB Ready
run: poetry run -P api python api/tests/integration_tests/vdb/tidb_vector/check_tiflash_ready.py
tidb
- name: Test Vector Stores
run: poetry run -P api bash dev/pytest/pytest_vdb.sh
run: poetry run -C api bash dev/pytest/pytest_vdb.sh

View File

@ -22,34 +22,25 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Check changed files
id: changed-files
uses: tj-actions/changed-files@v45
with:
files: web/**
# to run pnpm, should install package canvas, but it always install failed on amd64 under ubuntu-latest
# - name: Install pnpm
# uses: pnpm/action-setup@v4
# with:
# version: 10
# run_install: false
# - name: Setup Node.js
# uses: actions/setup-node@v4
# if: steps.changed-files.outputs.any_changed == 'true'
# with:
# node-version: 20
# cache: pnpm
# cache-dependency-path: ./web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
if: steps.changed-files.outputs.any_changed == 'true'
with:
node-version: 20
cache: pnpm
cache-dependency-path: ./web/package.json
# - name: Install dependencies
# if: steps.changed-files.outputs.any_changed == 'true'
# run: pnpm install --frozen-lockfile
- name: Install dependencies
if: steps.changed-files.outputs.any_changed == 'true'
run: pnpm install --frozen-lockfile
# - name: Run tests
# if: steps.changed-files.outputs.any_changed == 'true'
# run: pnpm test
- name: Run tests
if: steps.changed-files.outputs.any_changed == 'true'
run: pnpm test

5
.gitignore vendored
View File

@ -163,7 +163,6 @@ docker/volumes/db/data/*
docker/volumes/redis/data/*
docker/volumes/weaviate/*
docker/volumes/qdrant/*
docker/tidb/volumes/*
docker/volumes/etcd/*
docker/volumes/minio/*
docker/volumes/milvus/*
@ -176,7 +175,6 @@ docker/volumes/pgvector/data/*
docker/volumes/pgvecto_rs/data/*
docker/volumes/couchbase/*
docker/volumes/oceanbase/*
docker/volumes/plugin_daemon/*
!docker/volumes/oceanbase/init.d
docker/nginx/conf.d/default.conf
@ -198,6 +196,3 @@ api/.vscode
# pnpm
/.pnpm-store
# plugin migrate
plugins.jsonl

View File

@ -73,7 +73,7 @@ Dify requires the following dependencies to build, make sure they're installed o
* [Docker](https://www.docker.com/)
* [Docker Compose](https://docs.docker.com/compose/install/)
* [Node.js v18.x (LTS)](http://nodejs.org)
* [pnpm](https://pnpm.io/)
* [npm](https://www.npmjs.com/) version 8.x.x or [Yarn](https://yarnpkg.com/)
* [Python](https://www.python.org/) version 3.11.x or 3.12.x
### 4. Installations

View File

@ -70,7 +70,7 @@ Dify 依赖以下工具和库:
- [Docker](https://www.docker.com/)
- [Docker Compose](https://docs.docker.com/compose/install/)
- [Node.js v18.x (LTS)](http://nodejs.org)
- [pnpm](https://pnpm.io/)
- [npm](https://www.npmjs.com/) version 8.x.x or [Yarn](https://yarnpkg.com/)
- [Python](https://www.python.org/) version 3.11.x or 3.12.x
### 4. 安装

View File

@ -73,7 +73,7 @@ Dify を構築するには次の依存関係が必要です。それらがシス
- [Docker](https://www.docker.com/)
- [Docker Compose](https://docs.docker.com/compose/install/)
- [Node.js v18.x (LTS)](http://nodejs.org)
- [pnpm](https://pnpm.io/)
- [npm](https://www.npmjs.com/) version 8.x.x or [Yarn](https://yarnpkg.com/)
- [Python](https://www.python.org/) version 3.11.x or 3.12.x
### 4. インストール

View File

@ -72,7 +72,7 @@ Dify yêu cầu các phụ thuộc sau để build, hãy đảm bảo chúng đ
- [Docker](https://www.docker.com/)
- [Docker Compose](https://docs.docker.com/compose/install/)
- [Node.js v18.x (LTS)](http://nodejs.org)
- [pnpm](https://pnpm.io/)
- [npm](https://www.npmjs.com/) phiên bản 8.x.x hoặc [Yarn](https://yarnpkg.com/)
- [Python](https://www.python.org/) phiên bản 3.11.x hoặc 3.12.x
### 4. Cài đặt

23
LICENSE
View File

@ -1,12 +1,12 @@
# Open Source License
Dify is licensed under a modified version of the Apache License 2.0, with the following additional conditions:
Dify is licensed under the Apache License 2.0, with the following additional conditions:
1. Dify may be utilized commercially, including as a backend service for other applications or as an application development platform for enterprises. Should the conditions below be met, a commercial license must be obtained from the producer:
a. Multi-tenant service: Unless explicitly authorized by Dify in writing, you may not use the Dify source code to operate a multi-tenant environment.
a. Multi-tenant service: Unless explicitly authorized by Dify in writing, you may not use the Dify source code to operate a multi-tenant environment.
- Tenant Definition: Within the context of Dify, one tenant corresponds to one workspace. The workspace provides a separated area for each tenant's data and configurations.
b. LOGO and copyright information: In the process of using Dify's frontend, you may not remove or modify the LOGO or copyright information in the Dify console or applications. This restriction is inapplicable to uses of Dify that do not involve its frontend.
- Frontend Definition: For the purposes of this license, the "frontend" of Dify includes all components located in the `web/` directory when running Dify from the raw source code, or the "web" image when running Dify with Docker.
@ -21,4 +21,19 @@ Apart from the specific conditions mentioned above, all other rights and restric
The interactive design of this product is protected by appearance patent.
© 2025 LangGenius, Inc.
© 2024 LangGenius, Inc.
----------
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -25,9 +25,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="follow on LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -108,72 +105,6 @@ Please refer to our [FAQ](https://docs.dify.ai/getting-started/install-self-host
**7. Backend-as-a-Service**:
All of Dify's offerings come with corresponding APIs, so you could effortlessly integrate Dify into your own business logic.
## Feature Comparison
<table style="width: 100%;">
<tr>
<th align="center">Feature</th>
<th align="center">Dify.AI</th>
<th align="center">LangChain</th>
<th align="center">Flowise</th>
<th align="center">OpenAI Assistants API</th>
</tr>
<tr>
<td align="center">Programming Approach</td>
<td align="center">API + App-oriented</td>
<td align="center">Python Code</td>
<td align="center">App-oriented</td>
<td align="center">API-oriented</td>
</tr>
<tr>
<td align="center">Supported LLMs</td>
<td align="center">Rich Variety</td>
<td align="center">Rich Variety</td>
<td align="center">Rich Variety</td>
<td align="center">OpenAI-only</td>
</tr>
<tr>
<td align="center">RAG Engine</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Agent</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Workflow</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Observability</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Enterprise Feature (SSO/Access control)</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Local Deployment</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</table>
## Using Dify

View File

@ -21,9 +21,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="follow on LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">

View File

@ -21,9 +21,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="follow on LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">

View File

@ -21,9 +21,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="seguir en X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="seguir en LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Descargas de Docker" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">

View File

@ -21,9 +21,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="suivre sur X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="suivre sur LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Tirages Docker" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -55,7 +52,7 @@
Dify est une plateforme de développement d'applications LLM open source. Son interface intuitive combine un flux de travail d'IA, un pipeline RAG, des capacités d'agent, une gestion de modèles, des fonctionnalités d'observabilité, et plus encore, vous permettant de passer rapidement du prototype à la production. Voici une liste des fonctionnalités principales:
</br> </br>
**1. Flux de travail** :
**1. Flux de travail**:
Construisez et testez des flux de travail d'IA puissants sur un canevas visuel, en utilisant toutes les fonctionnalités suivantes et plus encore.
@ -63,25 +60,27 @@ Dify est une plateforme de développement d'applications LLM open source. Son in
**2. Prise en charge complète des modèles** :
**2. Prise en charge complète des modèles**:
Intégration transparente avec des centaines de LLM propriétaires / open source provenant de dizaines de fournisseurs d'inférence et de solutions auto-hébergées, couvrant GPT, Mistral, Llama3, et tous les modèles compatibles avec l'API OpenAI. Une liste complète des fournisseurs de modèles pris en charge se trouve [ici](https://docs.dify.ai/getting-started/readme/model-providers).
![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3)
**3. IDE de prompt** :
**3. IDE de prompt**:
Interface intuitive pour créer des prompts, comparer les performances des modèles et ajouter des fonctionnalités supplémentaires telles que la synthèse vocale à une application basée sur des chats.
**4. Pipeline RAG** :
**4. Pipeline RAG**:
Des capacités RAG étendues qui couvrent tout, de l'ingestion de documents à la récupération, avec un support prêt à l'emploi pour l'extraction de texte à partir de PDF, PPT et autres formats de document courants.
**5. Capacités d'agent** :
**5. Capac
ités d'agent**:
Vous pouvez définir des agents basés sur l'appel de fonction LLM ou ReAct, et ajouter des outils pré-construits ou personnalisés pour l'agent. Dify fournit plus de 50 outils intégrés pour les agents d'IA, tels que la recherche Google, DALL·E, Stable Diffusion et WolframAlpha.
**6. LLMOps** :
**6. LLMOps**:
Surveillez et analysez les journaux d'application et les performances au fil du temps. Vous pouvez continuellement améliorer les prompts, les ensembles de données et les modèles en fonction des données de production et des annotations.
**7. Backend-as-a-Service** :
**7. Backend-as-a-Service**:
Toutes les offres de Dify sont accompagnées d'API correspondantes, vous permettant d'intégrer facilement Dify dans votre propre logique métier.

View File

@ -21,9 +21,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="X(Twitter)でフォロー"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="LinkedInでフォロー"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -164,7 +161,7 @@ DifyはオープンソースのLLMアプリケーション開発プラットフ
- **企業/組織向けのDify</br>**
企業中心の機能を提供しています。[メールを送信](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry)して企業のニーズについて相談してください。 </br>
> AWSを使用しているスタートアップ企業や中小企業の場合は、[AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t23mebxzwjhu6)のDify Premiumをチェックして、ワンクリックで自分のAWS VPCにデプロイできます。さらに、手頃な価格のAMIオファリングして、ロゴやブランディングをカスタマイズしてアプリケーションを作成するオプションがあります。
> AWSを使用しているスタートアップ企業や中小企業の場合は、[AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6)のDify Premiumをチェックして、ワンクリックで自分のAWS VPCにデプロイできます。さらに、手頃な価格のAMIオファリングして、ロゴやブランディングをカスタマイズしてアプリケーションを作成するオプションがあります。
## 最新の情報を入手

View File

@ -21,9 +21,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="follow on LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -87,7 +84,9 @@ Dify is an open-source LLM app development platform. Its intuitive interface com
## Feature Comparison
<table style="width: 100%;">
<tr>
<tr
>
<th align="center">Feature</th>
<th align="center">Dify.AI</th>
<th align="center">LangChain</th>

View File

@ -21,9 +21,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="follow on LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">

View File

@ -25,9 +25,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="follow on LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">

View File

@ -22,9 +22,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="follow on LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -106,73 +103,6 @@ Prosimo, glejte naša pogosta vprašanja [FAQ](https://docs.dify.ai/getting-star
**7. Backend-as-a-Service**:
AVse ponudbe Difyja so opremljene z ustreznimi API-ji, tako da lahko Dify brez težav integrirate v svojo poslovno logiko.
## Primerjava Funkcij
<table style="width: 100%;">
<tr>
<th align="center">Funkcija</th>
<th align="center">Dify.AI</th>
<th align="center">LangChain</th>
<th align="center">Flowise</th>
<th align="center">OpenAI Assistants API</th>
</tr>
<tr>
<td align="center">Programski pristop</td>
<td align="center">API + usmerjeno v aplikacije</td>
<td align="center">Python koda</td>
<td align="center">Usmerjeno v aplikacije</td>
<td align="center">Usmerjeno v API</td>
</tr>
<tr>
<td align="center">Podprti LLM-ji</td>
<td align="center">Bogata izbira</td>
<td align="center">Bogata izbira</td>
<td align="center">Bogata izbira</td>
<td align="center">Samo OpenAI</td>
</tr>
<tr>
<td align="center">RAG pogon</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Agent</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Potek dela</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Spremljanje</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Funkcija za podjetja (SSO/nadzor dostopa)</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Lokalna namestitev</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</table>
## Uporaba Dify
@ -254,4 +184,4 @@ Zaradi zaščite vaše zasebnosti se izogibajte objavljanju varnostnih vprašanj
## Licenca
To skladišče je na voljo pod [odprtokodno licenco Dify](LICENSE) , ki je v bistvu Apache 2.0 z nekaj dodatnimi omejitvami.
To skladišče je na voljo pod [odprtokodno licenco Dify](LICENSE) , ki je v bistvu Apache 2.0 z nekaj dodatnimi omejitvami.

View File

@ -21,9 +21,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="X(Twitter)'da takip et"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="LinkedIn'da takip et"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Çekmeleri" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -65,6 +62,8 @@ Görsel bir arayüz üzerinde güçlü AI iş akışları oluşturun ve test edi
![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3)
Özür dilerim, haklısınız. Daha anlamlı ve akıcı bir çeviri yapmaya çalışayım. İşte güncellenmiş çeviri:
**3. Prompt IDE**:
Komut istemlerini oluşturmak, model performansını karşılaştırmak ve sohbet tabanlı uygulamalara metin-konuşma gibi ek özellikler eklemek için kullanıcı dostu bir arayüz.
@ -151,6 +150,8 @@ Görsel bir arayüz üzerinde güçlü AI iş akışları oluşturun ve test edi
## Dify'ı Kullanma
- **Cloud </br>**
İşte verdiğiniz metnin Türkçe çevirisi, kod bloğu içinde:
-
Herkesin sıfır kurulumla denemesi için bir [Dify Cloud](https://dify.ai) hizmeti sunuyoruz. Bu hizmet, kendi kendine dağıtılan versiyonun tüm yeteneklerini sağlar ve sandbox planında 200 ücretsiz GPT-4 çağrısı içerir.
- **Dify Topluluk Sürümünü Kendi Sunucunuzda Barındırma</br>**
@ -176,6 +177,8 @@ GitHub'da Dify'a yıldız verin ve yeni sürümlerden anında haberdar olun.
>- RAM >= 4GB
</br>
İşte verdiğiniz metnin Türkçe çevirisi, kod bloğu içinde:
Dify sunucusunu başlatmanın en kolay yolu, [docker-compose.yml](docker/docker-compose.yaml) dosyamızı çalıştırmaktır. Kurulum komutunu çalıştırmadan önce, makinenizde [Docker](https://docs.docker.com/get-docker/) ve [Docker Compose](https://docs.docker.com/compose/install/)'un kurulu olduğundan emin olun:
```bash

View File

@ -21,9 +21,6 @@
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="theo dõi trên X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="theo dõi trên LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">

View File

@ -1,10 +1,7 @@
.env
*.env.*
storage/generate_files/*
storage/privkeys/*
storage/tools/*
storage/upload_files/*
# Logs
logs
@ -12,8 +9,6 @@ logs
# jetbrains
.idea
.mypy_cache
.ruff_cache
# venv
.venv

View File

@ -409,6 +409,7 @@ MAX_VARIABLE_SIZE=204800
APP_MAX_EXECUTION_TIME=1200
APP_MAX_ACTIVE_REQUESTS=0
# Celery beat configuration
CELERY_BEAT_SCHEDULER_TIME=1
@ -421,22 +422,6 @@ POSITION_PROVIDER_PINS=
POSITION_PROVIDER_INCLUDES=
POSITION_PROVIDER_EXCLUDES=
# Plugin configuration
PLUGIN_DAEMON_KEY=lYkiYYT6owG+71oLerGzA7GXCgOT++6ovaezWAjpCjf+Sjc3ZtU+qUEi
PLUGIN_DAEMON_URL=http://127.0.0.1:5002
PLUGIN_REMOTE_INSTALL_PORT=5003
PLUGIN_REMOTE_INSTALL_HOST=localhost
PLUGIN_MAX_PACKAGE_SIZE=15728640
INNER_API_KEY=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1
INNER_API_KEY_FOR_PLUGIN=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1
# Marketplace configuration
MARKETPLACE_ENABLED=true
MARKETPLACE_API_URL=https://marketplace.dify.ai
# Endpoint configuration
ENDPOINT_URL_TEMPLATE=http://localhost:5002/e/{hook_id}
# Reset password token expiry minutes
RESET_PASSWORD_TOKEN_EXPIRY_MINUTES=5

View File

@ -53,12 +53,10 @@ ignore = [
"FURB152", # math-constant
"UP007", # non-pep604-annotation
"UP032", # f-string
"UP045", # non-pep604-annotation-optional
"B005", # strip-with-multi-characters
"B006", # mutable-argument-default
"B007", # unused-loop-control-variable
"B026", # star-arg-unpacking-after-keyword-arg
"B903", # class-as-data-structure
"B904", # raise-without-from-inside-except
"B905", # zip-without-explicit-strict
"N806", # non-lowercase-variable-in-function

View File

@ -4,7 +4,7 @@ FROM python:3.12-slim-bookworm AS base
WORKDIR /app/api
# Install Poetry
ENV POETRY_VERSION=2.0.1
ENV POETRY_VERSION=1.8.4
# if you located in China, you can use aliyun mirror to speed up
# RUN pip install --no-cache-dir poetry==${POETRY_VERSION} -i https://mirrors.aliyun.com/pypi/simple/
@ -48,20 +48,16 @@ ENV TZ=UTC
WORKDIR /app/api
RUN \
apt-get update \
# Install dependencies
&& apt-get install -y --no-install-recommends \
# basic environment
curl nodejs libgmp-dev libmpfr-dev libmpc-dev \
# For Security
expat libldap-2.5-0 perl libsqlite3-0 zlib1g \
# install a chinese font to support the use of tools like matplotlib
fonts-noto-cjk \
# install a package to improve the accuracy of guessing mime type and file extension
media-types \
# install libmagic to support the use of python-magic guess MIMETYPE
libmagic1 \
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl nodejs libgmp-dev libmpfr-dev libmpc-dev \
# if you located in China, you can use aliyun mirror to speed up
# && echo "deb http://mirrors.aliyun.com/debian testing main" > /etc/apt/sources.list \
&& echo "deb http://deb.debian.org/debian testing main" > /etc/apt/sources.list \
&& apt-get update \
# For Security
&& apt-get install -y --no-install-recommends expat=2.6.4-1 libldap-2.5-0=2.5.19+dfsg-1 perl=5.40.0-8 libsqlite3-0=3.46.1-1 zlib1g=1:1.3.dfsg+really1.3.1-1+b1 \
# install a chinese font to support the use of tools like matplotlib
&& apt-get install -y fonts-noto-cjk \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/*
@ -73,10 +69,6 @@ ENV PATH="${VIRTUAL_ENV}/bin:${PATH}"
# Download nltk data
RUN python -c "import nltk; nltk.download('punkt'); nltk.download('averaged_perceptron_tagger')"
ENV TIKTOKEN_CACHE_DIR=/app/api/.tiktoken_cache
RUN python -c "import tiktoken; tiktoken.encoding_for_model('gpt2')"
# Copy source code
COPY . /app/api/
@ -84,6 +76,7 @@ COPY . /app/api/
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ARG COMMIT_SHA
ENV COMMIT_SHA=${COMMIT_SHA}

View File

@ -37,13 +37,7 @@
4. Create environment.
Dify API service uses [Poetry](https://python-poetry.org/docs/) to manage dependencies. First, you need to add the poetry shell plugin, if you don't have it already, in order to run in a virtual environment. [Note: Poetry shell is no longer a native command so you need to install the poetry plugin beforehand]
```bash
poetry self add poetry-plugin-shell
```
Then, You can execute `poetry shell` to activate the environment.
Dify API service uses [Poetry](https://python-poetry.org/docs/) to manage dependencies. You can execute `poetry shell` to activate the environment.
5. Install dependencies
@ -85,5 +79,5 @@
2. Run the tests locally with mocked system environment variables in `tool.pytest_env` section in `pyproject.toml`
```bash
poetry run -P api bash dev/pytest/pytest_all_tests.sh
poetry run -C api bash dev/pytest/pytest_all_tests.sh
```

View File

@ -2,7 +2,6 @@ import logging
import time
from configs import dify_config
from contexts.wrapper import RecyclableContextVar
from dify_app import DifyApp
@ -17,12 +16,6 @@ def create_flask_app_with_configs() -> DifyApp:
dify_app = DifyApp(__name__)
dify_app.config.from_mapping(dify_config.model_dump())
# add before request hook
@dify_app.before_request
def before_request():
# add an unique identifier to each request
RecyclableContextVar.increment_thread_recycles()
return dify_app

View File

@ -25,8 +25,6 @@ from models.dataset import Document as DatasetDocument
from models.model import Account, App, AppAnnotationSetting, AppMode, Conversation, MessageAnnotation
from models.provider import Provider, ProviderModel
from services.account_service import RegisterService, TenantService
from services.plugin.data_migration import PluginDataMigration
from services.plugin.plugin_migration import PluginMigration
@click.command("reset-password", help="Reset the account password.")
@ -526,7 +524,7 @@ def add_qdrant_doc_id_index(field: str):
)
)
except Exception:
except Exception as e:
click.echo(click.style("Failed to create Qdrant client.", fg="red"))
click.echo(click.style(f"Index creation complete. Created {create_count} collection indexes.", fg="green"))
@ -595,7 +593,7 @@ def upgrade_db():
click.echo(click.style("Database migration successful!", fg="green"))
except Exception:
except Exception as e:
logging.exception("Failed to execute database migration")
finally:
lock.release()
@ -641,7 +639,7 @@ where sites.id is null limit 1000"""
account = accounts[0]
print("Fixing missing site for app {}".format(app.id))
app_was_created.send(app, account=account)
except Exception:
except Exception as e:
failed_app_ids.append(app_id)
click.echo(click.style("Failed to fix missing site for app {}".format(app_id), fg="red"))
logging.exception(f"Failed to fix app related site missing issue, app_id: {app_id}")
@ -651,69 +649,3 @@ where sites.id is null limit 1000"""
break
click.echo(click.style("Fix for missing app-related sites completed successfully!", fg="green"))
@click.command("migrate-data-for-plugin", help="Migrate data for plugin.")
def migrate_data_for_plugin():
"""
Migrate data for plugin.
"""
click.echo(click.style("Starting migrate data for plugin.", fg="white"))
PluginDataMigration.migrate()
click.echo(click.style("Migrate data for plugin completed.", fg="green"))
@click.command("extract-plugins", help="Extract plugins.")
@click.option("--output_file", prompt=True, help="The file to store the extracted plugins.", default="plugins.jsonl")
@click.option("--workers", prompt=True, help="The number of workers to extract plugins.", default=10)
def extract_plugins(output_file: str, workers: int):
"""
Extract plugins.
"""
click.echo(click.style("Starting extract plugins.", fg="white"))
PluginMigration.extract_plugins(output_file, workers)
click.echo(click.style("Extract plugins completed.", fg="green"))
@click.command("extract-unique-identifiers", help="Extract unique identifiers.")
@click.option(
"--output_file",
prompt=True,
help="The file to store the extracted unique identifiers.",
default="unique_identifiers.json",
)
@click.option(
"--input_file", prompt=True, help="The file to store the extracted unique identifiers.", default="plugins.jsonl"
)
def extract_unique_plugins(output_file: str, input_file: str):
"""
Extract unique plugins.
"""
click.echo(click.style("Starting extract unique plugins.", fg="white"))
PluginMigration.extract_unique_plugins_to_file(input_file, output_file)
click.echo(click.style("Extract unique plugins completed.", fg="green"))
@click.command("install-plugins", help="Install plugins.")
@click.option(
"--input_file", prompt=True, help="The file to store the extracted unique identifiers.", default="plugins.jsonl"
)
@click.option(
"--output_file", prompt=True, help="The file to store the installed plugins.", default="installed_plugins.jsonl"
)
@click.option("--workers", prompt=True, help="The number of workers to install plugins.", default=100)
def install_plugins(input_file: str, output_file: str, workers: int):
"""
Install plugins.
"""
click.echo(click.style("Starting install plugins.", fg="white"))
PluginMigration.install_plugins(input_file, output_file, workers)
click.echo(click.style("Install plugins completed.", fg="green"))

View File

@ -134,60 +134,6 @@ class CodeExecutionSandboxConfig(BaseSettings):
)
class PluginConfig(BaseSettings):
"""
Plugin configs
"""
PLUGIN_DAEMON_URL: HttpUrl = Field(
description="Plugin API URL",
default="http://localhost:5002",
)
PLUGIN_DAEMON_KEY: str = Field(
description="Plugin API key",
default="plugin-api-key",
)
INNER_API_KEY_FOR_PLUGIN: str = Field(description="Inner api key for plugin", default="inner-api-key")
PLUGIN_REMOTE_INSTALL_HOST: str = Field(
description="Plugin Remote Install Host",
default="localhost",
)
PLUGIN_REMOTE_INSTALL_PORT: PositiveInt = Field(
description="Plugin Remote Install Port",
default=5003,
)
PLUGIN_MAX_PACKAGE_SIZE: PositiveInt = Field(
description="Maximum allowed size for plugin packages in bytes",
default=15728640,
)
PLUGIN_MAX_BUNDLE_SIZE: PositiveInt = Field(
description="Maximum allowed size for plugin bundles in bytes",
default=15728640 * 12,
)
class MarketplaceConfig(BaseSettings):
"""
Configuration for marketplace
"""
MARKETPLACE_ENABLED: bool = Field(
description="Enable or disable marketplace",
default=True,
)
MARKETPLACE_API_URL: HttpUrl = Field(
description="Marketplace API URL",
default="https://marketplace.dify.ai",
)
class EndpointConfig(BaseSettings):
"""
Configuration for various application endpoints and URLs
@ -200,7 +146,7 @@ class EndpointConfig(BaseSettings):
)
CONSOLE_WEB_URL: str = Field(
description="Base URL for the console web interface,used for frontend references and CORS configuration",
description="Base URL for the console web interface," "used for frontend references and CORS configuration",
default="",
)
@ -214,10 +160,6 @@ class EndpointConfig(BaseSettings):
default="",
)
ENDPOINT_URL_TEMPLATE: str = Field(
description="Template url for endpoint plugin", default="http://localhost:5002/e/{hook_id}"
)
class FileAccessConfig(BaseSettings):
"""
@ -373,8 +315,8 @@ class HttpConfig(BaseSettings):
)
RESPECT_XFORWARD_HEADERS_ENABLED: bool = Field(
description="Enable handling of X-Forwarded-For, X-Forwarded-Proto, and X-Forwarded-Port headers"
" when the app is behind a single trusted reverse proxy.",
description="Enable or disable the X-Forwarded-For Proxy Fix middleware from Werkzeug"
" to respect X-* headers to redirect clients",
default=False,
)
@ -556,11 +498,6 @@ class AuthConfig(BaseSettings):
default=86400,
)
FORGOT_PASSWORD_LOCKOUT_DURATION: PositiveInt = Field(
description="Time (in seconds) a user must wait before retrying password reset after exceeding the rate limit.",
default=86400,
)
class ModerationConfig(BaseSettings):
"""
@ -851,8 +788,6 @@ class FeatureConfig(
AuthConfig, # Changed from OAuthConfig to AuthConfig
BillingConfig,
CodeExecutionSandboxConfig,
PluginConfig,
MarketplaceConfig,
DataSetConfig,
EndpointConfig,
FileAccessConfig,

View File

@ -1,40 +1,9 @@
from typing import Optional
from pydantic import Field, NonNegativeInt, computed_field
from pydantic import Field, NonNegativeInt
from pydantic_settings import BaseSettings
class HostedCreditConfig(BaseSettings):
HOSTED_MODEL_CREDIT_CONFIG: str = Field(
description="Model credit configuration in format 'model:credits,model:credits', e.g., 'gpt-4:20,gpt-4o:10'",
default="",
)
def get_model_credits(self, model_name: str) -> int:
"""
Get credit value for a specific model name.
Returns 1 if model is not found in configuration (default credit).
:param model_name: The name of the model to search for
:return: The credit value for the model
"""
if not self.HOSTED_MODEL_CREDIT_CONFIG:
return 1
try:
credit_map = dict(
item.strip().split(":", 1) for item in self.HOSTED_MODEL_CREDIT_CONFIG.split(",") if ":" in item
)
# Search for matching model pattern
for pattern, credit in credit_map.items():
if pattern.strip() == model_name:
return int(credit)
return 1 # Default quota if no match found
except (ValueError, AttributeError):
return 1 # Return default quota if parsing fails
class HostedOpenAiConfig(BaseSettings):
"""
Configuration for hosted OpenAI service
@ -212,7 +181,7 @@ class HostedFetchAppTemplateConfig(BaseSettings):
"""
HOSTED_FETCH_APP_TEMPLATES_MODE: str = Field(
description="Mode for fetching app templates: remote, db, or builtin default to remote,",
description="Mode for fetching app templates: remote, db, or builtin" " default to remote,",
default="remote",
)
@ -233,7 +202,5 @@ class HostedServiceConfig(
HostedZhipuAIConfig,
# moderation
HostedModerationConfig,
# credit config
HostedCreditConfig,
):
pass

View File

@ -1,4 +1,3 @@
import os
from typing import Any, Literal, Optional
from urllib.parse import quote_plus
@ -167,11 +166,6 @@ class DatabaseConfig(BaseSettings):
default=False,
)
RETRIEVAL_SERVICE_EXECUTORS: NonNegativeInt = Field(
description="Number of processes for the retrieval service, default to CPU cores.",
default=os.cpu_count(),
)
@computed_field
def SQLALCHEMY_ENGINE_OPTIONS(self) -> dict[str, Any]:
return {

View File

@ -9,7 +9,7 @@ class PackagingInfo(BaseSettings):
CURRENT_VERSION: str = Field(
description="Dify version",
default="1.0.0",
default="0.15.0",
)
COMMIT_SHA: str = Field(

View File

@ -15,7 +15,7 @@ AUDIO_EXTENSIONS.extend([ext.upper() for ext in AUDIO_EXTENSIONS])
if dify_config.ETL_TYPE == "Unstructured":
DOCUMENT_EXTENSIONS = ["txt", "markdown", "md", "mdx", "pdf", "html", "htm", "xlsx", "xls"]
DOCUMENT_EXTENSIONS.extend(("doc", "docx", "csv", "eml", "msg", "pptx", "xml", "epub"))
DOCUMENT_EXTENSIONS.extend(("docx", "csv", "eml", "msg", "pptx", "xml", "epub"))
if dify_config.UNSTRUCTURED_API_URL:
DOCUMENT_EXTENSIONS.append("ppt")
DOCUMENT_EXTENSIONS.extend([ext.upper() for ext in DOCUMENT_EXTENSIONS])

View File

@ -1,30 +1,9 @@
from contextvars import ContextVar
from threading import Lock
from typing import TYPE_CHECKING
from contexts.wrapper import RecyclableContextVar
if TYPE_CHECKING:
from core.plugin.entities.plugin_daemon import PluginModelProviderEntity
from core.tools.plugin_tool.provider import PluginToolProviderController
from core.workflow.entities.variable_pool import VariablePool
tenant_id: ContextVar[str] = ContextVar("tenant_id")
workflow_variable_pool: ContextVar["VariablePool"] = ContextVar("workflow_variable_pool")
"""
To avoid race-conditions caused by gunicorn thread recycling, using RecyclableContextVar to replace with
"""
plugin_tool_providers: RecyclableContextVar[dict[str, "PluginToolProviderController"]] = RecyclableContextVar(
ContextVar("plugin_tool_providers")
)
plugin_tool_providers_lock: RecyclableContextVar[Lock] = RecyclableContextVar(ContextVar("plugin_tool_providers_lock"))
plugin_model_providers: RecyclableContextVar[list["PluginModelProviderEntity"] | None] = RecyclableContextVar(
ContextVar("plugin_model_providers")
)
plugin_model_providers_lock: RecyclableContextVar[Lock] = RecyclableContextVar(
ContextVar("plugin_model_providers_lock")
)

View File

@ -1,65 +0,0 @@
from contextvars import ContextVar
from typing import Generic, TypeVar
T = TypeVar("T")
class HiddenValue:
pass
_default = HiddenValue()
class RecyclableContextVar(Generic[T]):
"""
RecyclableContextVar is a wrapper around ContextVar
It's safe to use in gunicorn with thread recycling, but features like `reset` are not available for now
NOTE: you need to call `increment_thread_recycles` before requests
"""
_thread_recycles: ContextVar[int] = ContextVar("thread_recycles")
@classmethod
def increment_thread_recycles(cls):
try:
recycles = cls._thread_recycles.get()
cls._thread_recycles.set(recycles + 1)
except LookupError:
cls._thread_recycles.set(0)
def __init__(self, context_var: ContextVar[T]):
self._context_var = context_var
self._updates = ContextVar[int](context_var.name + "_updates", default=0)
def get(self, default: T | HiddenValue = _default) -> T:
thread_recycles = self._thread_recycles.get(0)
self_updates = self._updates.get()
if thread_recycles > self_updates:
self._updates.set(thread_recycles)
# check if thread is recycled and should be updated
if thread_recycles < self_updates:
return self._context_var.get()
else:
# thread_recycles >= self_updates, means current context is invalid
if isinstance(default, HiddenValue) or default is _default:
raise LookupError
else:
return default
def set(self, value: T):
# it leads to a situation that self.updates is less than cls.thread_recycles if `set` was never called before
# increase it manually
thread_recycles = self._thread_recycles.get(0)
self_updates = self._updates.get()
if thread_recycles > self_updates:
self._updates.set(thread_recycles)
if self._updates.get() == self._thread_recycles.get(0):
# after increment,
self._updates.set(self._updates.get() + 1)
# set the context
self._context_var.set(value)

View File

@ -1,32 +1,12 @@
import mimetypes
import os
import platform
import re
import urllib.parse
import warnings
from collections.abc import Mapping
from typing import Any
from uuid import uuid4
import httpx
try:
import magic
except ImportError:
if platform.system() == "Windows":
warnings.warn(
"To use python-magic guess MIMETYPE, you need to run `pip install python-magic-bin`", stacklevel=2
)
elif platform.system() == "Darwin":
warnings.warn("To use python-magic guess MIMETYPE, you need to run `brew install libmagic`", stacklevel=2)
elif platform.system() == "Linux":
warnings.warn(
"To use python-magic guess MIMETYPE, you need to run `sudo apt-get install libmagic1`", stacklevel=2
)
else:
warnings.warn("To use python-magic guess MIMETYPE, you need to install `libmagic`", stacklevel=2)
magic = None # type: ignore
from pydantic import BaseModel
from configs import dify_config
@ -67,13 +47,6 @@ def guess_file_info_from_response(response: httpx.Response):
# If guessing fails, use Content-Type from response headers
mimetype = response.headers.get("Content-Type", "application/octet-stream")
# Use python-magic to guess MIME type if still unknown or generic
if mimetype == "application/octet-stream" and magic is not None:
try:
mimetype = magic.from_buffer(response.content[:1024], mime=True)
except magic.MagicException:
pass
extension = os.path.splitext(filename)[1]
# Ensure filename has an extension

View File

@ -2,7 +2,7 @@ from flask import Blueprint
from libs.external_api import ExternalApi
from .app.app_import import AppImportApi, AppImportCheckDependenciesApi, AppImportConfirmApi
from .app.app_import import AppImportApi, AppImportConfirmApi
from .explore.audio import ChatAudioApi, ChatTextApi
from .explore.completion import ChatApi, ChatStopApi, CompletionApi, CompletionStopApi
from .explore.conversation import (
@ -40,7 +40,6 @@ api.add_resource(RemoteFileUploadApi, "/remote-files/upload")
# Import App
api.add_resource(AppImportApi, "/apps/imports")
api.add_resource(AppImportConfirmApi, "/apps/imports/<string:import_id>/confirm")
api.add_resource(AppImportCheckDependenciesApi, "/apps/imports/<string:app_id>/check-dependencies")
# Import other controllers
from . import admin, apikey, extension, feature, ping, setup, version
@ -167,15 +166,4 @@ api.add_resource(
from .tag import tags
# Import workspace controllers
from .workspace import (
account,
agent_providers,
endpoint,
load_balancing_config,
members,
model_providers,
models,
plugin,
tool_providers,
workspace,
)
from .workspace import account, load_balancing_config, members, model_providers, models, tool_providers, workspace

View File

@ -2,8 +2,6 @@ from functools import wraps
from flask import request
from flask_restful import Resource, reqparse # type: ignore
from sqlalchemy import select
from sqlalchemy.orm import Session
from werkzeug.exceptions import NotFound, Unauthorized
from configs import dify_config
@ -56,10 +54,9 @@ class InsertExploreAppListApi(Resource):
parser.add_argument("position", type=int, required=True, nullable=False, location="json")
args = parser.parse_args()
with Session(db.engine) as session:
app = session.execute(select(App).filter(App.id == args["app_id"])).scalar_one_or_none()
app = App.query.filter(App.id == args["app_id"]).first()
if not app:
raise NotFound(f"App '{args['app_id']}' is not found")
raise NotFound(f'App \'{args["app_id"]}\' is not found')
site = app.site
if not site:
@ -73,10 +70,7 @@ class InsertExploreAppListApi(Resource):
privacy_policy = site.privacy_policy or args["privacy_policy"] or ""
custom_disclaimer = site.custom_disclaimer or args["custom_disclaimer"] or ""
with Session(db.engine) as session:
recommended_app = session.execute(
select(RecommendedApp).filter(RecommendedApp.app_id == args["app_id"])
).scalar_one_or_none()
recommended_app = RecommendedApp.query.filter(RecommendedApp.app_id == args["app_id"]).first()
if not recommended_app:
recommended_app = RecommendedApp(
@ -116,27 +110,17 @@ class InsertExploreAppApi(Resource):
@only_edition_cloud
@admin_required
def delete(self, app_id):
with Session(db.engine) as session:
recommended_app = session.execute(
select(RecommendedApp).filter(RecommendedApp.app_id == str(app_id))
).scalar_one_or_none()
recommended_app = RecommendedApp.query.filter(RecommendedApp.app_id == str(app_id)).first()
if not recommended_app:
return {"result": "success"}, 204
with Session(db.engine) as session:
app = session.execute(select(App).filter(App.id == recommended_app.app_id)).scalar_one_or_none()
app = App.query.filter(App.id == recommended_app.app_id).first()
if app:
app.is_public = False
with Session(db.engine) as session:
installed_apps = session.execute(
select(InstalledApp).filter(
InstalledApp.app_id == recommended_app.app_id,
InstalledApp.tenant_id != InstalledApp.app_owner_tenant_id,
)
).all()
installed_apps = InstalledApp.query.filter(
InstalledApp.app_id == recommended_app.app_id, InstalledApp.tenant_id != InstalledApp.app_owner_tenant_id
).all()
for installed_app in installed_apps:
db.session.delete(installed_app)

View File

@ -3,8 +3,6 @@ from typing import Any
import flask_restful # type: ignore
from flask_login import current_user # type: ignore
from flask_restful import Resource, fields, marshal_with
from sqlalchemy import select
from sqlalchemy.orm import Session
from werkzeug.exceptions import Forbidden
from extensions.ext_database import db
@ -28,16 +26,7 @@ api_key_list = {"data": fields.List(fields.Nested(api_key_fields), attribute="it
def _get_resource(resource_id, tenant_id, resource_model):
if resource_model == App:
with Session(db.engine) as session:
resource = session.execute(
select(resource_model).filter_by(id=resource_id, tenant_id=tenant_id)
).scalar_one_or_none()
else:
with Session(db.engine) as session:
resource = session.execute(
select(resource_model).filter_by(id=resource_id, tenant_id=tenant_id)
).scalar_one_or_none()
resource = resource_model.query.filter_by(id=resource_id, tenant_id=tenant_id).first()
if resource is None:
flask_restful.abort(404, message=f"{resource_model.__name__} not found.")

View File

@ -5,16 +5,14 @@ from flask_restful import Resource, marshal_with, reqparse # type: ignore
from sqlalchemy.orm import Session
from werkzeug.exceptions import Forbidden
from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import (
account_initialization_required,
setup_required,
)
from extensions.ext_database import db
from fields.app_fields import app_import_check_dependencies_fields, app_import_fields
from fields.app_fields import app_import_fields
from libs.login import login_required
from models import Account
from models.model import App
from services.app_dsl_service import AppDslService, ImportStatus
@ -90,20 +88,3 @@ class AppImportConfirmApi(Resource):
if result.status == ImportStatus.FAILED.value:
return result.model_dump(mode="json"), 400
return result.model_dump(mode="json"), 200
class AppImportCheckDependenciesApi(Resource):
@setup_required
@login_required
@get_app_model
@account_initialization_required
@marshal_with(app_import_check_dependencies_fields)
def get(self, app_model: App):
if not current_user.is_editor:
raise Forbidden()
with Session(db.engine) as session:
import_service = AppDslService(session)
result = import_service.check_dependencies(app_model=app_model)
return result.model_dump(mode="json"), 200

View File

@ -22,7 +22,7 @@ from controllers.console.wraps import account_initialization_required, setup_req
from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError
from core.model_runtime.errors.invoke import InvokeError
from libs.login import login_required
from models import App, AppMode
from models.model import AppMode
from services.audio_service import AudioService
from services.errors.audio import (
AudioTooLargeServiceError,
@ -79,7 +79,7 @@ class ChatMessageTextApi(Resource):
@login_required
@account_initialization_required
@get_app_model
def post(self, app_model: App):
def post(self, app_model):
from werkzeug.exceptions import InternalServerError
try:
@ -98,13 +98,9 @@ class ChatMessageTextApi(Resource):
and app_model.workflow.features_dict
):
text_to_speech = app_model.workflow.features_dict.get("text_to_speech")
if text_to_speech is None:
raise ValueError("TTS is not enabled")
voice = args.get("voice") or text_to_speech.get("voice")
else:
try:
if app_model.app_model_config is None:
raise ValueError("AppModelConfig not found")
voice = args.get("voice") or app_model.app_model_config.text_to_speech_dict.get("voice")
except Exception:
voice = None

View File

@ -2,7 +2,6 @@ from datetime import UTC, datetime
from flask_login import current_user # type: ignore
from flask_restful import Resource, marshal_with, reqparse # type: ignore
from sqlalchemy.orm import Session
from werkzeug.exceptions import Forbidden, NotFound
from constants.languages import supported_language
@ -51,37 +50,33 @@ class AppSite(Resource):
if not current_user.is_editor:
raise Forbidden()
with Session(db.engine) as session:
site = session.query(Site).filter(Site.app_id == app_model.id).first()
site = Site.query.filter(Site.app_id == app_model.id).one_or_404()
if not site:
raise NotFound
for attr_name in [
"title",
"icon_type",
"icon",
"icon_background",
"description",
"default_language",
"chat_color_theme",
"chat_color_theme_inverted",
"customize_domain",
"copyright",
"privacy_policy",
"custom_disclaimer",
"customize_token_strategy",
"prompt_public",
"show_workflow_steps",
"use_icon_as_answer_icon",
]:
value = args.get(attr_name)
if value is not None:
setattr(site, attr_name, value)
for attr_name in [
"title",
"icon_type",
"icon",
"icon_background",
"description",
"default_language",
"chat_color_theme",
"chat_color_theme_inverted",
"customize_domain",
"copyright",
"privacy_policy",
"custom_disclaimer",
"customize_token_strategy",
"prompt_public",
"show_workflow_steps",
"use_icon_as_answer_icon",
]:
value = args.get(attr_name)
if value is not None:
setattr(site, attr_name, value)
site.updated_by = current_user.id
site.updated_at = datetime.now(UTC).replace(tzinfo=None)
session.commit()
site.updated_by = current_user.id
site.updated_at = datetime.now(UTC).replace(tzinfo=None)
db.session.commit()
return site

View File

@ -20,7 +20,6 @@ from libs import helper
from libs.helper import TimestampField, uuid_value
from libs.login import current_user, login_required
from models import App
from models.account import Account
from models.model import AppMode
from services.app_generate_service import AppGenerateService
from services.errors.app import WorkflowHashNotEqualError
@ -97,9 +96,6 @@ class DraftWorkflowApi(Resource):
else:
abort(415)
if not isinstance(current_user, Account):
raise Forbidden()
workflow_service = WorkflowService()
try:
@ -143,9 +139,6 @@ class AdvancedChatDraftWorkflowRunApi(Resource):
if not current_user.is_editor:
raise Forbidden()
if not isinstance(current_user, Account):
raise Forbidden()
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, location="json")
parser.add_argument("query", type=str, required=True, location="json", default="")
@ -167,7 +160,7 @@ class AdvancedChatDraftWorkflowRunApi(Resource):
raise ConversationCompletedError()
except ValueError as e:
raise e
except Exception:
except Exception as e:
logging.exception("internal server error.")
raise InternalServerError()
@ -185,9 +178,6 @@ class AdvancedChatDraftRunIterationNodeApi(Resource):
if not current_user.is_editor:
raise Forbidden()
if not isinstance(current_user, Account):
raise Forbidden()
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, location="json")
args = parser.parse_args()
@ -204,7 +194,7 @@ class AdvancedChatDraftRunIterationNodeApi(Resource):
raise ConversationCompletedError()
except ValueError as e:
raise e
except Exception:
except Exception as e:
logging.exception("internal server error.")
raise InternalServerError()
@ -222,9 +212,6 @@ class WorkflowDraftRunIterationNodeApi(Resource):
if not current_user.is_editor:
raise Forbidden()
if not isinstance(current_user, Account):
raise Forbidden()
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, location="json")
args = parser.parse_args()
@ -241,7 +228,7 @@ class WorkflowDraftRunIterationNodeApi(Resource):
raise ConversationCompletedError()
except ValueError as e:
raise e
except Exception:
except Exception as e:
logging.exception("internal server error.")
raise InternalServerError()
@ -259,9 +246,6 @@ class DraftWorkflowRunApi(Resource):
if not current_user.is_editor:
raise Forbidden()
if not isinstance(current_user, Account):
raise Forbidden()
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, required=True, nullable=False, location="json")
parser.add_argument("files", type=list, required=False, location="json")
@ -310,20 +294,13 @@ class DraftWorkflowNodeRunApi(Resource):
if not current_user.is_editor:
raise Forbidden()
if not isinstance(current_user, Account):
raise Forbidden()
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, required=True, nullable=False, location="json")
args = parser.parse_args()
inputs = args.get("inputs")
if inputs == None:
raise ValueError("missing inputs")
workflow_service = WorkflowService()
workflow_node_execution = workflow_service.run_draft_workflow_node(
app_model=app_model, node_id=node_id, user_inputs=inputs, account=current_user
app_model=app_model, node_id=node_id, user_inputs=args.get("inputs"), account=current_user
)
return workflow_node_execution
@ -362,9 +339,6 @@ class PublishedWorkflowApi(Resource):
if not current_user.is_editor:
raise Forbidden()
if not isinstance(current_user, Account):
raise Forbidden()
workflow_service = WorkflowService()
workflow = workflow_service.publish_workflow(app_model=app_model, account=current_user)
@ -402,17 +376,12 @@ class DefaultBlockConfigApi(Resource):
if not current_user.is_editor:
raise Forbidden()
if not isinstance(current_user, Account):
raise Forbidden()
parser = reqparse.RequestParser()
parser.add_argument("q", type=str, location="args")
args = parser.parse_args()
q = args.get("q")
filters = None
if q:
if args.get("q"):
try:
filters = json.loads(args.get("q", ""))
except json.JSONDecodeError:
@ -438,9 +407,6 @@ class ConvertToWorkflowApi(Resource):
if not current_user.is_editor:
raise Forbidden()
if not isinstance(current_user, Account):
raise Forbidden()
if request.data:
parser = reqparse.RequestParser()
parser.add_argument("name", type=str, required=False, nullable=True, location="json")

View File

@ -59,9 +59,3 @@ class EmailCodeAccountDeletionRateLimitExceededError(BaseHTTPException):
error_code = "email_code_account_deletion_rate_limit_exceeded"
description = "Too many account deletion emails have been sent. Please try again in 5 minutes."
code = 429
class EmailPasswordResetLimitError(BaseHTTPException):
error_code = "email_password_reset_limit"
description = "Too many failed password reset attempts. Please try again in 24 hours."
code = 429

View File

@ -3,18 +3,10 @@ import secrets
from flask import request
from flask_restful import Resource, reqparse # type: ignore
from sqlalchemy import select
from sqlalchemy.orm import Session
from constants.languages import languages
from controllers.console import api
from controllers.console.auth.error import (
EmailCodeError,
EmailPasswordResetLimitError,
InvalidEmailError,
InvalidTokenError,
PasswordMismatchError,
)
from controllers.console.auth.error import EmailCodeError, InvalidEmailError, InvalidTokenError, PasswordMismatchError
from controllers.console.error import AccountInFreezeError, AccountNotFound, EmailSendIpLimitError
from controllers.console.wraps import setup_required
from events.tenant_event import tenant_was_created
@ -45,8 +37,7 @@ class ForgotPasswordSendEmailApi(Resource):
else:
language = "en-US"
with Session(db.engine) as session:
account = session.execute(select(Account).filter_by(email=args["email"])).scalar_one_or_none()
account = Account.query.filter_by(email=args["email"]).first()
token = None
if account is None:
if FeatureService.get_system_features().is_allow_register:
@ -71,10 +62,6 @@ class ForgotPasswordCheckApi(Resource):
user_email = args["email"]
is_forgot_password_error_rate_limit = AccountService.is_forgot_password_error_rate_limit(args["email"])
if is_forgot_password_error_rate_limit:
raise EmailPasswordResetLimitError()
token_data = AccountService.get_reset_password_data(args["token"])
if token_data is None:
raise InvalidTokenError()
@ -83,10 +70,8 @@ class ForgotPasswordCheckApi(Resource):
raise InvalidEmailError()
if args["code"] != token_data.get("code"):
AccountService.add_forgot_password_error_rate_limit(args["email"])
raise EmailCodeError()
AccountService.reset_forgot_password_error_rate_limit(args["email"])
return {"is_valid": True, "email": token_data.get("email")}
@ -119,8 +104,7 @@ class ForgotPasswordResetApi(Resource):
password_hashed = hash_password(new_password, salt)
base64_password_hashed = base64.b64encode(password_hashed).decode()
with Session(db.engine) as session:
account = session.execute(select(Account).filter_by(email=reset_data.get("email"))).scalar_one_or_none()
account = Account.query.filter_by(email=reset_data.get("email")).first()
if account:
account.password = base64_password_hashed
account.password_salt = base64_salt
@ -141,7 +125,7 @@ class ForgotPasswordResetApi(Resource):
)
except WorkSpaceNotAllowedCreateError:
pass
except AccountRegisterError:
except AccountRegisterError as are:
raise AccountInFreezeError()
return {"result": "success"}

View File

@ -5,8 +5,6 @@ from typing import Optional
import requests
from flask import current_app, redirect, request
from flask_restful import Resource # type: ignore
from sqlalchemy import select
from sqlalchemy.orm import Session
from werkzeug.exceptions import Unauthorized
from configs import dify_config
@ -137,8 +135,7 @@ def _get_account_by_openid_or_email(provider: str, user_info: OAuthUserInfo) ->
account: Optional[Account] = Account.get_by_openid(provider, user_info.id)
if not account:
with Session(db.engine) as session:
account = session.execute(select(Account).filter_by(email=user_info.email)).scalar_one_or_none()
account = Account.query.filter_by(email=user_info.email).first()
return account

View File

@ -4,8 +4,6 @@ import json
from flask import request
from flask_login import current_user # type: ignore
from flask_restful import Resource, marshal_with, reqparse # type: ignore
from sqlalchemy import select
from sqlalchemy.orm import Session
from werkzeug.exceptions import NotFound
from controllers.console import api
@ -78,10 +76,7 @@ class DataSourceApi(Resource):
def patch(self, binding_id, action):
binding_id = str(binding_id)
action = str(action)
with Session(db.engine) as session:
data_source_binding = session.execute(
select(DataSourceOauthBinding).filter_by(id=binding_id)
).scalar_one_or_none()
data_source_binding = DataSourceOauthBinding.query.filter_by(id=binding_id).first()
if data_source_binding is None:
raise NotFound("Data source binding not found.")
# enable binding
@ -113,53 +108,47 @@ class DataSourceNotionListApi(Resource):
def get(self):
dataset_id = request.args.get("dataset_id", default=None, type=str)
exist_page_ids = []
with Session(db.engine) as session:
# import notion in the exist dataset
if dataset_id:
dataset = DatasetService.get_dataset(dataset_id)
if not dataset:
raise NotFound("Dataset not found.")
if dataset.data_source_type != "notion_import":
raise ValueError("Dataset is not notion type.")
documents = session.execute(
select(Document).filter_by(
dataset_id=dataset_id,
tenant_id=current_user.current_tenant_id,
data_source_type="notion_import",
enabled=True,
)
).all()
if documents:
for document in documents:
data_source_info = json.loads(document.data_source_info)
exist_page_ids.append(data_source_info["notion_page_id"])
# get all authorized pages
data_source_bindings = session.scalars(
select(DataSourceOauthBinding).filter_by(
tenant_id=current_user.current_tenant_id, provider="notion", disabled=False
)
# import notion in the exist dataset
if dataset_id:
dataset = DatasetService.get_dataset(dataset_id)
if not dataset:
raise NotFound("Dataset not found.")
if dataset.data_source_type != "notion_import":
raise ValueError("Dataset is not notion type.")
documents = Document.query.filter_by(
dataset_id=dataset_id,
tenant_id=current_user.current_tenant_id,
data_source_type="notion_import",
enabled=True,
).all()
if not data_source_bindings:
return {"notion_info": []}, 200
pre_import_info_list = []
for data_source_binding in data_source_bindings:
source_info = data_source_binding.source_info
pages = source_info["pages"]
# Filter out already bound pages
for page in pages:
if page["page_id"] in exist_page_ids:
page["is_bound"] = True
else:
page["is_bound"] = False
pre_import_info = {
"workspace_name": source_info["workspace_name"],
"workspace_icon": source_info["workspace_icon"],
"workspace_id": source_info["workspace_id"],
"pages": pages,
}
pre_import_info_list.append(pre_import_info)
return {"notion_info": pre_import_info_list}, 200
if documents:
for document in documents:
data_source_info = json.loads(document.data_source_info)
exist_page_ids.append(data_source_info["notion_page_id"])
# get all authorized pages
data_source_bindings = DataSourceOauthBinding.query.filter_by(
tenant_id=current_user.current_tenant_id, provider="notion", disabled=False
).all()
if not data_source_bindings:
return {"notion_info": []}, 200
pre_import_info_list = []
for data_source_binding in data_source_bindings:
source_info = data_source_binding.source_info
pages = source_info["pages"]
# Filter out already bound pages
for page in pages:
if page["page_id"] in exist_page_ids:
page["is_bound"] = True
else:
page["is_bound"] = False
pre_import_info = {
"workspace_name": source_info["workspace_name"],
"workspace_icon": source_info["workspace_icon"],
"workspace_id": source_info["workspace_id"],
"pages": pages,
}
pre_import_info_list.append(pre_import_info)
return {"notion_info": pre_import_info_list}, 200
class DataSourceNotionApi(Resource):
@ -169,17 +158,14 @@ class DataSourceNotionApi(Resource):
def get(self, workspace_id, page_id, page_type):
workspace_id = str(workspace_id)
page_id = str(page_id)
with Session(db.engine) as session:
data_source_binding = session.execute(
select(DataSourceOauthBinding).filter(
db.and_(
DataSourceOauthBinding.tenant_id == current_user.current_tenant_id,
DataSourceOauthBinding.provider == "notion",
DataSourceOauthBinding.disabled == False,
DataSourceOauthBinding.source_info["workspace_id"] == f'"{workspace_id}"',
)
)
).scalar_one_or_none()
data_source_binding = DataSourceOauthBinding.query.filter(
db.and_(
DataSourceOauthBinding.tenant_id == current_user.current_tenant_id,
DataSourceOauthBinding.provider == "notion",
DataSourceOauthBinding.disabled == False,
DataSourceOauthBinding.source_info["workspace_id"] == f'"{workspace_id}"',
)
).first()
if not data_source_binding:
raise NotFound("Data source binding not found.")

View File

@ -14,7 +14,6 @@ from controllers.console.wraps import account_initialization_required, enterpris
from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError
from core.indexing_runner import IndexingRunner
from core.model_runtime.entities.model_entities import ModelType
from core.plugin.entities.plugin import ModelProviderID
from core.provider_manager import ProviderManager
from core.rag.datasource.vdb.vector_type import VectorType
from core.rag.extractor.entity.extract_setting import ExtractSetting
@ -53,12 +52,12 @@ class DatasetListApi(Resource):
# provider = request.args.get("provider", default="vendor")
search = request.args.get("keyword", default=None, type=str)
tag_ids = request.args.getlist("tag_ids")
include_all = request.args.get("include_all", default="false").lower() == "true"
if ids:
datasets, total = DatasetService.get_datasets_by_ids(ids, current_user.current_tenant_id)
else:
datasets, total = DatasetService.get_datasets(
page, limit, current_user.current_tenant_id, current_user, search, tag_ids, include_all
page, limit, current_user.current_tenant_id, current_user, search, tag_ids
)
# check embedding setting
@ -73,9 +72,7 @@ class DatasetListApi(Resource):
data = marshal(datasets, dataset_detail_fields)
for item in data:
# convert embedding_model_provider to plugin standard format
if item["indexing_technique"] == "high_quality":
item["embedding_model_provider"] = str(ModelProviderID(item["embedding_model_provider"]))
item_model = f"{item['embedding_model']}:{item['embedding_model_provider']}"
if item_model in model_names:
item["embedding_available"] = True
@ -460,7 +457,7 @@ class DatasetIndexingEstimateApi(Resource):
)
except LLMBadRequestError:
raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
"No Embedding Model available. Please configure a valid provider " "in the Settings -> Model Provider."
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
@ -622,7 +619,9 @@ class DatasetRetrievalSettingApi(Resource):
vector_type = dify_config.VECTOR_STORE
match vector_type:
case (
VectorType.RELYT
VectorType.MILVUS
| VectorType.RELYT
| VectorType.PGVECTOR
| VectorType.TIDB_VECTOR
| VectorType.CHROMA
| VectorType.TENCENT
@ -646,7 +645,6 @@ class DatasetRetrievalSettingApi(Resource):
| VectorType.TIDB_ON_QDRANT
| VectorType.LINDORM
| VectorType.COUCHBASE
| VectorType.MILVUS
):
return {
"retrieval_method": [

View File

@ -7,6 +7,7 @@ from flask import request
from flask_login import current_user # type: ignore
from flask_restful import Resource, fields, marshal, marshal_with, reqparse # type: ignore
from sqlalchemy import asc, desc
from transformers.hf_argparser import string_to_bool # type: ignore
from werkzeug.exceptions import Forbidden, NotFound
import services
@ -39,7 +40,6 @@ from core.indexing_runner import IndexingRunner
from core.model_manager import ModelManager
from core.model_runtime.entities.model_entities import ModelType
from core.model_runtime.errors.invoke import InvokeAuthorizationError
from core.plugin.manager.exc import PluginDaemonClientSideError
from core.rag.extractor.entity.extract_setting import ExtractSetting
from extensions.ext_database import db
from extensions.ext_redis import redis_client
@ -150,20 +150,8 @@ class DatasetDocumentListApi(Resource):
sort = request.args.get("sort", default="-created_at", type=str)
# "yes", "true", "t", "y", "1" convert to True, while others convert to False.
try:
fetch_val = request.args.get("fetch", default="false")
if isinstance(fetch_val, bool):
fetch = fetch_val
else:
if fetch_val.lower() in ("yes", "true", "t", "y", "1"):
fetch = True
elif fetch_val.lower() in ("no", "false", "f", "n", "0"):
fetch = False
else:
raise ArgumentTypeError(
f"Truthy value expected: got {fetch_val} but expected one of yes/no, true/false, t/f, y/n, 1/0 "
f"(case insensitive)."
)
except (ArgumentTypeError, ValueError, Exception):
fetch = string_to_bool(request.args.get("fetch", default="false"))
except (ArgumentTypeError, ValueError, Exception) as e:
fetch = False
dataset = DatasetService.get_dataset(dataset_id)
if not dataset:
@ -362,7 +350,8 @@ class DatasetInitApi(Resource):
)
except InvokeAuthorizationError:
raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
"No Embedding Model available. Please configure a valid provider "
"in the Settings -> Model Provider."
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
@ -441,8 +430,6 @@ class DocumentIndexingEstimateApi(DocumentResource):
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
except PluginDaemonClientSideError as ex:
raise ProviderNotInitializeError(ex.description)
except Exception as e:
raise IndexingEstimateError(str(e))
@ -539,12 +526,11 @@ class DocumentBatchIndexingEstimateApi(DocumentResource):
return response.model_dump(), 200
except LLMBadRequestError:
raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
"No Embedding Model available. Please configure a valid provider "
"in the Settings -> Model Provider."
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
except PluginDaemonClientSideError as ex:
raise ProviderNotInitializeError(ex.description)
except Exception as e:
raise IndexingEstimateError(str(e))
@ -617,7 +603,7 @@ class DocumentDetailApi(DocumentResource):
raise InvalidMetadataError(f"Invalid metadata value: {metadata}")
if metadata == "only":
response = {"id": document.id, "doc_type": document.doc_type, "doc_metadata": document.doc_metadata_details}
response = {"id": document.id, "doc_type": document.doc_type, "doc_metadata": document.doc_metadata}
elif metadata == "without":
dataset_process_rules = DatasetService.get_process_rules(dataset_id)
document_process_rules = document.dataset_process_rule.to_dict()
@ -678,7 +664,7 @@ class DocumentDetailApi(DocumentResource):
"disabled_by": document.disabled_by,
"archived": document.archived,
"doc_type": document.doc_type,
"doc_metadata": document.doc_metadata_details,
"doc_metadata": document.doc_metadata,
"segment_count": document.segment_count,
"average_segment_length": document.average_segment_length,
"hit_count": document.hit_count,

View File

@ -168,7 +168,8 @@ class DatasetDocumentSegmentApi(Resource):
)
except LLMBadRequestError:
raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
"No Embedding Model available. Please configure a valid provider "
"in the Settings -> Model Provider."
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
@ -216,7 +217,8 @@ class DatasetDocumentSegmentAddApi(Resource):
)
except LLMBadRequestError:
raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
"No Embedding Model available. Please configure a valid provider "
"in the Settings -> Model Provider."
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
@ -265,7 +267,8 @@ class DatasetDocumentSegmentUpdateApi(Resource):
)
except LLMBadRequestError:
raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
"No Embedding Model available. Please configure a valid provider "
"in the Settings -> Model Provider."
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
@ -365,9 +368,9 @@ class DatasetDocumentSegmentBatchImportApi(Resource):
result = []
for index, row in df.iterrows():
if document.doc_form == "qa_model":
data = {"content": row.iloc[0], "answer": row.iloc[1]}
data = {"content": row[0], "answer": row[1]}
else:
data = {"content": row.iloc[0]}
data = {"content": row[0]}
result.append(data)
if len(result) == 0:
raise ValueError("The CSV file is empty.")
@ -434,7 +437,8 @@ class ChildChunkAddApi(Resource):
)
except LLMBadRequestError:
raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
"No Embedding Model available. Please configure a valid provider "
"in the Settings -> Model Provider."
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)

View File

@ -1,143 +0,0 @@
from flask_login import current_user # type: ignore # type: ignore
from flask_restful import Resource, marshal_with, reqparse # type: ignore
from werkzeug.exceptions import NotFound
from controllers.console import api
from controllers.console.wraps import account_initialization_required, enterprise_license_required, setup_required
from fields.dataset_fields import dataset_metadata_fields
from libs.login import login_required
from services.dataset_service import DatasetService
from services.entities.knowledge_entities.knowledge_entities import (
MetadataArgs,
MetadataOperationData,
)
from services.metadata_service import MetadataService
def _validate_name(name):
if not name or len(name) < 1 or len(name) > 40:
raise ValueError("Name must be between 1 to 40 characters.")
return name
def _validate_description_length(description):
if len(description) > 400:
raise ValueError("Description cannot exceed 400 characters.")
return description
class DatasetListApi(Resource):
@setup_required
@login_required
@account_initialization_required
@enterprise_license_required
@marshal_with(dataset_metadata_fields)
def post(self, dataset_id):
parser = reqparse.RequestParser()
parser.add_argument("type", type=str, required=True, nullable=True, location="json")
parser.add_argument("name", type=str, required=True, nullable=True, location="json")
args = parser.parse_args()
metadata_args = MetadataArgs(**args)
dataset_id_str = str(dataset_id)
dataset = DatasetService.get_dataset(dataset_id_str)
if dataset is None:
raise NotFound("Dataset not found.")
DatasetService.check_dataset_permission(dataset, current_user)
metadata = MetadataService.create_metadata(dataset_id_str, metadata_args)
return metadata, 201
class DatasetMetadataApi(Resource):
@setup_required
@login_required
@account_initialization_required
@enterprise_license_required
def patch(self, dataset_id, metadata_id):
parser = reqparse.RequestParser()
parser.add_argument("name", type=str, required=True, nullable=True, location="json")
args = parser.parse_args()
dataset_id_str = str(dataset_id)
metadata_id_str = str(metadata_id)
dataset = DatasetService.get_dataset(dataset_id_str)
if dataset is None:
raise NotFound("Dataset not found.")
DatasetService.check_dataset_permission(dataset, current_user)
metadata = MetadataService.update_metadata_name(dataset_id_str, metadata_id_str, args.get("name"))
return metadata, 200
@setup_required
@login_required
@account_initialization_required
@enterprise_license_required
def delete(self, dataset_id, metadata_id):
dataset_id_str = str(dataset_id)
metadata_id_str = str(metadata_id)
dataset = DatasetService.get_dataset(dataset_id_str)
if dataset is None:
raise NotFound("Dataset not found.")
DatasetService.check_dataset_permission(dataset, current_user)
MetadataService.delete_metadata(dataset_id_str, metadata_id_str)
return 200
class DatasetMetadataBuiltInFieldApi(Resource):
@setup_required
@login_required
@account_initialization_required
@enterprise_license_required
def get(self):
built_in_fields = MetadataService.get_built_in_fields()
return built_in_fields, 200
class DatasetMetadataBuiltInFieldActionApi(Resource):
@setup_required
@login_required
@account_initialization_required
@enterprise_license_required
def post(self, dataset_id, action):
dataset_id_str = str(dataset_id)
dataset = DatasetService.get_dataset(dataset_id_str)
if dataset is None:
raise NotFound("Dataset not found.")
DatasetService.check_dataset_permission(dataset, current_user)
if action == "enable":
MetadataService.enable_built_in_field(dataset)
elif action == "disable":
MetadataService.disable_built_in_field(dataset)
return 200
class DocumentMetadataApi(Resource):
@setup_required
@login_required
@account_initialization_required
@enterprise_license_required
def post(self, dataset_id):
dataset_id_str = str(dataset_id)
dataset = DatasetService.get_dataset(dataset_id_str)
if dataset is None:
raise NotFound("Dataset not found.")
DatasetService.check_dataset_permission(dataset, current_user)
parser = reqparse.RequestParser()
parser.add_argument("operation_data", type=list, required=True, nullable=True, location="json")
args = parser.parse_args()
metadata_args = MetadataOperationData(**args)
MetadataService.update_documents_metadata(dataset, metadata_args)
return 200
api.add_resource(DatasetListApi, "/datasets/<uuid:dataset_id>/metadata")
api.add_resource(DatasetMetadataApi, "/datasets/<uuid:dataset_id>/metadata/<uuid:metadata_id>")
api.add_resource(DatasetMetadataBuiltInFieldApi, "/datasets/metadata/built-in")
api.add_resource(DatasetMetadataBuiltInFieldActionApi, "/datasets/metadata/built-in/<string:action>")
api.add_resource(DocumentMetadataApi, "/datasets/<uuid:dataset_id>/documents/metadata")

View File

@ -32,7 +32,7 @@ class ConversationListApi(InstalledAppResource):
pinned = None
if "pinned" in args and args["pinned"] is not None:
pinned = args["pinned"] == "true"
pinned = True if args["pinned"] == "true" else False
try:
with Session(db.engine) as session:

View File

@ -50,7 +50,7 @@ class MessageListApi(InstalledAppResource):
try:
return MessageService.pagination_by_first_id(
app_model, current_user, args["conversation_id"], args["first_id"], args["limit"]
app_model, current_user, args["conversation_id"], args["first_id"], args["limit"], "desc"
)
except services.errors.conversation.ConversationNotExistsError:
raise NotFound("Conversation Not Exists.")

View File

@ -2,11 +2,8 @@ import os
from flask import session
from flask_restful import Resource, reqparse # type: ignore
from sqlalchemy import select
from sqlalchemy.orm import Session
from configs import dify_config
from extensions.ext_database import db
from libs.helper import StrLen
from models.model import DifySetup
from services.account_service import TenantService
@ -45,11 +42,7 @@ class InitValidateAPI(Resource):
def get_init_validate_status():
if dify_config.EDITION == "SELF_HOSTED":
if os.environ.get("INIT_PASSWORD"):
if session.get("is_init_validated"):
return True
with Session(db.engine) as db_session:
return db_session.execute(select(DifySetup)).scalar_one_or_none()
return session.get("is_init_validated") or DifySetup.query.first()
return True

View File

@ -4,7 +4,7 @@ from flask_restful import Resource, reqparse # type: ignore
from configs import dify_config
from libs.helper import StrLen, email, extract_remote_ip
from libs.password import valid_password
from models.model import DifySetup, db
from models.model import DifySetup
from services.account_service import RegisterService, TenantService
from . import api
@ -52,9 +52,8 @@ class SetupApi(Resource):
def get_setup_status():
if dify_config.EDITION == "SELF_HOSTED":
return db.session.query(DifySetup).first()
else:
return True
return DifySetup.query.first()
return True
api.add_resource(SetupApi, "/setup")

View File

@ -1,56 +0,0 @@
from functools import wraps
from flask_login import current_user # type: ignore
from sqlalchemy.orm import Session
from werkzeug.exceptions import Forbidden
from extensions.ext_database import db
from models.account import TenantPluginPermission
def plugin_permission_required(
install_required: bool = False,
debug_required: bool = False,
):
def interceptor(view):
@wraps(view)
def decorated(*args, **kwargs):
user = current_user
tenant_id = user.current_tenant_id
with Session(db.engine) as session:
permission = (
session.query(TenantPluginPermission)
.filter(
TenantPluginPermission.tenant_id == tenant_id,
)
.first()
)
if not permission:
# no permission set, allow access for everyone
return view(*args, **kwargs)
if install_required:
if permission.install_permission == TenantPluginPermission.InstallPermission.NOBODY:
raise Forbidden()
if permission.install_permission == TenantPluginPermission.InstallPermission.ADMINS:
if not user.is_admin_or_owner:
raise Forbidden()
if permission.install_permission == TenantPluginPermission.InstallPermission.EVERYONE:
pass
if debug_required:
if permission.debug_permission == TenantPluginPermission.DebugPermission.NOBODY:
raise Forbidden()
if permission.debug_permission == TenantPluginPermission.DebugPermission.ADMINS:
if not user.is_admin_or_owner:
raise Forbidden()
if permission.debug_permission == TenantPluginPermission.DebugPermission.EVERYONE:
pass
return view(*args, **kwargs)
return decorated
return interceptor

View File

@ -1,36 +0,0 @@
from flask_login import current_user # type: ignore
from flask_restful import Resource # type: ignore
from controllers.console import api
from controllers.console.wraps import account_initialization_required, setup_required
from core.model_runtime.utils.encoders import jsonable_encoder
from libs.login import login_required
from services.agent_service import AgentService
class AgentProviderListApi(Resource):
@setup_required
@login_required
@account_initialization_required
def get(self):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
return jsonable_encoder(AgentService.list_agent_providers(user_id, tenant_id))
class AgentProviderApi(Resource):
@setup_required
@login_required
@account_initialization_required
def get(self, provider_name: str):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
return jsonable_encoder(AgentService.get_agent_provider(user_id, tenant_id, provider_name))
api.add_resource(AgentProviderListApi, "/workspaces/current/agent-providers")
api.add_resource(AgentProviderApi, "/workspaces/current/agent-provider/<path:provider_name>")

View File

@ -1,205 +0,0 @@
from flask_login import current_user # type: ignore
from flask_restful import Resource, reqparse # type: ignore
from werkzeug.exceptions import Forbidden
from controllers.console import api
from controllers.console.wraps import account_initialization_required, setup_required
from core.model_runtime.utils.encoders import jsonable_encoder
from libs.login import login_required
from services.plugin.endpoint_service import EndpointService
class EndpointCreateApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
user = current_user
if not user.is_admin_or_owner:
raise Forbidden()
parser = reqparse.RequestParser()
parser.add_argument("plugin_unique_identifier", type=str, required=True)
parser.add_argument("settings", type=dict, required=True)
parser.add_argument("name", type=str, required=True)
args = parser.parse_args()
plugin_unique_identifier = args["plugin_unique_identifier"]
settings = args["settings"]
name = args["name"]
return {
"success": EndpointService.create_endpoint(
tenant_id=user.current_tenant_id,
user_id=user.id,
plugin_unique_identifier=plugin_unique_identifier,
name=name,
settings=settings,
)
}
class EndpointListApi(Resource):
@setup_required
@login_required
@account_initialization_required
def get(self):
user = current_user
parser = reqparse.RequestParser()
parser.add_argument("page", type=int, required=True, location="args")
parser.add_argument("page_size", type=int, required=True, location="args")
args = parser.parse_args()
page = args["page"]
page_size = args["page_size"]
return jsonable_encoder(
{
"endpoints": EndpointService.list_endpoints(
tenant_id=user.current_tenant_id,
user_id=user.id,
page=page,
page_size=page_size,
)
}
)
class EndpointListForSinglePluginApi(Resource):
@setup_required
@login_required
@account_initialization_required
def get(self):
user = current_user
parser = reqparse.RequestParser()
parser.add_argument("page", type=int, required=True, location="args")
parser.add_argument("page_size", type=int, required=True, location="args")
parser.add_argument("plugin_id", type=str, required=True, location="args")
args = parser.parse_args()
page = args["page"]
page_size = args["page_size"]
plugin_id = args["plugin_id"]
return jsonable_encoder(
{
"endpoints": EndpointService.list_endpoints_for_single_plugin(
tenant_id=user.current_tenant_id,
user_id=user.id,
plugin_id=plugin_id,
page=page,
page_size=page_size,
)
}
)
class EndpointDeleteApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
user = current_user
parser = reqparse.RequestParser()
parser.add_argument("endpoint_id", type=str, required=True)
args = parser.parse_args()
if not user.is_admin_or_owner:
raise Forbidden()
endpoint_id = args["endpoint_id"]
return {
"success": EndpointService.delete_endpoint(
tenant_id=user.current_tenant_id, user_id=user.id, endpoint_id=endpoint_id
)
}
class EndpointUpdateApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
user = current_user
parser = reqparse.RequestParser()
parser.add_argument("endpoint_id", type=str, required=True)
parser.add_argument("settings", type=dict, required=True)
parser.add_argument("name", type=str, required=True)
args = parser.parse_args()
endpoint_id = args["endpoint_id"]
settings = args["settings"]
name = args["name"]
if not user.is_admin_or_owner:
raise Forbidden()
return {
"success": EndpointService.update_endpoint(
tenant_id=user.current_tenant_id,
user_id=user.id,
endpoint_id=endpoint_id,
name=name,
settings=settings,
)
}
class EndpointEnableApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
user = current_user
parser = reqparse.RequestParser()
parser.add_argument("endpoint_id", type=str, required=True)
args = parser.parse_args()
endpoint_id = args["endpoint_id"]
if not user.is_admin_or_owner:
raise Forbidden()
return {
"success": EndpointService.enable_endpoint(
tenant_id=user.current_tenant_id, user_id=user.id, endpoint_id=endpoint_id
)
}
class EndpointDisableApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
user = current_user
parser = reqparse.RequestParser()
parser.add_argument("endpoint_id", type=str, required=True)
args = parser.parse_args()
endpoint_id = args["endpoint_id"]
if not user.is_admin_or_owner:
raise Forbidden()
return {
"success": EndpointService.disable_endpoint(
tenant_id=user.current_tenant_id, user_id=user.id, endpoint_id=endpoint_id
)
}
api.add_resource(EndpointCreateApi, "/workspaces/current/endpoints/create")
api.add_resource(EndpointListApi, "/workspaces/current/endpoints/list")
api.add_resource(EndpointListForSinglePluginApi, "/workspaces/current/endpoints/list/plugin")
api.add_resource(EndpointDeleteApi, "/workspaces/current/endpoints/delete")
api.add_resource(EndpointUpdateApi, "/workspaces/current/endpoints/update")
api.add_resource(EndpointEnableApi, "/workspaces/current/endpoints/enable")
api.add_resource(EndpointDisableApi, "/workspaces/current/endpoints/disable")

View File

@ -112,10 +112,10 @@ class LoadBalancingConfigCredentialsValidateApi(Resource):
# Load Balancing Config
api.add_resource(
LoadBalancingCredentialsValidateApi,
"/workspaces/current/model-providers/<path:provider>/models/load-balancing-configs/credentials-validate",
"/workspaces/current/model-providers/<string:provider>/models/load-balancing-configs/credentials-validate",
)
api.add_resource(
LoadBalancingConfigCredentialsValidateApi,
"/workspaces/current/model-providers/<path:provider>/models/load-balancing-configs/<string:config_id>/credentials-validate",
"/workspaces/current/model-providers/<string:provider>/models/load-balancing-configs/<string:config_id>/credentials-validate",
)

View File

@ -79,7 +79,7 @@ class ModelProviderValidateApi(Resource):
response = {"result": "success" if result else "error"}
if not result:
response["error"] = error or "Unknown error"
response["error"] = error
return response
@ -125,10 +125,9 @@ class ModelProviderIconApi(Resource):
Get model provider icon
"""
def get(self, tenant_id: str, provider: str, icon_type: str, lang: str):
def get(self, provider: str, icon_type: str, lang: str):
model_provider_service = ModelProviderService()
icon, mimetype = model_provider_service.get_model_provider_icon(
tenant_id=tenant_id,
provider=provider,
icon_type=icon_type,
lang=lang,
@ -184,17 +183,53 @@ class ModelProviderPaymentCheckoutUrlApi(Resource):
return data
class ModelProviderFreeQuotaSubmitApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self, provider: str):
model_provider_service = ModelProviderService()
result = model_provider_service.free_quota_submit(tenant_id=current_user.current_tenant_id, provider=provider)
return result
class ModelProviderFreeQuotaQualificationVerifyApi(Resource):
@setup_required
@login_required
@account_initialization_required
def get(self, provider: str):
parser = reqparse.RequestParser()
parser.add_argument("token", type=str, required=False, nullable=True, location="args")
args = parser.parse_args()
model_provider_service = ModelProviderService()
result = model_provider_service.free_quota_qualification_verify(
tenant_id=current_user.current_tenant_id, provider=provider, token=args["token"]
)
return result
api.add_resource(ModelProviderListApi, "/workspaces/current/model-providers")
api.add_resource(ModelProviderCredentialApi, "/workspaces/current/model-providers/<path:provider>/credentials")
api.add_resource(ModelProviderValidateApi, "/workspaces/current/model-providers/<path:provider>/credentials/validate")
api.add_resource(ModelProviderApi, "/workspaces/current/model-providers/<path:provider>")
api.add_resource(ModelProviderCredentialApi, "/workspaces/current/model-providers/<string:provider>/credentials")
api.add_resource(ModelProviderValidateApi, "/workspaces/current/model-providers/<string:provider>/credentials/validate")
api.add_resource(ModelProviderApi, "/workspaces/current/model-providers/<string:provider>")
api.add_resource(
ModelProviderIconApi, "/workspaces/current/model-providers/<string:provider>/<string:icon_type>/<string:lang>"
)
api.add_resource(
PreferredProviderTypeUpdateApi, "/workspaces/current/model-providers/<path:provider>/preferred-provider-type"
PreferredProviderTypeUpdateApi, "/workspaces/current/model-providers/<string:provider>/preferred-provider-type"
)
api.add_resource(ModelProviderPaymentCheckoutUrlApi, "/workspaces/current/model-providers/<path:provider>/checkout-url")
api.add_resource(
ModelProviderIconApi,
"/workspaces/<string:tenant_id>/model-providers/<path:provider>/<string:icon_type>/<string:lang>",
ModelProviderPaymentCheckoutUrlApi, "/workspaces/current/model-providers/<string:provider>/checkout-url"
)
api.add_resource(
ModelProviderFreeQuotaSubmitApi, "/workspaces/current/model-providers/<string:provider>/free-quota-submit"
)
api.add_resource(
ModelProviderFreeQuotaQualificationVerifyApi,
"/workspaces/current/model-providers/<string:provider>/free-quota-qualification-verify",
)

View File

@ -325,7 +325,7 @@ class ModelProviderModelValidateApi(Resource):
response = {"result": "success" if result else "error"}
if not result:
response["error"] = error or ""
response["error"] = error
return response
@ -362,26 +362,26 @@ class ModelProviderAvailableModelApi(Resource):
return jsonable_encoder({"data": models})
api.add_resource(ModelProviderModelApi, "/workspaces/current/model-providers/<path:provider>/models")
api.add_resource(ModelProviderModelApi, "/workspaces/current/model-providers/<string:provider>/models")
api.add_resource(
ModelProviderModelEnableApi,
"/workspaces/current/model-providers/<path:provider>/models/enable",
"/workspaces/current/model-providers/<string:provider>/models/enable",
endpoint="model-provider-model-enable",
)
api.add_resource(
ModelProviderModelDisableApi,
"/workspaces/current/model-providers/<path:provider>/models/disable",
"/workspaces/current/model-providers/<string:provider>/models/disable",
endpoint="model-provider-model-disable",
)
api.add_resource(
ModelProviderModelCredentialApi, "/workspaces/current/model-providers/<path:provider>/models/credentials"
ModelProviderModelCredentialApi, "/workspaces/current/model-providers/<string:provider>/models/credentials"
)
api.add_resource(
ModelProviderModelValidateApi, "/workspaces/current/model-providers/<path:provider>/models/credentials/validate"
ModelProviderModelValidateApi, "/workspaces/current/model-providers/<string:provider>/models/credentials/validate"
)
api.add_resource(
ModelProviderModelParameterRuleApi, "/workspaces/current/model-providers/<path:provider>/models/parameter-rules"
ModelProviderModelParameterRuleApi, "/workspaces/current/model-providers/<string:provider>/models/parameter-rules"
)
api.add_resource(ModelProviderAvailableModelApi, "/workspaces/current/models/model-types/<string:model_type>")
api.add_resource(DefaultModelApi, "/workspaces/current/default-model")

View File

@ -1,475 +0,0 @@
import io
from flask import request, send_file
from flask_login import current_user # type: ignore
from flask_restful import Resource, reqparse # type: ignore
from werkzeug.exceptions import Forbidden
from configs import dify_config
from controllers.console import api
from controllers.console.workspace import plugin_permission_required
from controllers.console.wraps import account_initialization_required, setup_required
from core.model_runtime.utils.encoders import jsonable_encoder
from core.plugin.manager.exc import PluginDaemonClientSideError
from libs.login import login_required
from models.account import TenantPluginPermission
from services.plugin.plugin_permission_service import PluginPermissionService
from services.plugin.plugin_service import PluginService
class PluginDebuggingKeyApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
def get(self):
tenant_id = current_user.current_tenant_id
try:
return {
"key": PluginService.get_debugging_key(tenant_id),
"host": dify_config.PLUGIN_REMOTE_INSTALL_HOST,
"port": dify_config.PLUGIN_REMOTE_INSTALL_PORT,
}
except PluginDaemonClientSideError as e:
raise ValueError(e)
class PluginListApi(Resource):
@setup_required
@login_required
@account_initialization_required
def get(self):
tenant_id = current_user.current_tenant_id
try:
plugins = PluginService.list(tenant_id)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return jsonable_encoder({"plugins": plugins})
class PluginListInstallationsFromIdsApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("plugin_ids", type=list, required=True, location="json")
args = parser.parse_args()
try:
plugins = PluginService.list_installations_from_ids(tenant_id, args["plugin_ids"])
except PluginDaemonClientSideError as e:
raise ValueError(e)
return jsonable_encoder({"plugins": plugins})
class PluginIconApi(Resource):
@setup_required
def get(self):
req = reqparse.RequestParser()
req.add_argument("tenant_id", type=str, required=True, location="args")
req.add_argument("filename", type=str, required=True, location="args")
args = req.parse_args()
try:
icon_bytes, mimetype = PluginService.get_asset(args["tenant_id"], args["filename"])
except PluginDaemonClientSideError as e:
raise ValueError(e)
icon_cache_max_age = dify_config.TOOL_ICON_CACHE_MAX_AGE
return send_file(io.BytesIO(icon_bytes), mimetype=mimetype, max_age=icon_cache_max_age)
class PluginUploadFromPkgApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(install_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
file = request.files["pkg"]
# check file size
if file.content_length > dify_config.PLUGIN_MAX_PACKAGE_SIZE:
raise ValueError("File size exceeds the maximum allowed size")
content = file.read()
try:
response = PluginService.upload_pkg(tenant_id, content)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return jsonable_encoder(response)
class PluginUploadFromGithubApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(install_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("repo", type=str, required=True, location="json")
parser.add_argument("version", type=str, required=True, location="json")
parser.add_argument("package", type=str, required=True, location="json")
args = parser.parse_args()
try:
response = PluginService.upload_pkg_from_github(tenant_id, args["repo"], args["version"], args["package"])
except PluginDaemonClientSideError as e:
raise ValueError(e)
return jsonable_encoder(response)
class PluginUploadFromBundleApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(install_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
file = request.files["bundle"]
# check file size
if file.content_length > dify_config.PLUGIN_MAX_BUNDLE_SIZE:
raise ValueError("File size exceeds the maximum allowed size")
content = file.read()
try:
response = PluginService.upload_bundle(tenant_id, content)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return jsonable_encoder(response)
class PluginInstallFromPkgApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(install_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("plugin_unique_identifiers", type=list, required=True, location="json")
args = parser.parse_args()
# check if all plugin_unique_identifiers are valid string
for plugin_unique_identifier in args["plugin_unique_identifiers"]:
if not isinstance(plugin_unique_identifier, str):
raise ValueError("Invalid plugin unique identifier")
try:
response = PluginService.install_from_local_pkg(tenant_id, args["plugin_unique_identifiers"])
except PluginDaemonClientSideError as e:
raise ValueError(e)
return jsonable_encoder(response)
class PluginInstallFromGithubApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(install_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("repo", type=str, required=True, location="json")
parser.add_argument("version", type=str, required=True, location="json")
parser.add_argument("package", type=str, required=True, location="json")
parser.add_argument("plugin_unique_identifier", type=str, required=True, location="json")
args = parser.parse_args()
try:
response = PluginService.install_from_github(
tenant_id,
args["plugin_unique_identifier"],
args["repo"],
args["version"],
args["package"],
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return jsonable_encoder(response)
class PluginInstallFromMarketplaceApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(install_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("plugin_unique_identifiers", type=list, required=True, location="json")
args = parser.parse_args()
# check if all plugin_unique_identifiers are valid string
for plugin_unique_identifier in args["plugin_unique_identifiers"]:
if not isinstance(plugin_unique_identifier, str):
raise ValueError("Invalid plugin unique identifier")
try:
response = PluginService.install_from_marketplace_pkg(tenant_id, args["plugin_unique_identifiers"])
except PluginDaemonClientSideError as e:
raise ValueError(e)
return jsonable_encoder(response)
class PluginFetchManifestApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
def get(self):
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("plugin_unique_identifier", type=str, required=True, location="args")
args = parser.parse_args()
try:
return jsonable_encoder(
{
"manifest": PluginService.fetch_plugin_manifest(
tenant_id, args["plugin_unique_identifier"]
).model_dump()
}
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
class PluginFetchInstallTasksApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
def get(self):
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("page", type=int, required=True, location="args")
parser.add_argument("page_size", type=int, required=True, location="args")
args = parser.parse_args()
try:
return jsonable_encoder(
{"tasks": PluginService.fetch_install_tasks(tenant_id, args["page"], args["page_size"])}
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
class PluginFetchInstallTaskApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
def get(self, task_id: str):
tenant_id = current_user.current_tenant_id
try:
return jsonable_encoder({"task": PluginService.fetch_install_task(tenant_id, task_id)})
except PluginDaemonClientSideError as e:
raise ValueError(e)
class PluginDeleteInstallTaskApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
def post(self, task_id: str):
tenant_id = current_user.current_tenant_id
try:
return {"success": PluginService.delete_install_task(tenant_id, task_id)}
except PluginDaemonClientSideError as e:
raise ValueError(e)
class PluginDeleteAllInstallTaskItemsApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
try:
return {"success": PluginService.delete_all_install_task_items(tenant_id)}
except PluginDaemonClientSideError as e:
raise ValueError(e)
class PluginDeleteInstallTaskItemApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
def post(self, task_id: str, identifier: str):
tenant_id = current_user.current_tenant_id
try:
return {"success": PluginService.delete_install_task_item(tenant_id, task_id, identifier)}
except PluginDaemonClientSideError as e:
raise ValueError(e)
class PluginUpgradeFromMarketplaceApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("original_plugin_unique_identifier", type=str, required=True, location="json")
parser.add_argument("new_plugin_unique_identifier", type=str, required=True, location="json")
args = parser.parse_args()
try:
return jsonable_encoder(
PluginService.upgrade_plugin_with_marketplace(
tenant_id, args["original_plugin_unique_identifier"], args["new_plugin_unique_identifier"]
)
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
class PluginUpgradeFromGithubApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("original_plugin_unique_identifier", type=str, required=True, location="json")
parser.add_argument("new_plugin_unique_identifier", type=str, required=True, location="json")
parser.add_argument("repo", type=str, required=True, location="json")
parser.add_argument("version", type=str, required=True, location="json")
parser.add_argument("package", type=str, required=True, location="json")
args = parser.parse_args()
try:
return jsonable_encoder(
PluginService.upgrade_plugin_with_github(
tenant_id,
args["original_plugin_unique_identifier"],
args["new_plugin_unique_identifier"],
args["repo"],
args["version"],
args["package"],
)
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
class PluginUninstallApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
def post(self):
req = reqparse.RequestParser()
req.add_argument("plugin_installation_id", type=str, required=True, location="json")
args = req.parse_args()
tenant_id = current_user.current_tenant_id
try:
return {"success": PluginService.uninstall(tenant_id, args["plugin_installation_id"])}
except PluginDaemonClientSideError as e:
raise ValueError(e)
class PluginChangePermissionApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
user = current_user
if not user.is_admin_or_owner:
raise Forbidden()
req = reqparse.RequestParser()
req.add_argument("install_permission", type=str, required=True, location="json")
req.add_argument("debug_permission", type=str, required=True, location="json")
args = req.parse_args()
install_permission = TenantPluginPermission.InstallPermission(args["install_permission"])
debug_permission = TenantPluginPermission.DebugPermission(args["debug_permission"])
tenant_id = user.current_tenant_id
return {"success": PluginPermissionService.change_permission(tenant_id, install_permission, debug_permission)}
class PluginFetchPermissionApi(Resource):
@setup_required
@login_required
@account_initialization_required
def get(self):
tenant_id = current_user.current_tenant_id
permission = PluginPermissionService.get_permission(tenant_id)
if not permission:
return jsonable_encoder(
{
"install_permission": TenantPluginPermission.InstallPermission.EVERYONE,
"debug_permission": TenantPluginPermission.DebugPermission.EVERYONE,
}
)
return jsonable_encoder(
{
"install_permission": permission.install_permission,
"debug_permission": permission.debug_permission,
}
)
api.add_resource(PluginDebuggingKeyApi, "/workspaces/current/plugin/debugging-key")
api.add_resource(PluginListApi, "/workspaces/current/plugin/list")
api.add_resource(PluginListInstallationsFromIdsApi, "/workspaces/current/plugin/list/installations/ids")
api.add_resource(PluginIconApi, "/workspaces/current/plugin/icon")
api.add_resource(PluginUploadFromPkgApi, "/workspaces/current/plugin/upload/pkg")
api.add_resource(PluginUploadFromGithubApi, "/workspaces/current/plugin/upload/github")
api.add_resource(PluginUploadFromBundleApi, "/workspaces/current/plugin/upload/bundle")
api.add_resource(PluginInstallFromPkgApi, "/workspaces/current/plugin/install/pkg")
api.add_resource(PluginInstallFromGithubApi, "/workspaces/current/plugin/install/github")
api.add_resource(PluginUpgradeFromMarketplaceApi, "/workspaces/current/plugin/upgrade/marketplace")
api.add_resource(PluginUpgradeFromGithubApi, "/workspaces/current/plugin/upgrade/github")
api.add_resource(PluginInstallFromMarketplaceApi, "/workspaces/current/plugin/install/marketplace")
api.add_resource(PluginFetchManifestApi, "/workspaces/current/plugin/fetch-manifest")
api.add_resource(PluginFetchInstallTasksApi, "/workspaces/current/plugin/tasks")
api.add_resource(PluginFetchInstallTaskApi, "/workspaces/current/plugin/tasks/<task_id>")
api.add_resource(PluginDeleteInstallTaskApi, "/workspaces/current/plugin/tasks/<task_id>/delete")
api.add_resource(PluginDeleteAllInstallTaskItemsApi, "/workspaces/current/plugin/tasks/delete_all")
api.add_resource(PluginDeleteInstallTaskItemApi, "/workspaces/current/plugin/tasks/<task_id>/delete/<path:identifier>")
api.add_resource(PluginUninstallApi, "/workspaces/current/plugin/uninstall")
api.add_resource(PluginChangePermissionApi, "/workspaces/current/plugin/permission/change")
api.add_resource(PluginFetchPermissionApi, "/workspaces/current/plugin/permission/fetch")

View File

@ -25,10 +25,8 @@ class ToolProviderListApi(Resource):
@login_required
@account_initialization_required
def get(self):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
req = reqparse.RequestParser()
req.add_argument(
@ -49,43 +47,28 @@ class ToolBuiltinProviderListToolsApi(Resource):
@login_required
@account_initialization_required
def get(self, provider):
user = current_user
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
return jsonable_encoder(
BuiltinToolManageService.list_builtin_tool_provider_tools(
user_id,
tenant_id,
provider,
)
)
class ToolBuiltinProviderInfoApi(Resource):
@setup_required
@login_required
@account_initialization_required
def get(self, provider):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
return jsonable_encoder(BuiltinToolManageService.get_builtin_tool_provider_info(user_id, tenant_id, provider))
class ToolBuiltinProviderDeleteApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self, provider):
user = current_user
if not user.is_admin_or_owner:
if not current_user.is_admin_or_owner:
raise Forbidden()
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
return BuiltinToolManageService.delete_builtin_tool_provider(
user_id,
@ -99,13 +82,11 @@ class ToolBuiltinProviderUpdateApi(Resource):
@login_required
@account_initialization_required
def post(self, provider):
user = current_user
if not user.is_admin_or_owner:
if not current_user.is_admin_or_owner:
raise Forbidden()
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("credentials", type=dict, required=True, nullable=False, location="json")
@ -150,13 +131,11 @@ class ToolApiProviderAddApi(Resource):
@login_required
@account_initialization_required
def post(self):
user = current_user
if not user.is_admin_or_owner:
if not current_user.is_admin_or_owner:
raise Forbidden()
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("credentials", type=dict, required=True, nullable=False, location="json")
@ -189,11 +168,6 @@ class ToolApiProviderGetRemoteSchemaApi(Resource):
@login_required
@account_initialization_required
def get(self):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("url", type=str, required=True, nullable=False, location="args")
@ -201,8 +175,8 @@ class ToolApiProviderGetRemoteSchemaApi(Resource):
args = parser.parse_args()
return ApiToolManageService.get_api_tool_provider_remote_schema(
user_id,
tenant_id,
current_user.id,
current_user.current_tenant_id,
args["url"],
)
@ -212,10 +186,8 @@ class ToolApiProviderListToolsApi(Resource):
@login_required
@account_initialization_required
def get(self):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
@ -237,13 +209,11 @@ class ToolApiProviderUpdateApi(Resource):
@login_required
@account_initialization_required
def post(self):
user = current_user
if not user.is_admin_or_owner:
if not current_user.is_admin_or_owner:
raise Forbidden()
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("credentials", type=dict, required=True, nullable=False, location="json")
@ -278,13 +248,11 @@ class ToolApiProviderDeleteApi(Resource):
@login_required
@account_initialization_required
def post(self):
user = current_user
if not user.is_admin_or_owner:
if not current_user.is_admin_or_owner:
raise Forbidden()
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
@ -304,10 +272,8 @@ class ToolApiProviderGetApi(Resource):
@login_required
@account_initialization_required
def get(self):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
@ -327,11 +293,7 @@ class ToolBuiltinProviderCredentialsSchemaApi(Resource):
@login_required
@account_initialization_required
def get(self, provider):
user = current_user
tenant_id = user.current_tenant_id
return BuiltinToolManageService.list_builtin_provider_credentials_schema(provider, tenant_id)
return BuiltinToolManageService.list_builtin_provider_credentials_schema(provider)
class ToolApiProviderSchemaApi(Resource):
@ -382,13 +344,11 @@ class ToolWorkflowProviderCreateApi(Resource):
@login_required
@account_initialization_required
def post(self):
user = current_user
if not user.is_admin_or_owner:
if not current_user.is_admin_or_owner:
raise Forbidden()
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
reqparser = reqparse.RequestParser()
reqparser.add_argument("workflow_app_id", type=uuid_value, required=True, nullable=False, location="json")
@ -421,13 +381,11 @@ class ToolWorkflowProviderUpdateApi(Resource):
@login_required
@account_initialization_required
def post(self):
user = current_user
if not user.is_admin_or_owner:
if not current_user.is_admin_or_owner:
raise Forbidden()
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
reqparser = reqparse.RequestParser()
reqparser.add_argument("workflow_tool_id", type=uuid_value, required=True, nullable=False, location="json")
@ -463,13 +421,11 @@ class ToolWorkflowProviderDeleteApi(Resource):
@login_required
@account_initialization_required
def post(self):
user = current_user
if not user.is_admin_or_owner:
if not current_user.is_admin_or_owner:
raise Forbidden()
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
reqparser = reqparse.RequestParser()
reqparser.add_argument("workflow_tool_id", type=uuid_value, required=True, nullable=False, location="json")
@ -488,10 +444,8 @@ class ToolWorkflowProviderGetApi(Resource):
@login_required
@account_initialization_required
def get(self):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("workflow_tool_id", type=uuid_value, required=False, nullable=True, location="args")
@ -522,10 +476,8 @@ class ToolWorkflowProviderListToolApi(Resource):
@login_required
@account_initialization_required
def get(self):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
parser = reqparse.RequestParser()
parser.add_argument("workflow_tool_id", type=uuid_value, required=True, nullable=False, location="args")
@ -546,10 +498,8 @@ class ToolBuiltinListApi(Resource):
@login_required
@account_initialization_required
def get(self):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
return jsonable_encoder(
[
@ -567,10 +517,8 @@ class ToolApiListApi(Resource):
@login_required
@account_initialization_required
def get(self):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
return jsonable_encoder(
[
@ -588,10 +536,8 @@ class ToolWorkflowListApi(Resource):
@login_required
@account_initialization_required
def get(self):
user = current_user
user_id = user.id
tenant_id = user.current_tenant_id
user_id = current_user.id
tenant_id = current_user.current_tenant_id
return jsonable_encoder(
[
@ -617,18 +563,16 @@ class ToolLabelsApi(Resource):
api.add_resource(ToolProviderListApi, "/workspaces/current/tool-providers")
# builtin tool provider
api.add_resource(ToolBuiltinProviderListToolsApi, "/workspaces/current/tool-provider/builtin/<path:provider>/tools")
api.add_resource(ToolBuiltinProviderInfoApi, "/workspaces/current/tool-provider/builtin/<path:provider>/info")
api.add_resource(ToolBuiltinProviderDeleteApi, "/workspaces/current/tool-provider/builtin/<path:provider>/delete")
api.add_resource(ToolBuiltinProviderUpdateApi, "/workspaces/current/tool-provider/builtin/<path:provider>/update")
api.add_resource(ToolBuiltinProviderListToolsApi, "/workspaces/current/tool-provider/builtin/<provider>/tools")
api.add_resource(ToolBuiltinProviderDeleteApi, "/workspaces/current/tool-provider/builtin/<provider>/delete")
api.add_resource(ToolBuiltinProviderUpdateApi, "/workspaces/current/tool-provider/builtin/<provider>/update")
api.add_resource(
ToolBuiltinProviderGetCredentialsApi, "/workspaces/current/tool-provider/builtin/<path:provider>/credentials"
ToolBuiltinProviderGetCredentialsApi, "/workspaces/current/tool-provider/builtin/<provider>/credentials"
)
api.add_resource(
ToolBuiltinProviderCredentialsSchemaApi,
"/workspaces/current/tool-provider/builtin/<path:provider>/credentials_schema",
ToolBuiltinProviderCredentialsSchemaApi, "/workspaces/current/tool-provider/builtin/<provider>/credentials_schema"
)
api.add_resource(ToolBuiltinProviderIconApi, "/workspaces/current/tool-provider/builtin/<path:provider>/icon")
api.add_resource(ToolBuiltinProviderIconApi, "/workspaces/current/tool-provider/builtin/<provider>/icon")
# api tool provider
api.add_resource(ToolApiProviderAddApi, "/workspaces/current/tool-provider/api/add")

View File

@ -7,7 +7,6 @@ from flask_login import current_user # type: ignore
from configs import dify_config
from controllers.console.workspace.error import AccountNotInitializedError
from extensions.ext_database import db
from models.model import DifySetup
from services.feature_service import FeatureService, LicenseStatus
from services.operation_service import OperationService
@ -135,13 +134,9 @@ def setup_required(view):
@wraps(view)
def decorated(*args, **kwargs):
# check setup
if (
dify_config.EDITION == "SELF_HOSTED"
and os.environ.get("INIT_PASSWORD")
and not db.session.query(DifySetup).first()
):
if dify_config.EDITION == "SELF_HOSTED" and os.environ.get("INIT_PASSWORD") and not DifySetup.query.first():
raise NotInitValidateError()
elif dify_config.EDITION == "SELF_HOSTED" and not db.session.query(DifySetup).first():
elif dify_config.EDITION == "SELF_HOSTED" and not DifySetup.query.first():
raise NotSetupError()
return view(*args, **kwargs)

View File

@ -6,4 +6,4 @@ bp = Blueprint("files", __name__)
api = ExternalApi(bp)
from . import image_preview, tool_files, upload
from . import image_preview, tool_files

View File

@ -1,5 +1,3 @@
from urllib.parse import quote
from flask import Response, request
from flask_restful import Resource, reqparse # type: ignore
from werkzeug.exceptions import NotFound
@ -73,8 +71,7 @@ class FilePreviewApi(Resource):
if upload_file.size > 0:
response.headers["Content-Length"] = str(upload_file.size)
if args["as_attachment"]:
encoded_filename = quote(upload_file.name)
response.headers["Content-Disposition"] = f"attachment; filename*=UTF-8''{encoded_filename}"
response.headers["Content-Disposition"] = f"attachment; filename={upload_file.name}"
return response

View File

@ -1,69 +0,0 @@
from flask import request
from flask_restful import Resource, marshal_with # type: ignore
from werkzeug.exceptions import Forbidden
import services
from controllers.console.wraps import setup_required
from controllers.files import api
from controllers.files.error import UnsupportedFileTypeError
from controllers.inner_api.plugin.wraps import get_user
from controllers.service_api.app.error import FileTooLargeError
from core.file.helpers import verify_plugin_file_signature
from fields.file_fields import file_fields
from services.file_service import FileService
class PluginUploadFileApi(Resource):
@setup_required
@marshal_with(file_fields)
def post(self):
# get file from request
file = request.files["file"]
timestamp = request.args.get("timestamp")
nonce = request.args.get("nonce")
sign = request.args.get("sign")
tenant_id = request.args.get("tenant_id")
if not tenant_id:
raise Forbidden("Invalid request.")
user_id = request.args.get("user_id")
user = get_user(tenant_id, user_id)
filename = file.filename
mimetype = file.mimetype
if not filename or not mimetype:
raise Forbidden("Invalid request.")
if not timestamp or not nonce or not sign:
raise Forbidden("Invalid request.")
if not verify_plugin_file_signature(
filename=filename,
mimetype=mimetype,
tenant_id=tenant_id,
user_id=user_id,
timestamp=timestamp,
nonce=nonce,
sign=sign,
):
raise Forbidden("Invalid request.")
try:
upload_file = FileService.upload_file(
filename=filename,
content=file.read(),
mimetype=mimetype,
user=user,
source=None,
)
except services.errors.file.FileTooLargeError as file_too_large_error:
raise FileTooLargeError(file_too_large_error.description)
except services.errors.file.UnsupportedFileTypeError:
raise UnsupportedFileTypeError()
return upload_file, 201
api.add_resource(PluginUploadFileApi, "/files/upload/for-plugin")

View File

@ -5,5 +5,4 @@ from libs.external_api import ExternalApi
bp = Blueprint("inner_api", __name__, url_prefix="/inner/api")
api = ExternalApi(bp)
from .plugin import plugin
from .workspace import workspace

View File

@ -1,293 +0,0 @@
from flask_restful import Resource # type: ignore
from controllers.console.wraps import setup_required
from controllers.inner_api import api
from controllers.inner_api.plugin.wraps import get_user_tenant, plugin_data
from controllers.inner_api.wraps import plugin_inner_api_only
from core.file.helpers import get_signed_file_url_for_plugin
from core.model_runtime.utils.encoders import jsonable_encoder
from core.plugin.backwards_invocation.app import PluginAppBackwardsInvocation
from core.plugin.backwards_invocation.base import BaseBackwardsInvocationResponse
from core.plugin.backwards_invocation.encrypt import PluginEncrypter
from core.plugin.backwards_invocation.model import PluginModelBackwardsInvocation
from core.plugin.backwards_invocation.node import PluginNodeBackwardsInvocation
from core.plugin.backwards_invocation.tool import PluginToolBackwardsInvocation
from core.plugin.entities.request import (
RequestInvokeApp,
RequestInvokeEncrypt,
RequestInvokeLLM,
RequestInvokeModeration,
RequestInvokeParameterExtractorNode,
RequestInvokeQuestionClassifierNode,
RequestInvokeRerank,
RequestInvokeSpeech2Text,
RequestInvokeSummary,
RequestInvokeTextEmbedding,
RequestInvokeTool,
RequestInvokeTTS,
RequestRequestUploadFile,
)
from core.tools.entities.tool_entities import ToolProviderType
from libs.helper import compact_generate_response
from models.account import Account, Tenant
from models.model import EndUser
class PluginInvokeLLMApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeLLM)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeLLM):
def generator():
response = PluginModelBackwardsInvocation.invoke_llm(user_model.id, tenant_model, payload)
return PluginModelBackwardsInvocation.convert_to_event_stream(response)
return compact_generate_response(generator())
class PluginInvokeTextEmbeddingApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeTextEmbedding)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeTextEmbedding):
try:
return jsonable_encoder(
BaseBackwardsInvocationResponse(
data=PluginModelBackwardsInvocation.invoke_text_embedding(
user_id=user_model.id,
tenant=tenant_model,
payload=payload,
)
)
)
except Exception as e:
return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
class PluginInvokeRerankApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeRerank)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeRerank):
try:
return jsonable_encoder(
BaseBackwardsInvocationResponse(
data=PluginModelBackwardsInvocation.invoke_rerank(
user_id=user_model.id,
tenant=tenant_model,
payload=payload,
)
)
)
except Exception as e:
return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
class PluginInvokeTTSApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeTTS)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeTTS):
def generator():
response = PluginModelBackwardsInvocation.invoke_tts(
user_id=user_model.id,
tenant=tenant_model,
payload=payload,
)
return PluginModelBackwardsInvocation.convert_to_event_stream(response)
return compact_generate_response(generator())
class PluginInvokeSpeech2TextApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeSpeech2Text)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeSpeech2Text):
try:
return jsonable_encoder(
BaseBackwardsInvocationResponse(
data=PluginModelBackwardsInvocation.invoke_speech2text(
user_id=user_model.id,
tenant=tenant_model,
payload=payload,
)
)
)
except Exception as e:
return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
class PluginInvokeModerationApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeModeration)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeModeration):
try:
return jsonable_encoder(
BaseBackwardsInvocationResponse(
data=PluginModelBackwardsInvocation.invoke_moderation(
user_id=user_model.id,
tenant=tenant_model,
payload=payload,
)
)
)
except Exception as e:
return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
class PluginInvokeToolApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeTool)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeTool):
def generator():
return PluginToolBackwardsInvocation.convert_to_event_stream(
PluginToolBackwardsInvocation.invoke_tool(
tenant_id=tenant_model.id,
user_id=user_model.id,
tool_type=ToolProviderType.value_of(payload.tool_type),
provider=payload.provider,
tool_name=payload.tool,
tool_parameters=payload.tool_parameters,
),
)
return compact_generate_response(generator())
class PluginInvokeParameterExtractorNodeApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeParameterExtractorNode)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeParameterExtractorNode):
try:
return jsonable_encoder(
BaseBackwardsInvocationResponse(
data=PluginNodeBackwardsInvocation.invoke_parameter_extractor(
tenant_id=tenant_model.id,
user_id=user_model.id,
parameters=payload.parameters,
model_config=payload.model,
instruction=payload.instruction,
query=payload.query,
)
)
)
except Exception as e:
return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
class PluginInvokeQuestionClassifierNodeApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeQuestionClassifierNode)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeQuestionClassifierNode):
try:
return jsonable_encoder(
BaseBackwardsInvocationResponse(
data=PluginNodeBackwardsInvocation.invoke_question_classifier(
tenant_id=tenant_model.id,
user_id=user_model.id,
query=payload.query,
model_config=payload.model,
classes=payload.classes,
instruction=payload.instruction,
)
)
)
except Exception as e:
return jsonable_encoder(BaseBackwardsInvocationResponse(error=str(e)))
class PluginInvokeAppApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeApp)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeApp):
response = PluginAppBackwardsInvocation.invoke_app(
app_id=payload.app_id,
user_id=user_model.id,
tenant_id=tenant_model.id,
conversation_id=payload.conversation_id,
query=payload.query,
stream=payload.response_mode == "streaming",
inputs=payload.inputs,
files=payload.files,
)
return compact_generate_response(PluginAppBackwardsInvocation.convert_to_event_stream(response))
class PluginInvokeEncryptApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeEncrypt)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeEncrypt):
"""
encrypt or decrypt data
"""
try:
return BaseBackwardsInvocationResponse(
data=PluginEncrypter.invoke_encrypt(tenant_model, payload)
).model_dump()
except Exception as e:
return BaseBackwardsInvocationResponse(error=str(e)).model_dump()
class PluginInvokeSummaryApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestInvokeSummary)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestInvokeSummary):
try:
return BaseBackwardsInvocationResponse(
data={
"summary": PluginModelBackwardsInvocation.invoke_summary(
user_id=user_model.id,
tenant=tenant_model,
payload=payload,
)
}
).model_dump()
except Exception as e:
return BaseBackwardsInvocationResponse(error=str(e)).model_dump()
class PluginUploadFileRequestApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestRequestUploadFile)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestRequestUploadFile):
# generate signed url
url = get_signed_file_url_for_plugin(payload.filename, payload.mimetype, tenant_model.id, user_model.id)
return BaseBackwardsInvocationResponse(data={"url": url}).model_dump()
api.add_resource(PluginInvokeLLMApi, "/invoke/llm")
api.add_resource(PluginInvokeTextEmbeddingApi, "/invoke/text-embedding")
api.add_resource(PluginInvokeRerankApi, "/invoke/rerank")
api.add_resource(PluginInvokeTTSApi, "/invoke/tts")
api.add_resource(PluginInvokeSpeech2TextApi, "/invoke/speech2text")
api.add_resource(PluginInvokeModerationApi, "/invoke/moderation")
api.add_resource(PluginInvokeToolApi, "/invoke/tool")
api.add_resource(PluginInvokeParameterExtractorNodeApi, "/invoke/parameter-extractor")
api.add_resource(PluginInvokeQuestionClassifierNodeApi, "/invoke/question-classifier")
api.add_resource(PluginInvokeAppApi, "/invoke/app")
api.add_resource(PluginInvokeEncryptApi, "/invoke/encrypt")
api.add_resource(PluginInvokeSummaryApi, "/invoke/summary")
api.add_resource(PluginUploadFileRequestApi, "/upload/file/request")

View File

@ -1,116 +0,0 @@
from collections.abc import Callable
from functools import wraps
from typing import Optional
from flask import request
from flask_restful import reqparse # type: ignore
from pydantic import BaseModel
from sqlalchemy.orm import Session
from extensions.ext_database import db
from models.account import Account, Tenant
from models.model import EndUser
from services.account_service import AccountService
def get_user(tenant_id: str, user_id: str | None) -> Account | EndUser:
try:
with Session(db.engine) as session:
if not user_id:
user_id = "DEFAULT-USER"
if user_id == "DEFAULT-USER":
user_model = session.query(EndUser).filter(EndUser.session_id == "DEFAULT-USER").first()
if not user_model:
user_model = EndUser(
tenant_id=tenant_id,
type="service_api",
is_anonymous=True if user_id == "DEFAULT-USER" else False,
session_id=user_id,
)
session.add(user_model)
session.commit()
else:
user_model = AccountService.load_user(user_id)
if not user_model:
user_model = session.query(EndUser).filter(EndUser.id == user_id).first()
if not user_model:
raise ValueError("user not found")
except Exception:
raise ValueError("user not found")
return user_model
def get_user_tenant(view: Optional[Callable] = None):
def decorator(view_func):
@wraps(view_func)
def decorated_view(*args, **kwargs):
# fetch json body
parser = reqparse.RequestParser()
parser.add_argument("tenant_id", type=str, required=True, location="json")
parser.add_argument("user_id", type=str, required=True, location="json")
kwargs = parser.parse_args()
user_id = kwargs.get("user_id")
tenant_id = kwargs.get("tenant_id")
if not tenant_id:
raise ValueError("tenant_id is required")
if not user_id:
user_id = "DEFAULT-USER"
del kwargs["tenant_id"]
del kwargs["user_id"]
try:
tenant_model = (
db.session.query(Tenant)
.filter(
Tenant.id == tenant_id,
)
.first()
)
except Exception:
raise ValueError("tenant not found")
if not tenant_model:
raise ValueError("tenant not found")
kwargs["tenant_model"] = tenant_model
kwargs["user_model"] = get_user(tenant_id, user_id)
return view_func(*args, **kwargs)
return decorated_view
if view is None:
return decorator
else:
return decorator(view)
def plugin_data(view: Optional[Callable] = None, *, payload_type: type[BaseModel]):
def decorator(view_func):
def decorated_view(*args, **kwargs):
try:
data = request.get_json()
except Exception:
raise ValueError("invalid json")
try:
payload = payload_type(**data)
except Exception as e:
raise ValueError(f"invalid payload: {str(e)}")
kwargs["payload"] = payload
return view_func(*args, **kwargs)
return decorated_view
if view is None:
return decorator
else:
return decorator(view)

View File

@ -1,10 +1,8 @@
import json
from flask_restful import Resource, reqparse # type: ignore
from controllers.console.wraps import setup_required
from controllers.inner_api import api
from controllers.inner_api.wraps import enterprise_inner_api_only
from controllers.inner_api.wraps import inner_api_only
from events.tenant_event import tenant_was_created
from models.account import Account
from services.account_service import TenantService
@ -12,7 +10,7 @@ from services.account_service import TenantService
class EnterpriseWorkspace(Resource):
@setup_required
@enterprise_inner_api_only
@inner_api_only
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("name", type=str, required=True, location="json")
@ -31,34 +29,4 @@ class EnterpriseWorkspace(Resource):
return {"message": "enterprise workspace created."}
class EnterpriseWorkspaceNoOwnerEmail(Resource):
@setup_required
@enterprise_inner_api_only
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("name", type=str, required=True, location="json")
args = parser.parse_args()
tenant = TenantService.create_tenant(args["name"], is_from_dashboard=True)
tenant_was_created.send(tenant)
resp = {
"id": tenant.id,
"name": tenant.name,
"encrypt_public_key": tenant.encrypt_public_key,
"plan": tenant.plan,
"status": tenant.status,
"custom_config": json.loads(tenant.custom_config) if tenant.custom_config else {},
"created_at": tenant.created_at.isoformat() + "Z" if tenant.created_at else None,
"updated_at": tenant.updated_at.isoformat() + "Z" if tenant.updated_at else None,
}
return {
"message": "enterprise workspace created.",
"tenant": resp,
}
api.add_resource(EnterpriseWorkspace, "/enterprise/workspace")
api.add_resource(EnterpriseWorkspaceNoOwnerEmail, "/enterprise/workspace/ownerless")

View File

@ -10,7 +10,7 @@ from extensions.ext_database import db
from models.model import EndUser
def enterprise_inner_api_only(view):
def inner_api_only(view):
@wraps(view)
def decorated(*args, **kwargs):
if not dify_config.INNER_API:
@ -18,7 +18,7 @@ def enterprise_inner_api_only(view):
# get header 'X-Inner-Api-Key'
inner_api_key = request.headers.get("X-Inner-Api-Key")
if not inner_api_key or inner_api_key != dify_config.INNER_API_KEY_FOR_PLUGIN:
if not inner_api_key or inner_api_key != dify_config.INNER_API_KEY:
abort(401)
return view(*args, **kwargs)
@ -26,7 +26,7 @@ def enterprise_inner_api_only(view):
return decorated
def enterprise_inner_api_user_auth(view):
def inner_api_user_auth(view):
@wraps(view)
def decorated(*args, **kwargs):
if not dify_config.INNER_API:
@ -60,19 +60,3 @@ def enterprise_inner_api_user_auth(view):
return view(*args, **kwargs)
return decorated
def plugin_inner_api_only(view):
@wraps(view)
def decorated(*args, **kwargs):
if not dify_config.PLUGIN_DAEMON_KEY:
abort(404)
# get header 'X-Inner-Api-Key'
inner_api_key = request.headers.get("X-Inner-Api-Key")
if not inner_api_key or inner_api_key != dify_config.INNER_API_KEY_FOR_PLUGIN:
abort(404)
return view(*args, **kwargs)
return decorated

View File

@ -7,4 +7,4 @@ api = ExternalApi(bp)
from . import index
from .app import app, audio, completion, conversation, file, message, workflow
from .dataset import dataset, document, hit_testing, segment, upload_file
from .dataset import dataset, document, hit_testing, segment

View File

@ -10,7 +10,6 @@ from controllers.service_api.app.error import NotChatAppError
from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token
from core.app.entities.app_invoke_entities import InvokeFrom
from fields.conversation_fields import message_file_fields
from fields.message_fields import feedback_fields, retriever_resource_fields
from fields.raws import FilesContainedField
from libs.helper import TimestampField, uuid_value
from models.model import App, AppMode, EndUser
@ -19,6 +18,26 @@ from services.message_service import MessageService
class MessageListApi(Resource):
feedback_fields = {"rating": fields.String}
retriever_resource_fields = {
"id": fields.String,
"message_id": fields.String,
"position": fields.Integer,
"dataset_id": fields.String,
"dataset_name": fields.String,
"document_id": fields.String,
"document_name": fields.String,
"data_source_type": fields.String,
"segment_id": fields.String,
"score": fields.Float,
"hit_count": fields.Integer,
"word_count": fields.Integer,
"segment_position": fields.Integer,
"index_node_hash": fields.String,
"content": fields.String,
"created_at": TimestampField,
}
agent_thought_fields = {
"id": fields.String,
"chain_id": fields.String,
@ -70,7 +89,7 @@ class MessageListApi(Resource):
try:
return MessageService.pagination_by_first_id(
app_model, end_user, args["conversation_id"], args["first_id"], args["limit"], "desc"
app_model, end_user, args["conversation_id"], args["first_id"], args["limit"]
)
except services.errors.conversation.ConversationNotExistsError:
raise NotFound("Conversation Not Exists.")

View File

@ -31,11 +31,8 @@ class DatasetListApi(DatasetApiResource):
# provider = request.args.get("provider", default="vendor")
search = request.args.get("keyword", default=None, type=str)
tag_ids = request.args.getlist("tag_ids")
include_all = request.args.get("include_all", default="false").lower() == "true"
datasets, total = DatasetService.get_datasets(
page, limit, tenant_id, current_user, search, tag_ids, include_all
)
datasets, total = DatasetService.get_datasets(page, limit, tenant_id, current_user, search, tag_ids)
# check embedding setting
provider_manager = ProviderManager()
configurations = provider_manager.get_configurations(tenant_id=current_user.current_tenant_id)

View File

@ -18,7 +18,6 @@ from controllers.service_api.app.error import (
from controllers.service_api.dataset.error import (
ArchivedDocumentImmutableError,
DocumentIndexingError,
InvalidMetadataError,
)
from controllers.service_api.wraps import DatasetApiResource, cloud_edition_billing_resource_check
from core.errors.error import ProviderTokenNotInitError
@ -51,9 +50,6 @@ class DocumentAddByTextApi(DatasetApiResource):
"indexing_technique", type=str, choices=Dataset.INDEXING_TECHNIQUE_LIST, nullable=False, location="json"
)
parser.add_argument("retrieval_model", type=dict, required=False, nullable=False, location="json")
parser.add_argument("doc_type", type=str, required=False, nullable=True, location="json")
parser.add_argument("doc_metadata", type=dict, required=False, nullable=True, location="json")
args = parser.parse_args()
dataset_id = str(dataset_id)
tenant_id = str(tenant_id)
@ -65,28 +61,6 @@ class DocumentAddByTextApi(DatasetApiResource):
if not dataset.indexing_technique and not args["indexing_technique"]:
raise ValueError("indexing_technique is required.")
# Validate metadata if provided
if args.get("doc_type") or args.get("doc_metadata"):
if not args.get("doc_type") or not args.get("doc_metadata"):
raise InvalidMetadataError("Both doc_type and doc_metadata must be provided when adding metadata")
if args["doc_type"] not in DocumentService.DOCUMENT_METADATA_SCHEMA:
raise InvalidMetadataError(
"Invalid doc_type. Must be one of: " + ", ".join(DocumentService.DOCUMENT_METADATA_SCHEMA.keys())
)
if not isinstance(args["doc_metadata"], dict):
raise InvalidMetadataError("doc_metadata must be a dictionary")
# Validate metadata schema based on doc_type
if args["doc_type"] != "others":
metadata_schema = DocumentService.DOCUMENT_METADATA_SCHEMA[args["doc_type"]]
for key, value in args["doc_metadata"].items():
if key in metadata_schema and not isinstance(value, metadata_schema[key]):
raise InvalidMetadataError(f"Invalid type for metadata field {key}")
# set to MetaDataConfig
args["metadata"] = {"doc_type": args["doc_type"], "doc_metadata": args["doc_metadata"]}
text = args.get("text")
name = args.get("name")
if text is None or name is None:
@ -133,8 +107,6 @@ class DocumentUpdateByTextApi(DatasetApiResource):
"doc_language", type=str, default="English", required=False, nullable=False, location="json"
)
parser.add_argument("retrieval_model", type=dict, required=False, nullable=False, location="json")
parser.add_argument("doc_type", type=str, required=False, nullable=True, location="json")
parser.add_argument("doc_metadata", type=dict, required=False, nullable=True, location="json")
args = parser.parse_args()
dataset_id = str(dataset_id)
tenant_id = str(tenant_id)
@ -143,32 +115,6 @@ class DocumentUpdateByTextApi(DatasetApiResource):
if not dataset:
raise ValueError("Dataset is not exist.")
# indexing_technique is already set in dataset since this is an update
args["indexing_technique"] = dataset.indexing_technique
# Validate metadata if provided
if args.get("doc_type") or args.get("doc_metadata"):
if not args.get("doc_type") or not args.get("doc_metadata"):
raise InvalidMetadataError("Both doc_type and doc_metadata must be provided when adding metadata")
if args["doc_type"] not in DocumentService.DOCUMENT_METADATA_SCHEMA:
raise InvalidMetadataError(
"Invalid doc_type. Must be one of: " + ", ".join(DocumentService.DOCUMENT_METADATA_SCHEMA.keys())
)
if not isinstance(args["doc_metadata"], dict):
raise InvalidMetadataError("doc_metadata must be a dictionary")
# Validate metadata schema based on doc_type
if args["doc_type"] != "others":
metadata_schema = DocumentService.DOCUMENT_METADATA_SCHEMA[args["doc_type"]]
for key, value in args["doc_metadata"].items():
if key in metadata_schema and not isinstance(value, metadata_schema[key]):
raise InvalidMetadataError(f"Invalid type for metadata field {key}")
# set to MetaDataConfig
args["metadata"] = {"doc_type": args["doc_type"], "doc_metadata": args["doc_metadata"]}
if args["text"]:
text = args.get("text")
name = args.get("name")
@ -215,30 +161,6 @@ class DocumentAddByFileApi(DatasetApiResource):
args["doc_form"] = "text_model"
if "doc_language" not in args:
args["doc_language"] = "English"
# Validate metadata if provided
if args.get("doc_type") or args.get("doc_metadata"):
if not args.get("doc_type") or not args.get("doc_metadata"):
raise InvalidMetadataError("Both doc_type and doc_metadata must be provided when adding metadata")
if args["doc_type"] not in DocumentService.DOCUMENT_METADATA_SCHEMA:
raise InvalidMetadataError(
"Invalid doc_type. Must be one of: " + ", ".join(DocumentService.DOCUMENT_METADATA_SCHEMA.keys())
)
if not isinstance(args["doc_metadata"], dict):
raise InvalidMetadataError("doc_metadata must be a dictionary")
# Validate metadata schema based on doc_type
if args["doc_type"] != "others":
metadata_schema = DocumentService.DOCUMENT_METADATA_SCHEMA[args["doc_type"]]
for key, value in args["doc_metadata"].items():
if key in metadata_schema and not isinstance(value, metadata_schema[key]):
raise InvalidMetadataError(f"Invalid type for metadata field {key}")
# set to MetaDataConfig
args["metadata"] = {"doc_type": args["doc_type"], "doc_metadata": args["doc_metadata"]}
# get dataset info
dataset_id = str(dataset_id)
tenant_id = str(tenant_id)
@ -306,29 +228,6 @@ class DocumentUpdateByFileApi(DatasetApiResource):
if "doc_language" not in args:
args["doc_language"] = "English"
# Validate metadata if provided
if args.get("doc_type") or args.get("doc_metadata"):
if not args.get("doc_type") or not args.get("doc_metadata"):
raise InvalidMetadataError("Both doc_type and doc_metadata must be provided when adding metadata")
if args["doc_type"] not in DocumentService.DOCUMENT_METADATA_SCHEMA:
raise InvalidMetadataError(
"Invalid doc_type. Must be one of: " + ", ".join(DocumentService.DOCUMENT_METADATA_SCHEMA.keys())
)
if not isinstance(args["doc_metadata"], dict):
raise InvalidMetadataError("doc_metadata must be a dictionary")
# Validate metadata schema based on doc_type
if args["doc_type"] != "others":
metadata_schema = DocumentService.DOCUMENT_METADATA_SCHEMA[args["doc_type"]]
for key, value in args["doc_metadata"].items():
if key in metadata_schema and not isinstance(value, metadata_schema[key]):
raise InvalidMetadataError(f"Invalid type for metadata field {key}")
# set to MetaDataConfig
args["metadata"] = {"doc_type": args["doc_type"], "doc_metadata": args["doc_metadata"]}
# get dataset info
dataset_id = str(dataset_id)
tenant_id = str(tenant_id)
@ -336,10 +235,6 @@ class DocumentUpdateByFileApi(DatasetApiResource):
if not dataset:
raise ValueError("Dataset is not exist.")
# indexing_technique is already set in dataset since this is an update
args["indexing_technique"] = dataset.indexing_technique
if "file" in request.files:
# save file info
file = request.files["file"]

View File

@ -53,7 +53,8 @@ class SegmentApi(DatasetApiResource):
)
except LLMBadRequestError:
raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
"No Embedding Model available. Please configure a valid provider "
"in the Settings -> Model Provider."
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
@ -94,7 +95,8 @@ class SegmentApi(DatasetApiResource):
)
except LLMBadRequestError:
raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
"No Embedding Model available. Please configure a valid provider "
"in the Settings -> Model Provider."
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
@ -173,7 +175,8 @@ class DatasetSegmentApi(DatasetApiResource):
)
except LLMBadRequestError:
raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
"No Embedding Model available. Please configure a valid provider "
"in the Settings -> Model Provider."
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)

View File

@ -1,54 +0,0 @@
from werkzeug.exceptions import NotFound
from controllers.service_api import api
from controllers.service_api.wraps import (
DatasetApiResource,
)
from core.file import helpers as file_helpers
from extensions.ext_database import db
from models.dataset import Dataset
from models.model import UploadFile
from services.dataset_service import DocumentService
class UploadFileApi(DatasetApiResource):
def get(self, tenant_id, dataset_id, document_id):
"""Get upload file."""
# check dataset
dataset_id = str(dataset_id)
tenant_id = str(tenant_id)
dataset = db.session.query(Dataset).filter(Dataset.tenant_id == tenant_id, Dataset.id == dataset_id).first()
if not dataset:
raise NotFound("Dataset not found.")
# check document
document_id = str(document_id)
document = DocumentService.get_document(dataset.id, document_id)
if not document:
raise NotFound("Document not found.")
# check upload file
if document.data_source_type != "upload_file":
raise ValueError(f"Document data source type ({document.data_source_type}) is not upload_file.")
data_source_info = document.data_source_info_dict
if data_source_info and "upload_file_id" in data_source_info:
file_id = data_source_info["upload_file_id"]
upload_file = db.session.query(UploadFile).filter(UploadFile.id == file_id).first()
if not upload_file:
raise NotFound("UploadFile not found.")
else:
raise ValueError("Upload file id not found in document data source info.")
url = file_helpers.get_signed_file_url(upload_file_id=upload_file.id)
return {
"id": upload_file.id,
"name": upload_file.name,
"size": upload_file.size,
"extension": upload_file.extension,
"url": url,
"download_url": f"{url}&as_attachment=true",
"mime_type": upload_file.mime_type,
"created_by": upload_file.created_by,
"created_at": upload_file.created_at.timestamp(),
}, 200
api.add_resource(UploadFileApi, "/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/upload-file")

View File

@ -154,7 +154,7 @@ def validate_dataset_token(view=None):
) # TODO: only owner information is required, so only one is returned.
if tenant_account_join:
tenant, ta = tenant_account_join
account = db.session.query(Account).filter(Account.id == ta.account_id).first()
account = Account.query.filter_by(id=ta.account_id).first()
# Login admin
if account:
account.current_tenant = tenant
@ -195,11 +195,7 @@ def validate_and_get_api_token(scope: str | None = None):
with Session(db.engine, expire_on_commit=False) as session:
update_stmt = (
update(ApiToken)
.where(
ApiToken.token == auth_token,
(ApiToken.last_used_at.is_(None) | (ApiToken.last_used_at < cutoff_time)),
ApiToken.type == scope,
)
.where(ApiToken.token == auth_token, ApiToken.last_used_at < cutoff_time, ApiToken.type == scope)
.values(last_used_at=current_time)
.returning(ApiToken)
)
@ -240,7 +236,7 @@ def create_or_update_end_user_for_user_id(app_model: App, user_id: Optional[str]
tenant_id=app_model.tenant_id,
app_id=app_model.id,
type="service_api",
is_anonymous=user_id == "DEFAULT-USER",
is_anonymous=True if user_id == "DEFAULT-USER" else False,
session_id=user_id,
)
db.session.add(end_user)

View File

@ -39,7 +39,7 @@ class ConversationListApi(WebApiResource):
pinned = None
if "pinned" in args and args["pinned"] is not None:
pinned = args["pinned"] == "true"
pinned = True if args["pinned"] == "true" else False
try:
with Session(db.engine) as session:

View File

@ -21,7 +21,7 @@ from core.app.entities.app_invoke_entities import InvokeFrom
from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError
from core.model_runtime.errors.invoke import InvokeError
from fields.conversation_fields import message_file_fields
from fields.message_fields import agent_thought_fields, feedback_fields, retriever_resource_fields
from fields.message_fields import agent_thought_fields
from fields.raws import FilesContainedField
from libs import helper
from libs.helper import TimestampField, uuid_value
@ -34,6 +34,27 @@ from services.message_service import MessageService
class MessageListApi(WebApiResource):
feedback_fields = {"rating": fields.String}
retriever_resource_fields = {
"id": fields.String,
"message_id": fields.String,
"position": fields.Integer,
"dataset_id": fields.String,
"dataset_name": fields.String,
"document_id": fields.String,
"document_name": fields.String,
"data_source_type": fields.String,
"segment_id": fields.String,
"score": fields.Float,
"hit_count": fields.Integer,
"word_count": fields.Integer,
"segment_position": fields.Integer,
"index_node_hash": fields.String,
"content": fields.String,
"created_at": TimestampField,
}
message_fields = {
"id": fields.String,
"conversation_id": fields.String,
@ -70,7 +91,7 @@ class MessageListApi(WebApiResource):
try:
return MessageService.pagination_by_first_id(
app_model, end_user, args["conversation_id"], args["first_id"], args["limit"]
app_model, end_user, args["conversation_id"], args["first_id"], args["limit"], "desc"
)
except services.errors.conversation.ConversationNotExistsError:
raise NotFound("Conversation Not Exists.")

View File

@ -1,6 +1,7 @@
import json
import logging
import uuid
from datetime import UTC, datetime
from typing import Optional, Union, cast
from core.agent.entities import AgentEntity, AgentToolEntity
@ -31,16 +32,19 @@ from core.model_runtime.entities import (
from core.model_runtime.entities.message_entities import ImagePromptMessageContent
from core.model_runtime.entities.model_entities import ModelFeature
from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
from core.model_runtime.utils.encoders import jsonable_encoder
from core.prompt.utils.extract_thread_messages import extract_thread_messages
from core.tools.__base.tool import Tool
from core.tools.entities.tool_entities import (
ToolParameter,
ToolRuntimeVariablePool,
)
from core.tools.tool.dataset_retriever_tool import DatasetRetrieverTool
from core.tools.tool.tool import Tool
from core.tools.tool_manager import ToolManager
from core.tools.utils.dataset_retriever_tool import DatasetRetrieverTool
from extensions.ext_database import db
from factories import file_factory
from models.model import Conversation, Message, MessageAgentThought, MessageFile
from models.tools import ToolConversationVariables
logger = logging.getLogger(__name__)
@ -58,9 +62,11 @@ class BaseAgentRunner(AppRunner):
queue_manager: AppQueueManager,
message: Message,
user_id: str,
model_instance: ModelInstance,
memory: Optional[TokenBufferMemory] = None,
prompt_messages: Optional[list[PromptMessage]] = None,
variables_pool: Optional[ToolRuntimeVariablePool] = None,
db_variables: Optional[ToolConversationVariables] = None,
model_instance: ModelInstance,
) -> None:
self.tenant_id = tenant_id
self.application_generate_entity = application_generate_entity
@ -73,6 +79,8 @@ class BaseAgentRunner(AppRunner):
self.user_id = user_id
self.memory = memory
self.history_prompt_messages = self.organize_agent_history(prompt_messages=prompt_messages or [])
self.variables_pool = variables_pool
self.db_variables_pool = db_variables
self.model_instance = model_instance
# init callback
@ -133,10 +141,11 @@ class BaseAgentRunner(AppRunner):
agent_tool=tool,
invoke_from=self.application_generate_entity.invoke_from,
)
assert tool_entity.entity.description
tool_entity.load_variables(self.variables_pool)
message_tool = PromptMessageTool(
name=tool.tool_name,
description=tool_entity.entity.description.llm,
description=tool_entity.description.llm if tool_entity.description else "",
parameters={
"type": "object",
"properties": {},
@ -144,7 +153,7 @@ class BaseAgentRunner(AppRunner):
},
)
parameters = tool_entity.get_merged_runtime_parameters()
parameters = tool_entity.get_all_runtime_parameters()
for parameter in parameters:
if parameter.form != ToolParameter.ToolParameterForm.LLM:
continue
@ -177,11 +186,9 @@ class BaseAgentRunner(AppRunner):
"""
convert dataset retriever tool to prompt message tool
"""
assert tool.entity.description
prompt_tool = PromptMessageTool(
name=tool.entity.identity.name,
description=tool.entity.description.llm,
name=tool.identity.name if tool.identity else "unknown",
description=tool.description.llm if tool.description else "",
parameters={
"type": "object",
"properties": {},
@ -227,7 +234,8 @@ class BaseAgentRunner(AppRunner):
# save prompt tool
prompt_messages_tools.append(prompt_tool)
# save tool entity
tool_instances[dataset_tool.entity.identity.name] = dataset_tool
if dataset_tool.identity is not None:
tool_instances[dataset_tool.identity.name] = dataset_tool
return tool_instances, prompt_messages_tools
@ -312,24 +320,24 @@ class BaseAgentRunner(AppRunner):
def save_agent_thought(
self,
agent_thought: MessageAgentThought,
tool_name: str | None,
tool_input: Union[str, dict, None],
thought: str | None,
tool_name: str,
tool_input: Union[str, dict],
thought: str,
observation: Union[str, dict, None],
tool_invoke_meta: Union[str, dict, None],
answer: str | None,
answer: str,
messages_ids: list[str],
llm_usage: LLMUsage | None = None,
):
"""
Save agent thought
"""
updated_agent_thought = (
queried_thought = (
db.session.query(MessageAgentThought).filter(MessageAgentThought.id == agent_thought.id).first()
)
if not updated_agent_thought:
raise ValueError("agent thought not found")
agent_thought = updated_agent_thought
if not queried_thought:
raise ValueError(f"Agent thought {agent_thought.id} not found")
agent_thought = queried_thought
if thought:
agent_thought.thought = thought
@ -341,39 +349,39 @@ class BaseAgentRunner(AppRunner):
if isinstance(tool_input, dict):
try:
tool_input = json.dumps(tool_input, ensure_ascii=False)
except Exception:
except Exception as e:
tool_input = json.dumps(tool_input)
updated_agent_thought.tool_input = tool_input
agent_thought.tool_input = tool_input
if observation:
if isinstance(observation, dict):
try:
observation = json.dumps(observation, ensure_ascii=False)
except Exception:
except Exception as e:
observation = json.dumps(observation)
updated_agent_thought.observation = observation
agent_thought.observation = observation
if answer:
agent_thought.answer = answer
if messages_ids is not None and len(messages_ids) > 0:
updated_agent_thought.message_files = json.dumps(messages_ids)
agent_thought.message_files = json.dumps(messages_ids)
if llm_usage:
updated_agent_thought.message_token = llm_usage.prompt_tokens
updated_agent_thought.message_price_unit = llm_usage.prompt_price_unit
updated_agent_thought.message_unit_price = llm_usage.prompt_unit_price
updated_agent_thought.answer_token = llm_usage.completion_tokens
updated_agent_thought.answer_price_unit = llm_usage.completion_price_unit
updated_agent_thought.answer_unit_price = llm_usage.completion_unit_price
updated_agent_thought.tokens = llm_usage.total_tokens
updated_agent_thought.total_price = llm_usage.total_price
agent_thought.message_token = llm_usage.prompt_tokens
agent_thought.message_price_unit = llm_usage.prompt_price_unit
agent_thought.message_unit_price = llm_usage.prompt_unit_price
agent_thought.answer_token = llm_usage.completion_tokens
agent_thought.answer_price_unit = llm_usage.completion_price_unit
agent_thought.answer_unit_price = llm_usage.completion_unit_price
agent_thought.tokens = llm_usage.total_tokens
agent_thought.total_price = llm_usage.total_price
# check if tool labels is not empty
labels = updated_agent_thought.tool_labels or {}
tools = updated_agent_thought.tool.split(";") if updated_agent_thought.tool else []
labels = agent_thought.tool_labels or {}
tools = agent_thought.tool.split(";") if agent_thought.tool else []
for tool in tools:
if not tool:
continue
@ -384,20 +392,42 @@ class BaseAgentRunner(AppRunner):
else:
labels[tool] = {"en_US": tool, "zh_Hans": tool}
updated_agent_thought.tool_labels_str = json.dumps(labels)
agent_thought.tool_labels_str = json.dumps(labels)
if tool_invoke_meta is not None:
if isinstance(tool_invoke_meta, dict):
try:
tool_invoke_meta = json.dumps(tool_invoke_meta, ensure_ascii=False)
except Exception:
except Exception as e:
tool_invoke_meta = json.dumps(tool_invoke_meta)
updated_agent_thought.tool_meta_str = tool_invoke_meta
agent_thought.tool_meta_str = tool_invoke_meta
db.session.commit()
db.session.close()
def update_db_variables(self, tool_variables: ToolRuntimeVariablePool, db_variables: ToolConversationVariables):
"""
convert tool variables to db variables
"""
queried_variables = (
db.session.query(ToolConversationVariables)
.filter(
ToolConversationVariables.conversation_id == self.message.conversation_id,
)
.first()
)
if not queried_variables:
return
db_variables = queried_variables
db_variables.updated_at = datetime.now(UTC).replace(tzinfo=None)
db_variables.variables_str = json.dumps(jsonable_encoder(tool_variables.pool))
db.session.commit()
db.session.close()
def organize_agent_history(self, prompt_messages: list[PromptMessage]) -> list[PromptMessage]:
"""
Organize agent history
@ -434,11 +464,11 @@ class BaseAgentRunner(AppRunner):
tool_call_response: list[ToolPromptMessage] = []
try:
tool_inputs = json.loads(agent_thought.tool_input)
except Exception:
except Exception as e:
tool_inputs = {tool: {} for tool in tools}
try:
tool_responses = json.loads(agent_thought.observation)
except Exception:
except Exception as e:
tool_responses = dict.fromkeys(tools, agent_thought.observation)
for tool in tools:
@ -485,11 +515,7 @@ class BaseAgentRunner(AppRunner):
files = db.session.query(MessageFile).filter(MessageFile.message_id == message.id).all()
if not files:
return UserPromptMessage(content=message.query)
if message.app_model_config:
file_extra_config = FileUploadConfigManager.convert(message.app_model_config.to_dict())
else:
file_extra_config = None
file_extra_config = FileUploadConfigManager.convert(message.app_model_config.to_dict())
if not file_extra_config:
return UserPromptMessage(content=message.query)

View File

@ -1,6 +1,6 @@
import json
from abc import ABC, abstractmethod
from collections.abc import Generator, Mapping, Sequence
from collections.abc import Generator, Mapping
from typing import Any, Optional
from core.agent.base_agent_runner import BaseAgentRunner
@ -18,8 +18,8 @@ from core.model_runtime.entities.message_entities import (
)
from core.ops.ops_trace_manager import TraceQueueManager
from core.prompt.agent_history_prompt_transform import AgentHistoryPromptTransform
from core.tools.__base.tool import Tool
from core.tools.entities.tool_entities import ToolInvokeMeta
from core.tools.tool.tool import Tool
from core.tools.tool_engine import ToolEngine
from models.model import Message
@ -27,11 +27,11 @@ from models.model import Message
class CotAgentRunner(BaseAgentRunner, ABC):
_is_first_iteration = True
_ignore_observation_providers = ["wenxin"]
_historic_prompt_messages: list[PromptMessage]
_agent_scratchpad: list[AgentScratchpadUnit]
_instruction: str
_query: str
_prompt_messages_tools: Sequence[PromptMessageTool]
_historic_prompt_messages: list[PromptMessage] | None = None
_agent_scratchpad: list[AgentScratchpadUnit] | None = None
_instruction: str = "" # FIXME this must be str for now
_query: str | None = None
_prompt_messages_tools: list[PromptMessageTool] = []
def run(
self,
@ -42,7 +42,6 @@ class CotAgentRunner(BaseAgentRunner, ABC):
"""
Run Cot agent application
"""
app_generate_entity = self.application_generate_entity
self._repack_app_generate_entity(app_generate_entity)
self._init_react_state(query)
@ -55,19 +54,17 @@ class CotAgentRunner(BaseAgentRunner, ABC):
app_generate_entity.model_conf.stop.append("Observation")
app_config = self.app_config
assert app_config.agent
# init instruction
inputs = inputs or {}
instruction = app_config.prompt_template.simple_prompt_template or ""
self._instruction = self._fill_in_inputs_from_external_data_tools(instruction, inputs)
instruction = app_config.prompt_template.simple_prompt_template
self._instruction = self._fill_in_inputs_from_external_data_tools(instruction=instruction or "", inputs=inputs)
iteration_step = 1
max_iteration_steps = min(app_config.agent.max_iteration if app_config.agent else 5, 5) + 1
# convert tools into ModelRuntime Tool format
tool_instances, prompt_messages_tools = self._init_prompt_tools()
self._prompt_messages_tools = prompt_messages_tools
tool_instances, self._prompt_messages_tools = self._init_prompt_tools()
function_call_state = True
llm_usage: dict[str, Optional[LLMUsage]] = {"usage": None}
@ -119,7 +116,14 @@ class CotAgentRunner(BaseAgentRunner, ABC):
callbacks=[],
)
usage_dict: dict[str, Optional[LLMUsage]] = {}
if not isinstance(chunks, Generator):
raise ValueError("Expected streaming response from LLM")
# check llm result
if not chunks:
raise ValueError("failed to invoke llm")
usage_dict: dict[str, Optional[LLMUsage]] = {"usage": None}
react_chunks = CotAgentOutputParser.handle_react_stream_output(chunks, usage_dict)
scratchpad = AgentScratchpadUnit(
agent_response="",
@ -139,25 +143,25 @@ class CotAgentRunner(BaseAgentRunner, ABC):
if isinstance(chunk, AgentScratchpadUnit.Action):
action = chunk
# detect action
assert scratchpad.agent_response is not None
scratchpad.agent_response += json.dumps(chunk.model_dump())
if scratchpad.agent_response is not None:
scratchpad.agent_response += json.dumps(chunk.model_dump())
scratchpad.action_str = json.dumps(chunk.model_dump())
scratchpad.action = action
else:
assert scratchpad.agent_response is not None
scratchpad.agent_response += chunk
assert scratchpad.thought is not None
scratchpad.thought += chunk
if scratchpad.agent_response is not None:
scratchpad.agent_response += chunk
if scratchpad.thought is not None:
scratchpad.thought += chunk
yield LLMResultChunk(
model=self.model_config.model,
prompt_messages=prompt_messages,
system_fingerprint="",
delta=LLMResultChunkDelta(index=0, message=AssistantPromptMessage(content=chunk), usage=None),
)
assert scratchpad.thought is not None
scratchpad.thought = scratchpad.thought.strip() or "I am thinking about how to help you"
self._agent_scratchpad.append(scratchpad)
if scratchpad.thought is not None:
scratchpad.thought = scratchpad.thought.strip() or "I am thinking about how to help you"
if self._agent_scratchpad is not None:
self._agent_scratchpad.append(scratchpad)
# get llm usage
if "usage" in usage_dict:
@ -168,7 +172,7 @@ class CotAgentRunner(BaseAgentRunner, ABC):
self.save_agent_thought(
agent_thought=agent_thought,
tool_name=(scratchpad.action.action_name if scratchpad.action and not scratchpad.is_final() else ""),
tool_name=scratchpad.action.action_name if scratchpad.action else "",
tool_input={scratchpad.action.action_name: scratchpad.action.action_input} if scratchpad.action else {},
tool_invoke_meta={},
thought=scratchpad.thought or "",
@ -252,6 +256,8 @@ class CotAgentRunner(BaseAgentRunner, ABC):
answer=final_answer,
messages_ids=[],
)
if self.variables_pool is not None and self.db_variables_pool is not None:
self.update_db_variables(self.variables_pool, self.db_variables_pool)
# publish end event
self.queue_manager.publish(
QueueMessageEndEvent(
@ -269,7 +275,7 @@ class CotAgentRunner(BaseAgentRunner, ABC):
def _handle_invoke_action(
self,
action: AgentScratchpadUnit.Action,
tool_instances: Mapping[str, Tool],
tool_instances: dict[str, Tool],
message_file_ids: list[str],
trace_manager: Optional[TraceQueueManager] = None,
) -> tuple[str, ToolInvokeMeta]:
@ -309,7 +315,11 @@ class CotAgentRunner(BaseAgentRunner, ABC):
)
# publish files
for message_file_id in message_files:
for message_file_id, save_as in message_files:
if save_as is not None and self.variables_pool:
# FIXME the save_as type is confusing, it should be a string or not
self.variables_pool.set_file(tool_name=tool_call_name, value=message_file_id, name=str(save_as))
# publish message file
self.queue_manager.publish(
QueueMessageFileEvent(message_file_id=message_file_id), PublishFrom.APPLICATION_MANAGER
@ -332,7 +342,7 @@ class CotAgentRunner(BaseAgentRunner, ABC):
for key, value in inputs.items():
try:
instruction = instruction.replace(f"{{{{{key}}}}}", str(value))
except Exception:
except Exception as e:
continue
return instruction
@ -369,7 +379,7 @@ class CotAgentRunner(BaseAgentRunner, ABC):
return message
def _organize_historic_prompt_messages(
self, current_session_messages: list[PromptMessage] | None = None
self, current_session_messages: Optional[list[PromptMessage]] = None
) -> list[PromptMessage]:
"""
organize historic prompt messages
@ -381,7 +391,8 @@ class CotAgentRunner(BaseAgentRunner, ABC):
for message in self.history_prompt_messages:
if isinstance(message, AssistantPromptMessage):
if not current_scratchpad:
assert isinstance(message.content, str)
if not isinstance(message.content, str | None):
raise NotImplementedError("expected str type")
current_scratchpad = AgentScratchpadUnit(
agent_response=message.content,
thought=message.content or "I am thinking about how to help you",
@ -400,8 +411,9 @@ class CotAgentRunner(BaseAgentRunner, ABC):
except:
pass
elif isinstance(message, ToolPromptMessage):
if current_scratchpad:
assert isinstance(message.content, str)
if not current_scratchpad:
continue
if isinstance(message.content, str):
current_scratchpad.observation = message.content
else:
raise NotImplementedError("expected str type")

View File

@ -19,8 +19,8 @@ class CotChatAgentRunner(CotAgentRunner):
"""
Organize system prompt
"""
assert self.app_config.agent
assert self.app_config.agent.prompt
if not self.app_config.agent:
raise ValueError("Agent configuration is not set")
prompt_entity = self.app_config.agent.prompt
if not prompt_entity:
@ -83,10 +83,8 @@ class CotChatAgentRunner(CotAgentRunner):
assistant_message.content = "" # FIXME: type check tell mypy that assistant_message.content is str
for unit in agent_scratchpad:
if unit.is_final():
assert isinstance(assistant_message.content, str)
assistant_message.content += f"Final Answer: {unit.agent_response}"
else:
assert isinstance(assistant_message.content, str)
assistant_message.content += f"Thought: {unit.thought}\n\n"
if unit.action_str:
assistant_message.content += f"Action: {unit.action_str}\n\n"

View File

@ -1,9 +1,7 @@
from enum import StrEnum
from typing import Any, Optional, Union
from enum import Enum
from typing import Any, Literal, Optional, Union
from pydantic import BaseModel, Field
from core.tools.entities.tool_entities import ToolInvokeMessage, ToolProviderType
from pydantic import BaseModel
class AgentToolEntity(BaseModel):
@ -11,11 +9,10 @@ class AgentToolEntity(BaseModel):
Agent Tool Entity.
"""
provider_type: ToolProviderType
provider_type: Literal["builtin", "api", "workflow"]
provider_id: str
tool_name: str
tool_parameters: dict[str, Any] = Field(default_factory=dict)
plugin_unique_identifier: str | None = None
tool_parameters: dict[str, Any] = {}
class AgentPromptEntity(BaseModel):
@ -69,7 +66,7 @@ class AgentEntity(BaseModel):
Agent Entity.
"""
class Strategy(StrEnum):
class Strategy(Enum):
"""
Agent Strategy.
"""
@ -81,13 +78,5 @@ class AgentEntity(BaseModel):
model: str
strategy: Strategy
prompt: Optional[AgentPromptEntity] = None
tools: Optional[list[AgentToolEntity]] = None
tools: list[AgentToolEntity] | None = None
max_iteration: int = 5
class AgentInvokeMessage(ToolInvokeMessage):
"""
Agent Invoke Message.
"""
pass

View File

@ -46,20 +46,18 @@ class FunctionCallAgentRunner(BaseAgentRunner):
# convert tools into ModelRuntime Tool format
tool_instances, prompt_messages_tools = self._init_prompt_tools()
assert app_config.agent
iteration_step = 1
max_iteration_steps = min(app_config.agent.max_iteration, 5) + 1
# continue to run until there is not any tool call
function_call_state = True
llm_usage: dict[str, Optional[LLMUsage]] = {"usage": None}
llm_usage: dict[str, LLMUsage] = {"usage": LLMUsage.empty_usage()}
final_answer = ""
# get tracing instance
trace_manager = app_generate_entity.trace_manager
def increase_usage(final_llm_usage_dict: dict[str, Optional[LLMUsage]], usage: LLMUsage):
def increase_usage(final_llm_usage_dict: dict[str, LLMUsage], usage: LLMUsage):
if not final_llm_usage_dict["usage"]:
final_llm_usage_dict["usage"] = usage
else:
@ -109,7 +107,7 @@ class FunctionCallAgentRunner(BaseAgentRunner):
current_llm_usage = None
if isinstance(chunks, Generator):
if self.stream_tool_call and isinstance(chunks, Generator):
is_first_chunk = True
for chunk in chunks:
if is_first_chunk:
@ -126,7 +124,7 @@ class FunctionCallAgentRunner(BaseAgentRunner):
tool_call_inputs = json.dumps(
{tool_call[1]: tool_call[2] for tool_call in tool_calls}, ensure_ascii=False
)
except json.JSONDecodeError:
except json.JSONDecodeError as e:
# ensure ascii to avoid encoding error
tool_call_inputs = json.dumps({tool_call[1]: tool_call[2] for tool_call in tool_calls})
@ -142,7 +140,7 @@ class FunctionCallAgentRunner(BaseAgentRunner):
current_llm_usage = chunk.delta.usage
yield chunk
else:
elif not self.stream_tool_call and isinstance(chunks, LLMResult):
result = chunks
# check if there is any tool call
if self.check_blocking_tool_calls(result):
@ -153,7 +151,7 @@ class FunctionCallAgentRunner(BaseAgentRunner):
tool_call_inputs = json.dumps(
{tool_call[1]: tool_call[2] for tool_call in tool_calls}, ensure_ascii=False
)
except json.JSONDecodeError:
except json.JSONDecodeError as e:
# ensure ascii to avoid encoding error
tool_call_inputs = json.dumps({tool_call[1]: tool_call[2] for tool_call in tool_calls})
@ -185,6 +183,8 @@ class FunctionCallAgentRunner(BaseAgentRunner):
usage=result.usage,
),
)
else:
raise RuntimeError(f"invalid chunks type: {type(chunks)}")
assistant_message = AssistantPromptMessage(content="", tool_calls=[])
if tool_calls:
@ -243,12 +243,15 @@ class FunctionCallAgentRunner(BaseAgentRunner):
invoke_from=self.application_generate_entity.invoke_from,
agent_tool_callback=self.agent_callback,
trace_manager=trace_manager,
app_id=self.application_generate_entity.app_config.app_id,
message_id=self.message.id,
conversation_id=self.conversation.id,
)
# publish files
for message_file_id in message_files:
for message_file_id, save_as in message_files:
if save_as:
if self.variables_pool:
self.variables_pool.set_file(
tool_name=tool_call_name, value=message_file_id, name=save_as
)
# publish message file
self.queue_manager.publish(
QueueMessageFileEvent(message_file_id=message_file_id), PublishFrom.APPLICATION_MANAGER
@ -300,6 +303,8 @@ class FunctionCallAgentRunner(BaseAgentRunner):
iteration_step += 1
if self.variables_pool and self.db_variables_pool:
self.update_db_variables(self.variables_pool, self.db_variables_pool)
# publish end event
self.queue_manager.publish(
QueueMessageEndEvent(
@ -330,7 +335,9 @@ class FunctionCallAgentRunner(BaseAgentRunner):
return True
return False
def extract_tool_calls(self, llm_result_chunk: LLMResultChunk) -> list[tuple[str, str, dict[str, Any]]]:
def extract_tool_calls(
self, llm_result_chunk: LLMResultChunk
) -> Union[None, list[tuple[str, str, dict[str, Any]]]]:
"""
Extract tool calls from llm result chunk
@ -353,7 +360,7 @@ class FunctionCallAgentRunner(BaseAgentRunner):
return tool_calls
def extract_blocking_tool_calls(self, llm_result: LLMResult) -> list[tuple[str, str, dict[str, Any]]]:
def extract_blocking_tool_calls(self, llm_result: LLMResult) -> Union[None, list[tuple[str, str, dict[str, Any]]]]:
"""
Extract blocking tool calls from llm result
@ -376,7 +383,9 @@ class FunctionCallAgentRunner(BaseAgentRunner):
return tool_calls
def _init_system_message(self, prompt_template: str, prompt_messages: list[PromptMessage]) -> list[PromptMessage]:
def _init_system_message(
self, prompt_template: str, prompt_messages: Optional[list[PromptMessage]] = None
) -> list[PromptMessage]:
"""
Initialize system message
"""

View File

@ -1,89 +0,0 @@
import enum
from typing import Any, Optional
from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator
from core.entities.parameter_entities import CommonParameterType
from core.plugin.entities.parameters import (
PluginParameter,
as_normal_type,
cast_parameter_value,
init_frontend_parameter,
)
from core.tools.entities.common_entities import I18nObject
from core.tools.entities.tool_entities import (
ToolIdentity,
ToolProviderIdentity,
)
class AgentStrategyProviderIdentity(ToolProviderIdentity):
"""
Inherits from ToolProviderIdentity, without any additional fields.
"""
pass
class AgentStrategyParameter(PluginParameter):
class AgentStrategyParameterType(enum.StrEnum):
"""
Keep all the types from PluginParameterType
"""
STRING = CommonParameterType.STRING.value
NUMBER = CommonParameterType.NUMBER.value
BOOLEAN = CommonParameterType.BOOLEAN.value
SELECT = CommonParameterType.SELECT.value
SECRET_INPUT = CommonParameterType.SECRET_INPUT.value
FILE = CommonParameterType.FILE.value
FILES = CommonParameterType.FILES.value
APP_SELECTOR = CommonParameterType.APP_SELECTOR.value
MODEL_SELECTOR = CommonParameterType.MODEL_SELECTOR.value
TOOLS_SELECTOR = CommonParameterType.TOOLS_SELECTOR.value
# deprecated, should not use.
SYSTEM_FILES = CommonParameterType.SYSTEM_FILES.value
def as_normal_type(self):
return as_normal_type(self)
def cast_value(self, value: Any):
return cast_parameter_value(self, value)
type: AgentStrategyParameterType = Field(..., description="The type of the parameter")
def init_frontend_parameter(self, value: Any):
return init_frontend_parameter(self, self.type, value)
class AgentStrategyProviderEntity(BaseModel):
identity: AgentStrategyProviderIdentity
plugin_id: Optional[str] = Field(None, description="The id of the plugin")
class AgentStrategyIdentity(ToolIdentity):
"""
Inherits from ToolIdentity, without any additional fields.
"""
pass
class AgentStrategyEntity(BaseModel):
identity: AgentStrategyIdentity
parameters: list[AgentStrategyParameter] = Field(default_factory=list)
description: I18nObject = Field(..., description="The description of the agent strategy")
output_schema: Optional[dict] = None
# pydantic configs
model_config = ConfigDict(protected_namespaces=())
@field_validator("parameters", mode="before")
@classmethod
def set_parameters(cls, v, validation_info: ValidationInfo) -> list[AgentStrategyParameter]:
return v or []
class AgentProviderEntityWithPlugin(AgentStrategyProviderEntity):
strategies: list[AgentStrategyEntity] = Field(default_factory=list)

View File

@ -1,42 +0,0 @@
from abc import ABC, abstractmethod
from collections.abc import Generator, Sequence
from typing import Any, Optional
from core.agent.entities import AgentInvokeMessage
from core.agent.plugin_entities import AgentStrategyParameter
class BaseAgentStrategy(ABC):
"""
Agent Strategy
"""
def invoke(
self,
params: dict[str, Any],
user_id: str,
conversation_id: Optional[str] = None,
app_id: Optional[str] = None,
message_id: Optional[str] = None,
) -> Generator[AgentInvokeMessage, None, None]:
"""
Invoke the agent strategy.
"""
yield from self._invoke(params, user_id, conversation_id, app_id, message_id)
def get_parameters(self) -> Sequence[AgentStrategyParameter]:
"""
Get the parameters for the agent strategy.
"""
return []
@abstractmethod
def _invoke(
self,
params: dict[str, Any],
user_id: str,
conversation_id: Optional[str] = None,
app_id: Optional[str] = None,
message_id: Optional[str] = None,
) -> Generator[AgentInvokeMessage, None, None]:
pass

View File

@ -1,59 +0,0 @@
from collections.abc import Generator, Sequence
from typing import Any, Optional
from core.agent.entities import AgentInvokeMessage
from core.agent.plugin_entities import AgentStrategyEntity, AgentStrategyParameter
from core.agent.strategy.base import BaseAgentStrategy
from core.plugin.manager.agent import PluginAgentManager
from core.plugin.utils.converter import convert_parameters_to_plugin_format
class PluginAgentStrategy(BaseAgentStrategy):
"""
Agent Strategy
"""
tenant_id: str
declaration: AgentStrategyEntity
def __init__(self, tenant_id: str, declaration: AgentStrategyEntity):
self.tenant_id = tenant_id
self.declaration = declaration
def get_parameters(self) -> Sequence[AgentStrategyParameter]:
return self.declaration.parameters
def initialize_parameters(self, params: dict[str, Any]) -> dict[str, Any]:
"""
Initialize the parameters for the agent strategy.
"""
for parameter in self.declaration.parameters:
params[parameter.name] = parameter.init_frontend_parameter(params.get(parameter.name))
return params
def _invoke(
self,
params: dict[str, Any],
user_id: str,
conversation_id: Optional[str] = None,
app_id: Optional[str] = None,
message_id: Optional[str] = None,
) -> Generator[AgentInvokeMessage, None, None]:
"""
Invoke the agent strategy.
"""
manager = PluginAgentManager()
initialized_params = self.initialize_parameters(params)
params = convert_parameters_to_plugin_format(initialized_params)
yield from manager.invoke(
tenant_id=self.tenant_id,
user_id=user_id,
agent_provider=self.declaration.identity.provider,
agent_strategy=self.declaration.identity.name,
agent_params=params,
conversation_id=conversation_id,
app_id=app_id,
message_id=message_id,
)

View File

@ -4,8 +4,7 @@ from core.app.app_config.entities import EasyUIBasedAppConfig
from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity
from core.entities.model_entities import ModelStatus
from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError
from core.model_runtime.entities.llm_entities import LLMMode
from core.model_runtime.entities.model_entities import ModelPropertyKey, ModelType
from core.model_runtime.entities.model_entities import ModelType
from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
from core.provider_manager import ProviderManager
@ -64,14 +63,14 @@ class ModelConfigConverter:
stop = completion_params["stop"]
del completion_params["stop"]
model_schema = model_type_instance.get_model_schema(model_config.model, model_credentials)
# get model mode
model_mode = model_config.mode
if not model_mode:
model_mode = LLMMode.CHAT.value
if model_schema and model_schema.model_properties.get(ModelPropertyKey.MODE):
model_mode = LLMMode.value_of(model_schema.model_properties[ModelPropertyKey.MODE]).value
mode_enum = model_type_instance.get_model_mode(model=model_config.model, credentials=model_credentials)
model_mode = mode_enum.value
model_schema = model_type_instance.get_model_schema(model_config.model, model_credentials)
if not model_schema:
raise ValueError(f"Model {model_name} not exist.")

View File

@ -3,8 +3,7 @@ from typing import Any
from core.app.app_config.entities import ModelConfigEntity
from core.model_runtime.entities.model_entities import ModelPropertyKey, ModelType
from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory
from core.plugin.entities.plugin import ModelProviderID
from core.model_runtime.model_providers import model_provider_factory
from core.provider_manager import ProviderManager
@ -54,16 +53,9 @@ class ModelConfigManager:
raise ValueError("model must be of object type")
# model.provider
model_provider_factory = ModelProviderFactory(tenant_id)
provider_entities = model_provider_factory.get_providers()
model_provider_names = [provider.provider for provider in provider_entities]
if "provider" not in config["model"]:
raise ValueError(f"model.provider is required and must be in {str(model_provider_names)}")
if "/" not in config["model"]["provider"]:
config["model"]["provider"] = str(ModelProviderID(config["model"]["provider"]))
if config["model"]["provider"] not in model_provider_names:
if "provider" not in config["model"] or config["model"]["provider"] not in model_provider_names:
raise ValueError(f"model.provider is required and must be in {str(model_provider_names)}")
# model.name

View File

@ -17,8 +17,8 @@ class ModelConfigEntity(BaseModel):
provider: str
model: str
mode: Optional[str] = None
parameters: dict[str, Any] = Field(default_factory=dict)
stop: list[str] = Field(default_factory=list)
parameters: dict[str, Any] = {}
stop: list[str] = []
class AdvancedChatMessageEntity(BaseModel):
@ -132,7 +132,7 @@ class ExternalDataVariableEntity(BaseModel):
variable: str
type: str
config: dict[str, Any] = Field(default_factory=dict)
config: dict[str, Any] = {}
class DatasetRetrieveConfigEntity(BaseModel):
@ -188,7 +188,7 @@ class SensitiveWordAvoidanceEntity(BaseModel):
"""
type: str
config: dict[str, Any] = Field(default_factory=dict)
config: dict[str, Any] = {}
class TextToSpeechEntity(BaseModel):

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