Compare commits

...
390 Commits
Author SHA1 Message Date
Novice a9bae7aafd feat: add independent memory 2025-04-27 13:30:53 +08:00
Novice 48be8fb6cc chore: lack of the user message 2025-04-25 09:34:11 +08:00
GuanMuandGitHub dd02a9ac9d fix: enhance TOC navigation with scrollable overflow for better usability (#18636) 2025-04-23 23:17:28 +08:00
Junjie.MandGitHub b203139356 embedding in websites setting conversation_id requires hiding reset conversation button (#18623) 2025-04-23 22:57:42 +08:00
crazywoolaandGitHub c479fcf251 feat: add missing switches (#18619) 2025-04-23 18:02:18 +08:00
minglu7andGitHub d7c3e54eaa fix: improve translation of community code of conduct sentence (#18607) 2025-04-23 17:06:17 +08:00
Junjie.MandGitHub d5fe50e471 embedding in websites support initializes to specify the conversation_id (#18602) 2025-04-23 16:48:45 +08:00
WillandGitHub 205535c8e9 chore: fix reimported (#18610) 2025-04-23 16:48:00 +08:00
-LAN-andGitHub e9aedc701c chore: Updates version numbers for upcoming release (#18550)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-04-23 16:26:55 +08:00
cf464d252d fix#18595: update workflow duplicate env variable name (#18596)
Co-authored-by: tiankuo.zhou <tiankuo.zhou@lofty.com>
2025-04-23 15:55:46 +08:00
jayeeliuandGitHub 5e09ac696c fix: add composer configuration and delete DifyClient->file_client (#18574) 2025-04-23 15:43:19 +08:00
YeuolyandGitHub ba9357da96 fix: handle PluginPermissionDeniedError in EndpointCreateApi (#18597) 2025-04-23 15:29:58 +08:00
JoelandGitHub c6fb879cea fix: select struct output root object show the wrong type (#18582) 2025-04-23 11:06:29 +08:00
JyongandGitHub e2cb7006c4 check metadata_filtering_conditions could be None in auto mode (#18548) 2025-04-22 17:09:33 +08:00
3737e0b087 fix: clickjacking (#18516)
Signed-off-by: -LAN- <laipz8200@outlook.com>
Co-authored-by: -LAN- <laipz8200@outlook.com>
2025-04-22 16:48:45 +08:00
a1158cc946 fix: Update prompt message content types to use Literal and add union type for content (#17136)
Co-authored-by: 朱庆超 <zhuqingchao@xiaomi.com>
Co-authored-by: crazywoola <427733928@qq.com>
2025-04-22 16:17:55 +08:00
非法操作andGitHub 404f8a790c fix conversation log raise 500 (#18534) 2025-04-22 16:08:47 +08:00
非法操作andGitHub 35a008af18 fix can't resize workflow run panel (#18538) 2025-04-22 16:07:51 +08:00
elsie_elseandGitHub 79bf590576 docs: update enterprise inquiry links across all README language variants (#18541) 2025-04-22 16:07:26 +08:00
-LAN-andGitHub 61e39bccdf fix: Patch OpenTelemetry to handle None tokens (#18498)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-04-22 16:04:20 +08:00
6b7dfee88b fix: Validates session factory type in repository (#18497)
Signed-off-by: -LAN- <laipz8200@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-22 16:04:06 +08:00
elsie_elseandGitHub 21412a8c55 docs: replace outdated Enterprise inquiry link with a new one (#18528) 2025-04-22 14:54:21 +08:00
Junjie.MandGitHub 239e40c8d5 chore: remove useless frontend code file (#18532) 2025-04-22 14:46:49 +08:00
GuanMuandGitHub 1ce2c7f3e8 refactor: improve layout and structure of ProviderDetail component (#18523) 2025-04-22 13:57:45 +08:00
AichiB7AandGitHub de750a67ec [Observability] feat: add metrics of http response (#18499) 2025-04-22 13:19:22 +08:00
shiyiyue1102andGitHub 8e6ea4d117 support load .env config from nacos (#18186) 2025-04-22 13:12:36 +08:00
Charlie.WeiGitHubluowei <glpat-EjySCyNjWiLqAED-YmwM>crazywoolacrazywoolaCopilot
ef188564f3 Mermaid analysis optimization (#18089)
Co-authored-by: luowei <glpat-EjySCyNjWiLqAED-YmwM>
Co-authored-by: crazywoola <427733928@qq.com>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-22 13:06:47 +08:00
Dongyu LiandGitHub 413271eaa6 feat[plugin]:The plugin upload file change to be stored as a toolfile… (#18277) 2025-04-22 13:05:42 +08:00
laudingandGitHub eb1ce3dd6b feat: support huawei cloud vector database (#16141) 2025-04-22 13:03:35 +08:00
非法操作andGitHub 18e4f42c3c fix draft run node exception (#18520) 2025-04-22 13:02:38 +08:00
zxhlyhandGitHub e0e92921b5 fix: external knowledge setting in knowledge selector (#18519) 2025-04-22 11:29:45 +08:00
allenZhangandGitHub 94e22ba0fd feat: add search input field (#18409) 2025-04-22 11:07:18 +08:00
GuanMuandGitHub 67eefd0ba1 fix: update search model placeholder and add translations f (#18518) 2025-04-22 11:06:36 +08:00
Marcelo DíazandGitHub bf031af7b1 feat(embedded-chatbot): support overriding locale via URL params (#18509) 2025-04-22 11:03:01 +08:00
GuanMuandGitHub 617611ee22 fix: adjust padding and background for sticky header (#18515) 2025-04-22 11:00:22 +08:00
zxhlyhandGitHub d43b884c2a fix: filter empty marketplace collection (#18511) 2025-04-22 10:13:22 +08:00
Wu TianweiandGitHub 80f5ee1eb2 fix: fix workflow as a tool confirm dialog layout issue (#18494) 2025-04-22 09:59:14 +08:00
Wunmi SogunleandGitHub ee30497237 fix(markdown): correctly render links with inline code (#18500) 2025-04-22 09:56:53 +08:00
GuanMuandGitHub be964c78ec fix: update document link based on client locale (#18493) 2025-04-21 21:00:04 +08:00
-LAN-andGitHub 2543162dec fix: cannot delete workflow version if other version is published as a tool (#18486)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-04-21 17:58:22 +08:00
KVOJJJinandGitHub 3136eb8e4b Fix: json update in conversation variable (#18483) 2025-04-21 17:58:01 +08:00
tmuifeandGitHub 7b6523e54d Update Oracle db connection library and change connection pool to single connection (#18466) 2025-04-21 17:56:57 +08:00
doskoiandGitHub 30c051d485 fix: update Japanese translation for 'switchVersion' in plugin.ts to … (#18469) 2025-04-21 17:56:31 +08:00
NoviceandGitHub f191d372f0 fix(promptMessage): correct field_serializer implementation for content serialization (#18458) 2025-04-21 15:09:49 +08:00
WillandGitHub cb69cb2d64 fix weird syntax error (#18454) 2025-04-21 14:18:32 +08:00
JoelandGitHub 5d9c67e97e fix: handle array item type error in struct output (#18452) 2025-04-21 14:15:38 +08:00
doskoiandGitHub 0ba37592f7 fix: update Japanese translation for document link in plugin.ts, translation for "endpointsDocLink" label (#18446) 2025-04-21 14:14:09 +08:00
doskoiandGitHub e0e8667a0b fix: translate 'back' to '戻る' in Japanese plugin localization (#18444) 2025-04-21 14:12:44 +08:00
doskoiandGitHub 2157d9e17e fix: update Japanese translation for 'from' in plugin.ts to improve c… (#18449) 2025-04-21 14:12:21 +08:00
doskoiandGitHub 62e7fa1f63 "fix: Changed the translated text from '障害者' (#18427)" (#18438) 2025-04-21 12:41:31 +08:00
Hao ChengandGitHub 0ac7366cdc fix: correct unsupported German date format on document list page (#18426) 2025-04-21 10:06:21 +08:00
EthanandGitHub c768d97637 feat: update privacy policy URL and add validation for privacy policy link (#18422) 2025-04-21 10:04:33 +08:00
CorrectRoadandGitHub 9bd8e62702 fix: bump the minimal node requirement to fix eslint fail (#17938) 2025-04-21 09:51:39 +08:00
Kalo ChinandGitHub 9a3acdcff8 Fix: agent app debug re-rendering issue (#18389) 2025-04-19 17:25:52 +08:00
crazywoolaandGitHub 93c1ee225e fix: styles and missing imports (#18396) 2025-04-19 14:46:10 +08:00
1e32175cdc Feat/music annotation (#18391)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-19 11:59:00 +08:00
kurokoboandGitHub 00d9f037b5 fix: correct icons for gpt-4 series from non-openai providers (#18387) 2025-04-19 10:12:12 +08:00
-LAN-andGitHub 44a2eca449 refactor: Refactors workflow node execution handling (#18382)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-04-18 20:06:24 +08:00
souljoyandGitHub 20df6e9c00 Add docker environment variable PIP_MIRROR_URL for sandbox (#18371) 2025-04-18 18:50:03 +08:00
NFishandGitHub 7ba3e599d2 fix: update reset password token when email code verify success (#18364) 2025-04-18 17:14:51 +08:00
Xiyuan ChenandGitHub 4247a6b807 fix: reset_password security issue (#18363) 2025-04-18 05:06:09 -04:00
775dc47abe feat: llm support struct output (#17994)
Co-authored-by: twwu <twwu@dify.ai>
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
2025-04-18 16:53:43 +08:00
NoviceandGitHub da9269ca97 feat: structured output (#17877) 2025-04-18 16:33:53 +08:00
Rain WangandGitHub d2e3744ca3 Switching from CONSOLE_API_URL to FILES_URL in word_extractor.py (#18249) 2025-04-18 16:05:48 +08:00
GuanMuandGitHub 3914cf07e7 fix: Adjust span height and alignment in WorkplaceSelector component (#18361) 2025-04-18 16:00:12 +08:00
Wu TianweiandGitHub 1e7418095f feat/TanStack-Form (#18346) 2025-04-18 15:54:22 +08:00
zxhlyhandGitHub efe5db38ee Chore/slice workflow (#18351) 2025-04-18 13:59:12 +08:00
EthanandGitHub 523efbfea5 Fix: ValueError: Formatting field not found in record: 'req_id' (#18327) 2025-04-18 09:42:38 +08:00
crazywoolaandGitHub b96ecd072a fix: can not input R when debug (#18323) 2025-04-18 09:42:08 +08:00
28ffe7e3db fix: missing headers in some cases (#18283)
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: crazywoola <427733928@qq.com>
2025-04-17 21:10:58 +08:00
721294948c Diable expire_on_commit in the implemention of the WorkflowNodeExecut… (#18321)
Co-authored-by: lizb <lizb@sugon.com>
2025-04-17 21:09:19 +08:00
b287aaccec fix: Correctly render multiple think blocks in Markdown (#18310)
Co-authored-by: xzj16125 <xuzijie@noahgroup.com>
Co-authored-by: crazywoola <427733928@qq.com>
2025-04-17 19:50:41 +08:00
bbc6efd773 fix: curl request address (#18320)
Co-authored-by: devxing <devxing@gmail.com>
2025-04-17 19:50:20 +08:00
dc9c5a4bc7 make repository type be private (#18304)
Co-authored-by: lizb <lizb@sugon.com>
2025-04-17 18:49:22 +08:00
JyongandGitHub e90c532c3a fix retrival resource miss in chatflow (#18307) 2025-04-17 18:05:15 +08:00
刘小龙andGitHub 397e2a8522 datasets api create-by-file add reranking_mode properties (#18300) 2025-04-17 18:04:43 +08:00
YeuolyandGitHub 8f547e6340 fix(typing): validate OAuth code before processing access token (#18288) 2025-04-17 16:58:29 +08:00
VitorandGitHub defd5520ea fix: invalid new tool call creation logic during response handling in OAI-Compat model (#17781) 2025-04-17 16:52:49 +08:00
GuanMuandGitHub b6b608219a fix: update retrieval_model documentation (#18289) 2025-04-17 16:18:06 +08:00
-LAN-andGitHub 22a1bc337f fix: perferred model provider not match with provider. (#18282)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-04-17 15:44:00 +08:00
caa179a1d3 If the DSL version is less than 0.1.5, it causes errors in an intranet environment. (#18273)
Co-authored-by: warlocgao <warlocgao@tencent.com>
2025-04-17 15:25:31 +08:00
crazywoolaandGitHub e8e47aee21 fix: Access the text-generation app's API doc error (#18278) 2025-04-17 15:17:22 +08:00
Rain WangandGitHub 83f1aeec1d Fix ORDER BY (score, id) error in api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_sql.py line 249 (#18252) 2025-04-17 14:15:05 +08:00
-LAN-andGitHub 6d9dd3109e feat: add a abstract layer for WorkflowNodeExcetion (#18026) 2025-04-17 12:48:52 +09:00
GuanMuandGitHub 77fde04ef7 style: add left padding to editor component and remove unused CSS (#18247) 2025-04-17 11:47:59 +08:00
JoelandGitHub 9d139fa306 fix: Could not load the logo of workflow as Tool in Agent Node (#18243) 2025-04-17 11:22:06 +08:00
NoviceandGitHub 6d66e3f680 fix(follow_ups): handle empty LLM responses in context (#18237) 2025-04-17 10:41:56 +08:00
Rain WangandGitHub e8d98e3d89 Add analyzer_params config for milvus vectordb (#18180) 2025-04-17 10:38:56 +08:00
Chenming CandGitHub a1d20085e6 fix: change the method of update_dataset api in document (#18197) 2025-04-17 10:10:27 +08:00
AirLinandGitHub 6da7e6158f Add the parameter appid to apiserver (#18224) 2025-04-16 23:07:05 +08:00
NoviceandGitHub c91045a9d0 fix(fail-branch): prevent streaming output in exception branches (#17153) 2025-04-16 22:34:07 +08:00
PanpanandGitHub 44cdb3dcea feat: improve embedding sys.user_id and conversion id info usage (#18035) 2025-04-16 21:08:13 +08:00
YeuolyandGitHub 358fd28c28 feat: fetch app info in plugins (#18202) 2025-04-16 20:27:29 +08:00
e912928cce fix: create child chunk (#18209)
Co-authored-by: devxing <devxing@gmail.com>
2025-04-16 19:56:21 +08:00
cac0d3c33e fix: implement robust file type checks to align with existing logic (#17557)
Co-authored-by: Bowen Liang <liangbowen@gf.com.cn>
2025-04-16 19:21:50 +08:00
18f98f4fe1 fix: ruff check isoparse (#18033)
Co-authored-by: 刘江波 <jiangbo721@163.com>
2025-04-16 19:21:18 +08:00
诗浓andGitHub 4166f73d9d fix: page/limit param not effective (#18196) 2025-04-16 17:26:47 +08:00
KVOJJJinandGitHub bbd9fe9777 Fix:style of opening questions (#18194) 2025-04-16 17:25:25 +08:00
Junjie.MandGitHub b7e8517b31 feat: agent strategy parameter add help information (#18192) 2025-04-16 17:24:09 +08:00
Junjie.MandGitHub da7c8621f7 fix: agent strategy string type parameter default value invalid (#18185) 2025-04-16 17:03:18 +08:00
呆萌闷油瓶andGitHub 8cc37f3115 fix:the extraction function of the list operation node received 0 that should not be received (#18170) 2025-04-16 16:26:24 +08:00
-LAN-andGitHub c6e2970b65 chore: Reorganizes test file structure (#18155)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-04-16 16:09:17 +08:00
b006b9ac0c Http requests node add ssl verify (#18125)
Co-authored-by: lizb <lizb@sugon.com>
2025-04-16 15:59:34 +08:00
crazywoolaandGitHub e1455cecd8 feat: add switches for jina firecrawl watercrawl (#18153) 2025-04-16 15:50:15 +08:00
kenwoodjwandGitHub b247ef85bf fix dataset api retrieval model null handling (#18151)
Signed-off-by: kenwoodjw <blackxin55+@gmail.com>
2025-04-16 15:50:06 +08:00
GuanMuandGitHub fcdf965037 feat: add PATCH method support in Heading component (#18160) 2025-04-16 15:48:09 +08:00
YeuolyandGitHub 640ee80010 feat: add red corner mark to Badge component for marketplace plugins (#18162) 2025-04-16 15:15:23 +08:00
95283b4dd3 Feat/change split length method (#18097)
Co-authored-by: JzoNg <jzongcode@gmail.com>
2025-04-16 12:28:22 +08:00
AichiB7AandGitHub 2a0d7533d7 [Unit Test] Generate coverage number for UT (#18106) 2025-04-16 11:55:37 +08:00
Bowen LiangandGitHub 57b28576f0 chore: remove unused poetry.toml (#18112) 2025-04-16 11:55:19 +08:00
aead48726e fix: cannot regenerate with image(#15060) (#16611)
Co-authored-by: werido <359066432@qq.com>
2025-04-16 09:56:46 +08:00
kurokoboandGitHub cd17ce9250 fix: start api and worker after the database has become healthy (#18109) 2025-04-16 09:54:03 +08:00
Bowen LiangandGitHub 9d7357058a chore: merge lint dependency group into dev group of python packages (#18088) 2025-04-15 20:50:06 +08:00
Bowen LiangandGitHub 9889aa10bd chore: speed up git checkout by removing fetch-depth 0 to avoid pulling all tags and branches (#18103) 2025-04-15 20:21:21 +08:00
YeuolyandGitHub d619fa1767 feat: implement blob chunk handling in plugin manager (#18101) 2025-04-15 19:23:03 +08:00
7161d7ad96 feat: add base path to resources (#17655)
Co-authored-by: fhliu4 <fhliu4@iflytek.com>
2025-04-15 17:05:50 +08:00
12de1d175c build: introduce uv as Python package manager (#16317)
Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com>
2025-04-15 16:16:49 +08:00
KVOJJJinandGitHub f27a956c71 Feat: api page dark mode (#18078) 2025-04-15 16:13:18 +08:00
d119c7d629 ignore errors when creating duplicate indexes (#18069)
Co-authored-by: 璟义 <yangshangpo.ysp@alibaba-inc.com>
2025-04-15 15:48:16 +08:00
mouseminandGitHub 0a9031fd42 fix: plugin parameter aws_secret_key parameter not found (#18075) 2025-04-15 15:48:07 +08:00
Hash BrownandGitHub 438463b1c4 feat: edit question in Chat (#17961) 2025-04-15 15:37:08 +08:00
5dd9acbe44 fix: cot agent chinese json bug (#18073)
Co-authored-by: huangzhuo <huangzhuo1@xiaomi.com>
2025-04-15 15:36:44 +08:00
Junjie.MandGitHub 05b8b2a30c fix: plugin parameter type TOOLS_SELECTOR parameter not validation required (#18060) 2025-04-15 13:51:40 +08:00
AichiB7AandGitHub 6c167038af [Observability] Instrument with celery (#18029) 2025-04-15 11:35:34 +08:00
kenwoodjwandGitHub dfc123819e fix basic auth encoding (#18047)
Signed-off-by: kenwoodjw <blackxin55+@gmail.com>
2025-04-15 11:34:50 +08:00
-LAN-andGitHub be6a88cb77 fix: Prevents duplicate logs from SQLAlchemy engine (#18024)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-04-14 20:28:31 +08:00
YeuolyandGitHub 2134a76517 feat: add minimum dify version requirement to plugins (#18022) 2025-04-14 20:09:22 +08:00
Junjie.MandGitHub 9f8947f1dd feat: plugin tool selector add tool default description (#18018) 2025-04-14 19:08:53 +08:00
NoviceandGitHub 85004f8510 fix(typo): workflow ops triggered from (#18019) 2025-04-14 19:08:05 +08:00
GuanMuandGitHub f40e22dda6 fix(docs): update API documentation to replace 'Params' with 'Path' (#18004) 2025-04-14 17:25:41 +08:00
yusheng chenandGitHub 4c99e9dc73 refactor: type improvements that doesn't modify functionality (#17970) 2025-04-14 16:06:10 +08:00
kenwoodjwandGitHub 53efb2bad5 fix chat message type error (#17997)
Signed-off-by: kenwoodjw <blackxin55+@gmail.com>
2025-04-14 16:05:46 +08:00
KVOJJJinandGitHub ed63fbaf9a Feat: dataset dark mode (#17993) 2025-04-14 15:45:23 +08:00
NoviceandGitHub cd7fd100a7 fix(langfuse): qusetion classify node can't see cost in langfuse (#17982) 2025-04-14 15:28:26 +08:00
JoelandGitHub d80f4c7d3b chore: eslint add sonar (#17989) 2025-04-14 15:28:20 +08:00
crazywoolaandGitHub 8f9cbe1c49 Chore/cleanup warnings (#17974) 2025-04-14 11:27:14 +08:00
诗浓andGitHub f84832e0c2 feat: added export workflow as img (#17904) 2025-04-14 11:18:18 +08:00
GuanMuandGitHub 0975c3c399 style(retry-on-node): add margin-bottom to the container (#17972) 2025-04-14 11:05:59 +08:00
JasonfishandGitHub 1f722cde22 fix(api): Some params were ignored when creating empty Datasets through API (#17932) 2025-04-14 10:24:01 +08:00
crazywoolaandGitHub 4aecc9f090 fix: TypeError: a.variable_selector.join is not a function (#17950) 2025-04-14 09:27:08 +08:00
yusheng chenandGitHub c9a594100b refactor & perf of files datesets/Doc.tsx and template.xx.mdx (#17951) 2025-04-13 18:12:29 +08:00
yusheng chenandGitHub 7ca497f0d6 refactor & perf: improve type safety of component PluginList (#17498) 2025-04-13 10:52:54 +08:00
CorrectRoadandGitHub cf8d15e8d5 fix: fix wrong layer adding customized tools (#17937) 2025-04-13 10:25:34 +08:00
kurokoboandGitHub bc57fa0619 fix: start the plugin daemon after the database has become healthy (#17928) 2025-04-13 10:21:56 +08:00
Yongtao HuangandGitHub 5d72003ebb Remove dead code (#17899) 2025-04-11 20:33:52 +08:00
-LAN-andGitHub 08a693a0a0 fix: published workflow(tool) can be deleted. (#17900)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-04-11 19:39:09 +08:00
Wu TianweiandGitHub 59b2e1ab82 Chore/add unit test for utils (#17858) 2025-04-11 17:53:18 +08:00
QuantumGhostandGitHub 4ef297bf38 refactor(api): Enhance error handling in BasePluginManager (#17887) 2025-04-11 17:32:20 +08:00
-LAN-andGitHub 8e6f6d64a4 feat: re-add prompt messages to result and chunks in llm (#17883)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-04-11 17:04:49 +08:00
AichiB7AandGitHub 5f8d20b5b2 [Observability] Integrate OpenTelemetry (#17627) 2025-04-11 17:04:06 +08:00
zwq357smileandGitHub c285441233 fix: refactor SVG icon handling logic and optimize event listener management in embed.js to support mobile browsers #16719 (#16717) 2025-04-11 16:59:12 +08:00
GuanMuandGitHub 316cb00ada fix: adjust margin in DatasetCard component for better layout (#17879) 2025-04-11 16:44:00 +08:00
LaoandGitHub 0185f84cc8 Update the model modal:position the scrollbar further inside the modal (#17672) 2025-04-11 16:09:56 +08:00
KVOJJJinandGitHub 4b0d3c3688 fix: add annotation ctrl button for annotation add (#17873) 2025-04-11 16:00:51 +08:00
briqtandGitHub 91cfa90503 Fix external knowledge Issues: (#17685) (#17843) 2025-04-11 15:37:27 +08:00
Good WoodandGitHub cc08451eb8 fix: fix file number limit error (#17848) 2025-04-11 15:26:26 +08:00
yihongandGitHub f04d52c044 fix: autocorrect everything in api (#17859)
Signed-off-by: yihong0618 <zouzou0208@gmail.com>
2025-04-11 15:24:39 +08:00
Good WoodandGitHub fe19cc7568 fix: in variable settings, use Textarea to replace Input. (#17864) 2025-04-11 15:24:16 +08:00
YeuolyandGitHub b2f5ca356a enhance(plugin): replace json.loads with Pydantic model_validate_json (#17867) 2025-04-11 15:20:03 +08:00
KVOJJJinandGitHub 78da4ca024 fix: do not submit value when file input is optional (#17861) 2025-04-11 14:40:57 +08:00
GuanMuandGitHub 3ece713a05 feat: add optional search parameters to dataset query templates i (#17857) 2025-04-11 14:27:59 +08:00
NFishandGitHub bf26f1129e fix: run button disappeared when where is no inputs in form (#17854) 2025-04-11 13:52:19 +08:00
crazywoolaandGitHub 7ee5cc80a2 fix: text.split (#17842) 2025-04-11 11:37:47 +08:00
Yu ChenandGitHub 0ccd8bdfa8 chore: Modify watercrawl translation in en-US and zh-Hans (#17828) 2025-04-11 10:14:00 +08:00
Bowen LiangandGitHub 0a939feaa3 chore: remove non-existed extra msg for unstructured package (#17670) 2025-04-11 09:29:20 +08:00
Xu SongandGitHub 1e1d457548 fix: make prompt consistent with few-show examples (#11538) 2025-04-11 09:16:26 +08:00
zxfishhackandGitHub 5541a1f80e robust for json parser (#17687) 2025-04-10 22:18:26 +08:00
0e0220bdbf fix: return null url when upload local file (#17752)
Co-authored-by: achmad-kautsar <achmad.kautsar@insignia.co.id>
2025-04-10 18:05:18 +08:00
9d20561af4 create db if not exists (#17796)
Co-authored-by: wlleiiwang <wlleiiwang@tencent.com>
2025-04-10 18:03:22 +08:00
JoelandGitHub f8145480fc fix: parallel id caused append to wrong branch (#17794) 2025-04-10 17:44:55 +08:00
JimmiaxieandGitHub 605ab9e46c hotfix: Workflow page element warning problem #17787 (#17789) 2025-04-10 17:38:50 +08:00
KVOJJJinandGitHub 17a26da1e6 Feat: workflow dark mode (#17785) 2025-04-10 17:15:48 +08:00
Bowen LiangandGitHub 636a0ba37f chore: skip document segments fetching with non-existed dataset of DatasetDocument in add_document_to_index_task task (#17784) 2025-04-10 17:12:48 +08:00
Wu TianweiandGitHub 29720b7360 fix: adjust spacing in ViewHistory and Panel components (#17766) 2025-04-10 15:53:50 +08:00
诗浓andGitHub d0d02be711 feat: add consistent keyboard shortcut support and visual indicators across all app creation dialogs (#17138) 2025-04-10 14:58:39 +08:00
PanpanandGitHub 88cb81d3d6 fix: fix inputs lost (#17747) 2025-04-10 13:58:35 +08:00
Hanqing ZhaoandGitHub ef27942b8a Add and modify jp translation (#17748) 2025-04-10 13:56:36 +08:00
63aab5cdd6 feat: add search params to url (#17684)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-10 11:18:43 +08:00
QunandGitHub 0e136b42a2 enhance guessing mimetype of tool file (#17640) 2025-04-10 11:14:20 +08:00
GuanMuandGitHub 6df0215246 fix: Enhance error handling and retry logic in Apps component (#17733) 2025-04-10 11:12:34 +08:00
crazywoolaandGitHub 63ba607738 fix: 17712-get-messages-api-encountered-internal-server-error (#17716) 2025-04-10 11:09:38 +08:00
zxhlyhandGitHub 30f7118c7a Chore/slice workflow utils (#17730) 2025-04-10 10:03:19 +08:00
GuanMuandGitHub 9d5a0fdd8a Fix create blank app (#17724) 2025-04-10 10:01:44 +08:00
quicksandandGitHub 0b1f938389 fix: docker compose plugin s3 config default value (#17722) 2025-04-10 09:57:50 +08:00
d3157b46ee feat(large_language_model): Adds plugin-based token counting configuration option (#17706)
Signed-off-by: -LAN- <laipz8200@outlook.com>
Co-authored-by: Yeuoly <admin@srmxy.cn>
2025-04-09 20:52:58 +08:00
JyongandGitHub 8b3be4224d revert batch query (#17707) 2025-04-09 20:25:36 +08:00
quicksandandGitHub 1d5c07dedb fix : PLUGIN_S3_USE_AWS_MANAGED_IAM AND PLUGIN_S3_USE_PATH_STYLE … (#17702) 2025-04-09 19:16:01 +08:00
f148f1efa2 fix: Check collection exists before drop it. (#17692)
Co-authored-by: wlleiiwang <wlleiiwang@tencent.com>
2025-04-09 19:14:32 +08:00
JyongandGitHub abfcd9d3b6 fix segment query index not effect (#17704) 2025-04-09 19:09:08 +08:00
JoelandGitHub 6cf1ada36e chore: hide eslint complexity warning (#17698) 2025-04-09 18:31:31 +08:00
YeuolyandGitHub 33324ee23d refactor: add API endpoint to list latest plugin versions and query it in a asynchronous way (#17695) 2025-04-09 17:49:27 +08:00
-LAN-andGitHub 2c2efe2e1e chore(*): bump version to 1.2.0 (#17675)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-04-09 17:12:40 +08:00
-LAN-andGitHub ec29bcf013 feat(graph_engine): yield control to other threads before node run. (#17689)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-04-09 17:02:47 +08:00
JoelandGitHub c9f18aae0f chore: find code with high complexity (#17679) 2025-04-09 15:39:12 +08:00
KVOJJJinandGitHub df03c89a48 Chore: remove beta tag of app type (#17676) 2025-04-09 15:10:08 +08:00
crazywoolaandGitHub eb8584613b fix: Account.query => db.session.query(Account) (#17667) 2025-04-09 14:07:32 +08:00
quicksandandGitHub 9000f4ad05 feat: add plugin daemon oss env config (#17663) 2025-04-09 14:02:17 +08:00
Bowen LiangandGitHub 0b1259fc4a chore: add script for running mypy type checks and speed up mypy checks in CI jobs (#17489) 2025-04-09 13:03:53 +08:00
eb0e51d44d optimize: docker-compose.middleware.yaml update env_file dependence (#17646)
Co-authored-by: fanghao <fanghao@sci99.com>
2025-04-09 12:16:48 +08:00
yusheng chenandGitHub f633d1ee92 chore: add 'no-empty-function': 'error' to eslint.config.mjs (#17656) 2025-04-09 12:10:17 +08:00
f1e4d5ed6c Fix Performance Issues: (#17083)
Co-authored-by: Wang Han <wanghan@zhejianglab.org>
2025-04-09 11:22:53 +08:00
b5498a373a Accelerate migration (#17088)
Co-authored-by: Wang Han <wanghan@zhejianglab.org>
2025-04-09 10:12:16 +08:00
Bowen LiangandGitHub b73607da80 chore: bump Nodejs in web image from 20 to 22 LTS (#13341) 2025-04-09 09:40:11 +08:00
LaoandGitHub 106604682a Fixed the model-modal titles not being clearly distinguished between "Add" and "Setup" (#17634) 2025-04-08 21:00:00 +08:00
Wu TianweiandGitHub cd7ac20d80 feat: enhance index type handling and add error notification for missing embedding model (#16836) 2025-04-08 18:01:43 +08:00
Bowen LiangandGitHub be3ebea45b chore: bump pnpm to v10 in web dockerfile (#17611) 2025-04-08 17:12:25 +08:00
JoelandGitHub 5a6219c726 chore: add unit test to high frequency hooks (#17617) 2025-04-08 16:39:11 +08:00
IAOTWandGitHub 4124e804a0 fix(transport): add missing verify parameter to httpx.HTTPTransport (#17612) 2025-04-08 16:04:50 +08:00
JoelandGitHub 8d1a34bbb9 fix: Sass @import warning (#17604) 2025-04-08 14:33:55 +08:00
Steven LiandGitHub abead647e2 fix: Extract docx file fails when the file contains an invalid link (#17576) 2025-04-08 13:59:33 +08:00
07ed728605 fix: segment keywords bug (#17599)
Co-authored-by: huangzhuo <huangzhuo1@xiaomi.com>
2025-04-08 13:57:07 +08:00
Marcelo DíazandGitHub d796fcc0e7 feat: support select-type variables in Metadata Filtering (#17440 (#17445) 2025-04-08 11:15:07 +08:00
Good WoodandGitHub 7b7ac7a495 fix: compatibility issues for currentStrategy.features is null (#17581) 2025-04-08 09:24:42 +08:00
JyongandGitHub c8145ce581 deal db session in celery worker (#17549) 2025-04-07 20:31:26 +08:00
诗浓andGitHub da7f8ad936 fix: remove wrong padding-bottom=0 (#17551) 2025-04-07 18:16:01 +08:00
Junjie.MandGitHub 41df910771 fix: only the owner can edit the member information (#17538) 2025-04-07 17:42:12 +08:00
kenwoodjwandGitHub 01704a4c1b fix ChatCompletionResponse docs (#17534)
Signed-off-by: kenwoodjw <blackxin55+@gmail.com>
2025-04-07 17:42:05 +08:00
诗浓andGitHub 8ab9eb9857 fix: update QA document default language from "Chinese" to "Chinese Simplified" (#17541) 2025-04-07 17:38:29 +08:00
JyongandGitHub e1439c4a5b deal dataset metadata and metadata binding (#17544) 2025-04-07 17:38:06 +08:00
Junyan Qin (Chin)andGitHub e7f1d0deea feat(plugin/endpoint): add supports for provider to register array[tool] type field (#17523) 2025-04-07 16:15:20 +08:00
GeorgeCaoJandGitHub 0998b01321 fix: loop and interation node not showing tracing entry in chatflow (#17500) 2025-04-07 15:56:25 +08:00
crazywoolaandGitHub 743071d9bb chore: add missing i18n (#17517) 2025-04-07 13:11:42 +08:00
f54905e685 feat: Integrate WaterCrawl.dev as a new knowledge base provider (#16396)
Co-authored-by: crazywoola <427733928@qq.com>
2025-04-07 12:43:23 +08:00
JasonfishandGitHub 0afad94378 fix: Correct "The job is not exist" to "The job does not exist" (#17516) 2025-04-07 12:32:25 +08:00
zxhlyhandGitHub e350511102 fix: chat api doc (#17515) 2025-04-07 12:32:15 +08:00
JasonfishandGitHub fd443941a2 feat(improve-api-endpoints): Added Datasets and Annotation APIs (#12237) 2025-04-07 10:36:58 +08:00
quicksandandGitHub b146aaaeb7 optimize: plugin permission (#17507) 2025-04-07 10:17:53 +08:00
yusheng chenandGitHub dd4b03e812 refactor & perf of file var-reference-vars.tsx (#17444) 2025-04-07 10:13:22 +08:00
yusheng chenandGitHub bf69b97639 refactor & perf: declare const object outside component ConstantField (#17495) 2025-04-07 10:12:35 +08:00
yusheng chenandGitHub 6d59b8d85b refactor: type improvement of component AddVariablePopupWithPosition (#17497) 2025-04-07 10:12:20 +08:00
yusheng chenandGitHub e3dc9f3c31 refactor & perf: declare const object outside component VarReferencePicker (#17496) 2025-04-07 10:12:10 +08:00
42a42a7962 FEAT: support Tencent vectordb to full text search (#16865)
Co-authored-by: wlleiiwang <wlleiiwang@tencent.com>
2025-04-07 09:50:03 +08:00
yusheng chenandGitHub c05e03fc09 refactor & perf: import { noop } from 'lodash-es' across web (#17439) 2025-04-06 17:56:08 +08:00
KVOJJJinandGitHub 7016ccef10 Fix(webapp): long table scroll issue of workflow (#17491) 2025-04-06 17:54:41 +08:00
GuanMuandGitHub 6f636093b6 fix: adjust styling for active and disabled states in Stepper component (#17485) 2025-04-06 00:04:56 +08:00
crazywoolaandGitHub 3e698074e7 Fix/17466 cannot create a knowledge base by adding files (#17470) 2025-04-06 00:03:05 +08:00
yusheng chenandGitHub dc9194ca00 fix: change Switch.props.ref to optional prop to align with OriginalSwitch (#17443) 2025-04-05 14:56:54 +08:00
Alter-xyzandGitHub 6efa882ca3 chore: remove outdated contact (#17467) 2025-04-05 11:36:40 +08:00
Bowen LiangandGitHub fcd5fcca83 fix: skip file format check of mime types for generated files by tools (#17455) 2025-04-05 10:29:14 +08:00
yusheng chenandGitHub 95212af935 refactor: type improvement of file oneMoreStep.tsx (#17431) 2025-04-04 21:11:33 +08:00
crazywoolaandGitHub da2113bde9 fix: client side error (#17428) 2025-04-04 15:59:02 +08:00
PanpanandGitHub fc3f14c0ee fix: keep image url (#17430) 2025-04-04 15:55:48 +08:00
yusheng chenandGitHub 296e2ef90f doc: add JSDoc to file format.ts (#17426) 2025-04-03 22:48:54 +08:00
非法操作andGitHub dcb8939c7f feat: add resize debug panel width (#17427) 2025-04-03 21:30:04 +08:00
JoelandGitHub 31a6aabfe5 chore: add unit test to high frequency component (#17423) 2025-04-03 18:19:11 +08:00
Dongyu LiandGitHub 2e9997110a Fix/dsl kb encrypt (#17353) 2025-04-03 17:29:34 +08:00
Yongtao HuangandGitHub e1304dc0c3 Chore: fix some wrong annotations (#17413) 2025-04-03 17:27:07 +08:00
bravomarkandGitHub 5aa82629dd fix: #15548 Resolve errors in SQL queries caused by SELECT fields not appearing in the GROUP BY clause. (#17408) 2025-04-03 16:48:50 +08:00
NoviceandGitHub dcdec98c8e feat: agent node add memory (#15976) 2025-04-03 16:40:58 +08:00
3d76f09c3a feat: hide 'Find More in Marketplace in Tools' (#16955)
Co-authored-by: Xiaoba Yu <xb1823725853@gmail.com>
2025-04-03 16:35:59 +08:00
Good WoodandGitHub b3f4e90862 feat: remove file and file[] restriction in code node (#17382) 2025-04-03 16:23:19 +08:00
crazywoolaandGitHub 4902ddaf87 Feat/change workspace name (#17402) 2025-04-03 16:05:55 +08:00
TonnyandGitHub a83318cf4b feat: add langfuse llm node input and output (#17368) 2025-04-03 16:05:37 +08:00
GuanMuandGitHub 12faecdf89 fix: update tag input border styles for special mode and focus state (#17393) 2025-04-03 14:03:07 +08:00
crazywoolaandGitHub c92bc84316 Fix/15429 forgotpasswordresetapi session management (#17390) 2025-04-03 13:54:28 +08:00
JyongandGitHub 48c2168dff fix annotation failed when embedding model and dimension changed (#17347) 2025-04-03 13:03:22 +08:00
PerfectoandGitHub 16c722d1d8 fix: move hardcoded text to language settings (#16990) (#17133) 2025-04-02 22:35:51 +08:00
akouandGitHub 5cffcd6336 fix: improve error handling and default values in fetchPlan function (#17320) 2025-04-02 21:44:09 +08:00
JyongandGitHub 0bf816f2e8 fix duplicate documents returned by Get document list service API (#17351) 2025-04-02 21:39:28 +08:00
JyongandGitHub 6104b91d3f add doc support in knowledge base for unstructured (#17352) 2025-04-02 21:35:01 +08:00
GuanMuandGitHub 33c8cb7b3b fix: update docker-compose commands $17355 (#17356) 2025-04-02 21:33:33 +08:00
诗浓andGitHub 4f286c9073 fix: remove setPublishedAt on handleRestoreFromPublishedWorkflow (#17358) 2025-04-02 21:32:21 +08:00
fd1e40d22e Lindorm VDB bugfix (#17357)
Co-authored-by: jiangzhijie <jiangzhijie.jzj@alibaba-inc.com>
2025-04-02 21:31:59 +08:00
BenjaminandGitHub e2b8f40275 fix: update permission logic to ensure administrators always have access (#17360) 2025-04-02 21:29:14 +08:00
zxhlyhandGitHub 78409dfec1 fix: loop single run log (#17329) 2025-04-02 15:22:24 +08:00
JoelandGitHub 98345c0f65 fix: sandbox can upload multiple files by upload (#17323) 2025-04-02 14:02:44 +08:00
Kindy LinandGitHub 95c6bd1c8a feat: add i18n checker (ui) (#17283) 2025-04-02 13:58:31 +08:00
Dongyu LiandGitHub 8c77f2dc03 Feat/loop node (#17273) 2025-04-02 13:53:26 +08:00
11e95d2a61 Fix (api): Fix the processing logic of the retriever_resources field. (#17304)
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-02 12:44:29 +08:00
Jian GuoandGitHub abaefe22bc [Documentation] Update README under docker folder for middleware components. (#17284) 2025-04-02 11:02:05 +08:00
bda06df668 fix: add app quota check to import API (#17295)
Co-authored-by: crazywoola <427733928@qq.com>
2025-04-02 10:20:46 +08:00
Bowen LiangandGitHub 4a505c19df fix: enlarge workspace selector width to show current workspace (#17288) 2025-04-02 09:27:37 +08:00
csurongandGitHub d417ccaf49 fix: web docker file config (#17130) 2025-04-01 21:16:14 +08:00
JoelandGitHub b4aa1900e2 feat: add zod (#17277) 2025-04-01 18:10:11 +08:00
zxhlyhandGitHub 713902dc47 Feat/loop break node (#17268) 2025-04-01 16:52:07 +08:00
JyongandGitHub 627a9e2ce1 SaaS: batch upload limit check for sandbox plan (#17264) 2025-04-01 16:45:31 +08:00
NFishandGitHub 2ae7a70be9 fix: web app form<RunOnce> component is changing an uncontrolled inpu… (#17269) 2025-04-01 16:30:41 +08:00
zxhlyhandGitHub e58703877b chore: slice workflow store (#17254) 2025-04-01 16:02:52 +08:00
Xiyuan ChenandGitHub 9c4be5d098 Feat/education api (#17168) 2025-04-01 02:45:34 -04:00
d1801b1f2e Feat:edu frontend (#17251)
Co-authored-by: crazywoola <427733928@qq.com>
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
2025-04-01 13:58:10 +08:00
HuDenghuiandGitHub 4b5ec242e7 fix: Fix lodash module not found issue (#16953) 2025-04-01 12:16:49 +08:00
liuzhenghuaandGitHub d5b48a0aa3 fix: keywords field not persist in segements api (#17151) 2025-04-01 11:20:32 +08:00
Obada KhaliliandGitHub 6372cb7b41 Support variables in question classifier classes (#17227) 2025-04-01 11:19:36 +08:00
GuanMuandGitHub 931d3390f0 Fix api document (#17178) 2025-04-01 11:07:47 +08:00
ff388fe3e6 optimize lindorm vdb add_texts (#17212)
Co-authored-by: jiangzhijie <jiangzhijie.jzj@alibaba-inc.com>
2025-04-01 11:06:35 +08:00
ArcanerandGitHub ef1c1a12d2 fix: enhance validation of workflow file types (#16203) 2025-03-31 20:05:20 +08:00
PanpanandGitHub 24b1a625b3 feat: allow the embedding in websites to customize sys.user_id (#16062) 2025-03-31 18:55:42 +08:00
shirukaiandGitHub 6cf258a809 fix: code block syntax cannot be displayed correctly in react mode (#16904) 2025-03-31 16:27:29 +08:00
KVOJJJinandGitHub c66fda7c71 chore: independent page style update (#17176) 2025-03-31 15:44:04 +08:00
诗浓andGitHub ac850e559f feat: organize button adds organization of nodes inside iteration/loop nodes (#17068) 2025-03-31 15:17:17 +08:00
JoelandGitHub 7df36fe9f5 fix: run frontend test failed and enable run test in CI (#17017) 2025-03-31 14:36:01 +08:00
NFishandGitHub 161724fb17 Fix/workspace logo style (#17164) 2025-03-31 14:26:43 +08:00
KVOJJJinandGitHub cf05e9cf78 Fix: iframe mount delay of embedded chatbot (#17167) 2025-03-31 14:25:39 +08:00
非法操作andGitHub 44f911a0a8 chore: docstring not match the function parameter (#17162) 2025-03-31 13:19:15 +08:00
zxhlyhandGitHub 32527b26d5 fix: model page switch marketplace (#17147) 2025-03-31 11:13:05 +08:00
KVOJJJinandGitHub e008faf729 Feat: dark mode for independent pages (#17045) 2025-03-31 10:28:19 +08:00
Yingchun LaiandGitHub 46d235bca0 feat: poolize the ops trace instance (#15947) 2025-03-30 13:20:23 +08:00
horochxandGitHub a91b780936 perf: optimizing db WorkflowAppLog index (#14710) 2025-03-30 13:17:23 +08:00
aa4c6874f1 fix: When a WorkSpaceNotAllowedCreateError occurs, may account is not defined (#11144)
Co-authored-by: 刘江波 <jiangbo721@163.com>
2025-03-30 13:16:07 +08:00
adruandGitHub 6d7a54915e Added CONTRIBUTING.md translations for multiple languages (#17108) 2025-03-30 13:12:13 +08:00
GuanMuandGitHub f30b1c2358 fix: update border radius of ListboxOptions for improved UI consistency #17101 (#17102) 2025-03-29 23:43:18 +08:00
Good WoodandGitHub 42968cb945 fix: fix cannot select stream-tool-call in agent modal (#17015) 2025-03-29 14:47:28 +08:00
ClSlaidandGitHub 87034e26ae chore: add .env-local to gitigonre (#17042)
Signed-off-by: cl <cailue@apache.org>
2025-03-29 14:23:58 +08:00
ArcanerandGitHub becd03a4aa fix: enhance file extension condition check for if-else node (#17060) 2025-03-29 14:20:18 +08:00
a1aa325ce3 Chore/code format and Repair commit_id 3254018d more deleted codes and Fix naming error ambiguity between workflow_run_id and workflow_id (#17075)
Co-authored-by: 刘江波 <jiangbo721@163.com>
2025-03-29 14:15:53 +08:00
34cba83ac4 fix: bug that overwrote the llm model thought process when final_answ… (#17074)
Co-authored-by: 刘江波 <jiangbo721@163.com>
2025-03-28 23:41:32 +08:00
fb11264f42 docs: correct type desc (#17043)
Co-authored-by: bo.zhao <bo.zhao@iglooinsure.com>
2025-03-28 19:24:45 +08:00
Bowen LiangandGitHub bad31dfff1 ci: skip setting up opengauss for vdb tests (#17016) 2025-03-28 19:23:46 +08:00
NoviceandGitHub dd3844d1d3 fix(answer): The chat interface outputs twice (#17033) 2025-03-28 18:18:29 +08:00
GuanMuandGitHub bc22076ad8 fix: update account dropdown border radius for improved UI consistency #17030 (#17031) 2025-03-28 16:01:59 +08:00
過世秋風andGitHub 754e646b0c fix: _build_from_remote_url get extension is .bin (#17020) 2025-03-28 15:44:23 +08:00
KVOJJJinandGitHub 9feafb6dbd fix: show build app limit in app creation modal (#16940) 2025-03-28 15:39:21 +08:00
GuanMuandGitHub c7fcfc863d fix: add overflow hidden to Collapse component #17009 (#17011) 2025-03-28 14:39:16 +08:00
Chenhe GuandGitHub d565802ea1 remove business contact info in license (#16985) 2025-03-28 10:03:36 +08:00
crazywoolaandGitHub ea1d459423 Revert "feat: add langfuse llm node input and output" (#16947) 2025-03-27 17:42:12 +08:00
過世秋風andGitHub 377d11d13b fix: WorkflowRunDetailApi created_at、finished_at types changed to timestamps (#16821) 2025-03-27 17:23:30 +08:00
GuanMuandGitHub d65da600e5 fix: enhance filename validation and extraction in FileService #16867 (#16869) 2025-03-27 16:46:10 +08:00
JoeandGitHub 82189e1bc5 feat: add langfuse llm node input and output (#16924) 2025-03-27 16:32:12 +08:00
诗浓andGitHub 7f8bfb6e4a fix(ui): improve chart text spacing and prevent unnecessary truncation (#16894) 2025-03-27 15:58:23 +08:00
7f70cadacb feat: support Tablestore vector database (#16601)
Co-authored-by: xiaozhiqing.xzq <xiaozhiqing.xzq@alibaba-inc.com>
2025-03-27 15:53:33 +08:00
Yongtao HuangandGitHub 98f2e2c729 Fix wrong allowed extensions (#16893) 2025-03-27 15:42:12 +08:00
JoelandGitHub 395fdc4960 fix: some parallel logs missing (#16923) 2025-03-27 15:42:01 +08:00
Good WoodandGitHub 094b049c94 fix: fix the bug where pressing Enter in Chinese input mode on Safari… (#16914) 2025-03-27 15:41:06 +08:00
5d77730c78 feat(api): optimize conversation name generation logic in message app (#16917)
Co-authored-by: yuanlong <yuanlong@boco.com.cn>
Co-authored-by: crazywoola <427733928@qq.com>
2025-03-27 15:18:45 +08:00
a743d5dc71 feat: tencent vectordb: use grpc client and set upsert batch size (#16016)
Co-authored-by: wlleiiwang <wlleiiwang@tencent.com>
2025-03-27 12:20:16 +08:00
KVOJJJinandGitHub c23135c9e8 feat: webapp support change inputs after conversation started (#16901) 2025-03-27 11:58:16 +08:00
NoviceandGitHub 0722beeb0b fix(agent app): moderation feature can't work as expected (#16794) 2025-03-27 11:41:14 +08:00
8b89447549 Fix Custom Tool File Upload: Resolve Multiple Files Recognition and Multipart Boundary Issues (#14014)
Co-authored-by: crazywoola <427733928@qq.com>
2025-03-27 10:47:35 +08:00
非法操作andGitHub 8047d08b3b chore: remove third party models (#13885) 2025-03-27 10:22:32 +08:00
JyongandGitHub 30792a1e1a install pandoc (#16825) 2025-03-26 22:34:10 +08:00
非法操作andGitHub 91db2207b3 fix: tool's number and secet input display issue (#16834) 2025-03-26 21:17:04 +08:00
59a86dabee fix: fix missing oceanbase config enable_hybrid_search init (#16852)
Co-authored-by: 李远军 <4842@9ji.com>
2025-03-26 21:15:54 +08:00
非法操作andGitHub d87d66ab88 fix missing agent node help link (#16841) 2025-03-26 16:28:32 +08:00
JoelandGitHub 032d849f17 chore: handle Textarea component ref warning in react 19 (#16818) 2025-03-26 14:55:02 +08:00
c451f54925 feat: add the maximum number of iterations to env (#16805)
Co-authored-by: Xiaoba Yu <xb1823725853@gmail.com>
2025-03-26 14:17:59 +08:00
JyongandGitHub 6a857e01f6 fix multiple metadata filter's confusing setting (#16771) 2025-03-26 14:16:21 +08:00
2da780e4dc Add and modify jp translation (#16807)
Co-authored-by: crazywoola <427733928@qq.com>
2025-03-26 13:29:04 +08:00
Good WoodandGitHub 71edaba9df fix: fix ui layout when in mobile mode (#16793) 2025-03-26 11:01:13 +08:00
Yongtao HuangandGitHub 37134c5987 Remove the useless excluded item in mypy.ini (#16777) 2025-03-26 09:02:45 +08:00
taokuizuandGitHub 0c2a459c30 fix typo in _process_metadata_filter_func (#16780) 2025-03-26 09:01:41 +08:00
Bowen LiangandGitHub e0fc7f69dd fix the major and minor version of stub types declaration with build date stripped (#16709) 2025-03-25 21:23:44 +08:00
crazywoolaandGitHub bbf1639c63 feat: add jp_ja for knowledge api (#16766) 2025-03-25 20:31:01 +08:00
liuzhenghuaandGitHub cb12b4436f fix: provider credentials load error (#16695) 2025-03-25 18:56:53 +08:00
Yongtao HuangandGitHub 0277a37fca Fix some typos in CONTRIBUTING.md (#16761) 2025-03-25 18:53:09 +08:00
NoviceandGitHub ac3577bc56 chore(slider): fix the slider lint error (#16746) 2025-03-25 17:09:59 +08:00
GuanMuandGitHub 0811a23cd4 fix: trim whitespace from URL input in file uploader component #16700 (#16722) 2025-03-25 16:48:25 +08:00
NoviceandGitHub 05c6d57f29 fix(slider): the slider's style is not as expected (#16711) 2025-03-25 16:47:23 +08:00
JyongandGitHub 58c62f0a34 fix full-doc mode document doesn't reindex after enable or un_archive (#16737) 2025-03-25 16:26:14 +08:00
JyongandGitHub 2174225259 fix milvus filter search (#16725) 2025-03-25 16:22:43 +08:00
Wu TianweiandGitHub 6a0f1dad7f fix: update htmlContent type and wrap button in div for manualClose (#16716) 2025-03-25 14:58:42 +08:00
DavidandGitHub 40cadab8a6 fix: fix icon generation error on Windows platform (#16254) 2025-03-25 14:54:49 +08:00
6157f57872 feat: Add OceanBase hybrid search features (#16652)
Co-authored-by: 李远军 <4842@9ji.com>
Co-authored-by: yourchanges <yourchanges@gmail.com>
2025-03-25 14:32:00 +08:00
zhangkun-21andGitHub c4bb07184d Update prompts.py:Fix conversation title language for Italian conversations (#16696) 2025-03-25 14:29:08 +08:00
诗浓andGitHub af5d6ca27d fix: regex expressions distorted in code block (#16690) 2025-03-25 14:28:35 +08:00
JyongandGitHub 78f2ec8f32 mark weight type as optional (#16701) 2025-03-25 14:19:26 +08:00
kenwoodjwandGitHub a113356695 fix: pgvector metadata filter (#16688)
Signed-off-by: kenwoodjw <blackxin55+@gmail.com>
2025-03-25 11:34:33 +08:00
Yongtao HuangandGitHub 43753c8e9a Fix function's name mismatch (#16681) 2025-03-25 10:25:15 +08:00
fc8c765215 Fix/vdb lindorm (#16660)
Co-authored-by: jiangzhijie <jiangzhijie.jzj@alibaba-inc.com>
2025-03-25 09:19:06 +08:00
JyongandGitHub 86a1859d02 Metadata variable value fix (#16665) 2025-03-25 09:07:11 +08:00
GuanMuandGitHub 360986f38d Feat add childchunk api (#16094) 2025-03-24 20:58:28 +08:00
JyongandGitHub 1be0d26c1f fix metadata filter not affect in keyword-search and fulltext-search (#16644) 2025-03-24 18:35:16 +08:00
-LAN-andGitHub c167a1f4f4 chore: bump the package version to 1.1.3 (#16612)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-03-24 17:59:54 +08:00
lenboandGitHub 5eb0ca9b9d fix: fix inner API workspace Account.query error. (#16630) 2025-03-24 17:52:50 +08:00
Wu TianweiandGitHub 6e26ed2bb7 fix: update retrieval configuration to correctly handle reranking mod… (#16641) 2025-03-24 17:47:56 +08:00
-LAN-andGitHub 058d9c3525 chore: update release trigger to include all tags in build-push workflow (#16631)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-03-24 17:17:55 +08:00
KVOJJJinandGitHub b247fbb2ef Fix: style of sidebar with in mobile (#16629) 2025-03-24 16:30:23 +08:00
KVOJJJinandGitHub bc6f122364 Fix: style issue of app detail panel in jp (#16620) 2025-03-24 15:54:00 +08:00
KVOJJJinandGitHub 815d77856d Fix: show feedback status in conversation (#16615) 2025-03-24 15:20:18 +08:00
zxhlyhandGitHub 05eaef84bb fix: cancel marketplace debounced search when clear search keywords (#16614) 2025-03-24 15:17:12 +08:00
770c461a8f feat: add openGauss PQ acceleration feature (#16432)
Co-authored-by: chenhuan <huan.chen0728@foxmail>
2025-03-24 15:16:40 +08:00
JoelandGitHub 16b6ffd915 fix: sanitizer svg to avoid xss (#16606) 2025-03-24 14:36:07 +08:00
Wu TianweiandGitHub 9701b573e0 feat: add datasets detail context and provider for improved data vali… (#16451) 2025-03-24 14:30:26 +08:00
L8ngandGitHub 83cd14104d feat: datasets openapi list segements support paged resp (#16603) 2025-03-24 14:27:31 +08:00
KVOJJJinandGitHub e2988acc2f Fix: web app sidebar cannot close when long title conversation existed (#16593) 2025-03-24 12:51:21 +08:00
JoelandGitHub cea4669b76 fix: transition in simple select causes page crash (#16587) 2025-03-24 11:07:16 +08:00
zxhlyhandGitHub 17b4d4c7b2 fix: workflow if-else node variable tag style (#16583) 2025-03-24 11:06:10 +08:00
Ryosei KarakiandGitHub e95f0fcceb chore: enable eslint cache (#16570) 2025-03-24 10:17:54 +08:00
Wu TianweiandGitHub 6d5d6f0f24 fix: update app mode display text for advanced-chat type (#16578) 2025-03-24 10:17:04 +08:00
7ce8faf176 fix: fix variable-aggregator cannot pass node check in group mode (#16439)
Co-authored-by: crazywoola <427733928@qq.com>
2025-03-24 09:49:33 +08:00
f31e3313b0 feat: Make the logic of APP filtering and creation the same (#16079)
Co-authored-by: 刘江波 <jiangbo721@163.com>
2025-03-23 09:40:15 +08:00
Yingchun LaiandGitHub f6ac98a37d fix: fix the app max_active_requests been overwritten bug (#16513) 2025-03-23 09:34:23 +08:00
NFishandGitHub f8e7e301cd fix: update chatbot doc link at create app page (#16479) 2025-03-23 09:27:16 +08:00
dotdotdotandGitHub 35bafb3235 fix: the error occurring when passing inputs on iOS devices and some … (#16534) 2025-03-23 09:26:35 +08:00
LiuBodongandGitHub ae5d2ecf48 fix:weight_type missing when create document in dataset (#16503) 2025-03-22 20:21:57 +08:00
YeuolyandGitHub 1907d2a90a fix: optimize query for expired workflow runs by adding date filter and limiting results (#16491) 2025-03-22 11:17:21 +08:00
JaydenZhouandGitHub 4448a54cc1 use REDIS_PORT to replace 6379 in celery config (#16182) 2025-03-21 21:34:07 +08:00
YeuolyandGitHub bfc0d606dc feat: cleanup free tenants expired data like messages/conversations/workflow_runs/workflow_node_executions (#16490) 2025-03-21 21:30:35 +08:00
kenwoodjwandGitHub 3306228840 fix: workflow file add related-id in iteration node (#16255)
Signed-off-by: kenwoodjw <blackxin55+@gmail.com>
2025-03-21 20:57:02 +08:00
Yingchun LaiandGitHub d7e00ae691 feat: Skip Redis operations if RateLimit is disabled (#12226) 2025-03-21 19:55:27 +08:00
-LAN-andGitHub 0e2e2db3fa refactor: add OpikDataTrace instance builder. (#16444)
Signed-off-by: -LAN- <laipz8200@outlook.com>
2025-03-21 18:09:39 +08:00
1226 changed files with 45394 additions and 23465 deletions
+6 -6
View File
@@ -1,13 +1,13 @@
#!/bin/bash
npm add -g pnpm@9.12.2
npm add -g pnpm@10.8.0
cd web && pnpm install
pipx install poetry
pipx install uv
echo 'alias start-api="cd /workspaces/dify/api && poetry run python -m flask run --host 0.0.0.0 --port=5001 --debug"' >> ~/.bashrc
echo 'alias start-worker="cd /workspaces/dify/api && poetry run python -m celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion"' >> ~/.bashrc
echo 'alias start-api="cd /workspaces/dify/api && uv run python -m flask run --host 0.0.0.0 --port=5001 --debug"' >> ~/.bashrc
echo 'alias start-worker="cd /workspaces/dify/api && uv run python -m celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion"' >> ~/.bashrc
echo 'alias start-web="cd /workspaces/dify/web && pnpm dev"' >> ~/.bashrc
echo 'alias start-containers="cd /workspaces/dify/docker && docker-compose -f docker-compose.middleware.yaml -p dify up -d"' >> ~/.bashrc
echo 'alias stop-containers="cd /workspaces/dify/docker && docker-compose -f docker-compose.middleware.yaml -p dify down"' >> ~/.bashrc
echo 'alias start-containers="cd /workspaces/dify/docker && docker-compose -f docker-compose.middleware.yaml -p dify --env-file middleware.env up -d"' >> ~/.bashrc
echo 'alias stop-containers="cd /workspaces/dify/docker && docker-compose -f docker-compose.middleware.yaml -p dify --env-file middleware.env down"' >> ~/.bashrc
source /home/vscode/.bashrc
+1 -1
View File
@@ -1,3 +1,3 @@
#!/bin/bash
cd api && poetry install
cd api && uv sync
-36
View File
@@ -1,36 +0,0 @@
name: Setup Poetry and Python
inputs:
python-version:
description: Python version to use and the Poetry installed with
required: true
default: '3.11'
poetry-version:
description: Poetry version to set up
required: true
default: '2.0.1'
poetry-lockfile:
description: Path to the Poetry lockfile to restore cache from
required: true
default: ''
runs:
using: composite
steps:
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
cache: pip
- name: Install Poetry
shell: bash
run: pip install poetry==${{ inputs.poetry-version }}
- name: Restore Poetry cache
if: ${{ inputs.poetry-lockfile != '' }}
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
cache: poetry
cache-dependency-path: ${{ inputs.poetry-lockfile }}
+34
View File
@@ -0,0 +1,34 @@
name: Setup UV and Python
inputs:
python-version:
description: Python version to use and the UV installed with
required: true
default: '3.12'
uv-version:
description: UV version to set up
required: true
default: '0.6.14'
uv-lockfile:
description: Path to the UV lockfile to restore cache from
required: true
default: ''
enable-cache:
required: true
default: true
runs:
using: composite
steps:
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: ${{ inputs.uv-version }}
python-version: ${{ inputs.python-version }}
enable-cache: ${{ inputs.enable-cache }}
cache-dependency-glob: ${{ inputs.uv-lockfile }}
+30 -18
View File
@@ -17,6 +17,9 @@ jobs:
test:
name: API Tests
runs-on: ubuntu-latest
defaults:
run:
shell: bash
strategy:
matrix:
python-version:
@@ -27,35 +30,44 @@ jobs:
- 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
- name: Setup UV and Python
uses: ./.github/actions/setup-uv
with:
python-version: ${{ matrix.python-version }}
poetry-lockfile: api/poetry.lock
uv-lockfile: api/uv.lock
- name: Check Poetry lockfile
run: |
poetry check -C api --lock
poetry show -C api
- name: Check UV lockfile
run: uv lock --project api --check
- name: Install dependencies
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: uv sync --project api --dev
- name: Run Unit tests
run: poetry run -P api bash dev/pytest/pytest_unit_tests.sh
run: |
uv run --project api bash dev/pytest/pytest_unit_tests.sh
# Extract coverage percentage and create a summary
TOTAL_COVERAGE=$(python -c 'import json; print(json.load(open("coverage.json"))["totals"]["percent_covered_display"])')
# Create a detailed coverage summary
echo "### Test Coverage Summary :test_tube:" >> $GITHUB_STEP_SUMMARY
echo "Total Coverage: ${TOTAL_COVERAGE}%" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
uv run --project api coverage report >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
- name: Run dify config tests
run: poetry run -P api python dev/pytest/pytest_config_tests.py
run: uv run --project api dev/pytest/pytest_config_tests.py
- name: Run mypy
run: |
poetry run -C api python -m mypy --install-types --non-interactive .
- name: MyPy Cache
uses: actions/cache@v4
with:
path: api/.mypy_cache
key: mypy-${{ matrix.python-version }}-${{ runner.os }}-${{ hashFiles('api/uv.lock') }}
- name: Run MyPy Checks
run: dev/mypy-check
- name: Set up dotenvs
run: |
@@ -75,4 +87,4 @@ jobs:
ssrf_proxy
- name: Run Workflow
run: poetry run -P api bash dev/pytest/pytest_workflow.sh
run: uv run --project api bash dev/pytest/pytest_workflow.sh
+2 -2
View File
@@ -6,8 +6,8 @@ on:
- "main"
- "deploy/dev"
- "deploy/enterprise"
release:
types: [published]
tags:
- "*"
concurrency:
group: build-push-${{ github.head_ref || github.run_id }}
+5 -7
View File
@@ -24,13 +24,13 @@ jobs:
fetch-depth: 0
persist-credentials: false
- name: Setup Poetry and Python
uses: ./.github/actions/setup-poetry
- name: Setup UV and Python
uses: ./.github/actions/setup-uv
with:
poetry-lockfile: api/poetry.lock
uv-lockfile: api/uv.lock
- name: Install dependencies
run: poetry install -C api
run: uv sync --project api
- name: Prepare middleware env
run: |
@@ -54,6 +54,4 @@ jobs:
- name: Run DB Migration
env:
DEBUG: true
run: |
cd api
poetry run python -m flask upgrade-db
run: uv run --directory api flask upgrade-db
+1
View File
@@ -42,6 +42,7 @@ jobs:
with:
push: false
context: "{{defaultContext}}:${{ matrix.context }}"
file: "${{ matrix.file }}"
platforms: ${{ matrix.platform }}
cache-from: type=gha
cache-to: type=gha,mode=max
+12 -12
View File
@@ -18,7 +18,6 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Check changed files
@@ -29,24 +28,27 @@ jobs:
api/**
.github/workflows/style.yml
- name: Setup Poetry and Python
- name: Setup UV and Python
if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/actions/setup-poetry
uses: ./.github/actions/setup-uv
with:
uv-lockfile: api/uv.lock
enable-cache: false
- name: Install dependencies
if: steps.changed-files.outputs.any_changed == 'true'
run: poetry install -C api --only lint
run: uv sync --project api --dev
- name: Ruff check
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 ./
uv run --directory api ruff --version
uv run --directory api ruff check ./
uv run --directory api ruff format --check ./
- 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: uv run --project api dotenv-linter ./api/.env.example ./web/.env.example
- name: Lint hints
if: failure()
@@ -63,7 +65,6 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Check changed files
@@ -82,7 +83,7 @@ jobs:
uses: actions/setup-node@v4
if: steps.changed-files.outputs.any_changed == 'true'
with:
node-version: 20
node-version: 22
cache: pnpm
cache-dependency-path: ./web/package.json
@@ -102,7 +103,6 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Check changed files
@@ -133,7 +133,6 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Check changed files
@@ -153,6 +152,7 @@ jobs:
env:
BASH_SEVERITY: warning
DEFAULT_BRANCH: main
FILTER_REGEX_INCLUDE: pnpm-lock.yaml
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
IGNORE_GENERATED_FILES: true
IGNORE_GITIGNORED_FILES: true
+1 -2
View File
@@ -18,7 +18,7 @@ jobs:
strategy:
matrix:
node-version: [16, 18, 20]
node-version: [16, 18, 20, 22]
defaults:
run:
@@ -27,7 +27,6 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Use Node.js ${{ matrix.node-version }}
@@ -33,7 +33,7 @@ jobs:
- name: Set up Node.js
if: env.FILES_CHANGED == 'true'
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
+9 -13
View File
@@ -8,7 +8,7 @@ on:
- api/core/rag/datasource/**
- docker/**
- .github/workflows/vdb-tests.yml
- api/poetry.lock
- api/uv.lock
- api/pyproject.toml
concurrency:
@@ -29,22 +29,19 @@ jobs:
- 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
- name: Setup UV and Python
uses: ./.github/actions/setup-uv
with:
python-version: ${{ matrix.python-version }}
poetry-lockfile: api/poetry.lock
uv-lockfile: api/uv.lock
- name: Check Poetry lockfile
run: |
poetry check -C api --lock
poetry show -C api
- name: Check UV lockfile
run: uv lock --project api --check
- name: Install dependencies
run: poetry install -C api --with dev
run: uv sync --project api --dev
- name: Set up dotenvs
run: |
@@ -76,12 +73,11 @@ jobs:
milvus-standalone
pgvecto-rs
pgvector
opengauss
chroma
elasticsearch
- name: Check TiDB Ready
run: poetry run -P api python api/tests/integration_tests/vdb/tidb_vector/check_tiflash_ready.py
run: uv run --project api python api/tests/integration_tests/vdb/tidb_vector/check_tiflash_ready.py
- name: Test Vector Stores
run: poetry run -P api bash dev/pytest/pytest_vdb.sh
run: uv run --project api bash dev/pytest/pytest_vdb.sh
+20 -20
View File
@@ -23,7 +23,6 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Check changed files
@@ -31,25 +30,26 @@ jobs:
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: Install pnpm
if: steps.changed-files.outputs.any_changed == 'true'
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
# - name: Install dependencies
# if: steps.changed-files.outputs.any_changed == 'true'
# run: pnpm install --frozen-lockfile
- name: Setup Node.js
uses: actions/setup-node@v4
if: steps.changed-files.outputs.any_changed == 'true'
with:
node-version: 22
cache: pnpm
cache-dependency-path: ./web/package.json
# - name: Run tests
# if: steps.changed-files.outputs.any_changed == 'true'
# run: pnpm test
- 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
+2
View File
@@ -46,6 +46,7 @@ htmlcov/
.cache
nosetests.xml
coverage.xml
coverage.json
*.cover
*.py,cover
.hypothesis/
@@ -103,6 +104,7 @@ celerybeat.pid
# Environments
.env
.env-local
.venv
env/
venv/
+3 -3
View File
@@ -18,7 +18,7 @@ Need to update an existing model runtime, tool, or squash some bugs? Head over t
Join the fun, contribute, and let's build something awesome together! 💡✨
Don't forget to link an existing issue or open an new issue in the PR's description.
Don't forget to link an existing issue or open a new issue in the PR's description.
### Bug reports
@@ -68,7 +68,7 @@ How we prioritize:
4. Please add tests for your changes accordingly
5. Ensure your code passes the existing tests
6. Please link the issue in the PR description, `fixes #<issue_number>`
7. Get merrged!
7. Get merged!
### Setup the project
#### Frontend
@@ -90,4 +90,4 @@ We recommend reviewing this document carefully before proceeding with the setup,
Feel free to reach out if you encounter any issues during the setup process.
## Getting Help
If you ever get stuck or got a burning question while contributing, simply shoot your queries our way via the related GitHub issue, or hop onto our [Discord](https://discord.gg/8Tpq4AcN9c) for a quick chat.
If you ever get stuck or get a burning question while contributing, simply shoot your queries our way via the related GitHub issue, or hop onto our [Discord](https://discord.gg/8Tpq4AcN9c) for a quick chat.
+1 -1
View File
@@ -6,7 +6,7 @@
本指南和 Dify 一样在不断完善中。如果有任何滞后于项目实际情况的地方,恳请谅解,我们也欢迎任何改进建议。
关于许可证,请花一分钟阅读我们简短的[许可和贡献者协议](./LICENSE)。社区同时也遵循[行为准则](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)。
关于许可证,请花一分钟阅读我们简短的[许可和贡献者协议](./LICENSE)。同时也遵循社区[行为准则](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)。
## 开始之前
+93
View File
@@ -0,0 +1,93 @@
# CONTRIBUIR
Así que estás buscando contribuir a Dify - eso es fantástico, estamos ansiosos por ver lo que haces. Como una startup con personal y financiación limitados, tenemos grandes ambiciones de diseñar el flujo de trabajo más intuitivo para construir y gestionar aplicaciones LLM. Cualquier ayuda de la comunidad cuenta, realmente.
Necesitamos ser ágiles y enviar rápidamente dado donde estamos, pero también queremos asegurarnos de que colaboradores como tú obtengan una experiencia lo más fluida posible al contribuir. Hemos elaborado esta guía de contribución con ese propósito, con el objetivo de familiarizarte con la base de código y cómo trabajamos con los colaboradores, para que puedas pasar rápidamente a la parte divertida.
Esta guía, como Dify mismo, es un trabajo en constante progreso. Agradecemos mucho tu comprensión si a veces se queda atrás del proyecto real, y damos la bienvenida a cualquier comentario para que podamos mejorar.
En términos de licencia, por favor tómate un minuto para leer nuestro breve [Acuerdo de Licencia y Colaborador](./LICENSE). La comunidad también se adhiere al [código de conducta](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
## Antes de empezar
¿Buscas algo en lo que trabajar? Explora nuestros [buenos primeros issues](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) y elige uno para comenzar.
¿Tienes un nuevo modelo o herramienta genial para añadir? Abre un PR en nuestro [repositorio de plugins](https://github.com/langgenius/dify-plugins) y muéstranos lo que has construido.
¿Necesitas actualizar un modelo existente, herramienta o corregir algunos errores? Dirígete a nuestro [repositorio oficial de plugins](https://github.com/langgenius/dify-official-plugins) y haz tu magia.
¡Únete a la diversión, contribuye y construyamos algo increíble juntos! 💡✨
No olvides vincular un issue existente o abrir uno nuevo en la descripción del PR.
### Informes de errores
> [!IMPORTANT]
> Por favor, asegúrate de incluir la siguiente información al enviar un informe de error:
- Un título claro y descriptivo
- Una descripción detallada del error, incluyendo cualquier mensaje de error
- Pasos para reproducir el error
- Comportamiento esperado
- **Logs**, si están disponibles, para problemas del backend, esto es realmente importante, puedes encontrarlos en los logs de docker-compose
- Capturas de pantalla o videos, si es aplicable
Cómo priorizamos:
| Tipo de Issue | Prioridad |
| ------------------------------------------------------------ | --------------- |
| Errores en funciones principales (servicio en la nube, no poder iniciar sesión, aplicaciones que no funcionan, fallos de seguridad) | Crítica |
| Errores no críticos, mejoras de rendimiento | Prioridad Media |
| Correcciones menores (errores tipográficos, UI confusa pero funcional) | Prioridad Baja |
### Solicitudes de funcionalidades
> [!NOTE]
> Por favor, asegúrate de incluir la siguiente información al enviar una solicitud de funcionalidad:
- Un título claro y descriptivo
- Una descripción detallada de la funcionalidad
- Un caso de uso para la funcionalidad
- Cualquier otro contexto o capturas de pantalla sobre la solicitud de funcionalidad
Cómo priorizamos:
| Tipo de Funcionalidad | Prioridad |
| ------------------------------------------------------------ | --------------- |
| Funcionalidades de alta prioridad etiquetadas por un miembro del equipo | Prioridad Alta |
| Solicitudes populares de funcionalidades de nuestro [tablero de comentarios de la comunidad](https://github.com/langgenius/dify/discussions/categories/feedbacks) | Prioridad Media |
| Funcionalidades no principales y mejoras menores | Prioridad Baja |
| Valiosas pero no inmediatas | Futura-Funcionalidad |
## Enviando tu PR
### Proceso de Pull Request
1. Haz un fork del repositorio
2. Antes de redactar un PR, por favor crea un issue para discutir los cambios que quieres hacer
3. Crea una nueva rama para tus cambios
4. Por favor añade pruebas para tus cambios en consecuencia
5. Asegúrate de que tu código pasa las pruebas existentes
6. Por favor vincula el issue en la descripción del PR, `fixes #<número_del_issue>`
7. ¡Fusiona tu código!
### Configuración del proyecto
#### Frontend
Para configurar el servicio frontend, por favor consulta nuestra [guía completa](https://github.com/langgenius/dify/blob/main/web/README.md) en el archivo `web/README.md`. Este documento proporciona instrucciones detalladas para ayudarte a configurar el entorno frontend correctamente.
#### Backend
Para configurar el servicio backend, por favor consulta nuestras [instrucciones detalladas](https://github.com/langgenius/dify/blob/main/api/README.md) en el archivo `api/README.md`. Este documento contiene una guía paso a paso para ayudarte a poner en marcha el backend sin problemas.
#### Otras cosas a tener en cuenta
Recomendamos revisar este documento cuidadosamente antes de proceder con la configuración, ya que contiene información esencial sobre:
- Requisitos previos y dependencias
- Pasos de instalación
- Detalles de configuración
- Consejos comunes de solución de problemas
No dudes en contactarnos si encuentras algún problema durante el proceso de configuración.
## Obteniendo Ayuda
Si alguna vez te quedas atascado o tienes una pregunta urgente mientras contribuyes, simplemente envíanos tus consultas a través del issue relacionado de GitHub, o únete a nuestro [Discord](https://discord.gg/8Tpq4AcN9c) para una charla rápida.
+93
View File
@@ -0,0 +1,93 @@
# CONTRIBUER
Vous cherchez donc à contribuer à Dify - c'est fantastique, nous avons hâte de voir ce que vous allez faire. En tant que startup avec un personnel et un financement limités, nous avons de grandes ambitions pour concevoir le flux de travail le plus intuitif pour construire et gérer des applications LLM. Toute aide de la communauté compte, vraiment.
Nous devons être agiles et livrer rapidement compte tenu de notre position, mais nous voulons aussi nous assurer que des contributeurs comme vous obtiennent une expérience aussi fluide que possible lors de leur contribution. Nous avons élaboré ce guide de contribution dans ce but, visant à vous familiariser avec la base de code et comment nous travaillons avec les contributeurs, afin que vous puissiez rapidement passer à la partie amusante.
Ce guide, comme Dify lui-même, est un travail en constante évolution. Nous apprécions grandement votre compréhension si parfois il est en retard par rapport au projet réel, et nous accueillons tout commentaire pour nous aider à nous améliorer.
En termes de licence, veuillez prendre une minute pour lire notre bref [Accord de Licence et de Contributeur](./LICENSE). La communauté adhère également au [code de conduite](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
## Avant de vous lancer
Vous cherchez quelque chose à réaliser ? Parcourez nos [problèmes pour débutants](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) et choisissez-en un pour commencer !
Vous avez un nouveau modèle ou un nouvel outil à ajouter ? Ouvrez une PR dans notre [dépôt de plugins](https://github.com/langgenius/dify-plugins) et montrez-nous ce que vous avez créé.
Vous devez mettre à jour un modèle existant, un outil ou corriger des bugs ? Rendez-vous sur notre [dépôt officiel de plugins](https://github.com/langgenius/dify-official-plugins) et faites votre magie !
Rejoignez l'aventure, contribuez, et construisons ensemble quelque chose d'extraordinaire ! 💡✨
N'oubliez pas de lier un problème existant ou d'ouvrir un nouveau problème dans la description de votre PR.
### Rapports de bugs
> [!IMPORTANT]
> Veuillez vous assurer d'inclure les informations suivantes lors de la soumission d'un rapport de bug :
- Un titre clair et descriptif
- Une description détaillée du bug, y compris tous les messages d'erreur
- Les étapes pour reproduire le bug
- Comportement attendu
- **Logs**, si disponibles, pour les problèmes de backend, c'est vraiment important, vous pouvez les trouver dans les logs de docker-compose
- Captures d'écran ou vidéos, si applicable
Comment nous priorisons :
| Type de Problème | Priorité |
| ------------------------------------------------------------ | --------------- |
| Bugs dans les fonctions principales (service cloud, impossibilité de se connecter, applications qui ne fonctionnent pas, failles de sécurité) | Critique |
| Bugs non critiques, améliorations de performance | Priorité Moyenne |
| Corrections mineures (fautes de frappe, UI confuse mais fonctionnelle) | Priorité Basse |
### Demandes de fonctionnalités
> [!NOTE]
> Veuillez vous assurer d'inclure les informations suivantes lors de la soumission d'une demande de fonctionnalité :
- Un titre clair et descriptif
- Une description détaillée de la fonctionnalité
- Un cas d'utilisation pour la fonctionnalité
- Tout autre contexte ou captures d'écran concernant la demande de fonctionnalité
Comment nous priorisons :
| Type de Fonctionnalité | Priorité |
| ------------------------------------------------------------ | --------------- |
| Fonctionnalités hautement prioritaires étiquetées par un membre de l'équipe | Priorité Haute |
| Demandes populaires de fonctionnalités de notre [tableau de feedback communautaire](https://github.com/langgenius/dify/discussions/categories/feedbacks) | Priorité Moyenne |
| Fonctionnalités non essentielles et améliorations mineures | Priorité Basse |
| Précieuses mais non immédiates | Fonctionnalité Future |
## Soumettre votre PR
### Processus de Pull Request
1. Forkez le dépôt
2. Avant de rédiger une PR, veuillez créer un problème pour discuter des changements que vous souhaitez apporter
3. Créez une nouvelle branche pour vos changements
4. Veuillez ajouter des tests pour vos changements en conséquence
5. Assurez-vous que votre code passe les tests existants
6. Veuillez lier le problème dans la description de la PR, `fixes #<numéro_du_problème>`
7. Faites fusionner votre code !
### Configuration du projet
#### Frontend
Pour configurer le service frontend, veuillez consulter notre [guide complet](https://github.com/langgenius/dify/blob/main/web/README.md) dans le fichier `web/README.md`. Ce document fournit des instructions détaillées pour vous aider à configurer correctement l'environnement frontend.
#### Backend
Pour configurer le service backend, veuillez consulter nos [instructions détaillées](https://github.com/langgenius/dify/blob/main/api/README.md) dans le fichier `api/README.md`. Ce document contient un guide étape par étape pour vous aider à faire fonctionner le backend sans problème.
#### Autres choses à noter
Nous recommandons de revoir attentivement ce document avant de procéder à la configuration, car il contient des informations essentielles sur :
- Prérequis et dépendances
- Étapes d'installation
- Détails de configuration
- Conseils courants de dépannage
N'hésitez pas à nous contacter si vous rencontrez des problèmes pendant le processus de configuration.
## Obtenir de l'aide
Si jamais vous êtes bloqué ou avez une question urgente en contribuant, envoyez-nous simplement vos questions via le problème GitHub concerné, ou rejoignez notre [Discord](https://discord.gg/8Tpq4AcN9c) pour une discussion rapide.
+93
View File
@@ -0,0 +1,93 @@
# 기여하기
Dify에 기여하려고 하시는군요 - 정말 멋집니다, 당신이 무엇을 할지 기대가 됩니다. 인력과 자금이 제한된 스타트업으로서, 우리는 LLM 애플리케이션을 구축하고 관리하기 위한 가장 직관적인 워크플로우를 설계하고자 하는 큰 야망을 가지고 있습니다. 커뮤니티의 모든 도움은 정말 중요합니다.
우리는 현재 상황에서 민첩하게 빠르게 배포해야 하지만, 동시에 당신과 같은 기여자들이 기여하는 과정에서 최대한 원활한 경험을 얻을 수 있도록 하고 싶습니다. 우리는 이러한 목적으로 이 기여 가이드를 작성했으며, 여러분이 코드베이스와 우리가 기여자들과 어떻게 협업하는지에 대해 친숙해질 수 있도록 돕고, 빠르게 재미있는 부분으로 넘어갈 수 있도록 하고자 합니다.
이 가이드는 Dify 자체와 마찬가지로 끊임없이 진행 중인 작업입니다. 때로는 실제 프로젝트보다 뒤처질 수 있다는 점을 이해해 주시면 감사하겠으며, 개선을 위한 피드백은 언제든지 환영합니다.
라이센스 측면에서, 간략한 [라이센스 및 기여자 동의서](./LICENSE)를 읽어보는 시간을 가져주세요. 커뮤니티는 또한 [행동 강령](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)을 준수합니다.
## 시작하기 전에
처리할 작업을 찾고 계신가요? [초보자를 위한 이슈](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22)를 살펴보고 시작할 것을 선택하세요!
추가할 새로운 모델 런타임이나 도구가 있나요? 우리의 [플러그인 저장소](https://github.com/langgenius/dify-plugins)에 PR을 열고 당신이 만든 것을 보여주세요.
기존 모델 런타임, 도구를 업데이트하거나 버그를 수정해야 하나요? 우리의 [공식 플러그인 저장소](https://github.com/langgenius/dify-official-plugins)로 가서 당신의 마법을 펼치세요!
함께 즐기고, 기여하고, 멋진 것을 함께 만들어 봅시다! 💡✨
PR 설명에 기존 이슈를 연결하거나 새 이슈를 여는 것을 잊지 마세요.
### 버그 보고
> [!IMPORTANT]
> 버그 보고서를 제출할 때 다음 정보를 포함해 주세요:
- 명확하고 설명적인 제목
- 오류 메시지를 포함한 버그에 대한 상세한 설명
- 버그를 재현하는 단계
- 예상되는 동작
- 가능한 경우 **로그**, 백엔드 이슈의 경우 매우 중요합니다. docker-compose 로그에서 찾을 수 있습니다
- 해당되는 경우 스크린샷 또는 비디오
우선순위 결정 방법:
| 이슈 유형 | 우선순위 |
| ------------------------------------------------------------ | --------------- |
| 핵심 기능의 버그(클라우드 서비스, 로그인 불가, 애플리케이션 작동 불능, 보안 취약점) | 중대 |
| 비중요 버그, 성능 향상 | 중간 우선순위 |
| 사소한 수정(오타, 혼란스럽지만 작동하는 UI) | 낮은 우선순위 |
### 기능 요청
> [!NOTE]
> 기능 요청을 제출할 때 다음 정보를 포함해 주세요:
- 명확하고 설명적인 제목
- 기능에 대한 상세한 설명
- 해당 기능의 사용 사례
- 기능 요청에 관한 기타 컨텍스트 또는 스크린샷
우선순위 결정 방법:
| 기능 유형 | 우선순위 |
| ------------------------------------------------------------ | --------------- |
| 팀 구성원에 의해 레이블이 지정된 고우선순위 기능 | 높은 우선순위 |
| 우리의 [커뮤니티 피드백 보드](https://github.com/langgenius/dify/discussions/categories/feedbacks)에서 인기 있는 기능 요청 | 중간 우선순위 |
| 비핵심 기능 및 사소한 개선 | 낮은 우선순위 |
| 가치 있지만 즉시 필요하지 않은 기능 | 미래 기능 |
## PR 제출하기
### Pull Request 프로세스
1. 저장소를 포크하세요
2. PR을 작성하기 전에, 변경하고자 하는 내용에 대해 논의하기 위한 이슈를 생성해 주세요
3. 변경 사항을 위한 새 브랜치를 만드세요
4. 변경 사항에 대한 테스트를 적절히 추가해 주세요
5. 코드가 기존 테스트를 통과하는지 확인하세요
6. PR 설명에 이슈를 연결해 주세요, `fixes #<이슈_번호>`
7. 병합 완료!
### 프로젝트 설정하기
#### 프론트엔드
프론트엔드 서비스를 설정하려면, `web/README.md` 파일에 있는 우리의 [종합 가이드](https://github.com/langgenius/dify/blob/main/web/README.md)를 참조하세요. 이 문서는 프론트엔드 환경을 적절히 설정하는 데 도움이 되는 자세한 지침을 제공합니다.
#### 백엔드
백엔드 서비스를 설정하려면, `api/README.md` 파일에 있는 우리의 [상세 지침](https://github.com/langgenius/dify/blob/main/api/README.md)을 참조하세요. 이 문서는 백엔드를 원활하게 실행하는 데 도움이 되는 단계별 가이드를 포함하고 있습니다.
#### 기타 참고 사항
설정을 진행하기 전에 이 문서를 주의 깊게 검토하는 것을 권장합니다. 다음과 같은 필수 정보가 포함되어 있습니다:
- 필수 조건 및 종속성
- 설치 단계
- 구성 세부 정보
- 일반적인 문제 해결 팁
설정 과정에서 문제가 발생하면 언제든지 연락해 주세요.
## 도움 받기
기여하는 동안 막히거나 긴급한 질문이 있으면, 관련 GitHub 이슈를 통해 질문을 보내거나, 빠른 대화를 위해 우리의 [Discord](https://discord.gg/8Tpq4AcN9c)에 참여하세요.
+93
View File
@@ -0,0 +1,93 @@
# CONTRIBUINDO
Então você está procurando contribuir para o Dify - isso é incrível, mal podemos esperar para ver o que você vai fazer. Como uma startup com equipe e financiamento limitados, temos grandes ambições de projetar o fluxo de trabalho mais intuitivo para construir e gerenciar aplicações LLM. Qualquer ajuda da comunidade conta, verdadeiramente.
Precisamos ser ágeis e entregar rapidamente considerando onde estamos, mas também queremos garantir que colaboradores como você tenham uma experiência o mais tranquila possível ao contribuir. Montamos este guia de contribuição com esse propósito, visando familiarizá-lo com a base de código e como trabalhamos com os colaboradores, para que você possa rapidamente passar para a parte divertida.
Este guia, como o próprio Dify, é um trabalho em constante evolução. Agradecemos muito a sua compreensão se às vezes ele ficar atrasado em relação ao projeto real, e damos as boas-vindas a qualquer feedback para que possamos melhorar.
Em termos de licenciamento, por favor, dedique um minuto para ler nosso breve [Acordo de Licença e Contribuidor](./LICENSE). A comunidade também adere ao [código de conduta](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
## Antes de começar
Procurando algo para resolver? Navegue por nossos [problemas para iniciantes](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) e escolha um para começar!
Tem um novo modelo ou ferramenta para adicionar? Abra um PR em nosso [repositório de plugins](https://github.com/langgenius/dify-plugins) e mostre-nos o que você construiu.
Precisa atualizar um modelo existente, ferramenta ou corrigir alguns bugs? Vá para nosso [repositório oficial de plugins](https://github.com/langgenius/dify-official-plugins) e faça sua mágica!
Junte-se à diversão, contribua e vamos construir algo incrível juntos! 💡✨
Não se esqueça de vincular um problema existente ou abrir um novo problema na descrição do PR.
### Relatórios de bugs
> [!IMPORTANT]
> Por favor, certifique-se de incluir as seguintes informações ao enviar um relatório de bug:
- Um título claro e descritivo
- Uma descrição detalhada do bug, incluindo quaisquer mensagens de erro
- Passos para reproduzir o bug
- Comportamento esperado
- **Logs**, se disponíveis, para problemas de backend, isso é realmente importante, você pode encontrá-los nos logs do docker-compose
- Capturas de tela ou vídeos, se aplicável
Como priorizamos:
| Tipo de Problema | Prioridade |
| ------------------------------------------------------------ | --------------- |
| Bugs em funções centrais (serviço em nuvem, não conseguir fazer login, aplicações não funcionando, falhas de segurança) | Crítica |
| Bugs não críticos, melhorias de desempenho | Prioridade Média |
| Correções menores (erros de digitação, interface confusa mas funcional) | Prioridade Baixa |
### Solicitações de recursos
> [!NOTE]
> Por favor, certifique-se de incluir as seguintes informações ao enviar uma solicitação de recurso:
- Um título claro e descritivo
- Uma descrição detalhada do recurso
- Um caso de uso para o recurso
- Qualquer outro contexto ou capturas de tela sobre a solicitação de recurso
Como priorizamos:
| Tipo de Recurso | Prioridade |
| ------------------------------------------------------------ | --------------- |
| Recursos de alta prioridade conforme rotulado por um membro da equipe | Prioridade Alta |
| Solicitações populares de recursos do nosso [quadro de feedback da comunidade](https://github.com/langgenius/dify/discussions/categories/feedbacks) | Prioridade Média |
| Recursos não essenciais e melhorias menores | Prioridade Baixa |
| Valiosos mas não imediatos | Recurso Futuro |
## Enviando seu PR
### Processo de Pull Request
1. Faça um fork do repositório
2. Antes de elaborar um PR, por favor crie um problema para discutir as mudanças que você quer fazer
3. Crie um novo branch para suas alterações
4. Por favor, adicione testes para suas alterações conforme apropriado
5. Certifique-se de que seu código passa nos testes existentes
6. Por favor, vincule o problema na descrição do PR, `fixes #<número_do_problema>`
7. Faça o merge do seu código!
### Configurando o projeto
#### Frontend
Para configurar o serviço frontend, por favor consulte nosso [guia abrangente](https://github.com/langgenius/dify/blob/main/web/README.md) no arquivo `web/README.md`. Este documento fornece instruções detalhadas para ajudá-lo a configurar o ambiente frontend adequadamente.
#### Backend
Para configurar o serviço backend, por favor consulte nossas [instruções detalhadas](https://github.com/langgenius/dify/blob/main/api/README.md) no arquivo `api/README.md`. Este documento contém um guia passo a passo para ajudá-lo a colocar o backend em funcionamento sem problemas.
#### Outras coisas a observar
Recomendamos revisar este documento cuidadosamente antes de prosseguir com a configuração, pois ele contém informações essenciais sobre:
- Pré-requisitos e dependências
- Etapas de instalação
- Detalhes de configuração
- Dicas comuns de solução de problemas
Sinta-se à vontade para entrar em contato se encontrar quaisquer problemas durante o processo de configuração.
## Obtendo Ajuda
Se você ficar preso ou tiver uma dúvida urgente enquanto contribui, simplesmente envie suas perguntas através do problema relacionado no GitHub, ou entre no nosso [Discord](https://discord.gg/8Tpq4AcN9c) para uma conversa rápida.
+93
View File
@@ -0,0 +1,93 @@
# KATKIDA BULUNMAK
Demek Dify'a katkıda bulunmak istiyorsunuz - bu harika, ne yapacağınızı görmek için sabırsızlanıyoruz. Sınırlı personel ve finansmana sahip bir startup olarak, LLM uygulamaları oluşturmak ve yönetmek için en sezgisel iş akışını tasarlama konusunda büyük hedeflerimiz var. Topluluktan gelen her türlü yardım gerçekten önemli.
Bulunduğumuz noktada çevik olmamız ve hızlı hareket etmemiz gerekiyor, ancak sizin gibi katkıda bulunanların mümkün olduğunca sorunsuz bir deneyim yaşamasını da sağlamak istiyoruz. Bu katkı rehberini bu amaçla hazırladık; sizi kod tabanıyla ve katkıda bulunanlarla nasıl çalıştığımızla tanıştırmayı, böylece hızlıca eğlenceli kısma geçebilmenizi hedefliyoruz.
Bu rehber, Dify'ın kendisi gibi, sürekli gelişen bir çalışmadır. Bazen gerçek projenin gerisinde kalırsa anlayışınız için çok minnettarız ve gelişmemize yardımcı olacak her türlü geri bildirimi memnuniyetle karşılıyoruz.
Lisanslama konusunda, lütfen kısa [Lisans ve Katkıda Bulunan Anlaşmamızı](./LICENSE) okumak için bir dakikanızı ayırın. Topluluk ayrıca [davranış kurallarına](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md) da uyar.
## Başlamadan Önce
Üzerinde çalışacak bir şey mi arıyorsunuz? [İlk katkıda bulunanlar için iyi sorunlarımıza](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) göz atın ve başlamak için birini seçin!
Eklenecek harika bir yeni model runtime'ı veya aracınız mı var? [Eklenti depomuzda](https://github.com/langgenius/dify-plugins) bir PR açın ve ne yaptığınızı bize gösterin.
Mevcut bir model runtime'ını, aracı güncellemek veya bazı hataları düzeltmek mi istiyorsunuz? [Resmi eklenti depomuza](https://github.com/langgenius/dify-official-plugins) gidin ve sihrinizi gösterin!
Eğlenceye katılın, katkıda bulunun ve birlikte harika bir şeyler inşa edelim! 💡✨
PR açıklamasında mevcut bir sorunu bağlamayı veya yeni bir sorun açmayı unutmayın.
### Hata Raporları
> [!IMPORTANT]
> Lütfen bir hata raporu gönderirken aşağıdaki bilgileri dahil ettiğinizden emin olun:
- Net ve açıklayıcı bir başlık
- Hata mesajları dahil hatanın ayrıntılı bir açıklaması
- Hatayı tekrarlamak için adımlar
- Beklenen davranış
- Mümkünse **Loglar**, backend sorunları için, bu gerçekten önemlidir, bunları docker-compose loglarında bulabilirsiniz
- Uygunsa ekran görüntüleri veya videolar
Nasıl önceliklendiriyoruz:
| Sorun Türü | Öncelik |
| ------------------------------------------------------------ | --------------- |
| Temel işlevlerdeki hatalar (bulut hizmeti, giriş yapamama, çalışmayan uygulamalar, güvenlik açıkları) | Kritik |
| Kritik olmayan hatalar, performans artışları | Orta Öncelik |
| Küçük düzeltmeler (yazım hataları, kafa karıştırıcı ama çalışan UI) | Düşük Öncelik |
### Özellik İstekleri
> [!NOTE]
> Lütfen bir özellik isteği gönderirken aşağıdaki bilgileri dahil ettiğinizden emin olun:
- Net ve açıklayıcı bir başlık
- Özelliğin ayrıntılı bir açıklaması
- Özellik için bir kullanım durumu
- Özellik isteği hakkında diğer bağlamlar veya ekran görüntüleri
Nasıl önceliklendiriyoruz:
| Özellik Türü | Öncelik |
| ------------------------------------------------------------ | --------------- |
| Bir ekip üyesi tarafından etiketlenen Yüksek Öncelikli Özellikler | Yüksek Öncelik |
| [Topluluk geri bildirim panosundan](https://github.com/langgenius/dify/discussions/categories/feedbacks) popüler özellik istekleri | Orta Öncelik |
| Temel olmayan özellikler ve küçük geliştirmeler | Düşük Öncelik |
| Değerli ama acil olmayan | Gelecek-Özellik |
## PR'nizi Göndermek
### Pull Request Süreci
1. Depoyu fork edin
2. Bir PR taslağı oluşturmadan önce, yapmak istediğiniz değişiklikleri tartışmak için lütfen bir sorun oluşturun
3. Değişiklikleriniz için yeni bir dal oluşturun
4. Lütfen değişiklikleriniz için uygun testler ekleyin
5. Kodunuzun mevcut testleri geçtiğinden emin olun
6. Lütfen PR açıklamasında sorunu bağlayın, `fixes #<sorun_numarası>`
7. Kodunuzu birleştirin!
### Projeyi Kurma
#### Frontend
Frontend hizmetini kurmak için, lütfen `web/README.md` dosyasındaki kapsamlı [rehberimize](https://github.com/langgenius/dify/blob/main/web/README.md) bakın. Bu belge, frontend ortamını düzgün bir şekilde kurmanıza yardımcı olacak ayrıntılı talimatlar sağlar.
#### Backend
Backend hizmetini kurmak için, lütfen `api/README.md` dosyasındaki detaylı [talimatlarımıza](https://github.com/langgenius/dify/blob/main/api/README.md) bakın. Bu belge, backend'i sorunsuz bir şekilde çalıştırmanıza yardımcı olacak adım adım bir kılavuz içerir.
#### Dikkat Edilecek Diğer Şeyler
Kuruluma geçmeden önce bu belgeyi dikkatlice incelemenizi öneririz, çünkü şunlar hakkında temel bilgiler içerir:
- Ön koşullar ve bağımlılıklar
- Kurulum adımları
- Yapılandırma detayları
- Yaygın sorun giderme ipuçları
Kurulum süreci sırasında herhangi bir sorunla karşılaşırsanız bizimle iletişime geçmekten çekinmeyin.
## Yardım Almak
Katkıda bulunurken takılırsanız veya yanıcı bir sorunuz olursa, sorularınızı ilgili GitHub sorunu aracılığıyla bize gönderin veya hızlı bir sohbet için [Discord'umuza](https://discord.gg/8Tpq4AcN9c) katılın.
-2
View File
@@ -10,8 +10,6 @@ a. Multi-tenant service: Unless explicitly authorized by Dify in writing, you ma
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.
Please contact business@dify.ai by email to inquire about licensing matters.
2. As a contributor, you should agree that:
a. The producer can adjust the open-source agreement to be more strict or relaxed as deemed necessary.
+1 -1
View File
@@ -8,7 +8,7 @@
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Self-hosting</a> ·
<a href="https://docs.dify.ai">Documentation</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Enterprise inquiry</a>
<a href="https://dify.ai/pricing">Dify edition overview</a>
</p>
<p align="center">
+1 -1
View File
@@ -4,7 +4,7 @@
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">الاستضافة الذاتية</a> ·
<a href="https://docs.dify.ai">التوثيق</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">استفسار الشركات (للإنجليزية فقط)</a>
<a href="https://dify.ai/pricing">نظرة عامة على منتجات Dify</a>
</p>
<p align="center">
+1 -1
View File
@@ -8,7 +8,7 @@
<a href="https://cloud.dify.ai">ডিফাই ক্লাউড</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">সেল্ফ-হোস্টিং</a> ·
<a href="https://docs.dify.ai">ডকুমেন্টেশন</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">ব্যাবসায়িক অনুসন্ধান</a>
<a href="https://dify.ai/pricing">Dify পণ্যের রূপভেদ</a>
</p>
<p align="center">
+1 -3
View File
@@ -4,7 +4,7 @@
<a href="https://cloud.dify.ai">Dify 云服务</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">自托管</a> ·
<a href="https://docs.dify.ai">文档</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">(需用英文)常见问题解答 / 联系团队</a>
<a href="https://dify.ai/pricing">Dify 产品形态总览</a>
</div>
<p align="center">
@@ -254,8 +254,6 @@ docker compose up -d
- [Discord](https://discord.gg/FngNHpbcY7)。👉:分享您的应用程序并与社区交流。
- [X(Twitter)](https://twitter.com/dify_ai)。👉:分享您的应用程序并与社区交流。
- [商业许可](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry)。👉:有关商业用途许可 Dify.AI 的商业咨询。
- [微信]() 👉:扫描下方二维码,添加微信好友,备注 Dify,我们将邀请您加入 Dify 社区。
<img src="./images/wechat.png" alt="wechat" width="100"/>
## 安全问题
+1 -1
View File
@@ -8,7 +8,7 @@
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Selbstgehostetes</a> ·
<a href="https://docs.dify.ai">Dokumentation</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Anfrage an Unternehmen</a>
<a href="https://dify.ai/pricing">Überblick über die Dify-Produkte</a>
</p>
<p align="center">
+1 -1
View File
@@ -4,7 +4,7 @@
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Auto-alojamiento</a> ·
<a href="https://docs.dify.ai">Documentación</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Consultas empresariales (en inglés)</a>
<a href="https://dify.ai/pricing">Resumen de las ediciones de Dify</a>
</p>
<p align="center">
+1 -1
View File
@@ -4,7 +4,7 @@
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Auto-hébergement</a> ·
<a href="https://docs.dify.ai">Documentation</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Demande dentreprise (en anglais seulement)</a>
<a href="https://dify.ai/pricing">Présentation des différentes offres Dify</a>
</p>
<p align="center">
+1 -1
View File
@@ -4,7 +4,7 @@
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">セルフホスティング</a> ·
<a href="https://docs.dify.ai">ドキュメント</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">企業のお問い合わせ(英語のみ)</a>
<a href="https://dify.ai/pricing">Difyの各種エディションについて</a>
</p>
<p align="center">
+1 -1
View File
@@ -4,7 +4,7 @@
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Self-hosting</a> ·
<a href="https://docs.dify.ai">Documentation</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Commercial enquiries</a>
<a href="https://dify.ai/pricing">Dify product editions</a>
</p>
<p align="center">
+1 -1
View File
@@ -4,7 +4,7 @@
<a href="https://cloud.dify.ai">Dify 클라우드</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">셀프-호스팅</a> ·
<a href="https://docs.dify.ai">문서</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">기업 문의 (영어만 가능)</a>
<a href="https://dify.ai/pricing">Dify 제품 에디션 안내</a>
</p>
<p align="center">
+1 -1
View File
@@ -8,7 +8,7 @@
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Auto-hospedagem</a> ·
<a href="https://docs.dify.ai">Documentação</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Consultas empresariais</a>
<a href="https://dify.ai/pricing">Visão geral das edições do Dify</a>
</p>
<p align="center">
+1 -1
View File
@@ -8,7 +8,7 @@
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Samostojno gostovanje</a> ·
<a href="https://docs.dify.ai">Dokumentacija</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Povpraševanje za podjetja</a>
<a href="https://dify.ai/pricing">Pregled ponudb izdelkov Dify</a>
</p>
<p align="center">
+1 -1
View File
@@ -4,7 +4,7 @@
<a href="https://cloud.dify.ai">Dify Bulut</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Kendi Sunucunuzda Barındırma</a> ·
<a href="https://docs.dify.ai">Dokümantasyon</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Yalnızca İngilizce: Kurumsal Sorgulama</a>
<a href="https://dify.ai/pricing">Dify ürün seçeneklerine genel bakış</a>
</p>
<p align="center">
+1 -1
View File
@@ -8,7 +8,7 @@
<a href="https://cloud.dify.ai">Dify 雲端服務</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">自行託管</a> ·
<a href="https://docs.dify.ai">說明文件</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">企業諮詢</a>
<a href="https://dify.ai/pricing">產品方案概覽</a>
</p>
<p align="center">
+1 -1
View File
@@ -4,7 +4,7 @@
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Tự triển khai</a> ·
<a href="https://docs.dify.ai">Tài liệu</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Yêu cầu doanh nghiệp</a>
<a href="https://dify.ai/pricing">Tổng quan các lựa chọn sản phẩm Dify</a>
</p>
<p align="center">
+36 -4
View File
@@ -26,9 +26,6 @@ ACCESS_TOKEN_EXPIRE_MINUTES=60
# Refresh token expiration time in days
REFRESH_TOKEN_EXPIRE_DAYS=30
# celery configuration
CELERY_BROKER_URL=redis://:difyai123456@localhost:6379/1
# redis configuration
REDIS_HOST=localhost
REDIS_PORT=6379
@@ -50,6 +47,9 @@ REDIS_USE_CLUSTERS=false
REDIS_CLUSTERS=
REDIS_CLUSTERS_PASSWORD=
# celery configuration
CELERY_BROKER_URL=redis://:difyai123456@localhost:${REDIS_PORT}/1
# PostgreSQL database configuration
DB_USERNAME=postgres
DB_PASSWORD=difyai123456
@@ -137,7 +137,7 @@ WEB_API_CORS_ALLOW_ORIGINS=http://127.0.0.1:3000,*
CONSOLE_CORS_ALLOW_ORIGINS=http://127.0.0.1:3000,*
# Vector database configuration
# support: weaviate, qdrant, milvus, myscale, relyt, pgvecto_rs, pgvector, pgvector, chroma, opensearch, tidb_vector, couchbase, vikingdb, upstash, lindorm, oceanbase, opengauss
# support: weaviate, qdrant, milvus, myscale, relyt, pgvecto_rs, pgvector, pgvector, chroma, opensearch, tidb_vector, couchbase, vikingdb, upstash, lindorm, oceanbase, opengauss, tablestore
VECTOR_STORE=weaviate
# Weaviate configuration
@@ -165,6 +165,7 @@ MILVUS_URI=http://127.0.0.1:19530
MILVUS_TOKEN=
MILVUS_USER=root
MILVUS_PASSWORD=Milvus
MILVUS_ANALYZER_PARAMS=
# MyScale configuration
MYSCALE_HOST=127.0.0.1
@@ -189,6 +190,7 @@ TENCENT_VECTOR_DB_USERNAME=dify
TENCENT_VECTOR_DB_DATABASE=dify
TENCENT_VECTOR_DB_SHARD=1
TENCENT_VECTOR_DB_REPLICAS=2
TENCENT_VECTOR_DB_ENABLE_HYBRID_SEARCH=false
# ElasticSearch configuration
ELASTICSEARCH_HOST=127.0.0.1
@@ -212,6 +214,12 @@ PGVECTOR_DATABASE=postgres
PGVECTOR_MIN_CONNECTION=1
PGVECTOR_MAX_CONNECTION=5
# TableStore Vector configuration
TABLESTORE_ENDPOINT=https://instance-name.cn-hangzhou.ots.aliyuncs.com
TABLESTORE_INSTANCE_NAME=instance-name
TABLESTORE_ACCESS_KEY_ID=xxx
TABLESTORE_ACCESS_KEY_SECRET=xxx
# Tidb Vector configuration
TIDB_VECTOR_HOST=xxx.eu-central-1.xxx.aws.tidbcloud.com
TIDB_VECTOR_PORT=4000
@@ -297,6 +305,7 @@ OCEANBASE_VECTOR_USER=root@test
OCEANBASE_VECTOR_PASSWORD=difyai123456
OCEANBASE_VECTOR_DATABASE=test
OCEANBASE_MEMORY_LIMIT=6G
OCEANBASE_ENABLE_HYBRID_SEARCH=false
# openGauss configuration
OPENGAUSS_HOST=127.0.0.1
@@ -318,6 +327,7 @@ UPLOAD_AUDIO_FILE_SIZE_LIMIT=50
MULTIMODAL_SEND_FORMAT=base64
PROMPT_GENERATION_MAX_TOKENS=512
CODE_GENERATION_MAX_TOKENS=1024
PLUGIN_BASED_TOKEN_COUNTING_ENABLED=false
# Mail configuration, support: resend, smtp
MAIL_TYPE=
@@ -414,6 +424,12 @@ WORKFLOW_CALL_MAX_DEPTH=5
WORKFLOW_PARALLEL_DEPTH_LIMIT=3
MAX_VARIABLE_SIZE=204800
# Workflow storage configuration
# Options: rdbms, hybrid
# rdbms: Use only the relational database (default)
# hybrid: Save new data to object storage, read from both object storage and RDBMS
WORKFLOW_NODE_EXECUTION_STORAGE=rdbms
# App configuration
APP_MAX_EXECUTION_TIME=1200
APP_MAX_ACTIVE_REQUESTS=0
@@ -454,3 +470,19 @@ CREATE_TIDB_SERVICE_JOB_ENABLED=false
MAX_SUBMIT_COUNT=100
# Lockout duration in seconds
LOGIN_LOCKOUT_DURATION=86400
# Enable OpenTelemetry
ENABLE_OTEL=false
OTLP_BASE_ENDPOINT=http://localhost:4318
OTLP_API_KEY=
OTEL_EXPORTER_TYPE=otlp
OTEL_SAMPLING_RATE=0.1
OTEL_BATCH_EXPORT_SCHEDULE_DELAY=5000
OTEL_MAX_QUEUE_SIZE=2048
OTEL_MAX_EXPORT_BATCH_SIZE=512
OTEL_METRIC_EXPORT_INTERVAL=60000
OTEL_BATCH_EXPORT_TIMEOUT=10000
OTEL_METRIC_EXPORT_TIMEOUT=30000
# Prevent Clickjacking
ALLOW_EMBED=false
+5 -14
View File
@@ -3,20 +3,11 @@ FROM python:3.12-slim-bookworm AS base
WORKDIR /app/api
# Install Poetry
ENV POETRY_VERSION=2.0.1
# Install uv
ENV UV_VERSION=0.6.14
# 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/
RUN pip install --no-cache-dir uv==${UV_VERSION}
RUN pip install --no-cache-dir poetry==${POETRY_VERSION}
# Configure Poetry
ENV POETRY_CACHE_DIR=/tmp/poetry_cache
ENV POETRY_NO_INTERACTION=1
ENV POETRY_VIRTUALENVS_IN_PROJECT=true
ENV POETRY_VIRTUALENVS_CREATE=true
ENV POETRY_REQUESTS_TIMEOUT=15
FROM base AS packages
@@ -27,8 +18,8 @@ RUN apt-get update \
&& apt-get install -y --no-install-recommends gcc g++ libc-dev libffi-dev libgmp-dev libmpfr-dev libmpc-dev
# Install Python dependencies
COPY pyproject.toml poetry.lock ./
RUN poetry install --sync --no-cache --no-root
COPY pyproject.toml uv.lock ./
RUN uv sync --locked
# production stage
FROM base AS production
+16 -13
View File
@@ -3,7 +3,10 @@
## Usage
> [!IMPORTANT]
> In the v0.6.12 release, we deprecated `pip` as the package management tool for Dify API Backend service and replaced it with `poetry`.
>
> In the v1.3.0 release, `poetry` has been replaced with
> [`uv`](https://docs.astral.sh/uv/) as the package manager
> for Dify API backend service.
1. Start the docker-compose stack
@@ -37,19 +40,19 @@
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]
Dify API service uses [UV](https://docs.astral.sh/uv/) to manage dependencies.
First, you need to add the uv package manager, if you don't have it already.
```bash
poetry self add poetry-plugin-shell
pip install uv
# Or on macOS
brew install uv
```
Then, You can execute `poetry shell` to activate the environment.
5. Install dependencies
```bash
poetry env use 3.12
poetry install
uv sync --dev
```
6. Run migrate
@@ -57,21 +60,21 @@
Before the first launch, migrate the database to the latest version.
```bash
poetry run python -m flask db upgrade
uv run flask db upgrade
```
7. Start backend
```bash
poetry run python -m flask run --host 0.0.0.0 --port=5001 --debug
uv run flask run --host 0.0.0.0 --port=5001 --debug
```
8. Start Dify [web](../web) service.
9. Setup your application by visiting `http://localhost:3000`...
9. Setup your application by visiting `http://localhost:3000`.
10. If you need to handle and debug the async tasks (e.g. dataset importing and documents indexing), please start the worker service.
```bash
poetry run python -m celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion
uv run celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion
```
## Testing
@@ -79,11 +82,11 @@
1. Install dependencies for both the backend and the test environment
```bash
poetry install -C api --with dev
uv sync --dev
```
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
uv run -P api bash dev/pytest/pytest_all_tests.sh
```
+6
View File
@@ -51,8 +51,11 @@ def initialize_extensions(app: DifyApp):
ext_login,
ext_mail,
ext_migrate,
ext_otel,
ext_otel_patch,
ext_proxy_fix,
ext_redis,
ext_repositories,
ext_sentry,
ext_set_secretkey,
ext_storage,
@@ -73,6 +76,7 @@ def initialize_extensions(app: DifyApp):
ext_migrate,
ext_redis,
ext_storage,
ext_repositories,
ext_celery,
ext_login,
ext_mail,
@@ -81,6 +85,8 @@ def initialize_extensions(app: DifyApp):
ext_proxy_fix,
ext_blueprints,
ext_commands,
ext_otel_patch, # Apply patch before initializing OpenTelemetry
ext_otel,
]
for ext in extensions:
short_name = ext.__name__.split(".")[-1]
+22
View File
@@ -26,6 +26,7 @@ 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.clear_free_plan_tenant_expired_logs import ClearFreePlanTenantExpiredLogs
from services.plugin.data_migration import PluginDataMigration
from services.plugin.plugin_migration import PluginMigration
@@ -275,6 +276,7 @@ def migrate_knowledge_vector_database():
VectorType.ORACLE,
VectorType.ELASTICSEARCH,
VectorType.OPENGAUSS,
VectorType.TABLESTORE,
}
lower_collection_vector_types = {
VectorType.ANALYTICDB,
@@ -792,3 +794,23 @@ def install_plugins(input_file: str, output_file: str, workers: int):
PluginMigration.install_plugins(input_file, output_file, workers)
click.echo(click.style("Install plugins completed.", fg="green"))
@click.command("clear-free-plan-tenant-expired-logs", help="Clear free plan tenant expired logs.")
@click.option("--days", prompt=True, help="The days to clear free plan tenant expired logs.", default=30)
@click.option("--batch", prompt=True, help="The batch size to clear free plan tenant expired logs.", default=100)
@click.option(
"--tenant_ids",
prompt=True,
multiple=True,
help="The tenant ids to clear free plan tenant expired logs.",
)
def clear_free_plan_tenant_expired_logs(days: int, batch: int, tenant_ids: list[str]):
"""
Clear free plan tenant expired logs.
"""
click.echo(click.style("Starting clear free plan tenant expired logs.", fg="white"))
ClearFreePlanTenantExpiredLogs.process(days, batch, tenant_ids)
click.echo(click.style("Clear free plan tenant expired logs completed.", fg="green"))
+6
View File
@@ -9,9 +9,11 @@ from .enterprise import EnterpriseFeatureConfig
from .extra import ExtraServiceConfig
from .feature import FeatureConfig
from .middleware import MiddlewareConfig
from .observability import ObservabilityConfig
from .packaging import PackagingInfo
from .remote_settings_sources import RemoteSettingsSource, RemoteSettingsSourceConfig, RemoteSettingsSourceName
from .remote_settings_sources.apollo import ApolloSettingsSource
from .remote_settings_sources.nacos import NacosSettingsSource
logger = logging.getLogger(__name__)
@@ -33,6 +35,8 @@ class RemoteSettingsSourceFactory(PydanticBaseSettingsSource):
match remote_source_name:
case RemoteSettingsSourceName.APOLLO:
remote_source = ApolloSettingsSource(current_state)
case RemoteSettingsSourceName.NACOS:
remote_source = NacosSettingsSource(current_state)
case _:
logger.warning(f"Unsupported remote source: {remote_source_name}")
return {}
@@ -59,6 +63,8 @@ class DifyConfig(
MiddlewareConfig,
# Extra service configs
ExtraServiceConfig,
# Observability configs
ObservabilityConfig,
# Remote source configs
RemoteSettingsSourceConfig,
# Enterprise feature configs
+17 -2
View File
@@ -12,7 +12,7 @@ from pydantic import (
)
from pydantic_settings import BaseSettings
from configs.feature.hosted_service import HostedServiceConfig
from .hosted_service import HostedServiceConfig
class SecurityConfig(BaseSettings):
@@ -442,7 +442,7 @@ class LoggingConfig(BaseSettings):
class ModelLoadBalanceConfig(BaseSettings):
"""
Configuration for model load balancing
Configuration for model load balancing and token counting
"""
MODEL_LB_ENABLED: bool = Field(
@@ -450,6 +450,11 @@ class ModelLoadBalanceConfig(BaseSettings):
default=False,
)
PLUGIN_BASED_TOKEN_COUNTING_ENABLED: bool = Field(
description="Enable or disable plugin based token counting. If disabled, token counting will return 0.",
default=False,
)
class BillingConfig(BaseSettings):
"""
@@ -514,6 +519,11 @@ class WorkflowNodeExecutionConfig(BaseSettings):
default=100,
)
WORKFLOW_NODE_EXECUTION_STORAGE: str = Field(
default="rdbms",
description="Storage backend for WorkflowNodeExecution. Options: 'rdbms', 'hybrid'",
)
class AuthConfig(BaseSettings):
"""
@@ -848,6 +858,11 @@ class AccountConfig(BaseSettings):
default=5,
)
EDUCATION_ENABLED: bool = Field(
description="whether to enable education identity",
default=False,
)
class FeatureConfig(
# place the configs in alphabet order
@@ -1,6 +1,6 @@
from typing import Optional
from pydantic import Field, NonNegativeInt, computed_field
from pydantic import Field, NonNegativeInt
from pydantic_settings import BaseSettings
+4
View File
@@ -22,6 +22,7 @@ from .vdb.baidu_vector_config import BaiduVectorDBConfig
from .vdb.chroma_config import ChromaConfig
from .vdb.couchbase_config import CouchbaseConfig
from .vdb.elasticsearch_config import ElasticsearchConfig
from .vdb.huawei_cloud_config import HuaweiCloudConfig
from .vdb.lindorm_config import LindormConfig
from .vdb.milvus_config import MilvusConfig
from .vdb.myscale_config import MyScaleConfig
@@ -33,6 +34,7 @@ from .vdb.pgvector_config import PGVectorConfig
from .vdb.pgvectors_config import PGVectoRSConfig
from .vdb.qdrant_config import QdrantConfig
from .vdb.relyt_config import RelytConfig
from .vdb.tablestore_config import TableStoreConfig
from .vdb.tencent_vector_config import TencentVectorDBConfig
from .vdb.tidb_on_qdrant_config import TidbOnQdrantConfig
from .vdb.tidb_vector_config import TiDBVectorConfig
@@ -262,6 +264,7 @@ class MiddlewareConfig(
VectorStoreConfig,
AnalyticdbConfig,
ChromaConfig,
HuaweiCloudConfig,
MilvusConfig,
MyScaleConfig,
OpenSearchConfig,
@@ -283,5 +286,6 @@ class MiddlewareConfig(
OceanBaseVectorConfig,
BaiduVectorDBConfig,
OpenGaussConfig,
TableStoreConfig,
):
pass
@@ -0,0 +1,25 @@
from typing import Optional
from pydantic import Field
from pydantic_settings import BaseSettings
class HuaweiCloudConfig(BaseSettings):
"""
Configuration settings for Huawei cloud search service
"""
HUAWEI_CLOUD_HOSTS: Optional[str] = Field(
description="Hostname or IP address of the Huawei cloud search service instance",
default=None,
)
HUAWEI_CLOUD_USER: Optional[str] = Field(
description="Username for authenticating with Huawei cloud search service",
default=None,
)
HUAWEI_CLOUD_PASSWORD: Optional[str] = Field(
description="Password for authenticating with Huawei cloud search service",
default=None,
)
@@ -39,3 +39,8 @@ class MilvusConfig(BaseSettings):
"older versions",
default=True,
)
MILVUS_ANALYZER_PARAMS: Optional[str] = Field(
description='Milvus text analyzer parameters, e.g., {"type": "chinese"} for Chinese segmentation support.',
default=None,
)
@@ -33,3 +33,9 @@ class OceanBaseVectorConfig(BaseSettings):
description="Name of the OceanBase Vector database to connect to",
default=None,
)
OCEANBASE_ENABLE_HYBRID_SEARCH: bool = Field(
description="Enable hybrid search features (requires OceanBase >= 4.3.5.1). Set to false for compatibility "
"with older versions",
default=False,
)
@@ -43,3 +43,8 @@ class OpenGaussConfig(BaseSettings):
description="Max connection of the OpenGauss database",
default=5,
)
OPENGAUSS_ENABLE_PQ: bool = Field(
description="Enable openGauss PQ acceleration feature",
default=False,
)
@@ -0,0 +1,30 @@
from typing import Optional
from pydantic import Field
from pydantic_settings import BaseSettings
class TableStoreConfig(BaseSettings):
"""
Configuration settings for TableStore.
"""
TABLESTORE_ENDPOINT: Optional[str] = Field(
description="Endpoint address of the TableStore server (e.g. 'https://instance-name.cn-hangzhou.ots.aliyuncs.com')",
default=None,
)
TABLESTORE_INSTANCE_NAME: Optional[str] = Field(
description="Instance name to access TableStore server (eg. 'instance-name')",
default=None,
)
TABLESTORE_ACCESS_KEY_ID: Optional[str] = Field(
description="AccessKey id for the instance name",
default=None,
)
TABLESTORE_ACCESS_KEY_SECRET: Optional[str] = Field(
description="AccessKey secret for the instance name",
default=None,
)
@@ -48,3 +48,8 @@ class TencentVectorDBConfig(BaseSettings):
description="Name of the specific Tencent Vector Database to connect to",
default=None,
)
TENCENT_VECTOR_DB_ENABLE_HYBRID_SEARCH: bool = Field(
description="Enable hybrid search features",
default=False,
)
+9
View File
@@ -0,0 +1,9 @@
from configs.observability.otel.otel_config import OTelConfig
class ObservabilityConfig(OTelConfig):
"""
Observability configuration settings
"""
pass
@@ -0,0 +1,44 @@
from pydantic import Field
from pydantic_settings import BaseSettings
class OTelConfig(BaseSettings):
"""
OpenTelemetry configuration settings
"""
ENABLE_OTEL: bool = Field(
description="Whether to enable OpenTelemetry",
default=False,
)
OTLP_BASE_ENDPOINT: str = Field(
description="OTLP base endpoint",
default="http://localhost:4318",
)
OTLP_API_KEY: str = Field(
description="OTLP API key",
default="",
)
OTEL_EXPORTER_TYPE: str = Field(
description="OTEL exporter type",
default="otlp",
)
OTEL_SAMPLING_RATE: float = Field(default=0.1, description="Sampling rate for traces (0.0 to 1.0)")
OTEL_BATCH_EXPORT_SCHEDULE_DELAY: int = Field(
default=5000, description="Batch export schedule delay in milliseconds"
)
OTEL_MAX_QUEUE_SIZE: int = Field(default=2048, description="Maximum queue size for the batch span processor")
OTEL_MAX_EXPORT_BATCH_SIZE: int = Field(default=512, description="Maximum export batch size")
OTEL_METRIC_EXPORT_INTERVAL: int = Field(default=60000, description="Metric export interval in milliseconds")
OTEL_BATCH_EXPORT_TIMEOUT: int = Field(default=10000, description="Batch export timeout in milliseconds")
OTEL_METRIC_EXPORT_TIMEOUT: int = Field(default=30000, description="Metric export timeout in milliseconds")
+1 -1
View File
@@ -9,7 +9,7 @@ class PackagingInfo(BaseSettings):
CURRENT_VERSION: str = Field(
description="Dify version",
default="1.1.2",
default="1.3.0",
)
COMMIT_SHA: str = Field(
@@ -1,5 +1,3 @@
from typing import Optional
from pydantic import Field
from .apollo import ApolloSettingsSourceInfo
@@ -270,7 +270,7 @@ class ApolloClient:
while not self._stopping:
for namespace in self._notification_map:
self._do_heart_beat(namespace)
time.sleep(60 * 10) # 10分钟
time.sleep(60 * 10) # 10 minutes
def _do_heart_beat(self, namespace):
url = "{}/configs/{}/{}/{}?ip={}".format(self.config_url, self.app_id, self.cluster, namespace, self.ip)
@@ -3,3 +3,4 @@ from enum import StrEnum
class RemoteSettingsSourceName(StrEnum):
APOLLO = "apollo"
NACOS = "nacos"
@@ -0,0 +1,52 @@
import logging
import os
from collections.abc import Mapping
from typing import Any
from pydantic.fields import FieldInfo
from .http_request import NacosHttpClient
logger = logging.getLogger(__name__)
from configs.remote_settings_sources.base import RemoteSettingsSource
from .utils import _parse_config
class NacosSettingsSource(RemoteSettingsSource):
def __init__(self, configs: Mapping[str, Any]):
self.configs = configs
self.remote_configs: dict[str, Any] = {}
self.async_init()
def async_init(self):
data_id = os.getenv("DIFY_ENV_NACOS_DATA_ID", "dify-api-env.properties")
group = os.getenv("DIFY_ENV_NACOS_GROUP", "nacos-dify")
tenant = os.getenv("DIFY_ENV_NACOS_NAMESPACE", "")
params = {"dataId": data_id, "group": group, "tenant": tenant}
try:
content = NacosHttpClient().http_request("/nacos/v1/cs/configs", method="GET", headers={}, params=params)
self.remote_configs = self._parse_config(content)
except Exception as e:
logger.exception("[get-access-token] exception occurred")
raise
def _parse_config(self, content: str) -> dict:
if not content:
return {}
try:
return _parse_config(self, content)
except Exception as e:
raise RuntimeError(f"Failed to parse config: {e}")
def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
if not isinstance(self.remote_configs, dict):
raise ValueError(f"remote configs is not dict, but {type(self.remote_configs)}")
field_value = self.remote_configs.get(field_name)
if field_value is None:
return None, field_name, False
return field_value, field_name, False
@@ -0,0 +1,83 @@
import base64
import hashlib
import hmac
import logging
import os
import time
import requests
logger = logging.getLogger(__name__)
class NacosHttpClient:
def __init__(self):
self.username = os.getenv("DIFY_ENV_NACOS_USERNAME")
self.password = os.getenv("DIFY_ENV_NACOS_PASSWORD")
self.ak = os.getenv("DIFY_ENV_NACOS_ACCESS_KEY")
self.sk = os.getenv("DIFY_ENV_NACOS_SECRET_KEY")
self.server = os.getenv("DIFY_ENV_NACOS_SERVER_ADDR", "localhost:8848")
self.token = None
self.token_ttl = 18000
self.token_expire_time: float = 0
def http_request(self, url, method="GET", headers=None, params=None):
try:
self._inject_auth_info(headers, params)
response = requests.request(method, url="http://" + self.server + url, headers=headers, params=params)
response.raise_for_status()
return response.text
except requests.exceptions.RequestException as e:
return f"Request to Nacos failed: {e}"
def _inject_auth_info(self, headers, params, module="config"):
headers.update({"User-Agent": "Nacos-Http-Client-In-Dify:v0.0.1"})
if module == "login":
return
ts = str(int(time.time() * 1000))
if self.ak and self.sk:
sign_str = self.get_sign_str(params["group"], params["tenant"], ts)
headers["Spas-AccessKey"] = self.ak
headers["Spas-Signature"] = self.__do_sign(sign_str, self.sk)
headers["timeStamp"] = ts
if self.username and self.password:
self.get_access_token(force_refresh=False)
params["accessToken"] = self.token
def __do_sign(self, sign_str, sk):
return (
base64.encodebytes(hmac.new(sk.encode(), sign_str.encode(), digestmod=hashlib.sha1).digest())
.decode()
.strip()
)
def get_sign_str(self, group, tenant, ts):
sign_str = ""
if tenant:
sign_str = tenant + "+"
if group:
sign_str = sign_str + group + "+"
if sign_str:
sign_str += ts
return sign_str
def get_access_token(self, force_refresh=False):
current_time = time.time()
if self.token and not force_refresh and self.token_expire_time > current_time:
return self.token
params = {"username": self.username, "password": self.password}
url = "http://" + self.server + "/nacos/v1/auth/login"
try:
resp = requests.request("POST", url, headers=None, params=params)
resp.raise_for_status()
response_data = resp.json()
self.token = response_data.get("accessToken")
self.token_ttl = response_data.get("tokenTtl", 18000)
self.token_expire_time = current_time + self.token_ttl - 10
except Exception as e:
logger.exception("[get-access-token] exception occur")
raise
@@ -0,0 +1,31 @@
def _parse_config(self, content: str) -> dict[str, str]:
config: dict[str, str] = {}
if not content:
return config
for line in content.splitlines():
cleaned_line = line.strip()
if not cleaned_line or cleaned_line.startswith(("#", "!")):
continue
separator_index = -1
for i, c in enumerate(cleaned_line):
if c in ("=", ":") and (i == 0 or cleaned_line[i - 1] != "\\"):
separator_index = i
break
if separator_index == -1:
continue
key = cleaned_line[:separator_index].strip()
raw_value = cleaned_line[separator_index + 1 :].strip()
try:
decoded_value = bytes(raw_value, "utf-8").decode("unicode_escape")
decoded_value = decoded_value.replace(r"\=", "=").replace(r"\:", ":")
except UnicodeDecodeError:
decoded_value = raw_value
config[key] = decoded_value
return config
+2
View File
@@ -3,6 +3,8 @@ from configs import dify_config
HIDDEN_VALUE = "[__HIDDEN__]"
UUID_NIL = "00000000-0000-0000-0000-000000000000"
DEFAULT_FILE_NUMBER_LIMITS = 3
IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "webp", "gif", "svg"]
IMAGE_EXTENSIONS.extend([ext.upper() for ext in IMAGE_EXTENSIONS])
-39
View File
@@ -4,8 +4,6 @@ import platform
import re
import urllib.parse
import warnings
from collections.abc import Mapping
from typing import Any
from uuid import uuid4
import httpx
@@ -29,8 +27,6 @@ except ImportError:
from pydantic import BaseModel
from configs import dify_config
class FileInfo(BaseModel):
filename: str
@@ -87,38 +83,3 @@ def guess_file_info_from_response(response: httpx.Response):
mimetype=mimetype,
size=int(response.headers.get("Content-Length", -1)),
)
def get_parameters_from_feature_dict(*, features_dict: Mapping[str, Any], user_input_form: list[dict[str, Any]]):
return {
"opening_statement": features_dict.get("opening_statement"),
"suggested_questions": features_dict.get("suggested_questions", []),
"suggested_questions_after_answer": features_dict.get("suggested_questions_after_answer", {"enabled": False}),
"speech_to_text": features_dict.get("speech_to_text", {"enabled": False}),
"text_to_speech": features_dict.get("text_to_speech", {"enabled": False}),
"retriever_resource": features_dict.get("retriever_resource", {"enabled": False}),
"annotation_reply": features_dict.get("annotation_reply", {"enabled": False}),
"more_like_this": features_dict.get("more_like_this", {"enabled": False}),
"user_input_form": user_input_form,
"sensitive_word_avoidance": features_dict.get(
"sensitive_word_avoidance", {"enabled": False, "type": "", "configs": []}
),
"file_upload": features_dict.get(
"file_upload",
{
"image": {
"enabled": False,
"number_limits": 3,
"detail": "high",
"transfer_methods": ["remote_url", "local_file"],
}
},
),
"system_parameters": {
"image_file_size_limit": dify_config.UPLOAD_IMAGE_FILE_SIZE_LIMIT,
"video_file_size_limit": dify_config.UPLOAD_VIDEO_FILE_SIZE_LIMIT,
"audio_file_size_limit": dify_config.UPLOAD_AUDIO_FILE_SIZE_LIMIT,
"file_size_limit": dify_config.UPLOAD_FILE_SIZE_LIMIT,
"workflow_file_upload_limit": dify_config.WORKFLOW_FILE_UPLOAD_LIMIT,
},
}
+2 -2
View File
@@ -89,7 +89,7 @@ class AnnotationReplyActionStatusApi(Resource):
app_annotation_job_key = "{}_app_annotation_job_{}".format(action, str(job_id))
cache_result = redis_client.get(app_annotation_job_key)
if cache_result is None:
raise ValueError("The job is not exist.")
raise ValueError("The job does not exist.")
job_status = cache_result.decode()
error_msg = ""
@@ -226,7 +226,7 @@ class AnnotationBatchImportStatusApi(Resource):
indexing_cache_key = "app_annotation_batch_import_{}".format(str(job_id))
cache_result = redis_client.get(indexing_cache_key)
if cache_result is None:
raise ValueError("The job is not exist.")
raise ValueError("The job does not exist.")
job_status = cache_result.decode()
error_msg = ""
if job_status == "error":
+9 -2
View File
@@ -50,7 +50,15 @@ class AppListApi(Resource):
parser.add_argument(
"mode",
type=str,
choices=["chat", "workflow", "agent-chat", "channel", "all"],
choices=[
"completion",
"chat",
"advanced-chat",
"workflow",
"agent-chat",
"channel",
"all",
],
default="all",
location="args",
required=False,
@@ -130,7 +138,6 @@ class AppApi(Resource):
parser.add_argument("icon_type", type=str, location="json")
parser.add_argument("icon", type=str, location="json")
parser.add_argument("icon_background", type=str, location="json")
parser.add_argument("max_active_requests", type=int, location="json")
parser.add_argument("use_icon_as_answer_icon", type=bool, location="json")
args = parser.parse_args()
@@ -8,6 +8,7 @@ from werkzeug.exceptions import Forbidden
from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import (
account_initialization_required,
cloud_edition_billing_resource_check,
setup_required,
)
from extensions.ext_database import db
@@ -23,6 +24,7 @@ class AppImportApi(Resource):
@login_required
@account_initialization_required
@marshal_with(app_import_fields)
@cloud_edition_billing_resource_check("apps")
def post(self):
# Check user role first
if not current_user.is_editor:
-2
View File
@@ -80,8 +80,6 @@ class ChatMessageTextApi(Resource):
@account_initialization_required
@get_app_model
def post(self, app_model: App):
from werkzeug.exceptions import InternalServerError
try:
parser = reqparse.RequestParser()
parser.add_argument("message_id", type=str, location="json")
+30
View File
@@ -85,5 +85,35 @@ class RuleCodeGenerateApi(Resource):
return code_result
class RuleStructuredOutputGenerateApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("instruction", type=str, required=True, nullable=False, location="json")
parser.add_argument("model_config", type=dict, required=True, nullable=False, location="json")
args = parser.parse_args()
account = current_user
try:
structured_output = LLMGenerator.generate_structured_output(
tenant_id=account.current_tenant_id,
instruction=args["instruction"],
model_config=args["model_config"],
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
except QuotaExceededError:
raise ProviderQuotaExceededError()
except ModelCurrentlyNotSupportError:
raise ProviderModelCurrentlyNotSupportError()
except InvokeError as e:
raise CompletionRequestError(e.description)
return structured_output
api.add_resource(RuleGenerateApi, "/rule-generate")
api.add_resource(RuleCodeGenerateApi, "/rule-code-generate")
api.add_resource(RuleStructuredOutputGenerateApi, "/rule-structured-output-generate")
@@ -1,5 +1,4 @@
from datetime import datetime
from dateutil.parser import isoparse
from flask_restful import Resource, marshal_with, reqparse # type: ignore
from flask_restful.inputs import int_range # type: ignore
from sqlalchemy.orm import Session
@@ -41,10 +40,10 @@ class WorkflowAppLogApi(Resource):
args.status = WorkflowRunStatus(args.status) if args.status else None
if args.created_at__before:
args.created_at__before = datetime.fromisoformat(args.created_at__before.replace("Z", "+00:00"))
args.created_at__before = isoparse(args.created_at__before)
if args.created_at__after:
args.created_at__after = datetime.fromisoformat(args.created_at__after.replace("Z", "+00:00"))
args.created_at__after = isoparse(args.created_at__after)
# get paginate workflow app logs
workflow_app_service = WorkflowAppService()
@@ -74,7 +74,9 @@ class OAuthDataSourceBinding(Resource):
if not oauth_provider:
return {"error": "Invalid provider"}, 400
if "code" in request.args:
code = request.args.get("code")
code = request.args.get("code", "")
if not code:
return {"error": "Invalid code"}, 400
try:
oauth_provider.get_access_token(code)
except requests.exceptions.HTTPError as e:
+62 -37
View File
@@ -16,7 +16,7 @@ from controllers.console.auth.error import (
PasswordMismatchError,
)
from controllers.console.error import AccountInFreezeError, AccountNotFound, EmailSendIpLimitError
from controllers.console.wraps import setup_required
from controllers.console.wraps import email_password_login_enabled, setup_required
from events.tenant_event import tenant_was_created
from extensions.ext_database import db
from libs.helper import email, extract_remote_ip
@@ -30,6 +30,7 @@ from services.feature_service import FeatureService
class ForgotPasswordSendEmailApi(Resource):
@setup_required
@email_password_login_enabled
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("email", type=email, required=True, location="json")
@@ -62,6 +63,7 @@ class ForgotPasswordSendEmailApi(Resource):
class ForgotPasswordCheckApi(Resource):
@setup_required
@email_password_login_enabled
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("email", type=str, required=True, location="json")
@@ -86,12 +88,21 @@ class ForgotPasswordCheckApi(Resource):
AccountService.add_forgot_password_error_rate_limit(args["email"])
raise EmailCodeError()
# Verified, revoke the first token
AccountService.revoke_reset_password_token(args["token"])
# Refresh token data by generating a new token
_, new_token = AccountService.generate_reset_password_token(
user_email, code=args["code"], additional_data={"phase": "reset"}
)
AccountService.reset_forgot_password_error_rate_limit(args["email"])
return {"is_valid": True, "email": token_data.get("email")}
return {"is_valid": True, "email": token_data.get("email"), "token": new_token}
class ForgotPasswordResetApi(Resource):
@setup_required
@email_password_login_enabled
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("token", type=str, required=True, nullable=False, location="json")
@@ -99,53 +110,67 @@ class ForgotPasswordResetApi(Resource):
parser.add_argument("password_confirm", type=valid_password, required=True, nullable=False, location="json")
args = parser.parse_args()
new_password = args["new_password"]
password_confirm = args["password_confirm"]
if str(new_password).strip() != str(password_confirm).strip():
# Validate passwords match
if args["new_password"] != args["password_confirm"]:
raise PasswordMismatchError()
token = args["token"]
reset_data = AccountService.get_reset_password_data(token)
if reset_data is None:
# Validate token and get reset data
reset_data = AccountService.get_reset_password_data(args["token"])
if not reset_data:
raise InvalidTokenError()
# Must use token in reset phase
if reset_data.get("phase", "") != "reset":
raise InvalidTokenError()
AccountService.revoke_reset_password_token(token)
# Revoke token to prevent reuse
AccountService.revoke_reset_password_token(args["token"])
# Generate secure salt and hash password
salt = secrets.token_bytes(16)
base64_salt = base64.b64encode(salt).decode()
password_hashed = hash_password(args["new_password"], salt)
password_hashed = hash_password(new_password, salt)
base64_password_hashed = base64.b64encode(password_hashed).decode()
email = reset_data.get("email", "")
with Session(db.engine) as session:
account = session.execute(select(Account).filter_by(email=reset_data.get("email"))).scalar_one_or_none()
if account:
account.password = base64_password_hashed
account.password_salt = base64_salt
db.session.commit()
tenant = TenantService.get_join_tenants(account)
if not tenant and not FeatureService.get_system_features().is_allow_create_workspace:
tenant = TenantService.create_tenant(f"{account.name}'s Workspace")
TenantService.create_tenant_member(tenant, account, role="owner")
account.current_tenant = tenant
tenant_was_created.send(tenant)
else:
try:
account = AccountService.create_account_and_tenant(
email=reset_data.get("email", ""),
name=reset_data.get("email", ""),
password=password_confirm,
interface_language=languages[0],
)
except WorkSpaceNotAllowedCreateError:
pass
except AccountRegisterError:
raise AccountInFreezeError()
account = session.execute(select(Account).filter_by(email=email)).scalar_one_or_none()
if account:
self._update_existing_account(account, password_hashed, salt, session)
else:
self._create_new_account(email, args["password_confirm"])
return {"result": "success"}
def _update_existing_account(self, account, password_hashed, salt, session):
# Update existing account credentials
account.password = base64.b64encode(password_hashed).decode()
account.password_salt = base64.b64encode(salt).decode()
session.commit()
# Create workspace if needed
if (
not TenantService.get_join_tenants(account)
and FeatureService.get_system_features().is_allow_create_workspace
):
tenant = TenantService.create_tenant(f"{account.name}'s Workspace")
TenantService.create_tenant_member(tenant, account, role="owner")
account.current_tenant = tenant
tenant_was_created.send(tenant)
def _create_new_account(self, email, password):
# Create new account if allowed
try:
AccountService.create_account_and_tenant(
email=email,
name=email,
password=password,
interface_language=languages[0],
)
except WorkSpaceNotAllowedCreateError:
pass
except AccountRegisterError:
raise AccountInFreezeError()
api.add_resource(ForgotPasswordSendEmailApi, "/forgot-password")
api.add_resource(ForgotPasswordCheckApi, "/forgot-password/validity")
+3 -1
View File
@@ -22,7 +22,7 @@ from controllers.console.error import (
EmailSendIpLimitError,
NotAllowedCreateWorkspace,
)
from controllers.console.wraps import setup_required
from controllers.console.wraps import email_password_login_enabled, setup_required
from events.tenant_event import tenant_was_created
from libs.helper import email, extract_remote_ip
from libs.password import valid_password
@@ -38,6 +38,7 @@ class LoginApi(Resource):
"""Resource for user login."""
@setup_required
@email_password_login_enabled
def post(self):
"""Authenticate user and login."""
parser = reqparse.RequestParser()
@@ -110,6 +111,7 @@ class LogoutApi(Resource):
class ResetPasswordSendEmailApi(Resource):
@setup_required
@email_password_login_enabled
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("email", type=email, required=True, location="json")
+8 -4
View File
@@ -641,12 +641,10 @@ class DatasetRetrievalSettingApi(Resource):
VectorType.RELYT
| VectorType.TIDB_VECTOR
| VectorType.CHROMA
| VectorType.TENCENT
| VectorType.PGVECTO_RS
| VectorType.BAIDU
| VectorType.VIKINGDB
| VectorType.UPSTASH
| VectorType.OCEANBASE
):
return {"retrieval_method": [RetrievalMethod.SEMANTIC_SEARCH.value]}
case (
@@ -664,6 +662,10 @@ class DatasetRetrievalSettingApi(Resource):
| VectorType.COUCHBASE
| VectorType.MILVUS
| VectorType.OPENGAUSS
| VectorType.OCEANBASE
| VectorType.TABLESTORE
| VectorType.HUAWEI_CLOUD
| VectorType.TENCENT
):
return {
"retrieval_method": [
@@ -687,12 +689,10 @@ class DatasetRetrievalSettingMockApi(Resource):
| VectorType.RELYT
| VectorType.TIDB_VECTOR
| VectorType.CHROMA
| VectorType.TENCENT
| VectorType.PGVECTO_RS
| VectorType.BAIDU
| VectorType.VIKINGDB
| VectorType.UPSTASH
| VectorType.OCEANBASE
):
return {"retrieval_method": [RetrievalMethod.SEMANTIC_SEARCH.value]}
case (
@@ -708,6 +708,10 @@ class DatasetRetrievalSettingMockApi(Resource):
| VectorType.PGVECTOR
| VectorType.LINDORM
| VectorType.OPENGAUSS
| VectorType.OCEANBASE
| VectorType.TABLESTORE
| VectorType.TENCENT
| VectorType.HUAWEI_CLOUD
):
return {
"retrieval_method": [
@@ -398,7 +398,7 @@ class DatasetDocumentSegmentBatchImportApi(Resource):
indexing_cache_key = "segment_batch_import_{}".format(job_id)
cache_result = redis_client.get(indexing_cache_key)
if cache_result is None:
raise ValueError("The job is not exist.")
raise ValueError("The job does not exist.")
return {"job_id": job_id, "job_status": cache_result.decode()}, 200
@@ -21,12 +21,6 @@ def _validate_name(name):
return name
def _validate_description_length(description):
if description and len(description) > 400:
raise ValueError("Description cannot exceed 400 characters.")
return description
class ExternalApiTemplateListApi(Resource):
@setup_required
@login_required
@@ -14,18 +14,6 @@ from services.entities.knowledge_entities.knowledge_entities import (
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 DatasetMetadataCreateApi(Resource):
@setup_required
@login_required
+9 -2
View File
@@ -14,7 +14,12 @@ class WebsiteCrawlApi(Resource):
def post(self):
parser = reqparse.RequestParser()
parser.add_argument(
"provider", type=str, choices=["firecrawl", "jinareader"], required=True, nullable=True, location="json"
"provider",
type=str,
choices=["firecrawl", "watercrawl", "jinareader"],
required=True,
nullable=True,
location="json",
)
parser.add_argument("url", type=str, required=True, nullable=True, location="json")
parser.add_argument("options", type=dict, required=True, nullable=True, location="json")
@@ -34,7 +39,9 @@ class WebsiteCrawlStatusApi(Resource):
@account_initialization_required
def get(self, job_id: str):
parser = reqparse.RequestParser()
parser.add_argument("provider", type=str, choices=["firecrawl", "jinareader"], required=True, location="args")
parser.add_argument(
"provider", type=str, choices=["firecrawl", "watercrawl", "jinareader"], required=True, location="args"
)
args = parser.parse_args()
# get crawl status
try:
+12
View File
@@ -103,6 +103,18 @@ class AccountInFreezeError(BaseHTTPException):
)
class EducationVerifyLimitError(BaseHTTPException):
error_code = "education_verify_limit"
description = "Rate limit exceeded"
code = 429
class EducationActivateLimitError(BaseHTTPException):
error_code = "education_activate_limit"
description = "Rate limit exceeded"
code = 429
class CompilanceRateLimitError(BaseHTTPException):
error_code = "compilance_rate_limit"
description = "Rate limit exceeded for downloading compliance report."
+2 -4
View File
@@ -1,10 +1,10 @@
from flask_restful import marshal_with # type: ignore
from controllers.common import fields
from controllers.common import helpers as controller_helpers
from controllers.console import api
from controllers.console.app.error import AppUnavailableError
from controllers.console.explore.wraps import InstalledAppResource
from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict
from models.model import AppMode, InstalledApp
from services.app_service import AppService
@@ -36,9 +36,7 @@ class AppParameterApi(InstalledAppResource):
user_input_form = features_dict.get("user_input_form", [])
return controller_helpers.get_parameters_from_feature_dict(
features_dict=features_dict, user_input_form=user_input_form
)
return get_parameters_from_feature_dict(features_dict=features_dict, user_input_form=user_input_form)
class ExploreAppMetaApi(InstalledAppResource):
+83 -3
View File
@@ -15,7 +15,13 @@ from controllers.console.workspace.error import (
InvalidInvitationCodeError,
RepeatPasswordNotMatchError,
)
from controllers.console.wraps import account_initialization_required, enterprise_license_required, setup_required
from controllers.console.wraps import (
account_initialization_required,
cloud_edition_billing_enabled,
enterprise_license_required,
only_edition_cloud,
setup_required,
)
from extensions.ext_database import db
from fields.member_fields import account_fields
from libs.helper import TimestampField, timezone
@@ -280,8 +286,6 @@ class AccountDeleteApi(Resource):
class AccountDeleteUpdateFeedbackApi(Resource):
@setup_required
def post(self):
account = current_user
parser = reqparse.RequestParser()
parser.add_argument("email", type=str, required=True, location="json")
parser.add_argument("feedback", type=str, required=True, location="json")
@@ -292,6 +296,79 @@ class AccountDeleteUpdateFeedbackApi(Resource):
return {"result": "success"}
class EducationVerifyApi(Resource):
verify_fields = {
"token": fields.String,
}
@setup_required
@login_required
@account_initialization_required
@only_edition_cloud
@cloud_edition_billing_enabled
@marshal_with(verify_fields)
def get(self):
account = current_user
return BillingService.EducationIdentity.verify(account.id, account.email)
class EducationApi(Resource):
status_fields = {
"result": fields.Boolean,
}
@setup_required
@login_required
@account_initialization_required
@only_edition_cloud
@cloud_edition_billing_enabled
def post(self):
account = current_user
parser = reqparse.RequestParser()
parser.add_argument("token", type=str, required=True, location="json")
parser.add_argument("institution", type=str, required=True, location="json")
parser.add_argument("role", type=str, required=True, location="json")
args = parser.parse_args()
return BillingService.EducationIdentity.activate(account, args["token"], args["institution"], args["role"])
@setup_required
@login_required
@account_initialization_required
@only_edition_cloud
@cloud_edition_billing_enabled
@marshal_with(status_fields)
def get(self):
account = current_user
return BillingService.EducationIdentity.is_active(account.id)
class EducationAutoCompleteApi(Resource):
data_fields = {
"data": fields.List(fields.String),
"curr_page": fields.Integer,
"has_next": fields.Boolean,
}
@setup_required
@login_required
@account_initialization_required
@only_edition_cloud
@cloud_edition_billing_enabled
@marshal_with(data_fields)
def get(self):
parser = reqparse.RequestParser()
parser.add_argument("keywords", type=str, required=True, location="args")
parser.add_argument("page", type=int, required=False, location="args", default=0)
parser.add_argument("limit", type=int, required=False, location="args", default=20)
args = parser.parse_args()
return BillingService.EducationIdentity.autocomplete(args["keywords"], args["page"], args["limit"])
# Register API resources
api.add_resource(AccountInitApi, "/account/init")
api.add_resource(AccountProfileApi, "/account/profile")
@@ -305,5 +382,8 @@ api.add_resource(AccountIntegrateApi, "/account/integrates")
api.add_resource(AccountDeleteVerifyApi, "/account/delete/verify")
api.add_resource(AccountDeleteApi, "/account/delete")
api.add_resource(AccountDeleteUpdateFeedbackApi, "/account/delete/feedback")
api.add_resource(EducationVerifyApi, "/account/education/verify")
api.add_resource(EducationApi, "/account/education")
api.add_resource(EducationAutoCompleteApi, "/account/education/autocomplete")
# api.add_resource(AccountEmailApi, '/account/email')
# api.add_resource(AccountEmailVerifyApi, '/account/email-verify')
+13 -9
View File
@@ -5,6 +5,7 @@ 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 core.plugin.manager.exc import PluginPermissionDeniedError
from libs.login import login_required
from services.plugin.endpoint_service import EndpointService
@@ -28,15 +29,18 @@ class EndpointCreateApi(Resource):
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,
)
}
try:
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,
)
}
except PluginPermissionDeniedError as e:
raise ValueError(e.description) from e
class EndpointListApi(Resource):
+53 -9
View File
@@ -49,6 +49,23 @@ class PluginListApi(Resource):
return jsonable_encoder({"plugins": plugins})
class PluginListLatestVersionsApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
req = reqparse.RequestParser()
req.add_argument("plugin_ids", type=list, required=True, location="json")
args = req.parse_args()
try:
versions = PluginService.list_latest_versions(args["plugin_ids"])
except PluginDaemonClientSideError as e:
raise ValueError(e)
return jsonable_encoder({"versions": versions})
class PluginListInstallationsFromIdsApi(Resource):
@setup_required
@login_required
@@ -232,11 +249,36 @@ class PluginInstallFromMarketplaceApi(Resource):
return jsonable_encoder(response)
class PluginFetchMarketplacePkgApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(install_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_marketplace_pkg(
tenant_id,
args["plugin_unique_identifier"],
)
}
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
class PluginFetchManifestApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
@plugin_permission_required(install_required=True)
def get(self):
tenant_id = current_user.current_tenant_id
@@ -260,7 +302,7 @@ class PluginFetchInstallTasksApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
@plugin_permission_required(install_required=True)
def get(self):
tenant_id = current_user.current_tenant_id
@@ -281,7 +323,7 @@ class PluginFetchInstallTaskApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
@plugin_permission_required(install_required=True)
def get(self, task_id: str):
tenant_id = current_user.current_tenant_id
@@ -295,7 +337,7 @@ class PluginDeleteInstallTaskApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
@plugin_permission_required(install_required=True)
def post(self, task_id: str):
tenant_id = current_user.current_tenant_id
@@ -309,7 +351,7 @@ class PluginDeleteAllInstallTaskItemsApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
@plugin_permission_required(install_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
@@ -323,7 +365,7 @@ class PluginDeleteInstallTaskItemApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
@plugin_permission_required(install_required=True)
def post(self, task_id: str, identifier: str):
tenant_id = current_user.current_tenant_id
@@ -337,7 +379,7 @@ class PluginUpgradeFromMarketplaceApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
@plugin_permission_required(install_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
@@ -360,7 +402,7 @@ class PluginUpgradeFromGithubApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
@plugin_permission_required(install_required=True)
def post(self):
tenant_id = current_user.current_tenant_id
@@ -391,7 +433,7 @@ class PluginUninstallApi(Resource):
@setup_required
@login_required
@account_initialization_required
@plugin_permission_required(debug_required=True)
@plugin_permission_required(install_required=True)
def post(self):
req = reqparse.RequestParser()
req.add_argument("plugin_installation_id", type=str, required=True, location="json")
@@ -453,6 +495,7 @@ class PluginFetchPermissionApi(Resource):
api.add_resource(PluginDebuggingKeyApi, "/workspaces/current/plugin/debugging-key")
api.add_resource(PluginListApi, "/workspaces/current/plugin/list")
api.add_resource(PluginListLatestVersionsApi, "/workspaces/current/plugin/list/latest-versions")
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")
@@ -470,6 +513,7 @@ api.add_resource(PluginDeleteInstallTaskApi, "/workspaces/current/plugin/tasks/<
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(PluginFetchMarketplacePkgApi, "/workspaces/current/plugin/marketplace/pkg")
api.add_resource(PluginChangePermissionApi, "/workspaces/current/plugin/permission/change")
api.add_resource(PluginFetchPermissionApi, "/workspaces/current/plugin/permission/fetch")
@@ -216,6 +216,23 @@ class WebappLogoWorkspaceApi(Resource):
return {"id": upload_file.id}, 201
class WorkspaceInfoApi(Resource):
@setup_required
@login_required
@account_initialization_required
# Change workspace name
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("name", type=str, required=True, location="json")
args = parser.parse_args()
tenant = Tenant.query.filter(Tenant.id == current_user.current_tenant_id).one_or_404()
tenant.name = args["name"]
db.session.commit()
return {"result": "success", "tenant": marshal(WorkspaceService.get_tenant_info(tenant), tenant_fields)}
api.add_resource(TenantListApi, "/workspaces") # GET for getting all tenants
api.add_resource(WorkspaceListApi, "/all-workspaces") # GET for getting all tenants
api.add_resource(TenantApi, "/workspaces/current", endpoint="workspaces_current") # GET for getting current tenant info
@@ -223,3 +240,4 @@ api.add_resource(TenantApi, "/info", endpoint="info") # Deprecated
api.add_resource(SwitchWorkspaceApi, "/workspaces/switch") # POST for switching tenant
api.add_resource(CustomConfigWorkspaceApi, "/workspaces/custom-config")
api.add_resource(WebappLogoWorkspaceApi, "/workspaces/custom-config/webapp-logo/upload")
api.add_resource(WorkspaceInfoApi, "/workspaces/info") # POST for changing workspace info
+24
View File
@@ -54,6 +54,17 @@ def only_edition_self_hosted(view):
return decorated
def cloud_edition_billing_enabled(view):
@wraps(view)
def decorated(*args, **kwargs):
features = FeatureService.get_features(current_user.current_tenant_id)
if not features.billing.enabled:
abort(403, "Billing feature is not enabled.")
return view(*args, **kwargs)
return decorated
def cloud_edition_billing_resource_check(resource: str):
def interceptor(view):
@wraps(view)
@@ -199,3 +210,16 @@ def enterprise_license_required(view):
return view(*args, **kwargs)
return decorated
def email_password_login_enabled(view):
@wraps(view)
def decorated(*args, **kwargs):
features = FeatureService.get_system_features()
if features.enable_email_password_login:
return view(*args, **kwargs)
# otherwise, return 403
abort(403)
return decorated
+16 -7
View File
@@ -1,3 +1,5 @@
from mimetypes import guess_extension
from flask import request
from flask_restful import Resource, marshal_with # type: ignore
from werkzeug.exceptions import Forbidden
@@ -9,8 +11,8 @@ 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 core.tools.tool_file_manager import ToolFileManager
from fields.file_fields import file_fields
from services.file_service import FileService
class PluginUploadFileApi(Resource):
@@ -51,19 +53,26 @@ class PluginUploadFileApi(Resource):
raise Forbidden("Invalid request.")
try:
upload_file = FileService.upload_file(
filename=filename,
content=file.read(),
tool_file = ToolFileManager.create_file_by_raw(
user_id=user.id,
tenant_id=tenant_id,
file_binary=file.read(),
mimetype=mimetype,
user=user,
source=None,
filename=filename,
conversation_id=None,
)
extension = guess_extension(tool_file.mimetype) or ".bin"
preview_url = ToolFileManager.sign_file(tool_file_id=tool_file.id, extension=extension)
tool_file.mime_type = mimetype
tool_file.extension = extension
tool_file.preview_url = preview_url
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
return tool_file, 201
api.add_resource(PluginUploadFileApi, "/files/upload/for-plugin")
@@ -13,6 +13,7 @@ from core.plugin.backwards_invocation.model import PluginModelBackwardsInvocatio
from core.plugin.backwards_invocation.node import PluginNodeBackwardsInvocation
from core.plugin.backwards_invocation.tool import PluginToolBackwardsInvocation
from core.plugin.entities.request import (
RequestFetchAppInfo,
RequestInvokeApp,
RequestInvokeEncrypt,
RequestInvokeLLM,
@@ -278,6 +279,17 @@ class PluginUploadFileRequestApi(Resource):
return BaseBackwardsInvocationResponse(data={"url": url}).model_dump()
class PluginFetchAppInfoApi(Resource):
@setup_required
@plugin_inner_api_only
@get_user_tenant
@plugin_data(payload_type=RequestFetchAppInfo)
def post(self, user_model: Account | EndUser, tenant_model: Tenant, payload: RequestFetchAppInfo):
return BaseBackwardsInvocationResponse(
data=PluginAppBackwardsInvocation.fetch_app_info(payload.app_id, tenant_model.id)
).model_dump()
api.add_resource(PluginInvokeLLMApi, "/invoke/llm")
api.add_resource(PluginInvokeTextEmbeddingApi, "/invoke/text-embedding")
api.add_resource(PluginInvokeRerankApi, "/invoke/rerank")
@@ -291,3 +303,4 @@ 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")
api.add_resource(PluginFetchAppInfoApi, "/fetch/app/info")
@@ -6,6 +6,7 @@ from controllers.console.wraps import setup_required
from controllers.inner_api import api
from controllers.inner_api.wraps import enterprise_inner_api_only
from events.tenant_event import tenant_was_created
from extensions.ext_database import db
from models.account import Account
from services.account_service import TenantService
@@ -19,7 +20,7 @@ class EnterpriseWorkspace(Resource):
parser.add_argument("owner_email", type=str, required=True, location="json")
args = parser.parse_args()
account = Account.query.filter_by(email=args["owner_email"]).first()
account = db.session.query(Account).filter_by(email=args["owner_email"]).first()
if account is None:
return {"message": "owner account not found."}, 404
+2 -1
View File
@@ -6,5 +6,6 @@ bp = Blueprint("service_api", __name__, url_prefix="/v1")
api = ExternalApi(bp)
from . import index
from .app import app, audio, completion, conversation, file, message, workflow
from .app import annotation, app, audio, completion, conversation, file, message, workflow
from .dataset import dataset, document, hit_testing, metadata, segment, upload_file
from .workspace import models
@@ -0,0 +1,107 @@
from flask import request
from flask_restful import Resource, marshal, marshal_with, reqparse # type: ignore
from werkzeug.exceptions import Forbidden
from controllers.service_api import api
from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token
from extensions.ext_redis import redis_client
from fields.annotation_fields import (
annotation_fields,
)
from libs.login import current_user
from models.model import App, EndUser
from services.annotation_service import AppAnnotationService
class AnnotationReplyActionApi(Resource):
@validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.JSON))
def post(self, app_model: App, end_user: EndUser, action):
parser = reqparse.RequestParser()
parser.add_argument("score_threshold", required=True, type=float, location="json")
parser.add_argument("embedding_provider_name", required=True, type=str, location="json")
parser.add_argument("embedding_model_name", required=True, type=str, location="json")
args = parser.parse_args()
if action == "enable":
result = AppAnnotationService.enable_app_annotation(args, app_model.id)
elif action == "disable":
result = AppAnnotationService.disable_app_annotation(app_model.id)
else:
raise ValueError("Unsupported annotation reply action")
return result, 200
class AnnotationReplyActionStatusApi(Resource):
@validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY))
def get(self, app_model: App, end_user: EndUser, job_id, action):
job_id = str(job_id)
app_annotation_job_key = "{}_app_annotation_job_{}".format(action, str(job_id))
cache_result = redis_client.get(app_annotation_job_key)
if cache_result is None:
raise ValueError("The job does not exist.")
job_status = cache_result.decode()
error_msg = ""
if job_status == "error":
app_annotation_error_key = "{}_app_annotation_error_{}".format(action, str(job_id))
error_msg = redis_client.get(app_annotation_error_key).decode()
return {"job_id": job_id, "job_status": job_status, "error_msg": error_msg}, 200
class AnnotationListApi(Resource):
@validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY))
def get(self, app_model: App, end_user: EndUser):
page = request.args.get("page", default=1, type=int)
limit = request.args.get("limit", default=20, type=int)
keyword = request.args.get("keyword", default="", type=str)
annotation_list, total = AppAnnotationService.get_annotation_list_by_app_id(app_model.id, page, limit, keyword)
response = {
"data": marshal(annotation_list, annotation_fields),
"has_more": len(annotation_list) == limit,
"limit": limit,
"total": total,
"page": page,
}
return response, 200
@validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.JSON))
@marshal_with(annotation_fields)
def post(self, app_model: App, end_user: EndUser):
parser = reqparse.RequestParser()
parser.add_argument("question", required=True, type=str, location="json")
parser.add_argument("answer", required=True, type=str, location="json")
args = parser.parse_args()
annotation = AppAnnotationService.insert_app_annotation_directly(args, app_model.id)
return annotation
class AnnotationUpdateDeleteApi(Resource):
@validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.JSON))
@marshal_with(annotation_fields)
def post(self, app_model: App, end_user: EndUser, annotation_id):
if not current_user.is_editor:
raise Forbidden()
annotation_id = str(annotation_id)
parser = reqparse.RequestParser()
parser.add_argument("question", required=True, type=str, location="json")
parser.add_argument("answer", required=True, type=str, location="json")
args = parser.parse_args()
annotation = AppAnnotationService.update_app_annotation_directly(args, app_model.id, annotation_id)
return annotation
@validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY))
def delete(self, app_model: App, end_user: EndUser, annotation_id):
if not current_user.is_editor:
raise Forbidden()
annotation_id = str(annotation_id)
AppAnnotationService.delete_app_annotation(app_model.id, annotation_id)
return {"result": "success"}, 200
api.add_resource(AnnotationReplyActionApi, "/apps/annotation-reply/<string:action>")
api.add_resource(AnnotationReplyActionStatusApi, "/apps/annotation-reply/<string:action>/status/<uuid:job_id>")
api.add_resource(AnnotationListApi, "/apps/annotations")
api.add_resource(AnnotationUpdateDeleteApi, "/apps/annotations/<uuid:annotation_id>")
+2 -4
View File
@@ -1,10 +1,10 @@
from flask_restful import Resource, marshal_with # type: ignore
from controllers.common import fields
from controllers.common import helpers as controller_helpers
from controllers.service_api import api
from controllers.service_api.app.error import AppUnavailableError
from controllers.service_api.wraps import validate_app_token
from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict
from models.model import App, AppMode
from services.app_service import AppService
@@ -32,9 +32,7 @@ class AppParameterApi(Resource):
user_input_form = features_dict.get("user_input_form", [])
return controller_helpers.get_parameters_from_feature_dict(
features_dict=features_dict, user_input_form=user_input_form
)
return get_parameters_from_feature_dict(features_dict=features_dict, user_input_form=user_input_form)
class AppMetaApi(Resource):
+7 -2
View File
@@ -1,3 +1,4 @@
import json
import logging
from flask_restful import Resource, fields, marshal_with, reqparse # type: ignore
@@ -10,7 +11,7 @@ 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 agent_thought_fields, feedback_fields, retriever_resource_fields
from fields.message_fields import agent_thought_fields, feedback_fields
from fields.raws import FilesContainedField
from libs.helper import TimestampField, uuid_value
from models.model import App, AppMode, EndUser
@@ -28,7 +29,11 @@ class MessageListApi(Resource):
"answer": fields.String(attribute="re_sign_file_url_answer"),
"message_files": fields.List(fields.Nested(message_file_fields)),
"feedback": fields.Nested(feedback_fields, attribute="user_feedback", allow_null=True),
"retriever_resources": fields.List(fields.Nested(retriever_resource_fields)),
"retriever_resources": fields.Raw(
attribute=lambda obj: json.loads(obj.message_metadata).get("retriever_resources", [])
if obj.message_metadata
else []
),
"created_at": TimestampField,
"agent_thoughts": fields.List(fields.Nested(agent_thought_fields)),
"status": fields.String,
+9 -8
View File
@@ -1,6 +1,6 @@
import logging
from datetime import datetime
from dateutil.parser import isoparse
from flask_restful import Resource, fields, marshal_with, reqparse # type: ignore
from flask_restful.inputs import int_range # type: ignore
from sqlalchemy.orm import Session
@@ -27,6 +27,7 @@ from core.model_runtime.errors.invoke import InvokeError
from extensions.ext_database import db
from fields.workflow_app_log_fields import workflow_app_log_pagination_fields
from libs import helper
from libs.helper import TimestampField
from models.model import App, AppMode, EndUser
from models.workflow import WorkflowRun, WorkflowRunStatus
from services.app_generate_service import AppGenerateService
@@ -44,8 +45,8 @@ workflow_run_fields = {
"error": fields.String,
"total_steps": fields.Integer,
"total_tokens": fields.Integer,
"created_at": fields.DateTime,
"finished_at": fields.DateTime,
"created_at": TimestampField,
"finished_at": TimestampField,
"elapsed_time": fields.Float,
}
@@ -53,7 +54,7 @@ workflow_run_fields = {
class WorkflowRunDetailApi(Resource):
@validate_app_token
@marshal_with(workflow_run_fields)
def get(self, app_model: App, workflow_id: str):
def get(self, app_model: App, workflow_run_id: str):
"""
Get a workflow task running detail
"""
@@ -61,7 +62,7 @@ class WorkflowRunDetailApi(Resource):
if app_mode != AppMode.WORKFLOW:
raise NotWorkflowAppError()
workflow_run = db.session.query(WorkflowRun).filter(WorkflowRun.id == workflow_id).first()
workflow_run = db.session.query(WorkflowRun).filter(WorkflowRun.id == workflow_run_id).first()
return workflow_run
@@ -139,10 +140,10 @@ class WorkflowAppLogApi(Resource):
args.status = WorkflowRunStatus(args.status) if args.status else None
if args.created_at__before:
args.created_at__before = datetime.fromisoformat(args.created_at__before.replace("Z", "+00:00"))
args.created_at__before = isoparse(args.created_at__before)
if args.created_at__after:
args.created_at__after = datetime.fromisoformat(args.created_at__after.replace("Z", "+00:00"))
args.created_at__after = isoparse(args.created_at__after)
# get paginate workflow app logs
workflow_app_service = WorkflowAppService()
@@ -162,6 +163,6 @@ class WorkflowAppLogApi(Resource):
api.add_resource(WorkflowRunApi, "/workflows/run")
api.add_resource(WorkflowRunDetailApi, "/workflows/run/<string:workflow_id>")
api.add_resource(WorkflowRunDetailApi, "/workflows/run/<string:workflow_run_id>")
api.add_resource(WorkflowTaskStopApi, "/workflows/tasks/<string:task_id>/stop")
api.add_resource(WorkflowAppLogApi, "/workflows/logs")
+159 -3
View File
@@ -1,6 +1,6 @@
from flask import request
from flask_restful import marshal, reqparse # type: ignore
from werkzeug.exceptions import NotFound
from werkzeug.exceptions import Forbidden, NotFound
import services.dataset_service
from controllers.service_api import api
@@ -12,7 +12,8 @@ from core.provider_manager import ProviderManager
from fields.dataset_fields import dataset_detail_fields
from libs.login import current_user
from models.dataset import Dataset, DatasetPermissionEnum
from services.dataset_service import DatasetService
from services.dataset_service import DatasetPermissionService, DatasetService
from services.entities.knowledge_entities.knowledge_entities import RetrievalModel
def _validate_name(name):
@@ -21,6 +22,12 @@ def _validate_name(name):
return name
def _validate_description_length(description):
if len(description) > 400:
raise ValueError("Description cannot exceed 400 characters.")
return description
class DatasetListApi(DatasetApiResource):
"""Resource for datasets."""
@@ -114,8 +121,11 @@ class DatasetListApi(DatasetApiResource):
nullable=True,
required=False,
)
args = parser.parse_args()
parser.add_argument("retrieval_model", type=dict, required=False, nullable=True, location="json")
parser.add_argument("embedding_model", type=str, required=False, nullable=True, location="json")
parser.add_argument("embedding_model_provider", type=str, required=False, nullable=True, location="json")
args = parser.parse_args()
try:
dataset = DatasetService.create_empty_dataset(
tenant_id=tenant_id,
@@ -127,6 +137,11 @@ class DatasetListApi(DatasetApiResource):
provider=args["provider"],
external_knowledge_api_id=args["external_knowledge_api_id"],
external_knowledge_id=args["external_knowledge_id"],
embedding_model_provider=args["embedding_model_provider"],
embedding_model_name=args["embedding_model"],
retrieval_model=RetrievalModel(**args["retrieval_model"])
if args["retrieval_model"] is not None
else None,
)
except services.errors.dataset.DatasetNameDuplicateError:
raise DatasetNameDuplicateError()
@@ -137,11 +152,151 @@ class DatasetListApi(DatasetApiResource):
class DatasetApi(DatasetApiResource):
"""Resource for dataset."""
def get(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.")
try:
DatasetService.check_dataset_permission(dataset, current_user)
except services.errors.account.NoPermissionError as e:
raise Forbidden(str(e))
data = marshal(dataset, dataset_detail_fields)
if data.get("permission") == "partial_members":
part_users_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str)
data.update({"partial_member_list": part_users_list})
# check embedding setting
provider_manager = ProviderManager()
configurations = provider_manager.get_configurations(tenant_id=current_user.current_tenant_id)
embedding_models = configurations.get_models(model_type=ModelType.TEXT_EMBEDDING, only_active=True)
model_names = []
for embedding_model in embedding_models:
model_names.append(f"{embedding_model.model}:{embedding_model.provider.provider}")
if data["indexing_technique"] == "high_quality":
item_model = f"{data['embedding_model']}:{data['embedding_model_provider']}"
if item_model in model_names:
data["embedding_available"] = True
else:
data["embedding_available"] = False
else:
data["embedding_available"] = True
if data.get("permission") == "partial_members":
part_users_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str)
data.update({"partial_member_list": part_users_list})
return data, 200
def patch(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.")
parser = reqparse.RequestParser()
parser.add_argument(
"name",
nullable=False,
help="type is required. Name must be between 1 to 40 characters.",
type=_validate_name,
)
parser.add_argument("description", location="json", store_missing=False, type=_validate_description_length)
parser.add_argument(
"indexing_technique",
type=str,
location="json",
choices=Dataset.INDEXING_TECHNIQUE_LIST,
nullable=True,
help="Invalid indexing technique.",
)
parser.add_argument(
"permission",
type=str,
location="json",
choices=(DatasetPermissionEnum.ONLY_ME, DatasetPermissionEnum.ALL_TEAM, DatasetPermissionEnum.PARTIAL_TEAM),
help="Invalid permission.",
)
parser.add_argument("embedding_model", type=str, location="json", help="Invalid embedding model.")
parser.add_argument(
"embedding_model_provider", type=str, location="json", help="Invalid embedding model provider."
)
parser.add_argument("retrieval_model", type=dict, location="json", help="Invalid retrieval model.")
parser.add_argument("partial_member_list", type=list, location="json", help="Invalid parent user list.")
parser.add_argument(
"external_retrieval_model",
type=dict,
required=False,
nullable=True,
location="json",
help="Invalid external retrieval model.",
)
parser.add_argument(
"external_knowledge_id",
type=str,
required=False,
nullable=True,
location="json",
help="Invalid external knowledge id.",
)
parser.add_argument(
"external_knowledge_api_id",
type=str,
required=False,
nullable=True,
location="json",
help="Invalid external knowledge api id.",
)
args = parser.parse_args()
data = request.get_json()
# check embedding model setting
if data.get("indexing_technique") == "high_quality":
DatasetService.check_embedding_model_setting(
dataset.tenant_id, data.get("embedding_model_provider"), data.get("embedding_model")
)
# The role of the current user in the ta table must be admin, owner, editor, or dataset_operator
DatasetPermissionService.check_permission(
current_user, dataset, data.get("permission"), data.get("partial_member_list")
)
dataset = DatasetService.update_dataset(dataset_id_str, args, current_user)
if dataset is None:
raise NotFound("Dataset not found.")
result_data = marshal(dataset, dataset_detail_fields)
tenant_id = current_user.current_tenant_id
if data.get("partial_member_list") and data.get("permission") == "partial_members":
DatasetPermissionService.update_partial_member_list(
tenant_id, dataset_id_str, data.get("partial_member_list")
)
# clear partial member list when permission is only_me or all_team_members
elif (
data.get("permission") == DatasetPermissionEnum.ONLY_ME
or data.get("permission") == DatasetPermissionEnum.ALL_TEAM
):
DatasetPermissionService.clear_partial_member_list(dataset_id_str)
partial_member_list = DatasetPermissionService.get_dataset_partial_member_list(dataset_id_str)
result_data.update({"partial_member_list": partial_member_list})
return result_data, 200
def delete(self, _, dataset_id):
"""
Deletes a dataset given its ID.
Args:
_: ignore
dataset_id (UUID): The ID of the dataset to be deleted.
Returns:
@@ -157,6 +312,7 @@ class DatasetApi(DatasetApiResource):
try:
if DatasetService.delete_dataset(dataset_id_str, current_user):
DatasetPermissionService.clear_partial_member_list(dataset_id_str)
return {"result": "success"}, 204
else:
raise NotFound("Dataset not found.")
@@ -49,7 +49,9 @@ class DocumentAddByTextApi(DatasetApiResource):
parser.add_argument(
"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("retrieval_model", type=dict, required=False, nullable=True, location="json")
parser.add_argument("embedding_model", type=str, required=False, nullable=True, location="json")
parser.add_argument("embedding_model_provider", type=str, required=False, nullable=True, location="json")
args = parser.parse_args()
dataset_id = str(dataset_id)
@@ -57,7 +59,7 @@ class DocumentAddByTextApi(DatasetApiResource):
dataset = db.session.query(Dataset).filter(Dataset.tenant_id == tenant_id, Dataset.id == dataset_id).first()
if not dataset:
raise ValueError("Dataset is not exist.")
raise ValueError("Dataset does not exist.")
if not dataset.indexing_technique and not args["indexing_technique"]:
raise ValueError("indexing_technique is required.")
@@ -114,7 +116,7 @@ class DocumentUpdateByTextApi(DatasetApiResource):
dataset = db.session.query(Dataset).filter(Dataset.tenant_id == tenant_id, Dataset.id == dataset_id).first()
if not dataset:
raise ValueError("Dataset is not exist.")
raise ValueError("Dataset does not exist.")
# indexing_technique is already set in dataset since this is an update
args["indexing_technique"] = dataset.indexing_technique
@@ -172,7 +174,7 @@ class DocumentAddByFileApi(DatasetApiResource):
dataset = db.session.query(Dataset).filter(Dataset.tenant_id == tenant_id, Dataset.id == dataset_id).first()
if not dataset:
raise ValueError("Dataset is not exist.")
raise ValueError("Dataset does not exist.")
if not dataset.indexing_technique and not args.get("indexing_technique"):
raise ValueError("indexing_technique is required.")
@@ -239,7 +241,7 @@ class DocumentUpdateByFileApi(DatasetApiResource):
dataset = db.session.query(Dataset).filter(Dataset.tenant_id == tenant_id, Dataset.id == dataset_id).first()
if not dataset:
raise ValueError("Dataset is not exist.")
raise ValueError("Dataset does not exist.")
# indexing_technique is already set in dataset since this is an update
args["indexing_technique"] = dataset.indexing_technique
@@ -303,7 +305,7 @@ class DocumentDeleteApi(DatasetApiResource):
dataset = db.session.query(Dataset).filter(Dataset.tenant_id == tenant_id, Dataset.id == dataset_id).first()
if not dataset:
raise ValueError("Dataset is not exist.")
raise ValueError("Dataset does not exist.")
document = DocumentService.get_document(dataset.id, document_id)
@@ -341,7 +343,7 @@ class DocumentListApi(DatasetApiResource):
search = f"%{search}%"
query = query.filter(Document.name.like(search))
query = query.order_by(desc(Document.created_at))
query = query.order_by(desc(Document.created_at), desc(Document.position))
paginated_documents = query.paginate(page=page, per_page=limit, max_per_page=100, error_out=False)
documents = paginated_documents.items
@@ -13,18 +13,6 @@ from services.entities.knowledge_entities.knowledge_entities import (
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 DatasetMetadataCreateServiceApi(DatasetApiResource):
def post(self, tenant_id, dataset_id):
parser = reqparse.RequestParser()
+226 -25
View File
@@ -1,3 +1,4 @@
from flask import request
from flask_login import current_user # type: ignore
from flask_restful import marshal, reqparse # type: ignore
from werkzeug.exceptions import NotFound
@@ -13,10 +14,20 @@ from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError
from core.model_manager import ModelManager
from core.model_runtime.entities.model_entities import ModelType
from extensions.ext_database import db
from fields.segment_fields import segment_fields
from models.dataset import Dataset, DocumentSegment
from fields.segment_fields import child_chunk_fields, segment_fields
from models.dataset import Dataset
from services.dataset_service import DatasetService, DocumentService, SegmentService
from services.entities.knowledge_entities.knowledge_entities import SegmentUpdateArgs
from services.errors.chunk import (
ChildChunkDeleteIndexError,
ChildChunkIndexingError,
)
from services.errors.chunk import (
ChildChunkDeleteIndexError as ChildChunkDeleteIndexServiceError,
)
from services.errors.chunk import (
ChildChunkIndexingError as ChildChunkIndexingServiceError,
)
class SegmentApi(DatasetApiResource):
@@ -70,10 +81,12 @@ class SegmentApi(DatasetApiResource):
return {"error": "Segments is required"}, 400
def get(self, tenant_id, dataset_id, document_id):
"""Create single segment."""
"""Get segments."""
# check dataset
dataset_id = str(dataset_id)
tenant_id = str(tenant_id)
page = request.args.get("page", default=1, type=int)
limit = request.args.get("limit", default=20, type=int)
dataset = db.session.query(Dataset).filter(Dataset.tenant_id == tenant_id, Dataset.id == dataset_id).first()
if not dataset:
raise NotFound("Dataset not found.")
@@ -104,22 +117,25 @@ class SegmentApi(DatasetApiResource):
parser.add_argument("keyword", type=str, default=None, location="args")
args = parser.parse_args()
status_list = args["status"]
keyword = args["keyword"]
query = DocumentSegment.query.filter(
DocumentSegment.document_id == str(document_id), DocumentSegment.tenant_id == current_user.current_tenant_id
segments, total = SegmentService.get_segments(
document_id=document_id,
tenant_id=current_user.current_tenant_id,
status_list=args["status"],
keyword=args["keyword"],
page=page,
limit=limit,
)
if status_list:
query = query.filter(DocumentSegment.status.in_(status_list))
response = {
"data": marshal(segments, segment_fields),
"doc_form": document.doc_form,
"total": total,
"has_more": len(segments) == limit,
"limit": limit,
"page": page,
}
if keyword:
query = query.where(DocumentSegment.content.ilike(f"%{keyword}%"))
total = query.count()
segments = query.order_by(DocumentSegment.position).all()
return {"data": marshal(segments, segment_fields), "doc_form": document.doc_form, "total": total}, 200
return response, 200
class DatasetSegmentApi(DatasetApiResource):
@@ -138,9 +154,8 @@ class DatasetSegmentApi(DatasetApiResource):
if not document:
raise NotFound("Document not found.")
# check segment
segment = DocumentSegment.query.filter(
DocumentSegment.id == str(segment_id), DocumentSegment.tenant_id == current_user.current_tenant_id
).first()
segment_id = str(segment_id)
segment = SegmentService.get_segment_by_id(segment_id=segment_id, tenant_id=current_user.current_tenant_id)
if not segment:
raise NotFound("Segment not found.")
SegmentService.delete_segment(segment, document, dataset)
@@ -179,9 +194,7 @@ class DatasetSegmentApi(DatasetApiResource):
raise ProviderNotInitializeError(ex.description)
# check segment
segment_id = str(segment_id)
segment = DocumentSegment.query.filter(
DocumentSegment.id == str(segment_id), DocumentSegment.tenant_id == current_user.current_tenant_id
).first()
segment = SegmentService.get_segment_by_id(segment_id=segment_id, tenant_id=current_user.current_tenant_id)
if not segment:
raise NotFound("Segment not found.")
@@ -190,12 +203,200 @@ class DatasetSegmentApi(DatasetApiResource):
parser.add_argument("segment", type=dict, required=False, nullable=True, location="json")
args = parser.parse_args()
SegmentService.segment_create_args_validate(args["segment"], document)
segment = SegmentService.update_segment(SegmentUpdateArgs(**args["segment"]), segment, document, dataset)
return {"data": marshal(segment, segment_fields), "doc_form": document.doc_form}, 200
updated_segment = SegmentService.update_segment(
SegmentUpdateArgs(**args["segment"]), segment, document, dataset
)
return {"data": marshal(updated_segment, segment_fields), "doc_form": document.doc_form}, 200
class ChildChunkApi(DatasetApiResource):
"""Resource for child chunks."""
@cloud_edition_billing_resource_check("vector_space", "dataset")
@cloud_edition_billing_knowledge_limit_check("add_segment", "dataset")
def post(self, tenant_id, dataset_id, document_id, segment_id):
"""Create child chunk."""
# 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 segment
segment_id = str(segment_id)
segment = SegmentService.get_segment_by_id(segment_id=segment_id, tenant_id=current_user.current_tenant_id)
if not segment:
raise NotFound("Segment not found.")
# check embedding model setting
if dataset.indexing_technique == "high_quality":
try:
model_manager = ModelManager()
model_manager.get_model_instance(
tenant_id=current_user.current_tenant_id,
provider=dataset.embedding_model_provider,
model_type=ModelType.TEXT_EMBEDDING,
model=dataset.embedding_model,
)
except LLMBadRequestError:
raise ProviderNotInitializeError(
"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider."
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
# validate args
parser = reqparse.RequestParser()
parser.add_argument("content", type=str, required=True, nullable=False, location="json")
args = parser.parse_args()
try:
child_chunk = SegmentService.create_child_chunk(args.get("content"), segment, document, dataset)
except ChildChunkIndexingServiceError as e:
raise ChildChunkIndexingError(str(e))
return {"data": marshal(child_chunk, child_chunk_fields)}, 200
def get(self, tenant_id, dataset_id, document_id, segment_id):
"""Get child chunks."""
# 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 segment
segment_id = str(segment_id)
segment = SegmentService.get_segment_by_id(segment_id=segment_id, tenant_id=current_user.current_tenant_id)
if not segment:
raise NotFound("Segment not found.")
parser = reqparse.RequestParser()
parser.add_argument("limit", type=int, default=20, location="args")
parser.add_argument("keyword", type=str, default=None, location="args")
parser.add_argument("page", type=int, default=1, location="args")
args = parser.parse_args()
page = args["page"]
limit = min(args["limit"], 100)
keyword = args["keyword"]
child_chunks = SegmentService.get_child_chunks(segment_id, document_id, dataset_id, page, limit, keyword)
return {
"data": marshal(child_chunks.items, child_chunk_fields),
"total": child_chunks.total,
"total_pages": child_chunks.pages,
"page": page,
"limit": limit,
}, 200
class DatasetChildChunkApi(DatasetApiResource):
"""Resource for updating child chunks."""
@cloud_edition_billing_knowledge_limit_check("add_segment", "dataset")
def delete(self, tenant_id, dataset_id, document_id, segment_id, child_chunk_id):
"""Delete child chunk."""
# 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 segment
segment_id = str(segment_id)
segment = SegmentService.get_segment_by_id(segment_id=segment_id, tenant_id=current_user.current_tenant_id)
if not segment:
raise NotFound("Segment not found.")
# check child chunk
child_chunk_id = str(child_chunk_id)
child_chunk = SegmentService.get_child_chunk_by_id(
child_chunk_id=child_chunk_id, tenant_id=current_user.current_tenant_id
)
if not child_chunk:
raise NotFound("Child chunk not found.")
try:
SegmentService.delete_child_chunk(child_chunk, dataset)
except ChildChunkDeleteIndexServiceError as e:
raise ChildChunkDeleteIndexError(str(e))
return {"result": "success"}, 200
@cloud_edition_billing_resource_check("vector_space", "dataset")
@cloud_edition_billing_knowledge_limit_check("add_segment", "dataset")
def patch(self, tenant_id, dataset_id, document_id, segment_id, child_chunk_id):
"""Update child chunk."""
# 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.")
# get document
document = DocumentService.get_document(dataset_id, document_id)
if not document:
raise NotFound("Document not found.")
# get segment
segment = SegmentService.get_segment_by_id(segment_id=segment_id, tenant_id=current_user.current_tenant_id)
if not segment:
raise NotFound("Segment not found.")
# get child chunk
child_chunk = SegmentService.get_child_chunk_by_id(
child_chunk_id=child_chunk_id, tenant_id=current_user.current_tenant_id
)
if not child_chunk:
raise NotFound("Child chunk not found.")
# validate args
parser = reqparse.RequestParser()
parser.add_argument("content", type=str, required=True, nullable=False, location="json")
args = parser.parse_args()
try:
child_chunk = SegmentService.update_child_chunk(
args.get("content"), child_chunk, segment, document, dataset
)
except ChildChunkIndexingServiceError as e:
raise ChildChunkIndexingError(str(e))
return {"data": marshal(child_chunk, child_chunk_fields)}, 200
api.add_resource(SegmentApi, "/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/segments")
api.add_resource(
DatasetSegmentApi, "/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/segments/<uuid:segment_id>"
)
api.add_resource(
ChildChunkApi, "/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/segments/<uuid:segment_id>/child_chunks"
)
api.add_resource(
DatasetChildChunkApi,
"/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/segments/<uuid:segment_id>/child_chunks/<uuid:child_chunk_id>",
)
@@ -0,0 +1,21 @@
from flask_login import current_user # type: ignore
from flask_restful import Resource # type: ignore
from controllers.service_api import api
from controllers.service_api.wraps import validate_dataset_token
from core.model_runtime.utils.encoders import jsonable_encoder
from services.model_provider_service import ModelProviderService
class ModelProviderAvailableModelApi(Resource):
@validate_dataset_token
def get(self, _, model_type):
tenant_id = current_user.current_tenant_id
model_provider_service = ModelProviderService()
models = model_provider_service.get_models_by_model_type(tenant_id=tenant_id, model_type=model_type)
return jsonable_encoder({"data": models})
api.add_resource(ModelProviderAvailableModelApi, "/workspaces/current/models/model-types/<string:model_type>")
+21
View File
@@ -59,6 +59,27 @@ def validate_app_token(view: Optional[Callable] = None, *, fetch_user_arg: Optio
if tenant.status == TenantStatus.ARCHIVE:
raise Forbidden("The workspace's status is archived.")
tenant_account_join = (
db.session.query(Tenant, TenantAccountJoin)
.filter(Tenant.id == api_token.tenant_id)
.filter(TenantAccountJoin.tenant_id == Tenant.id)
.filter(TenantAccountJoin.role.in_(["owner"]))
.filter(Tenant.status == TenantStatus.NORMAL)
.one_or_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()
# Login admin
if account:
account.current_tenant = tenant
current_app.login_manager._update_request_context_with_user(account) # type: ignore
user_logged_in.send(current_app._get_current_object(), user=_get_user()) # type: ignore
else:
raise Unauthorized("Tenant owner account does not exist.")
else:
raise Unauthorized("Tenant does not exist.")
kwargs["app_model"] = app_model
if fetch_user_arg:
+2 -4
View File
@@ -1,10 +1,10 @@
from flask_restful import marshal_with # type: ignore
from controllers.common import fields
from controllers.common import helpers as controller_helpers
from controllers.web import api
from controllers.web.error import AppUnavailableError
from controllers.web.wraps import WebApiResource
from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict
from models.model import App, AppMode
from services.app_service import AppService
@@ -31,9 +31,7 @@ class AppParameterApi(WebApiResource):
user_input_form = features_dict.get("user_input_form", [])
return controller_helpers.get_parameters_from_feature_dict(
features_dict=features_dict, user_input_form=user_input_form
)
return get_parameters_from_feature_dict(features_dict=features_dict, user_input_form=user_input_form)
class AppMeta(WebApiResource):
+1
View File
@@ -46,6 +46,7 @@ class MessageListApi(WebApiResource):
"retriever_resources": fields.List(fields.Nested(retriever_resource_fields)),
"created_at": TimestampField,
"agent_thoughts": fields.List(fields.Nested(agent_thought_fields)),
"metadata": fields.Raw(attribute="message_metadata_dict"),
"status": fields.String,
"error": fields.String,
}

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