mirror of
https://github.com/langgenius/dify.git
synced 2024-11-16 11:42:29 +08:00
Compare commits
18 Commits
9d5ae15cf4
...
c15c36cb60
Author | SHA1 | Date | |
---|---|---|---|
|
c15c36cb60 | ||
|
51db59622c | ||
|
814c9fab1c | ||
|
3e1be72db8 | ||
|
fc37240b4f | ||
|
71029e4d6c | ||
|
b136e7821b | ||
|
c3f1b9978a | ||
|
fec99fcc5e | ||
|
d9216b686f | ||
|
4c151e1c25 | ||
|
db1d2aaff5 | ||
|
4322fdc910 | ||
|
2a5c5a4e15 | ||
|
4b2abf8ac2 | ||
|
365cb4b368 | ||
|
c85bff235d | ||
|
ad16180b1a |
2
.github/actions/setup-poetry/action.yml
vendored
2
.github/actions/setup-poetry/action.yml
vendored
|
@ -4,7 +4,7 @@ inputs:
|
||||||
python-version:
|
python-version:
|
||||||
description: Python version to use and the Poetry installed with
|
description: Python version to use and the Poetry installed with
|
||||||
required: true
|
required: true
|
||||||
default: '3.10'
|
default: '3.11'
|
||||||
poetry-version:
|
poetry-version:
|
||||||
description: Poetry version to set up
|
description: Poetry version to set up
|
||||||
required: true
|
required: true
|
||||||
|
|
1
.github/workflows/api-tests.yml
vendored
1
.github/workflows/api-tests.yml
vendored
|
@ -20,7 +20,6 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version:
|
python-version:
|
||||||
- "3.10"
|
|
||||||
- "3.11"
|
- "3.11"
|
||||||
- "3.12"
|
- "3.12"
|
||||||
|
|
||||||
|
|
1
.github/workflows/vdb-tests.yml
vendored
1
.github/workflows/vdb-tests.yml
vendored
|
@ -20,7 +20,6 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version:
|
python-version:
|
||||||
- "3.10"
|
|
||||||
- "3.11"
|
- "3.11"
|
||||||
- "3.12"
|
- "3.12"
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
alt="chat on Discord"></a>
|
alt="chat on Discord"></a>
|
||||||
|
<a href="https://reddit.com/r/difyai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
|
||||||
|
alt="join Reddit"></a>
|
||||||
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
alt="follow on X(Twitter)"></a>
|
alt="follow on X(Twitter)"></a>
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
alt="chat on Discord"></a>
|
alt="chat on Discord"></a>
|
||||||
|
<a href="https://reddit.com/r/difyai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
|
||||||
|
alt="join Reddit"></a>
|
||||||
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
alt="follow on X(Twitter)"></a>
|
alt="follow on X(Twitter)"></a>
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
alt="chat on Discord"></a>
|
alt="chat on Discord"></a>
|
||||||
|
<a href="https://reddit.com/r/difyai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
|
||||||
|
alt="join Reddit"></a>
|
||||||
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
alt="follow on X(Twitter)"></a>
|
alt="follow on X(Twitter)"></a>
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
alt="chat en Discord"></a>
|
alt="chat en Discord"></a>
|
||||||
|
<a href="https://reddit.com/r/difyai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
|
||||||
|
alt="join Reddit"></a>
|
||||||
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
alt="seguir en X(Twitter)"></a>
|
alt="seguir en X(Twitter)"></a>
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
alt="chat sur Discord"></a>
|
alt="chat sur Discord"></a>
|
||||||
|
<a href="https://reddit.com/r/difyai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
|
||||||
|
alt="join Reddit"></a>
|
||||||
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
alt="suivre sur X(Twitter)"></a>
|
alt="suivre sur X(Twitter)"></a>
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
alt="Discordでチャット"></a>
|
alt="Discordでチャット"></a>
|
||||||
|
<a href="https://reddit.com/r/difyai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
|
||||||
|
alt="Reddit"></a>
|
||||||
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
alt="X(Twitter)でフォロー"></a>
|
alt="X(Twitter)でフォロー"></a>
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
alt="chat on Discord"></a>
|
alt="chat on Discord"></a>
|
||||||
|
<a href="https://reddit.com/r/difyai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
|
||||||
|
alt="Follow Reddit"></a>
|
||||||
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
alt="follow on X(Twitter)"></a>
|
alt="follow on X(Twitter)"></a>
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
alt="chat on Discord"></a>
|
alt="chat on Discord"></a>
|
||||||
|
<a href="https://reddit.com/r/difyai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
|
||||||
|
alt="Follow Reddit"></a>
|
||||||
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
alt="follow on X(Twitter)"></a>
|
alt="follow on X(Twitter)"></a>
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
alt="chat on Discord"></a>
|
alt="chat on Discord"></a>
|
||||||
|
<a href="https://reddit.com/r/difyai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
|
||||||
|
alt="Follow Reddit"></a>
|
||||||
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
alt="follow on X(Twitter)"></a>
|
alt="follow on X(Twitter)"></a>
|
||||||
|
|
180
README_SI.md
Normal file
180
README_SI.md
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Predstavljamo nalaganje datotek Dify Workflow: znova ustvarite Google NotebookLM Podcast</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<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>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://dify.ai" target="_blank">
|
||||||
|
<img alt="Static Badge" src="https://img.shields.io/badge/Product-F04438"></a>
|
||||||
|
<a href="https://dify.ai/pricing" target="_blank">
|
||||||
|
<img alt="Static Badge" src="https://img.shields.io/badge/free-pricing?logo=free&color=%20%23155EEF&label=pricing&labelColor=%20%23528bff"></a>
|
||||||
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
|
alt="chat on Discord"></a>
|
||||||
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
|
alt="follow on X(Twitter)"></a>
|
||||||
|
<a href="https://hub.docker.com/u/langgenius" target="_blank">
|
||||||
|
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
|
||||||
|
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
|
||||||
|
<img alt="Commits last month" src="https://img.shields.io/github/commit-activity/m/langgenius/dify?labelColor=%20%2332b583&color=%20%2312b76a"></a>
|
||||||
|
<a href="https://github.com/langgenius/dify/" target="_blank">
|
||||||
|
<img alt="Issues closed" src="https://img.shields.io/github/issues-search?query=repo%3Alanggenius%2Fdify%20is%3Aclosed&label=issues%20closed&labelColor=%20%237d89b0&color=%20%235d6b98"></a>
|
||||||
|
<a href="https://github.com/langgenius/dify/discussions/" target="_blank">
|
||||||
|
<img alt="Discussion posts" src="https://img.shields.io/github/discussions/langgenius/dify?labelColor=%20%239b8afb&color=%20%237a5af8"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="./README.md"><img alt="README in English" src="https://img.shields.io/badge/English-d9d9d9"></a>
|
||||||
|
<a href="./README_CN.md"><img alt="简体中文版自述文件" src="https://img.shields.io/badge/简体中文-d9d9d9"></a>
|
||||||
|
<a href="./README_JA.md"><img alt="日本語のREADME" src="https://img.shields.io/badge/日本語-d9d9d9"></a>
|
||||||
|
<a href="./README_ES.md"><img alt="README en Español" src="https://img.shields.io/badge/Español-d9d9d9"></a>
|
||||||
|
<a href="./README_FR.md"><img alt="README en Français" src="https://img.shields.io/badge/Français-d9d9d9"></a>
|
||||||
|
<a href="./README_KL.md"><img alt="README tlhIngan Hol" src="https://img.shields.io/badge/Klingon-d9d9d9"></a>
|
||||||
|
<a href="./README_KR.md"><img alt="README in Korean" src="https://img.shields.io/badge/한국어-d9d9d9"></a>
|
||||||
|
<a href="./README_AR.md"><img alt="README بالعربية" src="https://img.shields.io/badge/العربية-d9d9d9"></a>
|
||||||
|
<a href="./README_TR.md"><img alt="Türkçe README" src="https://img.shields.io/badge/Türkçe-d9d9d9"></a>
|
||||||
|
<a href="./README_VI.md"><img alt="README Tiếng Việt" src="https://img.shields.io/badge/Ti%E1%BA%BFng%20Vi%E1%BB%87t-d9d9d9"></a>
|
||||||
|
<a href="./README_SI.md"><img alt="README Slovenščina" src="https://img.shields.io/badge/Sloven%C5%A1%C4%8Dina-d9d9d9"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
Dify je odprtokodna platforma za razvoj aplikacij LLM. Njegov intuitivni vmesnik združuje agentski potek dela z umetno inteligenco, cevovod RAG, zmogljivosti agentov, upravljanje modelov, funkcije opazovanja in več, kar vam omogoča hiter prehod od prototipa do proizvodnje.
|
||||||
|
|
||||||
|
## Hitri začetek
|
||||||
|
> Preden namestite Dify, se prepričajte, da vaša naprava izpolnjuje naslednje minimalne sistemske zahteve:
|
||||||
|
>
|
||||||
|
>- CPU >= 2 Core
|
||||||
|
>- RAM >= 4 GiB
|
||||||
|
|
||||||
|
</br>
|
||||||
|
|
||||||
|
Najlažji način za zagon strežnika Dify je prek docker compose . Preden zaženete Dify z naslednjimi ukazi, se prepričajte, da sta Docker in Docker Compose nameščena na vašem računalniku:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd dify
|
||||||
|
cd docker
|
||||||
|
cp .env.example .env
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Po zagonu lahko dostopate do nadzorne plošče Dify v brskalniku na [http://localhost/install](http://localhost/install) in začnete postopek inicializacije.
|
||||||
|
|
||||||
|
#### Iskanje pomoči
|
||||||
|
Prosimo, glejte naša pogosta vprašanja [FAQ](https://docs.dify.ai/getting-started/install-self-hosted/faqs) če naletite na težave pri nastavitvi Dify. Če imate še vedno težave, se obrnite na [skupnost ali nas](#community--contact).
|
||||||
|
|
||||||
|
> Če želite prispevati k Difyju ali narediti dodaten razvoj, glejte naš vodnik za [uvajanje iz izvorne kode](https://docs.dify.ai/getting-started/install-self-hosted/local-source-code)
|
||||||
|
|
||||||
|
## Ključne značilnosti
|
||||||
|
**1. Potek dela**:
|
||||||
|
Zgradite in preizkusite zmogljive poteke dela AI na vizualnem platnu, pri čemer izkoristite vse naslednje funkcije in več.
|
||||||
|
|
||||||
|
|
||||||
|
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**2. Celovita podpora za modele**:
|
||||||
|
Brezhibna integracija s stotinami lastniških/odprtokodnih LLM-jev ducatov ponudnikov sklepanja in samostojnih rešitev, ki pokrivajo GPT, Mistral, Llama3 in vse modele, združljive z API-jem OpenAI. Celoten seznam podprtih ponudnikov modelov najdete [tukaj](https://docs.dify.ai/getting-started/readme/model-providers).
|
||||||
|
|
||||||
|
![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3)
|
||||||
|
|
||||||
|
|
||||||
|
**3. Prompt IDE**:
|
||||||
|
intuitivni vmesnik za ustvarjanje pozivov, primerjavo zmogljivosti modela in dodajanje dodatnih funkcij, kot je pretvorba besedila v govor, aplikaciji, ki temelji na klepetu.
|
||||||
|
|
||||||
|
**4. RAG Pipeline**:
|
||||||
|
E Obsežne zmogljivosti RAG, ki pokrivajo vse od vnosa dokumenta do priklica, s podporo za ekstrakcijo besedila iz datotek PDF, PPT in drugih običajnih formatov dokumentov.
|
||||||
|
|
||||||
|
**5. Agent capabilities**:
|
||||||
|
definirate lahko agente, ki temeljijo na klicanju funkcij LLM ali ReAct, in dodate vnaprej izdelana orodja ali orodja po meri za agenta. Dify ponuja več kot 50 vgrajenih orodij za agente AI, kot so Google Search, DALL·E, Stable Diffusion in WolframAlpha.
|
||||||
|
|
||||||
|
**6. LLMOps**:
|
||||||
|
Spremljajte in analizirajte dnevnike aplikacij in učinkovitost skozi čas. Pozive, nabore podatkov in modele lahko nenehno izboljšujete na podlagi proizvodnih podatkov in opomb.
|
||||||
|
|
||||||
|
**7. Backend-as-a-Service**:
|
||||||
|
AVse ponudbe Difyja so opremljene z ustreznimi API-ji, tako da lahko Dify brez težav integrirate v svojo poslovno logiko.
|
||||||
|
|
||||||
|
|
||||||
|
## Uporaba Dify
|
||||||
|
|
||||||
|
- **Cloud </br>**
|
||||||
|
Gostimo storitev Dify Cloud za vsakogar, ki jo lahko preizkusite brez nastavitev. Zagotavlja vse zmožnosti različice za samostojno namestitev in vključuje 200 brezplačnih klicev GPT-4 v načrtu peskovnika.
|
||||||
|
|
||||||
|
- **Self-hosting Dify Community Edition</br>**
|
||||||
|
Hitro zaženite Dify v svojem okolju s tem [začetnim vodnikom](#quick-start) . Za dodatne reference in podrobnejša navodila uporabite našo [dokumentacijo](https://docs.dify.ai) .
|
||||||
|
|
||||||
|
|
||||||
|
- **Dify za podjetja/organizacije</br>**
|
||||||
|
Ponujamo dodatne funkcije, osredotočene na podjetja. Zabeležite svoja vprašanja prek tega klepetalnega robota ali nam pošljite e-pošto, da se pogovorimo o potrebah podjetja. </br>
|
||||||
|
> Za novoustanovljena podjetja in mala podjetja, ki uporabljajo AWS, si oglejte Dify Premium na AWS Marketplace in ga z enim klikom uvedite v svoj AWS VPC. To je cenovno ugodna ponudba AMI z možnostjo ustvarjanja aplikacij z logotipom in blagovno znamko po meri.
|
||||||
|
|
||||||
|
|
||||||
|
## Staying ahead
|
||||||
|
|
||||||
|
Star Dify on GitHub and be instantly notified of new releases.
|
||||||
|
|
||||||
|
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
|
||||||
|
|
||||||
|
|
||||||
|
## Napredne nastavitve
|
||||||
|
|
||||||
|
Če morate prilagoditi konfiguracijo, si oglejte komentarje v naši datoteki .env.example in posodobite ustrezne vrednosti v svoji .env datoteki. Poleg tega boste morda morali prilagoditi docker-compose.yamlsamo datoteko, na primer spremeniti različice slike, preslikave vrat ali namestitve nosilca, glede na vaše specifično okolje in zahteve za uvajanje. Po kakršnih koli spremembah ponovno zaženite docker-compose up -d. Celoten seznam razpoložljivih spremenljivk okolja najdete tukaj .
|
||||||
|
|
||||||
|
Če želite konfigurirati visoko razpoložljivo nastavitev, so na voljo Helm Charts in datoteke YAML, ki jih prispeva skupnost, ki omogočajo uvedbo Difyja v Kubernetes.
|
||||||
|
|
||||||
|
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
|
||||||
|
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
|
||||||
|
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
|
||||||
|
|
||||||
|
#### Uporaba Terraform za uvajanje
|
||||||
|
|
||||||
|
namestite Dify v Cloud Platform z enim klikom z uporabo [terraform](https://www.terraform.io/)
|
||||||
|
|
||||||
|
##### Azure Global
|
||||||
|
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
|
||||||
|
|
||||||
|
##### Google Cloud
|
||||||
|
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
|
||||||
|
|
||||||
|
## Prispevam
|
||||||
|
|
||||||
|
Za tiste, ki bi radi prispevali kodo, si oglejte naš vodnik za prispevke . Hkrati vas prosimo, da podprete Dify tako, da ga delite na družbenih medijih ter na dogodkih in konferencah.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
> Iščemo sodelavce za pomoč pri prevajanju Difyja v jezike, ki niso mandarinščina ali angleščina. Če želite pomagati, si oglejte i18n README za več informacij in nam pustite komentar v global-userskanalu našega strežnika skupnosti Discord .
|
||||||
|
|
||||||
|
## Skupnost in stik
|
||||||
|
|
||||||
|
* [Github Discussion](https://github.com/langgenius/dify/discussions). Najboljše za: izmenjavo povratnih informacij in postavljanje vprašanj.
|
||||||
|
* [GitHub Issues](https://github.com/langgenius/dify/issues). Najboljše za: hrošče, na katere naletite pri uporabi Dify.AI, in predloge funkcij. Oglejte si naš [vodnik za prispevke](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
|
||||||
|
* [Discord](https://discord.gg/FngNHpbcY7). Najboljše za: deljenje vaših aplikacij in druženje s skupnostjo.
|
||||||
|
* [X(Twitter)](https://twitter.com/dify_ai). Najboljše za: deljenje vaših aplikacij in druženje s skupnostjo.
|
||||||
|
|
||||||
|
**Contributors**
|
||||||
|
|
||||||
|
<a href="https://github.com/langgenius/dify/graphs/contributors">
|
||||||
|
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
## Star history
|
||||||
|
|
||||||
|
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)
|
||||||
|
|
||||||
|
|
||||||
|
## Varnostno razkritje
|
||||||
|
|
||||||
|
Zaradi zaščite vaše zasebnosti se izogibajte objavljanju varnostnih vprašanj na GitHub. Namesto tega pošljite vprašanja na security@dify.ai in zagotovili vam bomo podrobnejši odgovor.
|
||||||
|
|
||||||
|
## Licenca
|
||||||
|
|
||||||
|
To skladišče je na voljo pod [odprtokodno licenco Dify](LICENSE) , ki je v bistvu Apache 2.0 z nekaj dodatnimi omejitvami.
|
|
@ -15,6 +15,9 @@
|
||||||
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
alt="Discord'da sohbet et"></a>
|
alt="Discord'da sohbet et"></a>
|
||||||
|
<a href="https://reddit.com/r/difyai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
|
||||||
|
alt="Follow Reddit"></a>
|
||||||
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
alt="X(Twitter)'da takip et"></a>
|
alt="X(Twitter)'da takip et"></a>
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
|
||||||
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
|
||||||
alt="chat trên Discord"></a>
|
alt="chat trên Discord"></a>
|
||||||
|
<a href="https://reddit.com/r/difyai" target="_blank">
|
||||||
|
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
|
||||||
|
alt="Follow Reddit"></a>
|
||||||
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
|
||||||
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
|
||||||
alt="theo dõi trên X(Twitter)"></a>
|
alt="theo dõi trên X(Twitter)"></a>
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
## Usage
|
## 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`.
|
|
||||||
|
|
||||||
1. Start the docker-compose stack
|
1. Start the docker-compose stack
|
||||||
|
|
||||||
The backend require some middleware, including PostgreSQL, Redis, and Weaviate, which can be started together using `docker-compose`.
|
The backend require some middleware, including PostgreSQL, Redis, and Weaviate, which can be started together using `docker-compose`.
|
||||||
|
@ -30,26 +27,24 @@
|
||||||
SECRET_KEY=${secret_key}" .env
|
SECRET_KEY=${secret_key}" .env
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Create environment.
|
4. Prepare Python environment
|
||||||
|
|
||||||
Dify API service uses [Poetry](https://python-poetry.org/docs/) to manage dependencies. You can execute `poetry shell` to activate the environment.
|
Dify API services requires Python 3.11 or 3.12, and the [Poetry](https://python-poetry.org/docs/) for dependency management.
|
||||||
|
- To install Poetry, please refer to
|
||||||
|
the [Poetry's installation guide](https://python-poetry.org/docs/#installation). The simplest way is to run the `pip install poetry` command to install Poetry on pip.
|
||||||
|
- Run `poetry env use 3.12` to switch to the Python version for Poetry, please refer the usage of `poetry env use`
|
||||||
|
command in [Poetry docs](https://python-poetry.org/docs/managing-environments/#switching-between-environments).
|
||||||
|
- Run `poetry shell` to activate the shell environment with Poetry support.
|
||||||
|
|
||||||
5. Install dependencies
|
5. Install dependencies
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
poetry env use 3.10
|
cd api
|
||||||
|
poetry env use 3.12
|
||||||
poetry install
|
poetry install
|
||||||
```
|
```
|
||||||
|
|
||||||
In case of contributors missing to update dependencies for `pyproject.toml`, you can perform the following shell instead.
|
6. Run db migration
|
||||||
|
|
||||||
```bash
|
|
||||||
poetry shell # activate current environment
|
|
||||||
poetry add $(cat requirements.txt) # install dependencies of production and update pyproject.toml
|
|
||||||
poetry add $(cat requirements-dev.txt) --group dev # install dependencies of development and update pyproject.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
6. Run migrate
|
|
||||||
|
|
||||||
Before the first launch, migrate the database to the latest version.
|
Before the first launch, migrate the database to the latest version.
|
||||||
|
|
||||||
|
@ -57,15 +52,18 @@
|
||||||
poetry run python -m flask db upgrade
|
poetry run python -m flask db upgrade
|
||||||
```
|
```
|
||||||
|
|
||||||
7. Start backend
|
7. Start api service
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
poetry run python -m flask run --host 0.0.0.0 --port=5001 --debug
|
poetry run python -m flask run --host 0.0.0.0 --port=5001
|
||||||
```
|
```
|
||||||
|
|
||||||
8. Start Dify [web](../web) service.
|
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.
|
|
||||||
|
10. Start the worker service, if you need to handle and debug the async tasks (e.g. dataset importing and documents
|
||||||
|
indexing), please start the worker service.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
poetry run python -m celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion
|
poetry run python -m celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
python_version = sys.version_info
|
||||||
|
if not ((3, 11) <= python_version < (3, 13)):
|
||||||
|
print(f"Python 3.11 or 3.12 is required, current version is {python_version.major}.{python_version.minor}")
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
from configs import dify_config
|
from configs import dify_config
|
||||||
|
|
||||||
if not dify_config.DEBUG:
|
if not dify_config.DEBUG:
|
||||||
|
@ -30,9 +35,6 @@ from models import account, dataset, model, source, task, tool, tools, web # no
|
||||||
|
|
||||||
# DO NOT REMOVE ABOVE
|
# DO NOT REMOVE ABOVE
|
||||||
|
|
||||||
if sys.version_info[:2] == (3, 10):
|
|
||||||
print("Warning: Python 3.10 will not be supported in the next version.")
|
|
||||||
|
|
||||||
|
|
||||||
warnings.simplefilter("ignore", ResourceWarning)
|
warnings.simplefilter("ignore", ResourceWarning)
|
||||||
|
|
||||||
|
|
|
@ -589,7 +589,7 @@ def upgrade_db():
|
||||||
click.echo(click.style("Database migration successful!", fg="green"))
|
click.echo(click.style("Database migration successful!", fg="green"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"Database migration failed: {e}")
|
logging.exception("Failed to execute database migration")
|
||||||
finally:
|
finally:
|
||||||
lock.release()
|
lock.release()
|
||||||
else:
|
else:
|
||||||
|
@ -633,7 +633,7 @@ where sites.id is null limit 1000"""
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
failed_app_ids.append(app_id)
|
failed_app_ids.append(app_id)
|
||||||
click.echo(click.style("Failed to fix missing site for app {}".format(app_id), fg="red"))
|
click.echo(click.style("Failed to fix missing site for app {}".format(app_id), fg="red"))
|
||||||
logging.exception(f"Fix app related site missing issue failed, error: {e}")
|
logging.exception(f"Failed to fix app related site missing issue, app_id: {app_id}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not processed_count:
|
if not processed_count:
|
||||||
|
|
|
@ -17,6 +17,7 @@ language_timezone_mapping = {
|
||||||
"hi-IN": "Asia/Kolkata",
|
"hi-IN": "Asia/Kolkata",
|
||||||
"tr-TR": "Europe/Istanbul",
|
"tr-TR": "Europe/Istanbul",
|
||||||
"fa-IR": "Asia/Tehran",
|
"fa-IR": "Asia/Tehran",
|
||||||
|
"sl-SI": "Europe/Ljubljana",
|
||||||
}
|
}
|
||||||
|
|
||||||
languages = list(language_timezone_mapping.keys())
|
languages = list(language_timezone_mapping.keys())
|
||||||
|
|
|
@ -70,7 +70,7 @@ class ChatMessageAudioApi(Resource):
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"internal server error, {str(e)}.")
|
logging.exception("Failed to handle post request to ChatMessageAudioApi")
|
||||||
raise InternalServerError()
|
raise InternalServerError()
|
||||||
|
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ class ChatMessageTextApi(Resource):
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"internal server error, {str(e)}.")
|
logging.exception("Failed to handle post request to ChatMessageTextApi")
|
||||||
raise InternalServerError()
|
raise InternalServerError()
|
||||||
|
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ class TextModesApi(Resource):
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"internal server error, {str(e)}.")
|
logging.exception("Failed to handle get request to TextModesApi")
|
||||||
raise InternalServerError()
|
raise InternalServerError()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
@ -314,7 +314,7 @@ def _get_conversation(app_model, conversation_id):
|
||||||
raise NotFound("Conversation Not Exists.")
|
raise NotFound("Conversation Not Exists.")
|
||||||
|
|
||||||
if not conversation.read_at:
|
if not conversation.read_at:
|
||||||
conversation.read_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
conversation.read_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
conversation.read_account_id = current_user.id
|
conversation.read_account_id = current_user.id
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from flask_restful import Resource, marshal_with, reqparse
|
from flask_restful import Resource, marshal_with, reqparse
|
||||||
|
@ -75,7 +75,7 @@ class AppSite(Resource):
|
||||||
setattr(site, attr_name, value)
|
setattr(site, attr_name, value)
|
||||||
|
|
||||||
site.updated_by = current_user.id
|
site.updated_by = current_user.id
|
||||||
site.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
site.updated_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return site
|
return site
|
||||||
|
@ -99,7 +99,7 @@ class AppSiteAccessTokenReset(Resource):
|
||||||
|
|
||||||
site.code = Site.generate_code(16)
|
site.code = Site.generate_code(16)
|
||||||
site.updated_by = current_user.id
|
site.updated_by = current_user.id
|
||||||
site.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
site.updated_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return site
|
return site
|
||||||
|
|
|
@ -65,7 +65,7 @@ class ActivateApi(Resource):
|
||||||
account.timezone = args["timezone"]
|
account.timezone = args["timezone"]
|
||||||
account.interface_theme = "light"
|
account.interface_theme = "light"
|
||||||
account.status = AccountStatus.ACTIVE.value
|
account.status = AccountStatus.ACTIVE.value
|
||||||
account.initialized_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
account.initialized_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
token_pair = AccountService.login(account, ip_address=extract_remote_ip(request))
|
token_pair = AccountService.login(account, ip_address=extract_remote_ip(request))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
@ -106,7 +106,7 @@ class OAuthCallback(Resource):
|
||||||
|
|
||||||
if account.status == AccountStatus.PENDING.value:
|
if account.status == AccountStatus.PENDING.value:
|
||||||
account.status = AccountStatus.ACTIVE.value
|
account.status = AccountStatus.ACTIVE.value
|
||||||
account.initialized_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
account.initialized_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -83,7 +83,7 @@ class DataSourceApi(Resource):
|
||||||
if action == "enable":
|
if action == "enable":
|
||||||
if data_source_binding.disabled:
|
if data_source_binding.disabled:
|
||||||
data_source_binding.disabled = False
|
data_source_binding.disabled = False
|
||||||
data_source_binding.updated_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
data_source_binding.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.add(data_source_binding)
|
db.session.add(data_source_binding)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
else:
|
else:
|
||||||
|
@ -92,7 +92,7 @@ class DataSourceApi(Resource):
|
||||||
if action == "disable":
|
if action == "disable":
|
||||||
if not data_source_binding.disabled:
|
if not data_source_binding.disabled:
|
||||||
data_source_binding.disabled = True
|
data_source_binding.disabled = True
|
||||||
data_source_binding.updated_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
data_source_binding.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.add(data_source_binding)
|
db.session.add(data_source_binding)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from argparse import ArgumentTypeError
|
from argparse import ArgumentTypeError
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
from flask import request
|
from flask import request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
@ -665,7 +665,7 @@ class DocumentProcessingApi(DocumentResource):
|
||||||
raise InvalidActionError("Document not in indexing state.")
|
raise InvalidActionError("Document not in indexing state.")
|
||||||
|
|
||||||
document.paused_by = current_user.id
|
document.paused_by = current_user.id
|
||||||
document.paused_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
document.paused_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
document.is_paused = True
|
document.is_paused = True
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -745,7 +745,7 @@ class DocumentMetadataApi(DocumentResource):
|
||||||
document.doc_metadata[key] = value
|
document.doc_metadata[key] = value
|
||||||
|
|
||||||
document.doc_type = doc_type
|
document.doc_type = doc_type
|
||||||
document.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
document.updated_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return {"result": "success", "message": "Document metadata updated."}, 200
|
return {"result": "success", "message": "Document metadata updated."}, 200
|
||||||
|
@ -787,7 +787,7 @@ class DocumentStatusApi(DocumentResource):
|
||||||
document.enabled = True
|
document.enabled = True
|
||||||
document.disabled_at = None
|
document.disabled_at = None
|
||||||
document.disabled_by = None
|
document.disabled_by = None
|
||||||
document.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
document.updated_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
# Set cache to prevent indexing the same document multiple times
|
# Set cache to prevent indexing the same document multiple times
|
||||||
|
@ -804,9 +804,9 @@ class DocumentStatusApi(DocumentResource):
|
||||||
raise InvalidActionError("Document already disabled.")
|
raise InvalidActionError("Document already disabled.")
|
||||||
|
|
||||||
document.enabled = False
|
document.enabled = False
|
||||||
document.disabled_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
document.disabled_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
document.disabled_by = current_user.id
|
document.disabled_by = current_user.id
|
||||||
document.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
document.updated_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
# Set cache to prevent indexing the same document multiple times
|
# Set cache to prevent indexing the same document multiple times
|
||||||
|
@ -821,9 +821,9 @@ class DocumentStatusApi(DocumentResource):
|
||||||
raise InvalidActionError("Document already archived.")
|
raise InvalidActionError("Document already archived.")
|
||||||
|
|
||||||
document.archived = True
|
document.archived = True
|
||||||
document.archived_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
document.archived_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
document.archived_by = current_user.id
|
document.archived_by = current_user.id
|
||||||
document.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
document.updated_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
if document.enabled:
|
if document.enabled:
|
||||||
|
@ -840,7 +840,7 @@ class DocumentStatusApi(DocumentResource):
|
||||||
document.archived = False
|
document.archived = False
|
||||||
document.archived_at = None
|
document.archived_at = None
|
||||||
document.archived_by = None
|
document.archived_by = None
|
||||||
document.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
document.updated_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
# Set cache to prevent indexing the same document multiple times
|
# Set cache to prevent indexing the same document multiple times
|
||||||
|
@ -948,7 +948,7 @@ class DocumentRetryApi(DocumentResource):
|
||||||
raise DocumentAlreadyFinishedError()
|
raise DocumentAlreadyFinishedError()
|
||||||
retry_documents.append(document)
|
retry_documents.append(document)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"Document {document_id} retry failed: {str(e)}")
|
logging.exception(f"Failed to retry document, document id: {document_id}")
|
||||||
continue
|
continue
|
||||||
# retry document
|
# retry document
|
||||||
DocumentService.retry_document(dataset_id, retry_documents)
|
DocumentService.retry_document(dataset_id, retry_documents)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from flask import request
|
from flask import request
|
||||||
|
@ -188,7 +188,7 @@ class DatasetDocumentSegmentApi(Resource):
|
||||||
raise InvalidActionError("Segment is already disabled.")
|
raise InvalidActionError("Segment is already disabled.")
|
||||||
|
|
||||||
segment.enabled = False
|
segment.enabled = False
|
||||||
segment.disabled_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
segment.disabled_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
segment.disabled_by = current_user.id
|
segment.disabled_by = current_user.id
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from flask_restful import reqparse
|
from flask_restful import reqparse
|
||||||
|
@ -46,7 +46,7 @@ class CompletionApi(InstalledAppResource):
|
||||||
streaming = args["response_mode"] == "streaming"
|
streaming = args["response_mode"] == "streaming"
|
||||||
args["auto_generate_name"] = False
|
args["auto_generate_name"] = False
|
||||||
|
|
||||||
installed_app.last_used_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
installed_app.last_used_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -106,7 +106,7 @@ class ChatApi(InstalledAppResource):
|
||||||
|
|
||||||
args["auto_generate_name"] = False
|
args["auto_generate_name"] = False
|
||||||
|
|
||||||
installed_app.last_used_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
installed_app.last_used_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from flask_restful import Resource, inputs, marshal_with, reqparse
|
from flask_restful import Resource, inputs, marshal_with, reqparse
|
||||||
|
@ -81,7 +81,7 @@ class InstalledAppsListApi(Resource):
|
||||||
tenant_id=current_tenant_id,
|
tenant_id=current_tenant_id,
|
||||||
app_owner_tenant_id=app.tenant_id,
|
app_owner_tenant_id=app.tenant_id,
|
||||||
is_pinned=False,
|
is_pinned=False,
|
||||||
last_used_at=datetime.now(timezone.utc).replace(tzinfo=None),
|
last_used_at=datetime.now(UTC).replace(tzinfo=None),
|
||||||
)
|
)
|
||||||
db.session.add(new_installed_app)
|
db.session.add(new_installed_app)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
|
@ -60,7 +60,7 @@ class AccountInitApi(Resource):
|
||||||
raise InvalidInvitationCodeError()
|
raise InvalidInvitationCodeError()
|
||||||
|
|
||||||
invitation_code.status = "used"
|
invitation_code.status = "used"
|
||||||
invitation_code.used_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
invitation_code.used_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
invitation_code.used_by_tenant_id = account.current_tenant_id
|
invitation_code.used_by_tenant_id = account.current_tenant_id
|
||||||
invitation_code.used_by_account_id = account.id
|
invitation_code.used_by_account_id = account.id
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ class AccountInitApi(Resource):
|
||||||
account.timezone = args["timezone"]
|
account.timezone = args["timezone"]
|
||||||
account.interface_theme = "light"
|
account.interface_theme = "light"
|
||||||
account.status = "active"
|
account.status = "active"
|
||||||
account.initialized_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
account.initialized_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return {"result": "success"}
|
return {"result": "success"}
|
||||||
|
|
|
@ -72,7 +72,10 @@ class DefaultModelApi(Resource):
|
||||||
model=model_setting["model"],
|
model=model_setting["model"],
|
||||||
)
|
)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logging.exception(f"{model_setting['model_type']} save error: {ex}")
|
logging.exception(
|
||||||
|
f"Failed to update default model, model type: {model_setting['model_type']},"
|
||||||
|
f" model:{model_setting.get('model')}"
|
||||||
|
)
|
||||||
raise ex
|
raise ex
|
||||||
|
|
||||||
return {"result": "success"}
|
return {"result": "success"}
|
||||||
|
@ -156,7 +159,10 @@ class ModelProviderModelApi(Resource):
|
||||||
credentials=args["credentials"],
|
credentials=args["credentials"],
|
||||||
)
|
)
|
||||||
except CredentialsValidateFailedError as ex:
|
except CredentialsValidateFailedError as ex:
|
||||||
logging.exception(f"save model credentials error: {ex}")
|
logging.exception(
|
||||||
|
f"Failed to save model credentials, tenant_id: {tenant_id},"
|
||||||
|
f" model: {args.get('model')}, model_type: {args.get('model_type')}"
|
||||||
|
)
|
||||||
raise ValueError(str(ex))
|
raise ValueError(str(ex))
|
||||||
|
|
||||||
return {"result": "success"}, 200
|
return {"result": "success"}, 200
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
@ -198,7 +198,7 @@ def validate_and_get_api_token(scope=None):
|
||||||
if not api_token:
|
if not api_token:
|
||||||
raise Unauthorized("Access token is invalid")
|
raise Unauthorized("Access token is invalid")
|
||||||
|
|
||||||
api_token.last_used_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
api_token.last_used_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return api_token
|
return api_token
|
||||||
|
|
|
@ -59,7 +59,7 @@ class AudioApi(WebApiResource):
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"internal server error: {str(e)}")
|
logging.exception("Failed to handle post request to AudioApi")
|
||||||
raise InternalServerError()
|
raise InternalServerError()
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ class TextApi(WebApiResource):
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"internal server error: {str(e)}")
|
logging.exception("Failed to handle post request to TextApi")
|
||||||
raise InternalServerError()
|
raise InternalServerError()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
from collections.abc import Mapping, Sequence
|
from collections.abc import Mapping, Sequence
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
from typing import Optional, Union, cast
|
from typing import Optional, Union, cast
|
||||||
|
|
||||||
from core.agent.entities import AgentEntity, AgentToolEntity
|
from core.agent.entities import AgentEntity, AgentToolEntity
|
||||||
|
@ -419,7 +419,7 @@ class BaseAgentRunner(AppRunner):
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
|
|
||||||
db_variables.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
db_variables.updated_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db_variables.variables_str = json.dumps(jsonable_encoder(tool_variables.pool))
|
db_variables.variables_str = json.dumps(jsonable_encoder(tool_variables.pool))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db.session.close()
|
db.session.close()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
from enum import Enum
|
from enum import Enum, StrEnum
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, field_validator
|
from pydantic import BaseModel, Field, field_validator
|
||||||
|
@ -88,7 +88,7 @@ class PromptTemplateEntity(BaseModel):
|
||||||
advanced_completion_prompt_template: Optional[AdvancedCompletionPromptTemplateEntity] = None
|
advanced_completion_prompt_template: Optional[AdvancedCompletionPromptTemplateEntity] = None
|
||||||
|
|
||||||
|
|
||||||
class VariableEntityType(str, Enum):
|
class VariableEntityType(StrEnum):
|
||||||
TEXT_INPUT = "text-input"
|
TEXT_INPUT = "text-input"
|
||||||
SELECT = "select"
|
SELECT = "select"
|
||||||
PARAGRAPH = "paragraph"
|
PARAGRAPH = "paragraph"
|
||||||
|
|
|
@ -362,5 +362,5 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator):
|
||||||
if e.args[0] == "I/O operation on closed file.": # ignore this error
|
if e.args[0] == "I/O operation on closed file.": # ignore this error
|
||||||
raise GenerateTaskStoppedError()
|
raise GenerateTaskStoppedError()
|
||||||
else:
|
else:
|
||||||
logger.exception(e)
|
logger.exception(f"Failed to process generate task pipeline, conversation_id: {conversation.id}")
|
||||||
raise e
|
raise e
|
||||||
|
|
|
@ -242,7 +242,7 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc
|
||||||
start_listener_time = time.time()
|
start_listener_time = time.time()
|
||||||
yield MessageAudioStreamResponse(audio=audio_trunk.audio, task_id=task_id)
|
yield MessageAudioStreamResponse(audio=audio_trunk.audio, task_id=task_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(f"Failed to listen audio message, task_id: {task_id}")
|
||||||
break
|
break
|
||||||
if tts_publisher:
|
if tts_publisher:
|
||||||
yield MessageAudioEndStreamResponse(audio="", task_id=task_id)
|
yield MessageAudioEndStreamResponse(audio="", task_id=task_id)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
|
@ -80,7 +80,7 @@ class MessageBasedAppGenerator(BaseAppGenerator):
|
||||||
if e.args[0] == "I/O operation on closed file.": # ignore this error
|
if e.args[0] == "I/O operation on closed file.": # ignore this error
|
||||||
raise GenerateTaskStoppedError()
|
raise GenerateTaskStoppedError()
|
||||||
else:
|
else:
|
||||||
logger.exception(e)
|
logger.exception(f"Failed to handle response, conversation_id: {conversation.id}")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def _get_conversation_by_user(
|
def _get_conversation_by_user(
|
||||||
|
@ -200,7 +200,7 @@ class MessageBasedAppGenerator(BaseAppGenerator):
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db.session.refresh(conversation)
|
db.session.refresh(conversation)
|
||||||
else:
|
else:
|
||||||
conversation.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
conversation.updated_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
message = Message(
|
message = Message(
|
||||||
|
|
|
@ -298,5 +298,7 @@ class WorkflowAppGenerator(BaseAppGenerator):
|
||||||
if e.args[0] == "I/O operation on closed file.": # ignore this error
|
if e.args[0] == "I/O operation on closed file.": # ignore this error
|
||||||
raise GenerateTaskStoppedError()
|
raise GenerateTaskStoppedError()
|
||||||
else:
|
else:
|
||||||
logger.exception(e)
|
logger.exception(
|
||||||
|
f"Fails to process generate task pipeline, task_id: {application_generate_entity.task_id}"
|
||||||
|
)
|
||||||
raise e
|
raise e
|
||||||
|
|
|
@ -216,7 +216,7 @@ class WorkflowAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCycleMa
|
||||||
else:
|
else:
|
||||||
yield MessageAudioStreamResponse(audio=audio_trunk.audio, task_id=task_id)
|
yield MessageAudioStreamResponse(audio=audio_trunk.audio, task_id=task_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(f"Fails to get audio trunk, task_id: {task_id}")
|
||||||
break
|
break
|
||||||
if tts_publisher:
|
if tts_publisher:
|
||||||
yield MessageAudioEndStreamResponse(audio="", task_id=task_id)
|
yield MessageAudioEndStreamResponse(audio="", task_id=task_id)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from enum import Enum
|
from enum import Enum, StrEnum
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, field_validator
|
from pydantic import BaseModel, field_validator
|
||||||
|
@ -11,7 +11,7 @@ from core.workflow.nodes import NodeType
|
||||||
from core.workflow.nodes.base import BaseNodeData
|
from core.workflow.nodes.base import BaseNodeData
|
||||||
|
|
||||||
|
|
||||||
class QueueEvent(str, Enum):
|
class QueueEvent(StrEnum):
|
||||||
"""
|
"""
|
||||||
QueueEvent enum
|
QueueEvent enum
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -86,7 +86,7 @@ class MessageCycleManage:
|
||||||
conversation.name = name
|
conversation.name = name
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if dify_config.DEBUG:
|
if dify_config.DEBUG:
|
||||||
logging.exception(f"generate conversation name failed: {e}")
|
logging.exception(f"generate conversation name failed, conversation_id: {conversation_id}")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
db.session.merge(conversation)
|
db.session.merge(conversation)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from collections.abc import Mapping, Sequence
|
from collections.abc import Mapping, Sequence
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
from typing import Any, Optional, Union, cast
|
from typing import Any, Optional, Union, cast
|
||||||
|
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
@ -144,7 +144,7 @@ class WorkflowCycleManage:
|
||||||
workflow_run.elapsed_time = time.perf_counter() - start_at
|
workflow_run.elapsed_time = time.perf_counter() - start_at
|
||||||
workflow_run.total_tokens = total_tokens
|
workflow_run.total_tokens = total_tokens
|
||||||
workflow_run.total_steps = total_steps
|
workflow_run.total_steps = total_steps
|
||||||
workflow_run.finished_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
workflow_run.finished_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db.session.refresh(workflow_run)
|
db.session.refresh(workflow_run)
|
||||||
|
@ -191,7 +191,7 @@ class WorkflowCycleManage:
|
||||||
workflow_run.elapsed_time = time.perf_counter() - start_at
|
workflow_run.elapsed_time = time.perf_counter() - start_at
|
||||||
workflow_run.total_tokens = total_tokens
|
workflow_run.total_tokens = total_tokens
|
||||||
workflow_run.total_steps = total_steps
|
workflow_run.total_steps = total_steps
|
||||||
workflow_run.finished_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
workflow_run.finished_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ class WorkflowCycleManage:
|
||||||
for workflow_node_execution in running_workflow_node_executions:
|
for workflow_node_execution in running_workflow_node_executions:
|
||||||
workflow_node_execution.status = WorkflowNodeExecutionStatus.FAILED.value
|
workflow_node_execution.status = WorkflowNodeExecutionStatus.FAILED.value
|
||||||
workflow_node_execution.error = error
|
workflow_node_execution.error = error
|
||||||
workflow_node_execution.finished_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
workflow_node_execution.finished_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
workflow_node_execution.elapsed_time = (
|
workflow_node_execution.elapsed_time = (
|
||||||
workflow_node_execution.finished_at - workflow_node_execution.created_at
|
workflow_node_execution.finished_at - workflow_node_execution.created_at
|
||||||
).total_seconds()
|
).total_seconds()
|
||||||
|
@ -259,7 +259,7 @@ class WorkflowCycleManage:
|
||||||
NodeRunMetadataKey.ITERATION_ID: event.in_iteration_id,
|
NodeRunMetadataKey.ITERATION_ID: event.in_iteration_id,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
workflow_node_execution.created_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
workflow_node_execution.created_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
|
|
||||||
session.add(workflow_node_execution)
|
session.add(workflow_node_execution)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
@ -282,7 +282,7 @@ class WorkflowCycleManage:
|
||||||
execution_metadata = (
|
execution_metadata = (
|
||||||
json.dumps(jsonable_encoder(event.execution_metadata)) if event.execution_metadata else None
|
json.dumps(jsonable_encoder(event.execution_metadata)) if event.execution_metadata else None
|
||||||
)
|
)
|
||||||
finished_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
finished_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
elapsed_time = (finished_at - event.start_at).total_seconds()
|
elapsed_time = (finished_at - event.start_at).total_seconds()
|
||||||
|
|
||||||
db.session.query(WorkflowNodeExecution).filter(WorkflowNodeExecution.id == workflow_node_execution.id).update(
|
db.session.query(WorkflowNodeExecution).filter(WorkflowNodeExecution.id == workflow_node_execution.id).update(
|
||||||
|
@ -326,7 +326,7 @@ class WorkflowCycleManage:
|
||||||
inputs = WorkflowEntry.handle_special_values(event.inputs)
|
inputs = WorkflowEntry.handle_special_values(event.inputs)
|
||||||
process_data = WorkflowEntry.handle_special_values(event.process_data)
|
process_data = WorkflowEntry.handle_special_values(event.process_data)
|
||||||
outputs = WorkflowEntry.handle_special_values(event.outputs)
|
outputs = WorkflowEntry.handle_special_values(event.outputs)
|
||||||
finished_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
finished_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
elapsed_time = (finished_at - event.start_at).total_seconds()
|
elapsed_time = (finished_at - event.start_at).total_seconds()
|
||||||
execution_metadata = (
|
execution_metadata = (
|
||||||
json.dumps(jsonable_encoder(event.execution_metadata)) if event.execution_metadata else None
|
json.dumps(jsonable_encoder(event.execution_metadata)) if event.execution_metadata else None
|
||||||
|
@ -654,7 +654,7 @@ class WorkflowCycleManage:
|
||||||
if event.error is None
|
if event.error is None
|
||||||
else WorkflowNodeExecutionStatus.FAILED,
|
else WorkflowNodeExecutionStatus.FAILED,
|
||||||
error=None,
|
error=None,
|
||||||
elapsed_time=(datetime.now(timezone.utc).replace(tzinfo=None) - event.start_at).total_seconds(),
|
elapsed_time=(datetime.now(UTC).replace(tzinfo=None) - event.start_at).total_seconds(),
|
||||||
total_tokens=event.metadata.get("total_tokens", 0) if event.metadata else 0,
|
total_tokens=event.metadata.get("total_tokens", 0) if event.metadata else 0,
|
||||||
execution_metadata=event.metadata,
|
execution_metadata=event.metadata,
|
||||||
finished_at=int(time.time()),
|
finished_at=int(time.time()),
|
||||||
|
|
|
@ -240,7 +240,7 @@ class ProviderConfiguration(BaseModel):
|
||||||
if provider_record:
|
if provider_record:
|
||||||
provider_record.encrypted_config = json.dumps(credentials)
|
provider_record.encrypted_config = json.dumps(credentials)
|
||||||
provider_record.is_valid = True
|
provider_record.is_valid = True
|
||||||
provider_record.updated_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
provider_record.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
else:
|
else:
|
||||||
provider_record = Provider(
|
provider_record = Provider(
|
||||||
|
@ -394,7 +394,7 @@ class ProviderConfiguration(BaseModel):
|
||||||
if provider_model_record:
|
if provider_model_record:
|
||||||
provider_model_record.encrypted_config = json.dumps(credentials)
|
provider_model_record.encrypted_config = json.dumps(credentials)
|
||||||
provider_model_record.is_valid = True
|
provider_model_record.is_valid = True
|
||||||
provider_model_record.updated_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
provider_model_record.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
else:
|
else:
|
||||||
provider_model_record = ProviderModel(
|
provider_model_record = ProviderModel(
|
||||||
|
@ -468,7 +468,7 @@ class ProviderConfiguration(BaseModel):
|
||||||
|
|
||||||
if model_setting:
|
if model_setting:
|
||||||
model_setting.enabled = True
|
model_setting.enabled = True
|
||||||
model_setting.updated_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
model_setting.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
else:
|
else:
|
||||||
model_setting = ProviderModelSetting(
|
model_setting = ProviderModelSetting(
|
||||||
|
@ -503,7 +503,7 @@ class ProviderConfiguration(BaseModel):
|
||||||
|
|
||||||
if model_setting:
|
if model_setting:
|
||||||
model_setting.enabled = False
|
model_setting.enabled = False
|
||||||
model_setting.updated_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
model_setting.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
else:
|
else:
|
||||||
model_setting = ProviderModelSetting(
|
model_setting = ProviderModelSetting(
|
||||||
|
@ -570,7 +570,7 @@ class ProviderConfiguration(BaseModel):
|
||||||
|
|
||||||
if model_setting:
|
if model_setting:
|
||||||
model_setting.load_balancing_enabled = True
|
model_setting.load_balancing_enabled = True
|
||||||
model_setting.updated_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
model_setting.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
else:
|
else:
|
||||||
model_setting = ProviderModelSetting(
|
model_setting = ProviderModelSetting(
|
||||||
|
@ -605,7 +605,7 @@ class ProviderConfiguration(BaseModel):
|
||||||
|
|
||||||
if model_setting:
|
if model_setting:
|
||||||
model_setting.load_balancing_enabled = False
|
model_setting.load_balancing_enabled = False
|
||||||
model_setting.updated_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
model_setting.updated_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
else:
|
else:
|
||||||
model_setting = ProviderModelSetting(
|
model_setting = ProviderModelSetting(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class FileType(str, Enum):
|
class FileType(StrEnum):
|
||||||
IMAGE = "image"
|
IMAGE = "image"
|
||||||
DOCUMENT = "document"
|
DOCUMENT = "document"
|
||||||
AUDIO = "audio"
|
AUDIO = "audio"
|
||||||
|
@ -16,7 +16,7 @@ class FileType(str, Enum):
|
||||||
raise ValueError(f"No matching enum found for value '{value}'")
|
raise ValueError(f"No matching enum found for value '{value}'")
|
||||||
|
|
||||||
|
|
||||||
class FileTransferMethod(str, Enum):
|
class FileTransferMethod(StrEnum):
|
||||||
REMOTE_URL = "remote_url"
|
REMOTE_URL = "remote_url"
|
||||||
LOCAL_FILE = "local_file"
|
LOCAL_FILE = "local_file"
|
||||||
TOOL_FILE = "tool_file"
|
TOOL_FILE = "tool_file"
|
||||||
|
@ -29,7 +29,7 @@ class FileTransferMethod(str, Enum):
|
||||||
raise ValueError(f"No matching enum found for value '{value}'")
|
raise ValueError(f"No matching enum found for value '{value}'")
|
||||||
|
|
||||||
|
|
||||||
class FileBelongsTo(str, Enum):
|
class FileBelongsTo(StrEnum):
|
||||||
USER = "user"
|
USER = "user"
|
||||||
ASSISTANT = "assistant"
|
ASSISTANT = "assistant"
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class FileBelongsTo(str, Enum):
|
||||||
raise ValueError(f"No matching enum found for value '{value}'")
|
raise ValueError(f"No matching enum found for value '{value}'")
|
||||||
|
|
||||||
|
|
||||||
class FileAttribute(str, Enum):
|
class FileAttribute(StrEnum):
|
||||||
TYPE = "type"
|
TYPE = "type"
|
||||||
SIZE = "size"
|
SIZE = "size"
|
||||||
NAME = "name"
|
NAME = "name"
|
||||||
|
@ -51,5 +51,5 @@ class FileAttribute(str, Enum):
|
||||||
EXTENSION = "extension"
|
EXTENSION = "extension"
|
||||||
|
|
||||||
|
|
||||||
class ArrayFileAttribute(str, Enum):
|
class ArrayFileAttribute(StrEnum):
|
||||||
LENGTH = "length"
|
LENGTH = "length"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class CodeExecutionResponse(BaseModel):
|
||||||
data: Data
|
data: Data
|
||||||
|
|
||||||
|
|
||||||
class CodeLanguage(str, Enum):
|
class CodeLanguage(StrEnum):
|
||||||
PYTHON3 = "python3"
|
PYTHON3 = "python3"
|
||||||
JINJA2 = "jinja2"
|
JINJA2 = "jinja2"
|
||||||
JAVASCRIPT = "javascript"
|
JAVASCRIPT = "javascript"
|
||||||
|
|
|
@ -41,7 +41,7 @@ def check_moderation(model_config: ModelConfigWithCredentialsEntity, text: str)
|
||||||
if moderation_result is True:
|
if moderation_result is True:
|
||||||
return True
|
return True
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.exception(ex)
|
logger.exception(f"Fails to check moderation, provider_name: {provider_name}")
|
||||||
raise InvokeBadRequestError("Rate limit exceeded, please try again later.")
|
raise InvokeBadRequestError("Rate limit exceeded, please try again later.")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -29,7 +29,7 @@ def import_module_from_source(*, module_name: str, py_file_path: AnyStr, use_laz
|
||||||
spec.loader.exec_module(module)
|
spec.loader.exec_module(module)
|
||||||
return module
|
return module
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"Failed to load module {module_name} from {py_file_path}: {str(e)}")
|
logging.exception(f"Failed to load module {module_name} from script file '{py_file_path}'")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ class IndexingRunner:
|
||||||
except ProviderTokenNotInitError as e:
|
except ProviderTokenNotInitError as e:
|
||||||
dataset_document.indexing_status = "error"
|
dataset_document.indexing_status = "error"
|
||||||
dataset_document.error = str(e.description)
|
dataset_document.error = str(e.description)
|
||||||
dataset_document.stopped_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
dataset_document.stopped_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
except ObjectDeletedError:
|
except ObjectDeletedError:
|
||||||
logging.warning("Document deleted, document id: {}".format(dataset_document.id))
|
logging.warning("Document deleted, document id: {}".format(dataset_document.id))
|
||||||
|
@ -92,7 +92,7 @@ class IndexingRunner:
|
||||||
logging.exception("consume document failed")
|
logging.exception("consume document failed")
|
||||||
dataset_document.indexing_status = "error"
|
dataset_document.indexing_status = "error"
|
||||||
dataset_document.error = str(e)
|
dataset_document.error = str(e)
|
||||||
dataset_document.stopped_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
dataset_document.stopped_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
def run_in_splitting_status(self, dataset_document: DatasetDocument):
|
def run_in_splitting_status(self, dataset_document: DatasetDocument):
|
||||||
|
@ -140,13 +140,13 @@ class IndexingRunner:
|
||||||
except ProviderTokenNotInitError as e:
|
except ProviderTokenNotInitError as e:
|
||||||
dataset_document.indexing_status = "error"
|
dataset_document.indexing_status = "error"
|
||||||
dataset_document.error = str(e.description)
|
dataset_document.error = str(e.description)
|
||||||
dataset_document.stopped_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
dataset_document.stopped_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("consume document failed")
|
logging.exception("consume document failed")
|
||||||
dataset_document.indexing_status = "error"
|
dataset_document.indexing_status = "error"
|
||||||
dataset_document.error = str(e)
|
dataset_document.error = str(e)
|
||||||
dataset_document.stopped_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
dataset_document.stopped_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
def run_in_indexing_status(self, dataset_document: DatasetDocument):
|
def run_in_indexing_status(self, dataset_document: DatasetDocument):
|
||||||
|
@ -198,13 +198,13 @@ class IndexingRunner:
|
||||||
except ProviderTokenNotInitError as e:
|
except ProviderTokenNotInitError as e:
|
||||||
dataset_document.indexing_status = "error"
|
dataset_document.indexing_status = "error"
|
||||||
dataset_document.error = str(e.description)
|
dataset_document.error = str(e.description)
|
||||||
dataset_document.stopped_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
dataset_document.stopped_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("consume document failed")
|
logging.exception("consume document failed")
|
||||||
dataset_document.indexing_status = "error"
|
dataset_document.indexing_status = "error"
|
||||||
dataset_document.error = str(e)
|
dataset_document.error = str(e)
|
||||||
dataset_document.stopped_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
dataset_document.stopped_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
def indexing_estimate(
|
def indexing_estimate(
|
||||||
|
@ -357,7 +357,7 @@ class IndexingRunner:
|
||||||
after_indexing_status="splitting",
|
after_indexing_status="splitting",
|
||||||
extra_update_params={
|
extra_update_params={
|
||||||
DatasetDocument.word_count: sum(len(text_doc.page_content) for text_doc in text_docs),
|
DatasetDocument.word_count: sum(len(text_doc.page_content) for text_doc in text_docs),
|
||||||
DatasetDocument.parsing_completed_at: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None),
|
DatasetDocument.parsing_completed_at: datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -449,7 +449,7 @@ class IndexingRunner:
|
||||||
doc_store.add_documents(documents)
|
doc_store.add_documents(documents)
|
||||||
|
|
||||||
# update document status to indexing
|
# update document status to indexing
|
||||||
cur_time = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
cur_time = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
self._update_document_index_status(
|
self._update_document_index_status(
|
||||||
document_id=dataset_document.id,
|
document_id=dataset_document.id,
|
||||||
after_indexing_status="indexing",
|
after_indexing_status="indexing",
|
||||||
|
@ -464,7 +464,7 @@ class IndexingRunner:
|
||||||
dataset_document_id=dataset_document.id,
|
dataset_document_id=dataset_document.id,
|
||||||
update_params={
|
update_params={
|
||||||
DocumentSegment.status: "indexing",
|
DocumentSegment.status: "indexing",
|
||||||
DocumentSegment.indexing_at: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None),
|
DocumentSegment.indexing_at: datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -554,7 +554,7 @@ class IndexingRunner:
|
||||||
qa_documents.append(qa_document)
|
qa_documents.append(qa_document)
|
||||||
format_documents.extend(qa_documents)
|
format_documents.extend(qa_documents)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(e)
|
logging.exception("Failed to format qa document")
|
||||||
|
|
||||||
all_qa_documents.extend(format_documents)
|
all_qa_documents.extend(format_documents)
|
||||||
|
|
||||||
|
@ -669,7 +669,7 @@ class IndexingRunner:
|
||||||
after_indexing_status="completed",
|
after_indexing_status="completed",
|
||||||
extra_update_params={
|
extra_update_params={
|
||||||
DatasetDocument.tokens: tokens,
|
DatasetDocument.tokens: tokens,
|
||||||
DatasetDocument.completed_at: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None),
|
DatasetDocument.completed_at: datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
|
||||||
DatasetDocument.indexing_latency: indexing_end_at - indexing_start_at,
|
DatasetDocument.indexing_latency: indexing_end_at - indexing_start_at,
|
||||||
DatasetDocument.error: None,
|
DatasetDocument.error: None,
|
||||||
},
|
},
|
||||||
|
@ -694,7 +694,7 @@ class IndexingRunner:
|
||||||
{
|
{
|
||||||
DocumentSegment.status: "completed",
|
DocumentSegment.status: "completed",
|
||||||
DocumentSegment.enabled: True,
|
DocumentSegment.enabled: True,
|
||||||
DocumentSegment.completed_at: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None),
|
DocumentSegment.completed_at: datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -727,7 +727,7 @@ class IndexingRunner:
|
||||||
{
|
{
|
||||||
DocumentSegment.status: "completed",
|
DocumentSegment.status: "completed",
|
||||||
DocumentSegment.enabled: True,
|
DocumentSegment.enabled: True,
|
||||||
DocumentSegment.completed_at: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None),
|
DocumentSegment.completed_at: datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -838,7 +838,7 @@ class IndexingRunner:
|
||||||
doc_store.add_documents(documents)
|
doc_store.add_documents(documents)
|
||||||
|
|
||||||
# update document status to indexing
|
# update document status to indexing
|
||||||
cur_time = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
cur_time = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
self._update_document_index_status(
|
self._update_document_index_status(
|
||||||
document_id=dataset_document.id,
|
document_id=dataset_document.id,
|
||||||
after_indexing_status="indexing",
|
after_indexing_status="indexing",
|
||||||
|
@ -853,7 +853,7 @@ class IndexingRunner:
|
||||||
dataset_document_id=dataset_document.id,
|
dataset_document_id=dataset_document.id,
|
||||||
update_params={
|
update_params={
|
||||||
DocumentSegment.status: "indexing",
|
DocumentSegment.status: "indexing",
|
||||||
DocumentSegment.indexing_at: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None),
|
DocumentSegment.indexing_at: datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -102,7 +102,7 @@ class LLMGenerator:
|
||||||
except InvokeError:
|
except InvokeError:
|
||||||
questions = []
|
questions = []
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(e)
|
logging.exception("Failed to generate suggested questions after answer")
|
||||||
questions = []
|
questions = []
|
||||||
|
|
||||||
return questions
|
return questions
|
||||||
|
@ -148,7 +148,7 @@ class LLMGenerator:
|
||||||
error = str(e)
|
error = str(e)
|
||||||
error_step = "generate rule config"
|
error_step = "generate rule config"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(e)
|
logging.exception(f"Failed to generate rule config, model: {model_config.get('name')}")
|
||||||
rule_config["error"] = str(e)
|
rule_config["error"] = str(e)
|
||||||
|
|
||||||
rule_config["error"] = f"Failed to {error_step}. Error: {error}" if error else ""
|
rule_config["error"] = f"Failed to {error_step}. Error: {error}" if error else ""
|
||||||
|
@ -234,7 +234,7 @@ class LLMGenerator:
|
||||||
error_step = "generate conversation opener"
|
error_step = "generate conversation opener"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(e)
|
logging.exception(f"Failed to generate rule config, model: {model_config.get('name')}")
|
||||||
rule_config["error"] = str(e)
|
rule_config["error"] = str(e)
|
||||||
|
|
||||||
rule_config["error"] = f"Failed to {error_step}. Error: {error}" if error else ""
|
rule_config["error"] = f"Failed to {error_step}. Error: {error}" if error else ""
|
||||||
|
@ -286,7 +286,9 @@ class LLMGenerator:
|
||||||
error = str(e)
|
error = str(e)
|
||||||
return {"code": "", "language": code_language, "error": f"Failed to generate code. Error: {error}"}
|
return {"code": "", "language": code_language, "error": f"Failed to generate code. Error: {error}"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(e)
|
logging.exception(
|
||||||
|
f"Failed to invoke LLM model, model: {model_config.get('name')}, language: {code_language}"
|
||||||
|
)
|
||||||
return {"code": "", "language": code_language, "error": f"An unexpected error occurred: {str(e)}"}
|
return {"code": "", "language": code_language, "error": f"An unexpected error occurred: {str(e)}"}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from enum import Enum
|
from enum import Enum, StrEnum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, field_validator
|
from pydantic import BaseModel, Field, field_validator
|
||||||
|
@ -93,7 +93,7 @@ class ImagePromptMessageContent(PromptMessageContent):
|
||||||
Model class for image prompt message content.
|
Model class for image prompt message content.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class DETAIL(str, Enum):
|
class DETAIL(StrEnum):
|
||||||
LOW = "low"
|
LOW = "low"
|
||||||
HIGH = "high"
|
HIGH = "high"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from enum import Enum
|
from enum import Enum, StrEnum
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict
|
from pydantic import BaseModel, ConfigDict
|
||||||
|
@ -89,7 +89,7 @@ class ModelFeature(Enum):
|
||||||
STREAM_TOOL_CALL = "stream-tool-call"
|
STREAM_TOOL_CALL = "stream-tool-call"
|
||||||
|
|
||||||
|
|
||||||
class DefaultParameterName(str, Enum):
|
class DefaultParameterName(StrEnum):
|
||||||
"""
|
"""
|
||||||
Enum class for parameter template variable.
|
Enum class for parameter template variable.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -103,7 +103,7 @@ class AzureRerankModel(RerankModel):
|
||||||
return RerankResult(model=model, docs=rerank_documents)
|
return RerankResult(model=model, docs=rerank_documents)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Exception in Azure rerank: {e}")
|
logger.exception(f"Failed to invoke rerank model, model: {model}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def validate_credentials(self, model: str, credentials: dict) -> None:
|
def validate_credentials(self, model: str, credentials: dict) -> None:
|
||||||
|
|
|
@ -113,7 +113,7 @@ class SageMakerRerankModel(RerankModel):
|
||||||
return RerankResult(model=model, docs=rerank_documents)
|
return RerankResult(model=model, docs=rerank_documents)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Exception {e}, line : {line}")
|
logger.exception(f"Failed to invoke rerank model, model: {model}")
|
||||||
|
|
||||||
def validate_credentials(self, model: str, credentials: dict) -> None:
|
def validate_credentials(self, model: str, credentials: dict) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -78,7 +78,7 @@ class SageMakerSpeech2TextModel(Speech2TextModel):
|
||||||
json_obj = json.loads(json_str)
|
json_obj = json.loads(json_str)
|
||||||
asr_text = json_obj["text"]
|
asr_text = json_obj["text"]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"failed to invoke speech2text model, {e}")
|
logger.exception(f"failed to invoke speech2text model, model: {model}")
|
||||||
raise CredentialsValidateFailedError(str(e))
|
raise CredentialsValidateFailedError(str(e))
|
||||||
|
|
||||||
return asr_text
|
return asr_text
|
||||||
|
|
|
@ -117,7 +117,7 @@ class SageMakerEmbeddingModel(TextEmbeddingModel):
|
||||||
return TextEmbeddingResult(embeddings=all_embeddings, usage=usage, model=model)
|
return TextEmbeddingResult(embeddings=all_embeddings, usage=usage, model=model)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Exception {e}, line : {line}")
|
logger.exception(f"Failed to invoke text embedding model, model: {model}, line: {line}")
|
||||||
|
|
||||||
def get_num_tokens(self, model: str, credentials: dict, texts: list[str]) -> int:
|
def get_num_tokens(self, model: str, credentials: dict, texts: list[str]) -> int:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
from collections import UserDict
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,9 +11,9 @@ class ChatRole:
|
||||||
FUNCTION = "function"
|
FUNCTION = "function"
|
||||||
|
|
||||||
|
|
||||||
class _Dict(dict):
|
class _Dict(UserDict):
|
||||||
__setattr__ = dict.__setitem__
|
__setattr__ = UserDict.__setitem__
|
||||||
__getattr__ = dict.__getitem__
|
__getattr__ = UserDict.__getitem__
|
||||||
|
|
||||||
def __missing__(self, key):
|
def __missing__(self, key):
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -126,6 +126,6 @@ class OutputModeration(BaseModel):
|
||||||
result: ModerationOutputsResult = moderation_factory.moderation_for_outputs(moderation_buffer)
|
result: ModerationOutputsResult = moderation_factory.moderation_for_outputs(moderation_buffer)
|
||||||
return result
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("Moderation Output error: %s", e)
|
logger.exception(f"Moderation Output error, app_id: {app_id}")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, field_validator
|
from pydantic import BaseModel, ConfigDict, field_validator
|
||||||
|
@ -122,7 +122,7 @@ trace_info_info_map = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TraceTaskName(str, Enum):
|
class TraceTaskName(StrEnum):
|
||||||
CONVERSATION_TRACE = "conversation"
|
CONVERSATION_TRACE = "conversation"
|
||||||
WORKFLOW_TRACE = "workflow"
|
WORKFLOW_TRACE = "workflow"
|
||||||
MESSAGE_TRACE = "message"
|
MESSAGE_TRACE = "message"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||||
|
@ -39,7 +39,7 @@ def validate_input_output(v, field_name):
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
||||||
class LevelEnum(str, Enum):
|
class LevelEnum(StrEnum):
|
||||||
DEBUG = "DEBUG"
|
DEBUG = "DEBUG"
|
||||||
WARNING = "WARNING"
|
WARNING = "WARNING"
|
||||||
ERROR = "ERROR"
|
ERROR = "ERROR"
|
||||||
|
@ -178,7 +178,7 @@ class LangfuseSpan(BaseModel):
|
||||||
return validate_input_output(v, field_name)
|
return validate_input_output(v, field_name)
|
||||||
|
|
||||||
|
|
||||||
class UnitEnum(str, Enum):
|
class UnitEnum(StrEnum):
|
||||||
CHARACTERS = "CHARACTERS"
|
CHARACTERS = "CHARACTERS"
|
||||||
TOKENS = "TOKENS"
|
TOKENS = "TOKENS"
|
||||||
SECONDS = "SECONDS"
|
SECONDS = "SECONDS"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, field_validator
|
from pydantic import BaseModel, Field, field_validator
|
||||||
|
@ -8,7 +8,7 @@ from pydantic_core.core_schema import ValidationInfo
|
||||||
from core.ops.utils import replace_text_with_content
|
from core.ops.utils import replace_text_with_content
|
||||||
|
|
||||||
|
|
||||||
class LangSmithRunType(str, Enum):
|
class LangSmithRunType(StrEnum):
|
||||||
tool = "tool"
|
tool = "tool"
|
||||||
chain = "chain"
|
chain = "chain"
|
||||||
llm = "llm"
|
llm = "llm"
|
||||||
|
|
|
@ -711,7 +711,7 @@ class TraceQueueManager:
|
||||||
trace_task.app_id = self.app_id
|
trace_task.app_id = self.app_id
|
||||||
trace_manager_queue.put(trace_task)
|
trace_manager_queue.put(trace_task)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"Error adding trace task: {e}")
|
logging.exception(f"Error adding trace task, trace_type {trace_task.trace_type}")
|
||||||
finally:
|
finally:
|
||||||
self.start_timer()
|
self.start_timer()
|
||||||
|
|
||||||
|
@ -730,7 +730,7 @@ class TraceQueueManager:
|
||||||
if tasks:
|
if tasks:
|
||||||
self.send_to_celery(tasks)
|
self.send_to_celery(tasks)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"Error processing trace tasks: {e}")
|
logging.exception("Error processing trace tasks")
|
||||||
|
|
||||||
def start_timer(self):
|
def start_timer(self):
|
||||||
global trace_manager_timer
|
global trace_manager_timer
|
||||||
|
|
|
@ -23,7 +23,7 @@ if TYPE_CHECKING:
|
||||||
from core.file.models import File
|
from core.file.models import File
|
||||||
|
|
||||||
|
|
||||||
class ModelMode(str, enum.Enum):
|
class ModelMode(enum.StrEnum):
|
||||||
COMPLETION = "completion"
|
COMPLETION = "completion"
|
||||||
CHAT = "chat"
|
CHAT = "chat"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class KeyWordType(str, Enum):
|
class KeyWordType(StrEnum):
|
||||||
JIEBA = "jieba"
|
JIEBA = "jieba"
|
||||||
|
|
|
@ -242,7 +242,7 @@ class CouchbaseVector(BaseVector):
|
||||||
try:
|
try:
|
||||||
self._cluster.query(query, named_parameters={"doc_ids": ids}).execute()
|
self._cluster.query(query, named_parameters={"doc_ids": ids}).execute()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(f"Failed to delete documents, ids: {ids}")
|
||||||
|
|
||||||
def delete_by_document_id(self, document_id: str):
|
def delete_by_document_id(self, document_id: str):
|
||||||
query = f"""
|
query = f"""
|
||||||
|
|
|
@ -79,7 +79,7 @@ class LindormVectorStore(BaseVector):
|
||||||
existing_docs = self._client.mget(index=self._collection_name, body={"ids": batch_ids}, _source=False)
|
existing_docs = self._client.mget(index=self._collection_name, body={"ids": batch_ids}, _source=False)
|
||||||
return {doc["_id"] for doc in existing_docs["docs"] if doc["found"]}
|
return {doc["_id"] for doc in existing_docs["docs"] if doc["found"]}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Error fetching batch {batch_ids}: {e}")
|
logger.exception(f"Error fetching batch {batch_ids}")
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
@retry(stop=stop_after_attempt(3), wait=wait_fixed(60))
|
@retry(stop=stop_after_attempt(3), wait=wait_fixed(60))
|
||||||
|
@ -96,7 +96,7 @@ class LindormVectorStore(BaseVector):
|
||||||
)
|
)
|
||||||
return {doc["_id"] for doc in existing_docs["docs"] if doc["found"]}
|
return {doc["_id"] for doc in existing_docs["docs"] if doc["found"]}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Error fetching batch {batch_ids}: {e}")
|
logger.exception(f"Error fetching batch ids: {batch_ids}")
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
if ids is None:
|
if ids is None:
|
||||||
|
@ -177,7 +177,7 @@ class LindormVectorStore(BaseVector):
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Index '{self._collection_name}' does not exist. No deletion performed.")
|
logger.warning(f"Index '{self._collection_name}' does not exist. No deletion performed.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Error occurred while deleting the index: {e}")
|
logger.exception(f"Error occurred while deleting the index: {self._collection_name}")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def text_exists(self, id: str) -> bool:
|
def text_exists(self, id: str) -> bool:
|
||||||
|
@ -201,7 +201,7 @@ class LindormVectorStore(BaseVector):
|
||||||
try:
|
try:
|
||||||
response = self._client.search(index=self._collection_name, body=query)
|
response = self._client.search(index=self._collection_name, body=query)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Error executing search: {e}")
|
logger.exception(f"Error executing vector search, query: {query}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
docs_and_scores = []
|
docs_and_scores = []
|
||||||
|
|
|
@ -142,7 +142,7 @@ class MyScaleVector(BaseVector):
|
||||||
for r in self._client.query(sql).named_results()
|
for r in self._client.query(sql).named_results()
|
||||||
]
|
]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"\033[91m\033[1m{type(e)}\033[0m \033[95m{str(e)}\033[0m")
|
logging.exception(f"\033[91m\033[1m{type(e)}\033[0m \033[95m{str(e)}\033[0m") # noqa:TRY401
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def delete(self) -> None:
|
def delete(self) -> None:
|
||||||
|
|
|
@ -158,7 +158,7 @@ class OpenSearchVector(BaseVector):
|
||||||
try:
|
try:
|
||||||
response = self._client.search(index=self._collection_name.lower(), body=query)
|
response = self._client.search(index=self._collection_name.lower(), body=query)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Error executing search: {e}")
|
logger.exception(f"Error executing vector search, query: {query}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
docs = []
|
docs = []
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class VectorType(str, Enum):
|
class VectorType(StrEnum):
|
||||||
ANALYTICDB = "analyticdb"
|
ANALYTICDB = "analyticdb"
|
||||||
CHROMA = "chroma"
|
CHROMA = "chroma"
|
||||||
MILVUS = "milvus"
|
MILVUS = "milvus"
|
||||||
|
|
|
@ -69,7 +69,7 @@ class CacheEmbedding(Embeddings):
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Failed transform embedding: %s", e)
|
logging.exception("Failed transform embedding")
|
||||||
cache_embeddings = []
|
cache_embeddings = []
|
||||||
try:
|
try:
|
||||||
for i, embedding in zip(embedding_queue_indices, embedding_queue_embeddings):
|
for i, embedding in zip(embedding_queue_indices, embedding_queue_embeddings):
|
||||||
|
@ -89,7 +89,7 @@ class CacheEmbedding(Embeddings):
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
logger.exception("Failed to embed documents: %s", ex)
|
logger.exception("Failed to embed documents: %s")
|
||||||
raise ex
|
raise ex
|
||||||
|
|
||||||
return text_embeddings
|
return text_embeddings
|
||||||
|
@ -112,7 +112,7 @@ class CacheEmbedding(Embeddings):
|
||||||
embedding_results = (embedding_results / np.linalg.norm(embedding_results)).tolist()
|
embedding_results = (embedding_results / np.linalg.norm(embedding_results)).tolist()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if dify_config.DEBUG:
|
if dify_config.DEBUG:
|
||||||
logging.exception(f"Failed to embed query text: {ex}")
|
logging.exception(f"Failed to embed query text '{text[:10]}...({len(text)} chars)'")
|
||||||
raise ex
|
raise ex
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -126,7 +126,7 @@ class CacheEmbedding(Embeddings):
|
||||||
redis_client.setex(embedding_cache_key, 600, encoded_str)
|
redis_client.setex(embedding_cache_key, 600, encoded_str)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if dify_config.DEBUG:
|
if dify_config.DEBUG:
|
||||||
logging.exception("Failed to add embedding to redis %s", ex)
|
logging.exception(f"Failed to add embedding to redis for the text '{text[:10]}...({len(text)} chars)'")
|
||||||
raise ex
|
raise ex
|
||||||
|
|
||||||
return embedding_results
|
return embedding_results
|
||||||
|
|
|
@ -114,10 +114,10 @@ class WordExtractor(BaseExtractor):
|
||||||
mime_type=mime_type or "",
|
mime_type=mime_type or "",
|
||||||
created_by=self.user_id,
|
created_by=self.user_id,
|
||||||
created_by_role=CreatedByRole.ACCOUNT,
|
created_by_role=CreatedByRole.ACCOUNT,
|
||||||
created_at=datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None),
|
created_at=datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
|
||||||
used=True,
|
used=True,
|
||||||
used_by=self.user_id,
|
used_by=self.user_id,
|
||||||
used_at=datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None),
|
used_at=datetime.datetime.now(datetime.UTC).replace(tzinfo=None),
|
||||||
)
|
)
|
||||||
|
|
||||||
db.session.add(upload_file)
|
db.session.add(upload_file)
|
||||||
|
@ -229,7 +229,7 @@ class WordExtractor(BaseExtractor):
|
||||||
for i in url_pattern.findall(x.text):
|
for i in url_pattern.findall(x.text):
|
||||||
hyperlinks_url = str(i)
|
hyperlinks_url = str(i)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception("Failed to parse HYPERLINK xml")
|
||||||
|
|
||||||
def parse_paragraph(paragraph):
|
def parse_paragraph(paragraph):
|
||||||
paragraph_content = []
|
paragraph_content = []
|
||||||
|
|
|
@ -159,7 +159,7 @@ class QAIndexProcessor(BaseIndexProcessor):
|
||||||
qa_documents.append(qa_document)
|
qa_documents.append(qa_document)
|
||||||
format_documents.extend(qa_documents)
|
format_documents.extend(qa_documents)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(e)
|
logging.exception("Failed to format qa document")
|
||||||
|
|
||||||
all_qa_documents.extend(format_documents)
|
all_qa_documents.extend(format_documents)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class RerankMode(str, Enum):
|
class RerankMode(StrEnum):
|
||||||
RERANKING_MODEL = "reranking_model"
|
RERANKING_MODEL = "reranking_model"
|
||||||
WEIGHTED_SCORE = "weighted_score"
|
WEIGHTED_SCORE = "weighted_score"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from enum import Enum
|
from enum import Enum, StrEnum
|
||||||
from typing import Any, Optional, Union, cast
|
from typing import Any, Optional, Union, cast
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, field_validator
|
from pydantic import BaseModel, Field, field_validator
|
||||||
|
@ -137,7 +137,7 @@ class ToolParameterOption(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class ToolParameter(BaseModel):
|
class ToolParameter(BaseModel):
|
||||||
class ToolParameterType(str, Enum):
|
class ToolParameterType(StrEnum):
|
||||||
STRING = "string"
|
STRING = "string"
|
||||||
NUMBER = "number"
|
NUMBER = "number"
|
||||||
BOOLEAN = "boolean"
|
BOOLEAN = "boolean"
|
||||||
|
|
|
@ -57,13 +57,12 @@ class ASRTool(BuiltinTool):
|
||||||
name="model",
|
name="model",
|
||||||
label=I18nObject(en_US="Model", zh_Hans="Model"),
|
label=I18nObject(en_US="Model", zh_Hans="Model"),
|
||||||
human_description=I18nObject(
|
human_description=I18nObject(
|
||||||
en_US="All available ASR models",
|
en_US="All available ASR models. You can config model in the Model Provider of Settings.",
|
||||||
zh_Hans="所有可用的 ASR 模型",
|
zh_Hans="所有可用的 ASR 模型。你可以在设置中的模型供应商里配置。",
|
||||||
),
|
),
|
||||||
type=ToolParameter.ToolParameterType.SELECT,
|
type=ToolParameter.ToolParameterType.SELECT,
|
||||||
form=ToolParameter.ToolParameterForm.FORM,
|
form=ToolParameter.ToolParameterForm.FORM,
|
||||||
required=True,
|
required=True,
|
||||||
default=options[0].value,
|
|
||||||
options=options,
|
options=options,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -77,13 +77,12 @@ class TTSTool(BuiltinTool):
|
||||||
name="model",
|
name="model",
|
||||||
label=I18nObject(en_US="Model", zh_Hans="Model"),
|
label=I18nObject(en_US="Model", zh_Hans="Model"),
|
||||||
human_description=I18nObject(
|
human_description=I18nObject(
|
||||||
en_US="All available TTS models",
|
en_US="All available TTS models. You can config model in the Model Provider of Settings.",
|
||||||
zh_Hans="所有可用的 TTS 模型",
|
zh_Hans="所有可用的 TTS 模型。你可以在设置中的模型供应商里配置。",
|
||||||
),
|
),
|
||||||
type=ToolParameter.ToolParameterType.SELECT,
|
type=ToolParameter.ToolParameterType.SELECT,
|
||||||
form=ToolParameter.ToolParameterForm.FORM,
|
form=ToolParameter.ToolParameterForm.FORM,
|
||||||
required=True,
|
required=True,
|
||||||
default=options[0].value,
|
|
||||||
options=options,
|
options=options,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -38,7 +38,7 @@ def send_mail(parmas: SendEmailToolParameters):
|
||||||
server.sendmail(parmas.email_account, parmas.sender_to, msg.as_string())
|
server.sendmail(parmas.email_account, parmas.sender_to, msg.as_string())
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("send email failed: %s", e)
|
logging.exception("send email failed")
|
||||||
return False
|
return False
|
||||||
else: # NONE or TLS
|
else: # NONE or TLS
|
||||||
try:
|
try:
|
||||||
|
@ -49,5 +49,5 @@ def send_mail(parmas: SendEmailToolParameters):
|
||||||
server.sendmail(parmas.email_account, parmas.sender_to, msg.as_string())
|
server.sendmail(parmas.email_account, parmas.sender_to, msg.as_string())
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("send email failed: %s", e)
|
logging.exception("send email failed")
|
||||||
return False
|
return False
|
||||||
|
|
52
api/core/tools/provider/builtin/fal/tools/wizper.py
Normal file
52
api/core/tools/provider/builtin/fal/tools/wizper.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import fal_client
|
||||||
|
|
||||||
|
from core.file.enums import FileAttribute, FileType
|
||||||
|
from core.file.file_manager import download, get_attr
|
||||||
|
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||||
|
from core.tools.tool.builtin_tool import BuiltinTool
|
||||||
|
|
||||||
|
|
||||||
|
class WizperTool(BuiltinTool):
|
||||||
|
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
|
||||||
|
audio_file = tool_parameters.get("audio_file")
|
||||||
|
task = tool_parameters.get("task", "transcribe")
|
||||||
|
language = tool_parameters.get("language", "en")
|
||||||
|
chunk_level = tool_parameters.get("chunk_level", "segment")
|
||||||
|
version = tool_parameters.get("version", "3")
|
||||||
|
|
||||||
|
if audio_file.type != FileType.AUDIO:
|
||||||
|
return [self.create_text_message("Not a valid audio file.")]
|
||||||
|
|
||||||
|
api_key = self.runtime.credentials["fal_api_key"]
|
||||||
|
|
||||||
|
os.environ["FAL_KEY"] = api_key
|
||||||
|
|
||||||
|
audio_binary = io.BytesIO(download(audio_file))
|
||||||
|
mime_type = get_attr(file=audio_file, attr=FileAttribute.MIME_TYPE)
|
||||||
|
file_data = audio_binary.getvalue()
|
||||||
|
|
||||||
|
try:
|
||||||
|
audio_url = fal_client.upload(file_data, mime_type)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return [self.create_text_message(f"Error uploading audio file: {str(e)}")]
|
||||||
|
|
||||||
|
arguments = {
|
||||||
|
"audio_url": audio_url,
|
||||||
|
"task": task,
|
||||||
|
"language": language,
|
||||||
|
"chunk_level": chunk_level,
|
||||||
|
"version": version,
|
||||||
|
}
|
||||||
|
|
||||||
|
result = fal_client.subscribe(
|
||||||
|
"fal-ai/wizper",
|
||||||
|
arguments=arguments,
|
||||||
|
with_logs=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.create_json_message(result)
|
489
api/core/tools/provider/builtin/fal/tools/wizper.yaml
Normal file
489
api/core/tools/provider/builtin/fal/tools/wizper.yaml
Normal file
|
@ -0,0 +1,489 @@
|
||||||
|
identity:
|
||||||
|
name: wizper
|
||||||
|
author: Kalo Chin
|
||||||
|
label:
|
||||||
|
en_US: Wizper
|
||||||
|
zh_Hans: Wizper
|
||||||
|
description:
|
||||||
|
human:
|
||||||
|
en_US: Transcribe an audio file using the Whisper model.
|
||||||
|
zh_Hans: 使用 Whisper 模型转录音频文件。
|
||||||
|
llm: Transcribe an audio file using the Whisper model.
|
||||||
|
parameters:
|
||||||
|
- name: audio_file
|
||||||
|
type: file
|
||||||
|
required: true
|
||||||
|
label:
|
||||||
|
en_US: Audio File
|
||||||
|
zh_Hans: 音频文件
|
||||||
|
human_description:
|
||||||
|
en_US: "Upload an audio file to transcribe. Supports mp3, mp4, mpeg, mpga, m4a, wav, or webm formats."
|
||||||
|
zh_Hans: "上传要转录的音频文件。支持 mp3、mp4、mpeg、mpga、m4a、wav 或 webm 格式。"
|
||||||
|
llm_description: "Audio file to transcribe. Supported formats: mp3, mp4, mpeg, mpga, m4a, wav, or webm."
|
||||||
|
form: llm
|
||||||
|
- name: task
|
||||||
|
type: select
|
||||||
|
required: true
|
||||||
|
label:
|
||||||
|
en_US: Task
|
||||||
|
zh_Hans: 任务
|
||||||
|
human_description:
|
||||||
|
en_US: "Choose whether to transcribe the audio in its original language or translate it to English"
|
||||||
|
zh_Hans: "选择是以原始语言转录音频还是将其翻译成英语"
|
||||||
|
llm_description: "Task to perform on the audio file. Either transcribe or translate. Default value: 'transcribe'. If 'translate' is selected as the task, the audio will be translated to English, regardless of the language selected."
|
||||||
|
form: form
|
||||||
|
default: transcribe
|
||||||
|
options:
|
||||||
|
- value: transcribe
|
||||||
|
label:
|
||||||
|
en_US: Transcribe
|
||||||
|
zh_Hans: 转录
|
||||||
|
- value: translate
|
||||||
|
label:
|
||||||
|
en_US: Translate
|
||||||
|
zh_Hans: 翻译
|
||||||
|
- name: language
|
||||||
|
type: select
|
||||||
|
required: true
|
||||||
|
label:
|
||||||
|
en_US: Language
|
||||||
|
zh_Hans: 语言
|
||||||
|
human_description:
|
||||||
|
en_US: "Select the primary language spoken in the audio file"
|
||||||
|
zh_Hans: "选择音频文件中使用的主要语言"
|
||||||
|
llm_description: "Language of the audio file."
|
||||||
|
form: form
|
||||||
|
default: en
|
||||||
|
options:
|
||||||
|
- value: af
|
||||||
|
label:
|
||||||
|
en_US: Afrikaans
|
||||||
|
zh_Hans: 南非语
|
||||||
|
- value: am
|
||||||
|
label:
|
||||||
|
en_US: Amharic
|
||||||
|
zh_Hans: 阿姆哈拉语
|
||||||
|
- value: ar
|
||||||
|
label:
|
||||||
|
en_US: Arabic
|
||||||
|
zh_Hans: 阿拉伯语
|
||||||
|
- value: as
|
||||||
|
label:
|
||||||
|
en_US: Assamese
|
||||||
|
zh_Hans: 阿萨姆语
|
||||||
|
- value: az
|
||||||
|
label:
|
||||||
|
en_US: Azerbaijani
|
||||||
|
zh_Hans: 阿塞拜疆语
|
||||||
|
- value: ba
|
||||||
|
label:
|
||||||
|
en_US: Bashkir
|
||||||
|
zh_Hans: 巴什基尔语
|
||||||
|
- value: be
|
||||||
|
label:
|
||||||
|
en_US: Belarusian
|
||||||
|
zh_Hans: 白俄罗斯语
|
||||||
|
- value: bg
|
||||||
|
label:
|
||||||
|
en_US: Bulgarian
|
||||||
|
zh_Hans: 保加利亚语
|
||||||
|
- value: bn
|
||||||
|
label:
|
||||||
|
en_US: Bengali
|
||||||
|
zh_Hans: 孟加拉语
|
||||||
|
- value: bo
|
||||||
|
label:
|
||||||
|
en_US: Tibetan
|
||||||
|
zh_Hans: 藏语
|
||||||
|
- value: br
|
||||||
|
label:
|
||||||
|
en_US: Breton
|
||||||
|
zh_Hans: 布列塔尼语
|
||||||
|
- value: bs
|
||||||
|
label:
|
||||||
|
en_US: Bosnian
|
||||||
|
zh_Hans: 波斯尼亚语
|
||||||
|
- value: ca
|
||||||
|
label:
|
||||||
|
en_US: Catalan
|
||||||
|
zh_Hans: 加泰罗尼亚语
|
||||||
|
- value: cs
|
||||||
|
label:
|
||||||
|
en_US: Czech
|
||||||
|
zh_Hans: 捷克语
|
||||||
|
- value: cy
|
||||||
|
label:
|
||||||
|
en_US: Welsh
|
||||||
|
zh_Hans: 威尔士语
|
||||||
|
- value: da
|
||||||
|
label:
|
||||||
|
en_US: Danish
|
||||||
|
zh_Hans: 丹麦语
|
||||||
|
- value: de
|
||||||
|
label:
|
||||||
|
en_US: German
|
||||||
|
zh_Hans: 德语
|
||||||
|
- value: el
|
||||||
|
label:
|
||||||
|
en_US: Greek
|
||||||
|
zh_Hans: 希腊语
|
||||||
|
- value: en
|
||||||
|
label:
|
||||||
|
en_US: English
|
||||||
|
zh_Hans: 英语
|
||||||
|
- value: es
|
||||||
|
label:
|
||||||
|
en_US: Spanish
|
||||||
|
zh_Hans: 西班牙语
|
||||||
|
- value: et
|
||||||
|
label:
|
||||||
|
en_US: Estonian
|
||||||
|
zh_Hans: 爱沙尼亚语
|
||||||
|
- value: eu
|
||||||
|
label:
|
||||||
|
en_US: Basque
|
||||||
|
zh_Hans: 巴斯克语
|
||||||
|
- value: fa
|
||||||
|
label:
|
||||||
|
en_US: Persian
|
||||||
|
zh_Hans: 波斯语
|
||||||
|
- value: fi
|
||||||
|
label:
|
||||||
|
en_US: Finnish
|
||||||
|
zh_Hans: 芬兰语
|
||||||
|
- value: fo
|
||||||
|
label:
|
||||||
|
en_US: Faroese
|
||||||
|
zh_Hans: 法罗语
|
||||||
|
- value: fr
|
||||||
|
label:
|
||||||
|
en_US: French
|
||||||
|
zh_Hans: 法语
|
||||||
|
- value: gl
|
||||||
|
label:
|
||||||
|
en_US: Galician
|
||||||
|
zh_Hans: 加利西亚语
|
||||||
|
- value: gu
|
||||||
|
label:
|
||||||
|
en_US: Gujarati
|
||||||
|
zh_Hans: 古吉拉特语
|
||||||
|
- value: ha
|
||||||
|
label:
|
||||||
|
en_US: Hausa
|
||||||
|
zh_Hans: 毫萨语
|
||||||
|
- value: haw
|
||||||
|
label:
|
||||||
|
en_US: Hawaiian
|
||||||
|
zh_Hans: 夏威夷语
|
||||||
|
- value: he
|
||||||
|
label:
|
||||||
|
en_US: Hebrew
|
||||||
|
zh_Hans: 希伯来语
|
||||||
|
- value: hi
|
||||||
|
label:
|
||||||
|
en_US: Hindi
|
||||||
|
zh_Hans: 印地语
|
||||||
|
- value: hr
|
||||||
|
label:
|
||||||
|
en_US: Croatian
|
||||||
|
zh_Hans: 克罗地亚语
|
||||||
|
- value: ht
|
||||||
|
label:
|
||||||
|
en_US: Haitian Creole
|
||||||
|
zh_Hans: 海地克里奥尔语
|
||||||
|
- value: hu
|
||||||
|
label:
|
||||||
|
en_US: Hungarian
|
||||||
|
zh_Hans: 匈牙利语
|
||||||
|
- value: hy
|
||||||
|
label:
|
||||||
|
en_US: Armenian
|
||||||
|
zh_Hans: 亚美尼亚语
|
||||||
|
- value: id
|
||||||
|
label:
|
||||||
|
en_US: Indonesian
|
||||||
|
zh_Hans: 印度尼西亚语
|
||||||
|
- value: is
|
||||||
|
label:
|
||||||
|
en_US: Icelandic
|
||||||
|
zh_Hans: 冰岛语
|
||||||
|
- value: it
|
||||||
|
label:
|
||||||
|
en_US: Italian
|
||||||
|
zh_Hans: 意大利语
|
||||||
|
- value: ja
|
||||||
|
label:
|
||||||
|
en_US: Japanese
|
||||||
|
zh_Hans: 日语
|
||||||
|
- value: jw
|
||||||
|
label:
|
||||||
|
en_US: Javanese
|
||||||
|
zh_Hans: 爪哇语
|
||||||
|
- value: ka
|
||||||
|
label:
|
||||||
|
en_US: Georgian
|
||||||
|
zh_Hans: 格鲁吉亚语
|
||||||
|
- value: kk
|
||||||
|
label:
|
||||||
|
en_US: Kazakh
|
||||||
|
zh_Hans: 哈萨克语
|
||||||
|
- value: km
|
||||||
|
label:
|
||||||
|
en_US: Khmer
|
||||||
|
zh_Hans: 高棉语
|
||||||
|
- value: kn
|
||||||
|
label:
|
||||||
|
en_US: Kannada
|
||||||
|
zh_Hans: 卡纳达语
|
||||||
|
- value: ko
|
||||||
|
label:
|
||||||
|
en_US: Korean
|
||||||
|
zh_Hans: 韩语
|
||||||
|
- value: la
|
||||||
|
label:
|
||||||
|
en_US: Latin
|
||||||
|
zh_Hans: 拉丁语
|
||||||
|
- value: lb
|
||||||
|
label:
|
||||||
|
en_US: Luxembourgish
|
||||||
|
zh_Hans: 卢森堡语
|
||||||
|
- value: ln
|
||||||
|
label:
|
||||||
|
en_US: Lingala
|
||||||
|
zh_Hans: 林加拉语
|
||||||
|
- value: lo
|
||||||
|
label:
|
||||||
|
en_US: Lao
|
||||||
|
zh_Hans: 老挝语
|
||||||
|
- value: lt
|
||||||
|
label:
|
||||||
|
en_US: Lithuanian
|
||||||
|
zh_Hans: 立陶宛语
|
||||||
|
- value: lv
|
||||||
|
label:
|
||||||
|
en_US: Latvian
|
||||||
|
zh_Hans: 拉脱维亚语
|
||||||
|
- value: mg
|
||||||
|
label:
|
||||||
|
en_US: Malagasy
|
||||||
|
zh_Hans: 马尔加什语
|
||||||
|
- value: mi
|
||||||
|
label:
|
||||||
|
en_US: Maori
|
||||||
|
zh_Hans: 毛利语
|
||||||
|
- value: mk
|
||||||
|
label:
|
||||||
|
en_US: Macedonian
|
||||||
|
zh_Hans: 马其顿语
|
||||||
|
- value: ml
|
||||||
|
label:
|
||||||
|
en_US: Malayalam
|
||||||
|
zh_Hans: 马拉雅拉姆语
|
||||||
|
- value: mn
|
||||||
|
label:
|
||||||
|
en_US: Mongolian
|
||||||
|
zh_Hans: 蒙古语
|
||||||
|
- value: mr
|
||||||
|
label:
|
||||||
|
en_US: Marathi
|
||||||
|
zh_Hans: 马拉地语
|
||||||
|
- value: ms
|
||||||
|
label:
|
||||||
|
en_US: Malay
|
||||||
|
zh_Hans: 马来语
|
||||||
|
- value: mt
|
||||||
|
label:
|
||||||
|
en_US: Maltese
|
||||||
|
zh_Hans: 马耳他语
|
||||||
|
- value: my
|
||||||
|
label:
|
||||||
|
en_US: Burmese
|
||||||
|
zh_Hans: 缅甸语
|
||||||
|
- value: ne
|
||||||
|
label:
|
||||||
|
en_US: Nepali
|
||||||
|
zh_Hans: 尼泊尔语
|
||||||
|
- value: nl
|
||||||
|
label:
|
||||||
|
en_US: Dutch
|
||||||
|
zh_Hans: 荷兰语
|
||||||
|
- value: nn
|
||||||
|
label:
|
||||||
|
en_US: Norwegian Nynorsk
|
||||||
|
zh_Hans: 新挪威语
|
||||||
|
- value: no
|
||||||
|
label:
|
||||||
|
en_US: Norwegian
|
||||||
|
zh_Hans: 挪威语
|
||||||
|
- value: oc
|
||||||
|
label:
|
||||||
|
en_US: Occitan
|
||||||
|
zh_Hans: 奥克语
|
||||||
|
- value: pa
|
||||||
|
label:
|
||||||
|
en_US: Punjabi
|
||||||
|
zh_Hans: 旁遮普语
|
||||||
|
- value: pl
|
||||||
|
label:
|
||||||
|
en_US: Polish
|
||||||
|
zh_Hans: 波兰语
|
||||||
|
- value: ps
|
||||||
|
label:
|
||||||
|
en_US: Pashto
|
||||||
|
zh_Hans: 普什图语
|
||||||
|
- value: pt
|
||||||
|
label:
|
||||||
|
en_US: Portuguese
|
||||||
|
zh_Hans: 葡萄牙语
|
||||||
|
- value: ro
|
||||||
|
label:
|
||||||
|
en_US: Romanian
|
||||||
|
zh_Hans: 罗马尼亚语
|
||||||
|
- value: ru
|
||||||
|
label:
|
||||||
|
en_US: Russian
|
||||||
|
zh_Hans: 俄语
|
||||||
|
- value: sa
|
||||||
|
label:
|
||||||
|
en_US: Sanskrit
|
||||||
|
zh_Hans: 梵语
|
||||||
|
- value: sd
|
||||||
|
label:
|
||||||
|
en_US: Sindhi
|
||||||
|
zh_Hans: 信德语
|
||||||
|
- value: si
|
||||||
|
label:
|
||||||
|
en_US: Sinhala
|
||||||
|
zh_Hans: 僧伽罗语
|
||||||
|
- value: sk
|
||||||
|
label:
|
||||||
|
en_US: Slovak
|
||||||
|
zh_Hans: 斯洛伐克语
|
||||||
|
- value: sl
|
||||||
|
label:
|
||||||
|
en_US: Slovenian
|
||||||
|
zh_Hans: 斯洛文尼亚语
|
||||||
|
- value: sn
|
||||||
|
label:
|
||||||
|
en_US: Shona
|
||||||
|
zh_Hans: 修纳语
|
||||||
|
- value: so
|
||||||
|
label:
|
||||||
|
en_US: Somali
|
||||||
|
zh_Hans: 索马里语
|
||||||
|
- value: sq
|
||||||
|
label:
|
||||||
|
en_US: Albanian
|
||||||
|
zh_Hans: 阿尔巴尼亚语
|
||||||
|
- value: sr
|
||||||
|
label:
|
||||||
|
en_US: Serbian
|
||||||
|
zh_Hans: 塞尔维亚语
|
||||||
|
- value: su
|
||||||
|
label:
|
||||||
|
en_US: Sundanese
|
||||||
|
zh_Hans: 巽他语
|
||||||
|
- value: sv
|
||||||
|
label:
|
||||||
|
en_US: Swedish
|
||||||
|
zh_Hans: 瑞典语
|
||||||
|
- value: sw
|
||||||
|
label:
|
||||||
|
en_US: Swahili
|
||||||
|
zh_Hans: 斯瓦希里语
|
||||||
|
- value: ta
|
||||||
|
label:
|
||||||
|
en_US: Tamil
|
||||||
|
zh_Hans: 泰米尔语
|
||||||
|
- value: te
|
||||||
|
label:
|
||||||
|
en_US: Telugu
|
||||||
|
zh_Hans: 泰卢固语
|
||||||
|
- value: tg
|
||||||
|
label:
|
||||||
|
en_US: Tajik
|
||||||
|
zh_Hans: 塔吉克语
|
||||||
|
- value: th
|
||||||
|
label:
|
||||||
|
en_US: Thai
|
||||||
|
zh_Hans: 泰语
|
||||||
|
- value: tk
|
||||||
|
label:
|
||||||
|
en_US: Turkmen
|
||||||
|
zh_Hans: 土库曼语
|
||||||
|
- value: tl
|
||||||
|
label:
|
||||||
|
en_US: Tagalog
|
||||||
|
zh_Hans: 他加禄语
|
||||||
|
- value: tr
|
||||||
|
label:
|
||||||
|
en_US: Turkish
|
||||||
|
zh_Hans: 土耳其语
|
||||||
|
- value: tt
|
||||||
|
label:
|
||||||
|
en_US: Tatar
|
||||||
|
zh_Hans: 鞑靼语
|
||||||
|
- value: uk
|
||||||
|
label:
|
||||||
|
en_US: Ukrainian
|
||||||
|
zh_Hans: 乌克兰语
|
||||||
|
- value: ur
|
||||||
|
label:
|
||||||
|
en_US: Urdu
|
||||||
|
zh_Hans: 乌尔都语
|
||||||
|
- value: uz
|
||||||
|
label:
|
||||||
|
en_US: Uzbek
|
||||||
|
zh_Hans: 乌兹别克语
|
||||||
|
- value: vi
|
||||||
|
label:
|
||||||
|
en_US: Vietnamese
|
||||||
|
zh_Hans: 越南语
|
||||||
|
- value: yi
|
||||||
|
label:
|
||||||
|
en_US: Yiddish
|
||||||
|
zh_Hans: 意第绪语
|
||||||
|
- value: yo
|
||||||
|
label:
|
||||||
|
en_US: Yoruba
|
||||||
|
zh_Hans: 约鲁巴语
|
||||||
|
- value: yue
|
||||||
|
label:
|
||||||
|
en_US: Cantonese
|
||||||
|
zh_Hans: 粤语
|
||||||
|
- value: zh
|
||||||
|
label:
|
||||||
|
en_US: Chinese
|
||||||
|
zh_Hans: 中文
|
||||||
|
- name: chunk_level
|
||||||
|
type: select
|
||||||
|
label:
|
||||||
|
en_US: Chunk Level
|
||||||
|
zh_Hans: 分块级别
|
||||||
|
human_description:
|
||||||
|
en_US: "Choose how the transcription should be divided into chunks"
|
||||||
|
zh_Hans: "选择如何将转录内容分成块"
|
||||||
|
llm_description: "Level of the chunks to return."
|
||||||
|
form: form
|
||||||
|
default: segment
|
||||||
|
options:
|
||||||
|
- value: segment
|
||||||
|
label:
|
||||||
|
en_US: Segment
|
||||||
|
zh_Hans: 段
|
||||||
|
- name: version
|
||||||
|
type: select
|
||||||
|
label:
|
||||||
|
en_US: Version
|
||||||
|
zh_Hans: 版本
|
||||||
|
human_description:
|
||||||
|
en_US: "Select which version of the Whisper large model to use"
|
||||||
|
zh_Hans: "选择要使用的 Whisper large 模型版本"
|
||||||
|
llm_description: "Version of the model to use. All of the models are the Whisper large variant."
|
||||||
|
form: form
|
||||||
|
default: "3"
|
||||||
|
options:
|
||||||
|
- value: "3"
|
||||||
|
label:
|
||||||
|
en_US: Version 3
|
||||||
|
zh_Hans: 版本 3
|
|
@ -1,4 +1,4 @@
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
from typing import Any, Union
|
from typing import Any, Union
|
||||||
|
|
||||||
from pytz import timezone as pytz_timezone
|
from pytz import timezone as pytz_timezone
|
||||||
|
@ -20,7 +20,7 @@ class CurrentTimeTool(BuiltinTool):
|
||||||
tz = tool_parameters.get("timezone", "UTC")
|
tz = tool_parameters.get("timezone", "UTC")
|
||||||
fm = tool_parameters.get("format") or "%Y-%m-%d %H:%M:%S %Z"
|
fm = tool_parameters.get("format") or "%Y-%m-%d %H:%M:%S %Z"
|
||||||
if tz == "UTC":
|
if tz == "UTC":
|
||||||
return self.create_text_message(f"{datetime.now(timezone.utc).strftime(fm)}")
|
return self.create_text_message(f"{datetime.now(UTC).strftime(fm)}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tz = pytz_timezone(tz)
|
tz = pytz_timezone(tz)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from enum import Enum
|
from enum import Enum, StrEnum
|
||||||
from typing import TYPE_CHECKING, Any, Optional, Union
|
from typing import TYPE_CHECKING, Any, Optional, Union
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, field_validator
|
from pydantic import BaseModel, ConfigDict, field_validator
|
||||||
|
@ -62,7 +62,7 @@ class Tool(BaseModel, ABC):
|
||||||
def __init__(self, **data: Any):
|
def __init__(self, **data: Any):
|
||||||
super().__init__(**data)
|
super().__init__(**data)
|
||||||
|
|
||||||
class VariableKey(str, Enum):
|
class VariableKey(StrEnum):
|
||||||
IMAGE = "image"
|
IMAGE = "image"
|
||||||
DOCUMENT = "document"
|
DOCUMENT = "document"
|
||||||
VIDEO = "video"
|
VIDEO = "video"
|
||||||
|
|
|
@ -175,7 +175,7 @@ class WorkflowTool(Tool):
|
||||||
|
|
||||||
files.append(file_dict)
|
files.append(file_dict)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(f"Failed to transform file {file}")
|
||||||
else:
|
else:
|
||||||
parameters_result[parameter.name] = tool_parameters.get(parameter.name)
|
parameters_result[parameter.name] = tool_parameters.get(parameter.name)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import json
|
import json
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ class ToolEngine:
|
||||||
"""
|
"""
|
||||||
Invoke the tool with the given arguments.
|
Invoke the tool with the given arguments.
|
||||||
"""
|
"""
|
||||||
started_at = datetime.now(timezone.utc)
|
started_at = datetime.now(UTC)
|
||||||
meta = ToolInvokeMeta(
|
meta = ToolInvokeMeta(
|
||||||
time_cost=0.0,
|
time_cost=0.0,
|
||||||
error=None,
|
error=None,
|
||||||
|
@ -176,7 +176,7 @@ class ToolEngine:
|
||||||
meta.error = str(e)
|
meta.error = str(e)
|
||||||
raise ToolEngineInvokeError(meta)
|
raise ToolEngineInvokeError(meta)
|
||||||
finally:
|
finally:
|
||||||
ended_at = datetime.now(timezone.utc)
|
ended_at = datetime.now(UTC)
|
||||||
meta.time_cost = (ended_at - started_at).total_seconds()
|
meta.time_cost = (ended_at - started_at).total_seconds()
|
||||||
|
|
||||||
return meta, response
|
return meta, response
|
||||||
|
|
|
@ -98,7 +98,7 @@ class ToolFileManager:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
blob = response.content
|
blob = response.content
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Failed to download file from {file_url}: {e}")
|
logger.exception(f"Failed to download file from {file_url}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
mimetype = guess_type(file_url)[0] or "octet/stream"
|
mimetype = guess_type(file_url)[0] or "octet/stream"
|
||||||
|
|
|
@ -388,7 +388,7 @@ class ToolManager:
|
||||||
yield provider
|
yield provider
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"load builtin provider {provider} error: {e}")
|
logger.exception(f"load builtin provider {provider}")
|
||||||
continue
|
continue
|
||||||
# set builtin providers loaded
|
# set builtin providers loaded
|
||||||
cls._builtin_providers_loaded = True
|
cls._builtin_providers_loaded = True
|
||||||
|
|
|
@ -40,7 +40,7 @@ class ToolFileMessageTransformer:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(f"Failed to download image from {url}")
|
||||||
result.append(
|
result.append(
|
||||||
ToolInvokeMessage(
|
ToolInvokeMessage(
|
||||||
type=ToolInvokeMessage.MessageType.TEXT,
|
type=ToolInvokeMessage.MessageType.TEXT,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class SegmentType(str, Enum):
|
class SegmentType(StrEnum):
|
||||||
NONE = "none"
|
NONE = "none"
|
||||||
NUMBER = "number"
|
NUMBER = "number"
|
||||||
STRING = "string"
|
STRING = "string"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
@ -8,7 +8,7 @@ from core.model_runtime.entities.llm_entities import LLMUsage
|
||||||
from models.workflow import WorkflowNodeExecutionStatus
|
from models.workflow import WorkflowNodeExecutionStatus
|
||||||
|
|
||||||
|
|
||||||
class NodeRunMetadataKey(str, Enum):
|
class NodeRunMetadataKey(StrEnum):
|
||||||
"""
|
"""
|
||||||
Node Run Metadata Key.
|
Node Run Metadata Key.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class SystemVariableKey(str, Enum):
|
class SystemVariableKey(StrEnum):
|
||||||
"""
|
"""
|
||||||
System Variables.
|
System Variables.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ class RouteNodeState(BaseModel):
|
||||||
raise Exception(f"Invalid route status {run_result.status}")
|
raise Exception(f"Invalid route status {run_result.status}")
|
||||||
|
|
||||||
self.node_run_result = run_result
|
self.node_run_result = run_result
|
||||||
self.finished_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
self.finished_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
|
|
||||||
|
|
||||||
class RuntimeRouteState(BaseModel):
|
class RuntimeRouteState(BaseModel):
|
||||||
|
@ -81,7 +81,7 @@ class RuntimeRouteState(BaseModel):
|
||||||
|
|
||||||
:param node_id: node id
|
:param node_id: node id
|
||||||
"""
|
"""
|
||||||
state = RouteNodeState(node_id=node_id, start_at=datetime.now(timezone.utc).replace(tzinfo=None))
|
state = RouteNodeState(node_id=node_id, start_at=datetime.now(UTC).replace(tzinfo=None))
|
||||||
self.node_state_mapping[state.id] = state
|
self.node_state_mapping[state.id] = state
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,7 @@ class GraphEngine:
|
||||||
"answer"
|
"answer"
|
||||||
].strip()
|
].strip()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Graph run failed: {str(e)}")
|
logger.exception("Graph run failed")
|
||||||
yield GraphRunFailedEvent(error=str(e))
|
yield GraphRunFailedEvent(error=str(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -692,7 +692,7 @@ class GraphEngine:
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Node {node_instance.node_data.title} run failed: {str(e)}")
|
logger.exception(f"Node {node_instance.node_data.title} run failed")
|
||||||
raise e
|
raise e
|
||||||
finally:
|
finally:
|
||||||
db.session.close()
|
db.session.close()
|
||||||
|
|
|
@ -69,7 +69,7 @@ class BaseNode(Generic[GenericNodeData]):
|
||||||
try:
|
try:
|
||||||
result = self._run()
|
result = self._run()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Node {self.node_id} failed to run: {e}")
|
logger.exception(f"Node {self.node_id} failed to run")
|
||||||
result = NodeRunResult(
|
result = NodeRunResult(
|
||||||
status=WorkflowNodeExecutionStatus.FAILED,
|
status=WorkflowNodeExecutionStatus.FAILED,
|
||||||
error=str(e),
|
error=str(e),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
class NodeType(str, Enum):
|
class NodeType(StrEnum):
|
||||||
START = "start"
|
START = "start"
|
||||||
END = "end"
|
END = "end"
|
||||||
ANSWER = "answer"
|
ANSWER = "answer"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
@ -6,7 +6,7 @@ from pydantic import Field
|
||||||
from core.workflow.nodes.base import BaseIterationNodeData, BaseIterationState, BaseNodeData
|
from core.workflow.nodes.base import BaseIterationNodeData, BaseIterationState, BaseNodeData
|
||||||
|
|
||||||
|
|
||||||
class ErrorHandleMode(str, Enum):
|
class ErrorHandleMode(StrEnum):
|
||||||
TERMINATED = "terminated"
|
TERMINATED = "terminated"
|
||||||
CONTINUE_ON_ERROR = "continue-on-error"
|
CONTINUE_ON_ERROR = "continue-on-error"
|
||||||
REMOVE_ABNORMAL_OUTPUT = "remove-abnormal-output"
|
REMOVE_ABNORMAL_OUTPUT = "remove-abnormal-output"
|
||||||
|
|
|
@ -2,7 +2,7 @@ import logging
|
||||||
import uuid
|
import uuid
|
||||||
from collections.abc import Generator, Mapping, Sequence
|
from collections.abc import Generator, Mapping, Sequence
|
||||||
from concurrent.futures import Future, wait
|
from concurrent.futures import Future, wait
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
from queue import Empty, Queue
|
from queue import Empty, Queue
|
||||||
from typing import TYPE_CHECKING, Any, Optional, cast
|
from typing import TYPE_CHECKING, Any, Optional, cast
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ class IterationNode(BaseNode[IterationNodeData]):
|
||||||
thread_pool_id=self.thread_pool_id,
|
thread_pool_id=self.thread_pool_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
start_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
start_at = datetime.now(UTC).replace(tzinfo=None)
|
||||||
|
|
||||||
yield IterationRunStartedEvent(
|
yield IterationRunStartedEvent(
|
||||||
iteration_id=self.id,
|
iteration_id=self.id,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from collections.abc import Mapping, Sequence
|
from collections.abc import Mapping, Sequence
|
||||||
from os import path
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
@ -180,7 +179,6 @@ class ToolNode(BaseNode[ToolNodeData]):
|
||||||
for response in tool_response:
|
for response in tool_response:
|
||||||
if response.type in {ToolInvokeMessage.MessageType.IMAGE_LINK, ToolInvokeMessage.MessageType.IMAGE}:
|
if response.type in {ToolInvokeMessage.MessageType.IMAGE_LINK, ToolInvokeMessage.MessageType.IMAGE}:
|
||||||
url = str(response.message) if response.message else None
|
url = str(response.message) if response.message else None
|
||||||
ext = path.splitext(url)[1] if url else ".bin"
|
|
||||||
tool_file_id = str(url).split("/")[-1].split(".")[0]
|
tool_file_id = str(url).split("/")[-1].split(".")[0]
|
||||||
transfer_method = response.meta.get("transfer_method", FileTransferMethod.TOOL_FILE)
|
transfer_method = response.meta.get("transfer_method", FileTransferMethod.TOOL_FILE)
|
||||||
|
|
||||||
|
@ -202,7 +200,6 @@ class ToolNode(BaseNode[ToolNodeData]):
|
||||||
)
|
)
|
||||||
result.append(file)
|
result.append(file)
|
||||||
elif response.type == ToolInvokeMessage.MessageType.BLOB:
|
elif response.type == ToolInvokeMessage.MessageType.BLOB:
|
||||||
# get tool file id
|
|
||||||
tool_file_id = str(response.message).split("/")[-1].split(".")[0]
|
tool_file_id = str(response.message).split("/")[-1].split(".")[0]
|
||||||
with Session(db.engine) as session:
|
with Session(db.engine) as session:
|
||||||
stmt = select(ToolFile).where(ToolFile.id == tool_file_id)
|
stmt = select(ToolFile).where(ToolFile.id == tool_file_id)
|
||||||
|
@ -211,7 +208,6 @@ class ToolNode(BaseNode[ToolNodeData]):
|
||||||
raise ValueError(f"tool file {tool_file_id} not exists")
|
raise ValueError(f"tool file {tool_file_id} not exists")
|
||||||
mapping = {
|
mapping = {
|
||||||
"tool_file_id": tool_file_id,
|
"tool_file_id": tool_file_id,
|
||||||
"type": FileType.IMAGE,
|
|
||||||
"transfer_method": FileTransferMethod.TOOL_FILE,
|
"transfer_method": FileTransferMethod.TOOL_FILE,
|
||||||
}
|
}
|
||||||
file = file_factory.build_from_mapping(
|
file = file_factory.build_from_mapping(
|
||||||
|
@ -228,13 +224,8 @@ class ToolNode(BaseNode[ToolNodeData]):
|
||||||
tool_file = session.scalar(stmt)
|
tool_file = session.scalar(stmt)
|
||||||
if tool_file is None:
|
if tool_file is None:
|
||||||
raise ToolFileError(f"Tool file {tool_file_id} does not exist")
|
raise ToolFileError(f"Tool file {tool_file_id} does not exist")
|
||||||
if "." in url:
|
|
||||||
extension = "." + url.split("/")[-1].split(".")[1]
|
|
||||||
else:
|
|
||||||
extension = ".bin"
|
|
||||||
mapping = {
|
mapping = {
|
||||||
"tool_file_id": tool_file_id,
|
"tool_file_id": tool_file_id,
|
||||||
"type": FileType.IMAGE,
|
|
||||||
"transfer_method": transfer_method,
|
"transfer_method": transfer_method,
|
||||||
"url": url,
|
"url": url,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
from enum import Enum
|
from enum import StrEnum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from core.workflow.nodes.base import BaseNodeData
|
from core.workflow.nodes.base import BaseNodeData
|
||||||
|
|
||||||
|
|
||||||
class WriteMode(str, Enum):
|
class WriteMode(StrEnum):
|
||||||
OVER_WRITE = "over-write"
|
OVER_WRITE = "over-write"
|
||||||
APPEND = "append"
|
APPEND = "append"
|
||||||
CLEAR = "clear"
|
CLEAR = "clear"
|
||||||
|
|
|
@ -33,7 +33,7 @@ def handle(sender, **kwargs):
|
||||||
raise NotFound("Document not found")
|
raise NotFound("Document not found")
|
||||||
|
|
||||||
document.indexing_status = "parsing"
|
document.indexing_status = "parsing"
|
||||||
document.processing_started_at = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
document.processing_started_at = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
|
||||||
documents.append(document)
|
documents.append(document)
|
||||||
db.session.add(document)
|
db.session.add(document)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
from core.app.entities.app_invoke_entities import AgentChatAppGenerateEntity, ChatAppGenerateEntity
|
from core.app.entities.app_invoke_entities import AgentChatAppGenerateEntity, ChatAppGenerateEntity
|
||||||
from events.message_event import message_was_created
|
from events.message_event import message_was_created
|
||||||
|
@ -17,5 +17,5 @@ def handle(sender, **kwargs):
|
||||||
db.session.query(Provider).filter(
|
db.session.query(Provider).filter(
|
||||||
Provider.tenant_id == application_generate_entity.app_config.tenant_id,
|
Provider.tenant_id == application_generate_entity.app_config.tenant_id,
|
||||||
Provider.provider_name == application_generate_entity.model_conf.provider,
|
Provider.provider_name == application_generate_entity.model_conf.provider,
|
||||||
).update({"last_used": datetime.now(timezone.utc).replace(tzinfo=None)})
|
).update({"last_used": datetime.now(UTC).replace(tzinfo=None)})
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
|
@ -70,7 +70,7 @@ class Storage:
|
||||||
try:
|
try:
|
||||||
self.storage_runner.save(filename, data)
|
self.storage_runner.save(filename, data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Failed to save file: %s", e)
|
logging.exception(f"Failed to save file {filename}")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def load(self, filename: str, /, *, stream: bool = False) -> Union[bytes, Generator]:
|
def load(self, filename: str, /, *, stream: bool = False) -> Union[bytes, Generator]:
|
||||||
|
@ -80,42 +80,42 @@ class Storage:
|
||||||
else:
|
else:
|
||||||
return self.load_once(filename)
|
return self.load_once(filename)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Failed to load file: %s", e)
|
logging.exception(f"Failed to load file {filename}")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def load_once(self, filename: str) -> bytes:
|
def load_once(self, filename: str) -> bytes:
|
||||||
try:
|
try:
|
||||||
return self.storage_runner.load_once(filename)
|
return self.storage_runner.load_once(filename)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Failed to load_once file: %s", e)
|
logging.exception(f"Failed to load_once file {filename}")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def load_stream(self, filename: str) -> Generator:
|
def load_stream(self, filename: str) -> Generator:
|
||||||
try:
|
try:
|
||||||
return self.storage_runner.load_stream(filename)
|
return self.storage_runner.load_stream(filename)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Failed to load_stream file: %s", e)
|
logging.exception(f"Failed to load_stream file {filename}")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def download(self, filename, target_filepath):
|
def download(self, filename, target_filepath):
|
||||||
try:
|
try:
|
||||||
self.storage_runner.download(filename, target_filepath)
|
self.storage_runner.download(filename, target_filepath)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Failed to download file: %s", e)
|
logging.exception(f"Failed to download file {filename}")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def exists(self, filename):
|
def exists(self, filename):
|
||||||
try:
|
try:
|
||||||
return self.storage_runner.exists(filename)
|
return self.storage_runner.exists(filename)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Failed to check file exists: %s", e)
|
logging.exception(f"Failed to check file exists {filename}")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def delete(self, filename):
|
def delete(self, filename):
|
||||||
try:
|
try:
|
||||||
return self.storage_runner.delete(filename)
|
return self.storage_runner.delete(filename)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Failed to delete file: %s", e)
|
logging.exception(f"Failed to delete file {filename}")
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user