diff --git a/.github/actions/setup-poetry/action.yml b/.github/actions/setup-poetry/action.yml new file mode 100644 index 0000000000..5feab33d1d --- /dev/null +++ b/.github/actions/setup-poetry/action.yml @@ -0,0 +1,36 @@ +name: Setup Poetry and Python + +inputs: + python-version: + description: Python version to use and the Poetry installed with + required: true + default: '3.10' + poetry-version: + description: Poetry version to set up + required: true + default: '1.8.4' + poetry-lockfile: + description: Path to the Poetry lockfile to restore cache from + required: true + default: '' + +runs: + using: composite + steps: + - name: Set up Python ${{ inputs.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + cache: pip + + - name: Install Poetry + shell: bash + run: pip install poetry==${{ inputs.poetry-version }} + + - name: Restore Poetry cache + if: ${{ inputs.poetry-lockfile != '' }} + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + cache: poetry + cache-dependency-path: ${{ inputs.poetry-lockfile }} diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 059be38362..138fb886d6 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,34 +1,32 @@ -# Checklist: +# Summary + +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. + +> [!Tip] +> Close issue syntax: `Fixes #` or `Resolves #`, see [documentation](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) for more details. + + +# Screenshots + + + + + + + + + + +
Before: After:
......
+ +# Checklist > [!IMPORTANT] > Please review the checklist below before submitting your pull request. -- [ ] Please open an issue before creating a PR or link to an existing issue -- [ ] I have performed a self-review of my own code -- [ ] I have commented my code, particularly in hard-to-understand areas -- [ ] I ran `dev/reformat`(backend) and `cd web && npx lint-staged`(frontend) to appease the lint gods - -# Description - -Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. Close issue syntax: `Fixes #`, see [documentation](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) for more details. - -Fixes - -## Type of Change - -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update, included: [Dify Document](https://github.com/langgenius/dify-docs) -- [ ] Improvement, including but not limited to code refactoring, performance optimization, and UI/UX improvement -- [ ] Dependency upgrade - -# Testing Instructions - -Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration - -- [ ] Test A -- [ ] Test B - - +- [x] I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!) +- [x] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change. +- [x] I've updated the documentation accordingly. +- [x] I ran `dev/reformat`(backend) and `cd web && npx lint-staged`(frontend) to appease the lint gods diff --git a/.github/workflows/api-tests.yml b/.github/workflows/api-tests.yml index eb09abe77c..76e844aaad 100644 --- a/.github/workflows/api-tests.yml +++ b/.github/workflows/api-tests.yml @@ -28,15 +28,11 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Install Poetry - uses: abatilo/actions-poetry@v3 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - name: Setup Poetry and Python ${{ matrix.python-version }} + uses: ./.github/actions/setup-poetry with: python-version: ${{ matrix.python-version }} - cache: poetry - cache-dependency-path: api/poetry.lock + poetry-lockfile: api/poetry.lock - name: Check Poetry lockfile run: | diff --git a/.github/workflows/db-migration-test.yml b/.github/workflows/db-migration-test.yml index c6fe87264d..f4eb0f8e33 100644 --- a/.github/workflows/db-migration-test.yml +++ b/.github/workflows/db-migration-test.yml @@ -15,25 +15,15 @@ concurrency: jobs: db-migration-test: runs-on: ubuntu-latest - strategy: - matrix: - python-version: - - "3.10" steps: - name: Checkout code uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - name: Setup Poetry and Python + uses: ./.github/actions/setup-poetry with: - python-version: ${{ matrix.python-version }} - cache-dependency-path: | - api/pyproject.toml - api/poetry.lock - - - name: Install Poetry - uses: abatilo/actions-poetry@v3 + poetry-lockfile: api/poetry.lock - name: Install dependencies run: poetry install -C api diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 9377fa84f6..f7221fb4d1 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -22,34 +22,28 @@ jobs: id: changed-files uses: tj-actions/changed-files@v45 with: - files: api/** + files: | + api/** + .github/workflows/style.yml - - name: Install Poetry + - name: Setup Poetry and Python if: steps.changed-files.outputs.any_changed == 'true' - uses: abatilo/actions-poetry@v3 + uses: ./.github/actions/setup-poetry - - name: Set up Python - uses: actions/setup-python@v5 - if: steps.changed-files.outputs.any_changed == 'true' - with: - python-version: '3.10' - - - name: Python dependencies + - name: Install dependencies if: steps.changed-files.outputs.any_changed == 'true' run: poetry install -C api --only lint - name: Ruff check if: steps.changed-files.outputs.any_changed == 'true' - run: poetry run -C api ruff check ./api + run: | + poetry run -C api ruff check ./api + poetry run -C api ruff format --check ./api - name: Dotenv check if: steps.changed-files.outputs.any_changed == 'true' run: poetry run -C api dotenv-linter ./api/.env.example ./web/.env.example - - name: Ruff formatter check - if: steps.changed-files.outputs.any_changed == 'true' - run: poetry run -C api ruff format --check ./api - - name: Lint hints if: failure() run: echo "Please run 'dev/reformat' to fix the fixable linting errors." diff --git a/.github/workflows/vdb-tests.yml b/.github/workflows/vdb-tests.yml index 8ea38fde76..caddd23bab 100644 --- a/.github/workflows/vdb-tests.yml +++ b/.github/workflows/vdb-tests.yml @@ -28,15 +28,11 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Install Poetry - uses: abatilo/actions-poetry@v3 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - name: Setup Poetry and Python ${{ matrix.python-version }} + uses: ./.github/actions/setup-poetry with: python-version: ${{ matrix.python-version }} - cache: poetry - cache-dependency-path: api/poetry.lock + poetry-lockfile: api/poetry.lock - name: Check Poetry lockfile run: | diff --git a/README.md b/README.md index 4779048001..4c2d803854 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,9 @@ chat on Discord + + join Reddit follow on X(Twitter) @@ -177,3 +180,4 @@ To protect your privacy, please avoid posting security issues on GitHub. Instead ## License This repository is available under the [Dify Open Source License](LICENSE), which is essentially Apache 2.0 with a few additional restrictions. + diff --git a/README_AR.md b/README_AR.md index e46ba73738..a4cfd744c0 100644 --- a/README_AR.md +++ b/README_AR.md @@ -15,6 +15,9 @@ chat on Discord + + join Reddit follow on X(Twitter) diff --git a/README_CN.md b/README_CN.md index 070951699a..2a3f12dd05 100644 --- a/README_CN.md +++ b/README_CN.md @@ -15,6 +15,9 @@ chat on Discord + + join Reddit follow on X(Twitter) diff --git a/README_ES.md b/README_ES.md index 7da5ac7b61..ab79ec9f85 100644 --- a/README_ES.md +++ b/README_ES.md @@ -15,6 +15,9 @@ chat en Discord + + join Reddit seguir en X(Twitter) diff --git a/README_FR.md b/README_FR.md index 15f6f2650f..1c963b495f 100644 --- a/README_FR.md +++ b/README_FR.md @@ -15,6 +15,9 @@ chat sur Discord + + join Reddit suivre sur X(Twitter) diff --git a/README_JA.md b/README_JA.md index a2e6b173f5..b0f06ff259 100644 --- a/README_JA.md +++ b/README_JA.md @@ -15,6 +15,9 @@ Discordでチャット + + Reddit X(Twitter)でフォロー diff --git a/README_KL.md b/README_KL.md index 8f2affdce5..be727774e9 100644 --- a/README_KL.md +++ b/README_KL.md @@ -15,6 +15,9 @@ chat on Discord + + Follow Reddit follow on X(Twitter) diff --git a/README_KR.md b/README_KR.md index 6c3a9ed7f6..9f8e072ba6 100644 --- a/README_KR.md +++ b/README_KR.md @@ -15,6 +15,9 @@ chat on Discord + + Follow Reddit follow on X(Twitter) diff --git a/README_PT.md b/README_PT.md index 3d66b76802..d822cbea67 100644 --- a/README_PT.md +++ b/README_PT.md @@ -19,6 +19,9 @@ chat on Discord + + Follow Reddit follow on X(Twitter) @@ -238,4 +241,4 @@ Para proteger sua privacidade, evite postar problemas de segurança no GitHub. E ## Licença -Este repositório está disponível sob a [Licença de Código Aberto Dify](LICENSE), que é essencialmente Apache 2.0 com algumas restrições adicionais. \ No newline at end of file +Este repositório está disponível sob a [Licença de Código Aberto Dify](LICENSE), que é essencialmente Apache 2.0 com algumas restrições adicionais. diff --git a/README_SI.md b/README_SI.md new file mode 100644 index 0000000000..41a44600e8 --- /dev/null +++ b/README_SI.md @@ -0,0 +1,180 @@ +![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) + +

+ 📌 Predstavljamo nalaganje datotek Dify Workflow: znova ustvarite Google NotebookLM Podcast +

+ +

+ Dify Cloud · + Samostojno gostovanje · + Dokumentacija · + Povpraševanje za podjetja +

+ +

+ + Static Badge + + Static Badge + + chat on Discord + + follow on X(Twitter) + + Docker Pulls + + Commits last month + + Issues closed + + Discussion posts +

+ +

+ README in English + 简体中文版自述文件 + 日本語のREADME + README en Español + README en Français + README tlhIngan Hol + README in Korean + README بالعربية + Türkçe README + README Tiếng Việt + README Slovenščina +

+ + +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 + +
+ +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
** +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
** +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
** +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.
+ > 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** + + + + + +## 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. \ No newline at end of file diff --git a/README_TR.md b/README_TR.md index a75889e576..38fada34e9 100644 --- a/README_TR.md +++ b/README_TR.md @@ -15,6 +15,9 @@ Discord'da sohbet et + + Follow Reddit X(Twitter)'da takip et diff --git a/README_VI.md b/README_VI.md index 8d49e49766..6f296e508c 100644 --- a/README_VI.md +++ b/README_VI.md @@ -15,6 +15,9 @@ chat trên Discord + + Follow Reddit theo dõi trên X(Twitter) @@ -235,4 +238,4 @@ Triển khai Dify lên nền tảng đám mây với một cú nhấp chuột b ## Giấy phép -Kho lưu trữ này có sẵn theo [Giấy phép Mã nguồn Mở Dify](LICENSE), về cơ bản là Apache 2.0 với một vài hạn chế bổ sung. \ No newline at end of file +Kho lưu trữ này có sẵn theo [Giấy phép Mã nguồn Mở Dify](LICENSE), về cơ bản là Apache 2.0 với một vài hạn chế bổ sung. diff --git a/api/.env.example b/api/.env.example index 5dfc398df2..5751605b48 100644 --- a/api/.env.example +++ b/api/.env.example @@ -367,6 +367,10 @@ LOG_FILE= LOG_FILE_MAX_SIZE=20 # Log file max backup count LOG_FILE_BACKUP_COUNT=5 +# Log dateformat +LOG_DATEFORMAT=%Y-%m-%d %H:%M:%S +# Log Timezone +LOG_TZ=UTC # Indexing configuration INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=1000 @@ -396,3 +400,5 @@ POSITION_PROVIDER_EXCLUDES= # Reset password token expiry minutes RESET_PASSWORD_TOKEN_EXPIRY_MINUTES=5 + +CREATE_TIDB_SERVICE_JOB_ENABLED=false \ No newline at end of file diff --git a/api/Dockerfile b/api/Dockerfile index eb37303182..175535b188 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -4,7 +4,7 @@ FROM python:3.10-slim-bookworm AS base WORKDIR /app/api # Install Poetry -ENV POETRY_VERSION=1.8.3 +ENV POETRY_VERSION=1.8.4 # if you located in China, you can use aliyun mirror to speed up # RUN pip install --no-cache-dir poetry==${POETRY_VERSION} -i https://mirrors.aliyun.com/pypi/simple/ @@ -55,7 +55,7 @@ RUN apt-get update \ && echo "deb http://deb.debian.org/debian testing main" > /etc/apt/sources.list \ && apt-get update \ # For Security - && apt-get install -y --no-install-recommends expat=2.6.3-2 libldap-2.5-0=2.5.18+dfsg-3+b1 perl=5.40.0-6 libsqlite3-0=3.46.1-1 zlib1g=1:1.3.dfsg+really1.3.1-1+b1 \ + && apt-get install -y --no-install-recommends expat=2.6.4-1 libldap-2.5-0=2.5.18+dfsg-3+b1 perl=5.40.0-7 libsqlite3-0=3.46.1-1 zlib1g=1:1.3.dfsg+really1.3.1-1+b1 \ # install a chinese font to support the use of tools like matplotlib && apt-get install -y fonts-noto-cjk \ && apt-get autoremove -y \ diff --git a/api/app.py b/api/app.py index 60cd622ef4..a667a84fd6 100644 --- a/api/app.py +++ b/api/app.py @@ -1,4 +1,5 @@ import os +import sys from configs import dify_config @@ -29,6 +30,9 @@ from models import account, dataset, model, source, task, tool, tools, web # no # 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) @@ -49,7 +53,6 @@ if dify_config.TESTING: @app.after_request def after_request(response): """Add Version headers to the response.""" - response.set_cookie("remember_token", "", expires=0) response.headers.add("X-Version", dify_config.CURRENT_VERSION) response.headers.add("X-Env", dify_config.DEPLOY_ENV) return response diff --git a/api/configs/feature/__init__.py b/api/configs/feature/__init__.py index f011b638e3..f368a19469 100644 --- a/api/configs/feature/__init__.py +++ b/api/configs/feature/__init__.py @@ -376,7 +376,7 @@ class LoggingConfig(BaseSettings): LOG_TZ: Optional[str] = Field( description="Timezone for log timestamps (e.g., 'America/New_York')", - default=None, + default="UTC", ) @@ -611,6 +611,11 @@ class DataSetConfig(BaseSettings): default=500, ) + CREATE_TIDB_SERVICE_JOB_ENABLED: bool = Field( + description="Enable or disable create tidb service job", + default=False, + ) + class WorkspaceConfig(BaseSettings): """ diff --git a/api/configs/packaging/__init__.py b/api/configs/packaging/__init__.py index b5cb1f06d9..65065efbc0 100644 --- a/api/configs/packaging/__init__.py +++ b/api/configs/packaging/__init__.py @@ -9,7 +9,7 @@ class PackagingInfo(BaseSettings): CURRENT_VERSION: str = Field( description="Dify version", - default="0.11.0", + default="0.11.1", ) COMMIT_SHA: str = Field( diff --git a/api/constants/languages.py b/api/constants/languages.py index 524dc61b57..a6394da819 100644 --- a/api/constants/languages.py +++ b/api/constants/languages.py @@ -17,6 +17,7 @@ language_timezone_mapping = { "hi-IN": "Asia/Kolkata", "tr-TR": "Europe/Istanbul", "fa-IR": "Asia/Tehran", + "sl-SI": "Europe/Ljubljana", } languages = list(language_timezone_mapping.keys()) diff --git a/api/controllers/console/datasets/datasets_document.py b/api/controllers/console/datasets/datasets_document.py index 6d6886fcb8..60848039c5 100644 --- a/api/controllers/console/datasets/datasets_document.py +++ b/api/controllers/console/datasets/datasets_document.py @@ -317,8 +317,11 @@ class DatasetInitApi(Resource): raise ValueError("embedding model and embedding model provider are required for high quality indexing.") try: model_manager = ModelManager() - model_manager.get_default_model_instance( - tenant_id=current_user.current_tenant_id, model_type=ModelType.TEXT_EMBEDDING + model_manager.get_model_instance( + tenant_id=current_user.current_tenant_id, + provider=args["embedding_model_provider"], + model_type=ModelType.TEXT_EMBEDDING, + model=args["embedding_model"], ) except InvokeAuthorizationError: raise ProviderNotInitializeError( diff --git a/api/controllers/service_api/app/conversation.py b/api/controllers/service_api/app/conversation.py index 815fd6a27a..c62fd77d36 100644 --- a/api/controllers/service_api/app/conversation.py +++ b/api/controllers/service_api/app/conversation.py @@ -62,9 +62,10 @@ class ConversationDetailApi(Resource): conversation_id = str(c_id) try: - return ConversationService.delete(app_model, conversation_id, end_user) + ConversationService.delete(app_model, conversation_id, end_user) except services.errors.conversation.ConversationNotExistsError: raise NotFound("Conversation Not Exists.") + return {"result": "success"}, 200 class ConversationRenameApi(Resource): diff --git a/api/controllers/service_api/app/message.py b/api/controllers/service_api/app/message.py index ecff804adc..ada40ec9cb 100644 --- a/api/controllers/service_api/app/message.py +++ b/api/controllers/service_api/app/message.py @@ -10,6 +10,7 @@ from controllers.service_api.app.error import NotChatAppError from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token from core.app.entities.app_invoke_entities import InvokeFrom from fields.conversation_fields import message_file_fields +from fields.raws import FilesContainedField from libs.helper import TimestampField, uuid_value from models.model import App, AppMode, EndUser from services.errors.message import SuggestedQuestionsAfterAnswerDisabledError @@ -55,7 +56,7 @@ class MessageListApi(Resource): "id": fields.String, "conversation_id": fields.String, "parent_message_id": fields.String, - "inputs": fields.Raw, + "inputs": FilesContainedField, "query": fields.String, "answer": fields.String(attribute="re_sign_file_url_answer"), "message_files": fields.List(fields.Nested(message_file_fields)), diff --git a/api/core/agent/base_agent_runner.py b/api/core/agent/base_agent_runner.py index 507455c176..860ec5de0c 100644 --- a/api/core/agent/base_agent_runner.py +++ b/api/core/agent/base_agent_runner.py @@ -30,6 +30,7 @@ from core.model_runtime.entities import ( ToolPromptMessage, UserPromptMessage, ) +from core.model_runtime.entities.message_entities import ImagePromptMessageContent from core.model_runtime.entities.model_entities import ModelFeature from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from core.model_runtime.utils.encoders import jsonable_encoder @@ -65,7 +66,7 @@ class BaseAgentRunner(AppRunner): prompt_messages: Optional[list[PromptMessage]] = None, variables_pool: Optional[ToolRuntimeVariablePool] = None, db_variables: Optional[ToolConversationVariables] = None, - model_instance: ModelInstance = None, + model_instance: ModelInstance | None = None, ) -> None: self.tenant_id = tenant_id self.application_generate_entity = application_generate_entity @@ -508,24 +509,27 @@ class BaseAgentRunner(AppRunner): def organize_agent_user_prompt(self, message: Message) -> UserPromptMessage: files = db.session.query(MessageFile).filter(MessageFile.message_id == message.id).all() - if files: - file_extra_config = FileUploadConfigManager.convert(message.app_model_config.to_dict()) - - if file_extra_config: - file_objs = file_factory.build_from_message_files( - message_files=files, tenant_id=self.tenant_id, config=file_extra_config - ) - else: - file_objs = [] - - if not file_objs: - return UserPromptMessage(content=message.query) - else: - prompt_message_contents: list[PromptMessageContent] = [] - prompt_message_contents.append(TextPromptMessageContent(data=message.query)) - for file_obj in file_objs: - prompt_message_contents.append(file_manager.to_prompt_message_content(file_obj)) - - return UserPromptMessage(content=prompt_message_contents) - else: + if not files: return UserPromptMessage(content=message.query) + file_extra_config = FileUploadConfigManager.convert(message.app_model_config.to_dict()) + if not file_extra_config: + return UserPromptMessage(content=message.query) + + image_detail_config = file_extra_config.image_config.detail if file_extra_config.image_config else None + image_detail_config = image_detail_config or ImagePromptMessageContent.DETAIL.LOW + + file_objs = file_factory.build_from_message_files( + message_files=files, tenant_id=self.tenant_id, config=file_extra_config + ) + if not file_objs: + return UserPromptMessage(content=message.query) + prompt_message_contents: list[PromptMessageContent] = [] + prompt_message_contents.append(TextPromptMessageContent(data=message.query)) + for file in file_objs: + prompt_message_contents.append( + file_manager.to_prompt_message_content( + file, + image_detail_config=image_detail_config, + ) + ) + return UserPromptMessage(content=prompt_message_contents) diff --git a/api/core/agent/cot_chat_agent_runner.py b/api/core/agent/cot_chat_agent_runner.py index 6261a9b12c..d8d047fe91 100644 --- a/api/core/agent/cot_chat_agent_runner.py +++ b/api/core/agent/cot_chat_agent_runner.py @@ -10,6 +10,7 @@ from core.model_runtime.entities import ( TextPromptMessageContent, UserPromptMessage, ) +from core.model_runtime.entities.message_entities import ImagePromptMessageContent from core.model_runtime.utils.encoders import jsonable_encoder @@ -36,8 +37,24 @@ class CotChatAgentRunner(CotAgentRunner): if self.files: prompt_message_contents: list[PromptMessageContent] = [] prompt_message_contents.append(TextPromptMessageContent(data=query)) - for file_obj in self.files: - prompt_message_contents.append(file_manager.to_prompt_message_content(file_obj)) + + # get image detail config + image_detail_config = ( + self.application_generate_entity.file_upload_config.image_config.detail + if ( + self.application_generate_entity.file_upload_config + and self.application_generate_entity.file_upload_config.image_config + ) + else None + ) + image_detail_config = image_detail_config or ImagePromptMessageContent.DETAIL.LOW + for file in self.files: + prompt_message_contents.append( + file_manager.to_prompt_message_content( + file, + image_detail_config=image_detail_config, + ) + ) prompt_messages.append(UserPromptMessage(content=prompt_message_contents)) else: diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py index 9083b4e85f..cd546dee12 100644 --- a/api/core/agent/fc_agent_runner.py +++ b/api/core/agent/fc_agent_runner.py @@ -22,6 +22,7 @@ from core.model_runtime.entities import ( ToolPromptMessage, UserPromptMessage, ) +from core.model_runtime.entities.message_entities import ImagePromptMessageContent from core.prompt.agent_history_prompt_transform import AgentHistoryPromptTransform from core.tools.entities.tool_entities import ToolInvokeMeta from core.tools.tool_engine import ToolEngine @@ -397,8 +398,24 @@ class FunctionCallAgentRunner(BaseAgentRunner): if self.files: prompt_message_contents: list[PromptMessageContent] = [] prompt_message_contents.append(TextPromptMessageContent(data=query)) - for file_obj in self.files: - prompt_message_contents.append(file_manager.to_prompt_message_content(file_obj)) + + # get image detail config + image_detail_config = ( + self.application_generate_entity.file_upload_config.image_config.detail + if ( + self.application_generate_entity.file_upload_config + and self.application_generate_entity.file_upload_config.image_config + ) + else None + ) + image_detail_config = image_detail_config or ImagePromptMessageContent.DETAIL.LOW + for file in self.files: + prompt_message_contents.append( + file_manager.to_prompt_message_content( + file, + image_detail_config=image_detail_config, + ) + ) prompt_messages.append(UserPromptMessage(content=prompt_message_contents)) else: diff --git a/api/core/app/app_config/entities.py b/api/core/app/app_config/entities.py index 6c6e342a07..9b72452d7a 100644 --- a/api/core/app/app_config/entities.py +++ b/api/core/app/app_config/entities.py @@ -4,7 +4,7 @@ from typing import Any, Optional from pydantic import BaseModel, Field, field_validator -from core.file import FileExtraConfig, FileTransferMethod, FileType +from core.file import FileTransferMethod, FileType, FileUploadConfig from core.model_runtime.entities.message_entities import PromptMessageRole from models.model import AppMode @@ -211,7 +211,7 @@ class TracingConfigEntity(BaseModel): class AppAdditionalFeatures(BaseModel): - file_upload: Optional[FileExtraConfig] = None + file_upload: Optional[FileUploadConfig] = None opening_statement: Optional[str] = None suggested_questions: list[str] = [] suggested_questions_after_answer: bool = False diff --git a/api/core/app/app_config/features/file_upload/manager.py b/api/core/app/app_config/features/file_upload/manager.py index d0f75d0b75..2043ea0e41 100644 --- a/api/core/app/app_config/features/file_upload/manager.py +++ b/api/core/app/app_config/features/file_upload/manager.py @@ -1,7 +1,7 @@ from collections.abc import Mapping from typing import Any -from core.file import FileExtraConfig +from core.file import FileUploadConfig class FileUploadConfigManager: @@ -29,19 +29,18 @@ class FileUploadConfigManager: if is_vision: data["image_config"]["detail"] = file_upload_dict.get("image", {}).get("detail", "low") - return FileExtraConfig.model_validate(data) + return FileUploadConfig.model_validate(data) @classmethod - def validate_and_set_defaults(cls, config: dict, is_vision: bool = True) -> tuple[dict, list[str]]: + def validate_and_set_defaults(cls, config: dict) -> tuple[dict, list[str]]: """ Validate and set defaults for file upload feature :param config: app model config args - :param is_vision: if True, the feature is vision feature """ if not config.get("file_upload"): config["file_upload"] = {} else: - FileExtraConfig.model_validate(config["file_upload"]) + FileUploadConfig.model_validate(config["file_upload"]) return config, ["file_upload"] diff --git a/api/core/app/apps/advanced_chat/app_config_manager.py b/api/core/app/apps/advanced_chat/app_config_manager.py index b52f235849..cb606953cd 100644 --- a/api/core/app/apps/advanced_chat/app_config_manager.py +++ b/api/core/app/apps/advanced_chat/app_config_manager.py @@ -52,9 +52,7 @@ class AdvancedChatAppConfigManager(BaseAppConfigManager): related_config_keys = [] # file upload validation - config, current_related_config_keys = FileUploadConfigManager.validate_and_set_defaults( - config=config, is_vision=False - ) + config, current_related_config_keys = FileUploadConfigManager.validate_and_set_defaults(config=config) related_config_keys.extend(current_related_config_keys) # opening_statement diff --git a/api/core/app/apps/advanced_chat/app_generator.py b/api/core/app/apps/advanced_chat/app_generator.py index 3010f8a03f..0b88345061 100644 --- a/api/core/app/apps/advanced_chat/app_generator.py +++ b/api/core/app/apps/advanced_chat/app_generator.py @@ -26,7 +26,6 @@ from core.ops.ops_trace_manager import TraceQueueManager from extensions.ext_database import db from factories import file_factory from models.account import Account -from models.enums import CreatedByRole from models.model import App, Conversation, EndUser, Message from models.workflow import Workflow @@ -98,13 +97,10 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator): # parse files files = args["files"] if args.get("files") else [] file_extra_config = FileUploadConfigManager.convert(workflow.features_dict, is_vision=False) - role = CreatedByRole.ACCOUNT if isinstance(user, Account) else CreatedByRole.END_USER if file_extra_config: file_objs = file_factory.build_from_mappings( mappings=files, tenant_id=app_model.tenant_id, - user_id=user.id, - role=role, config=file_extra_config, ) else: @@ -127,10 +123,11 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator): application_generate_entity = AdvancedChatAppGenerateEntity( task_id=str(uuid.uuid4()), app_config=app_config, + file_upload_config=file_extra_config, conversation_id=conversation.id if conversation else None, inputs=conversation.inputs if conversation - else self._prepare_user_inputs(user_inputs=inputs, app_config=app_config, user_id=user.id, role=role), + else self._prepare_user_inputs(user_inputs=inputs, app_config=app_config), query=query, files=file_objs, parent_message_id=args.get("parent_message_id") if invoke_from != InvokeFrom.SERVICE_API else UUID_NIL, diff --git a/api/core/app/apps/agent_chat/app_generator.py b/api/core/app/apps/agent_chat/app_generator.py index 73d433d94d..d1564a260e 100644 --- a/api/core/app/apps/agent_chat/app_generator.py +++ b/api/core/app/apps/agent_chat/app_generator.py @@ -23,7 +23,6 @@ from core.ops.ops_trace_manager import TraceQueueManager from extensions.ext_database import db from factories import file_factory from models import Account, App, EndUser -from models.enums import CreatedByRole logger = logging.getLogger(__name__) @@ -103,8 +102,6 @@ class AgentChatAppGenerator(MessageBasedAppGenerator): # always enable retriever resource in debugger mode override_model_config_dict["retriever_resource"] = {"enabled": True} - role = CreatedByRole.ACCOUNT if isinstance(user, Account) else CreatedByRole.END_USER - # parse files files = args.get("files") or [] file_extra_config = FileUploadConfigManager.convert(override_model_config_dict or app_model_config.to_dict()) @@ -112,8 +109,6 @@ class AgentChatAppGenerator(MessageBasedAppGenerator): file_objs = file_factory.build_from_mappings( mappings=files, tenant_id=app_model.tenant_id, - user_id=user.id, - role=role, config=file_extra_config, ) else: @@ -135,10 +130,11 @@ class AgentChatAppGenerator(MessageBasedAppGenerator): task_id=str(uuid.uuid4()), app_config=app_config, model_conf=ModelConfigConverter.convert(app_config), + file_upload_config=file_extra_config, conversation_id=conversation.id if conversation else None, inputs=conversation.inputs if conversation - else self._prepare_user_inputs(user_inputs=inputs, app_config=app_config, user_id=user.id, role=role), + else self._prepare_user_inputs(user_inputs=inputs, app_config=app_config), query=query, files=file_objs, parent_message_id=args.get("parent_message_id") if invoke_from != InvokeFrom.SERVICE_API else UUID_NIL, diff --git a/api/core/app/apps/base_app_generator.py b/api/core/app/apps/base_app_generator.py index d8e38476c7..6e6da95401 100644 --- a/api/core/app/apps/base_app_generator.py +++ b/api/core/app/apps/base_app_generator.py @@ -2,12 +2,11 @@ from collections.abc import Mapping from typing import TYPE_CHECKING, Any, Optional from core.app.app_config.entities import VariableEntityType -from core.file import File, FileExtraConfig +from core.file import File, FileUploadConfig from factories import file_factory if TYPE_CHECKING: from core.app.app_config.entities import AppConfig, VariableEntity - from models.enums import CreatedByRole class BaseAppGenerator: @@ -16,8 +15,6 @@ class BaseAppGenerator: *, user_inputs: Optional[Mapping[str, Any]], app_config: "AppConfig", - user_id: str, - role: "CreatedByRole", ) -> Mapping[str, Any]: user_inputs = user_inputs or {} # Filter input variables from form configuration, handle required fields, default values, and option values @@ -34,9 +31,7 @@ class BaseAppGenerator: k: file_factory.build_from_mapping( mapping=v, tenant_id=app_config.tenant_id, - user_id=user_id, - role=role, - config=FileExtraConfig( + config=FileUploadConfig( allowed_file_types=entity_dictionary[k].allowed_file_types, allowed_extensions=entity_dictionary[k].allowed_file_extensions, allowed_upload_methods=entity_dictionary[k].allowed_file_upload_methods, @@ -50,9 +45,7 @@ class BaseAppGenerator: k: file_factory.build_from_mappings( mappings=v, tenant_id=app_config.tenant_id, - user_id=user_id, - role=role, - config=FileExtraConfig( + config=FileUploadConfig( allowed_file_types=entity_dictionary[k].allowed_file_types, allowed_extensions=entity_dictionary[k].allowed_file_extensions, allowed_upload_methods=entity_dictionary[k].allowed_file_upload_methods, diff --git a/api/core/app/apps/chat/app_generator.py b/api/core/app/apps/chat/app_generator.py index d0ba90cc5e..e683dfef3f 100644 --- a/api/core/app/apps/chat/app_generator.py +++ b/api/core/app/apps/chat/app_generator.py @@ -23,7 +23,6 @@ from core.ops.ops_trace_manager import TraceQueueManager from extensions.ext_database import db from factories import file_factory from models.account import Account -from models.enums import CreatedByRole from models.model import App, EndUser logger = logging.getLogger(__name__) @@ -101,8 +100,6 @@ class ChatAppGenerator(MessageBasedAppGenerator): # always enable retriever resource in debugger mode override_model_config_dict["retriever_resource"] = {"enabled": True} - role = CreatedByRole.ACCOUNT if isinstance(user, Account) else CreatedByRole.END_USER - # parse files files = args["files"] if args.get("files") else [] file_extra_config = FileUploadConfigManager.convert(override_model_config_dict or app_model_config.to_dict()) @@ -110,8 +107,6 @@ class ChatAppGenerator(MessageBasedAppGenerator): file_objs = file_factory.build_from_mappings( mappings=files, tenant_id=app_model.tenant_id, - user_id=user.id, - role=role, config=file_extra_config, ) else: @@ -133,10 +128,11 @@ class ChatAppGenerator(MessageBasedAppGenerator): task_id=str(uuid.uuid4()), app_config=app_config, model_conf=ModelConfigConverter.convert(app_config), + file_upload_config=file_extra_config, conversation_id=conversation.id if conversation else None, inputs=conversation.inputs if conversation - else self._prepare_user_inputs(user_inputs=inputs, app_config=app_config, user_id=user.id, role=role), + else self._prepare_user_inputs(user_inputs=inputs, app_config=app_config), query=query, files=file_objs, parent_message_id=args.get("parent_message_id") if invoke_from != InvokeFrom.SERVICE_API else UUID_NIL, diff --git a/api/core/app/apps/completion/app_generator.py b/api/core/app/apps/completion/app_generator.py index 3bb05d05d8..22ee8b0967 100644 --- a/api/core/app/apps/completion/app_generator.py +++ b/api/core/app/apps/completion/app_generator.py @@ -22,7 +22,6 @@ from core.ops.ops_trace_manager import TraceQueueManager from extensions.ext_database import db from factories import file_factory from models import Account, App, EndUser, Message -from models.enums import CreatedByRole from services.errors.app import MoreLikeThisDisabledError from services.errors.message import MessageNotExistsError @@ -88,8 +87,6 @@ class CompletionAppGenerator(MessageBasedAppGenerator): tenant_id=app_model.tenant_id, config=args.get("model_config") ) - role = CreatedByRole.ACCOUNT if isinstance(user, Account) else CreatedByRole.END_USER - # parse files files = args["files"] if args.get("files") else [] file_extra_config = FileUploadConfigManager.convert(override_model_config_dict or app_model_config.to_dict()) @@ -97,8 +94,6 @@ class CompletionAppGenerator(MessageBasedAppGenerator): file_objs = file_factory.build_from_mappings( mappings=files, tenant_id=app_model.tenant_id, - user_id=user.id, - role=role, config=file_extra_config, ) else: @@ -110,7 +105,6 @@ class CompletionAppGenerator(MessageBasedAppGenerator): ) # get tracing instance - user_id = user.id if isinstance(user, Account) else user.session_id trace_manager = TraceQueueManager(app_model.id) # init application generate entity @@ -118,7 +112,8 @@ class CompletionAppGenerator(MessageBasedAppGenerator): task_id=str(uuid.uuid4()), app_config=app_config, model_conf=ModelConfigConverter.convert(app_config), - inputs=self._prepare_user_inputs(user_inputs=inputs, app_config=app_config, user_id=user.id, role=role), + file_upload_config=file_extra_config, + inputs=self._prepare_user_inputs(user_inputs=inputs, app_config=app_config), query=query, files=file_objs, user_id=user.id, @@ -259,14 +254,11 @@ class CompletionAppGenerator(MessageBasedAppGenerator): override_model_config_dict["model"] = model_dict # parse files - role = CreatedByRole.ACCOUNT if isinstance(user, Account) else CreatedByRole.END_USER file_extra_config = FileUploadConfigManager.convert(override_model_config_dict) if file_extra_config: file_objs = file_factory.build_from_mappings( mappings=message.message_files, tenant_id=app_model.tenant_id, - user_id=user.id, - role=role, config=file_extra_config, ) else: diff --git a/api/core/app/apps/workflow/app_config_manager.py b/api/core/app/apps/workflow/app_config_manager.py index 8b98e74b85..b0aa21c731 100644 --- a/api/core/app/apps/workflow/app_config_manager.py +++ b/api/core/app/apps/workflow/app_config_manager.py @@ -46,9 +46,7 @@ class WorkflowAppConfigManager(BaseAppConfigManager): related_config_keys = [] # file upload validation - config, current_related_config_keys = FileUploadConfigManager.validate_and_set_defaults( - config=config, is_vision=False - ) + config, current_related_config_keys = FileUploadConfigManager.validate_and_set_defaults(config=config) related_config_keys.extend(current_related_config_keys) # text_to_speech diff --git a/api/core/app/apps/workflow/app_generator.py b/api/core/app/apps/workflow/app_generator.py index 6e9c6804f9..a0080ece20 100644 --- a/api/core/app/apps/workflow/app_generator.py +++ b/api/core/app/apps/workflow/app_generator.py @@ -25,7 +25,6 @@ from core.ops.ops_trace_manager import TraceQueueManager from extensions.ext_database import db from factories import file_factory from models import Account, App, EndUser, Workflow -from models.enums import CreatedByRole logger = logging.getLogger(__name__) @@ -70,15 +69,11 @@ class WorkflowAppGenerator(BaseAppGenerator): ): files: Sequence[Mapping[str, Any]] = args.get("files") or [] - role = CreatedByRole.ACCOUNT if isinstance(user, Account) else CreatedByRole.END_USER - # parse files file_extra_config = FileUploadConfigManager.convert(workflow.features_dict, is_vision=False) system_files = file_factory.build_from_mappings( mappings=files, tenant_id=app_model.tenant_id, - user_id=user.id, - role=role, config=file_extra_config, ) @@ -100,7 +95,8 @@ class WorkflowAppGenerator(BaseAppGenerator): application_generate_entity = WorkflowAppGenerateEntity( task_id=str(uuid.uuid4()), app_config=app_config, - inputs=self._prepare_user_inputs(user_inputs=inputs, app_config=app_config, user_id=user.id, role=role), + file_upload_config=file_extra_config, + inputs=self._prepare_user_inputs(user_inputs=inputs, app_config=app_config), files=system_files, user_id=user.id, stream=stream, diff --git a/api/core/app/apps/workflow_app_runner.py b/api/core/app/apps/workflow_app_runner.py index 9a01e8a253..2872390d46 100644 --- a/api/core/app/apps/workflow_app_runner.py +++ b/api/core/app/apps/workflow_app_runner.py @@ -361,6 +361,7 @@ class WorkflowBasedAppRunner(AppRunner): node_run_index=workflow_entry.graph_engine.graph_runtime_state.node_run_steps, output=event.pre_iteration_output, parallel_mode_run_id=event.parallel_mode_run_id, + duration=event.duration, ) ) elif isinstance(event, (IterationRunSucceededEvent | IterationRunFailedEvent)): diff --git a/api/core/app/entities/app_invoke_entities.py b/api/core/app/entities/app_invoke_entities.py index f2eba29323..31c3a996e1 100644 --- a/api/core/app/entities/app_invoke_entities.py +++ b/api/core/app/entities/app_invoke_entities.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validat from constants import UUID_NIL from core.app.app_config.entities import AppConfig, EasyUIBasedAppConfig, WorkflowUIBasedAppConfig from core.entities.provider_configuration import ProviderModelBundle -from core.file.models import File +from core.file import File, FileUploadConfig from core.model_runtime.entities.model_entities import AIModelEntity from core.ops.ops_trace_manager import TraceQueueManager @@ -80,6 +80,7 @@ class AppGenerateEntity(BaseModel): # app config app_config: AppConfig + file_upload_config: Optional[FileUploadConfig] = None inputs: Mapping[str, Any] files: Sequence[File] diff --git a/api/core/app/entities/queue_entities.py b/api/core/app/entities/queue_entities.py index f1542ec5d8..69bc0d7f9e 100644 --- a/api/core/app/entities/queue_entities.py +++ b/api/core/app/entities/queue_entities.py @@ -111,6 +111,7 @@ class QueueIterationNextEvent(AppQueueEvent): """iteratoin run in parallel mode run id""" node_run_index: int output: Optional[Any] = None # output for the current iteration + duration: Optional[float] = None @field_validator("output", mode="before") @classmethod @@ -307,6 +308,8 @@ class QueueNodeSucceededEvent(AppQueueEvent): execution_metadata: Optional[dict[NodeRunMetadataKey, Any]] = None error: Optional[str] = None + """single iteration duration map""" + iteration_duration_map: Optional[dict[str, float]] = None class QueueNodeInIterationFailedEvent(AppQueueEvent): diff --git a/api/core/app/entities/task_entities.py b/api/core/app/entities/task_entities.py index 7e9aad54be..03cc6941a8 100644 --- a/api/core/app/entities/task_entities.py +++ b/api/core/app/entities/task_entities.py @@ -434,6 +434,7 @@ class IterationNodeNextStreamResponse(StreamResponse): parallel_id: Optional[str] = None parallel_start_node_id: Optional[str] = None parallel_mode_run_id: Optional[str] = None + duration: Optional[float] = None event: StreamEvent = StreamEvent.ITERATION_NEXT workflow_run_id: str diff --git a/api/core/app/task_pipeline/workflow_cycle_manage.py b/api/core/app/task_pipeline/workflow_cycle_manage.py index b89edf9079..042339969f 100644 --- a/api/core/app/task_pipeline/workflow_cycle_manage.py +++ b/api/core/app/task_pipeline/workflow_cycle_manage.py @@ -624,6 +624,7 @@ class WorkflowCycleManage: parallel_id=event.parallel_id, parallel_start_node_id=event.parallel_start_node_id, parallel_mode_run_id=event.parallel_mode_run_id, + duration=event.duration, ), ) diff --git a/api/core/file/__init__.py b/api/core/file/__init__.py index bdaf8793fa..fe9e52258a 100644 --- a/api/core/file/__init__.py +++ b/api/core/file/__init__.py @@ -2,13 +2,13 @@ from .constants import FILE_MODEL_IDENTITY from .enums import ArrayFileAttribute, FileAttribute, FileBelongsTo, FileTransferMethod, FileType from .models import ( File, - FileExtraConfig, + FileUploadConfig, ImageConfig, ) __all__ = [ "FileType", - "FileExtraConfig", + "FileUploadConfig", "FileTransferMethod", "FileBelongsTo", "File", diff --git a/api/core/file/file_manager.py b/api/core/file/file_manager.py index ff9220d35f..eb260a8f84 100644 --- a/api/core/file/file_manager.py +++ b/api/core/file/file_manager.py @@ -33,25 +33,28 @@ def get_attr(*, file: File, attr: FileAttribute): raise ValueError(f"Invalid file attribute: {attr}") -def to_prompt_message_content(f: File, /): +def to_prompt_message_content( + f: File, + /, + *, + image_detail_config: ImagePromptMessageContent.DETAIL = ImagePromptMessageContent.DETAIL.LOW, +): """ - Convert a File object to an ImagePromptMessageContent object. + Convert a File object to an ImagePromptMessageContent or AudioPromptMessageContent object. - This function takes a File object and converts it to an ImagePromptMessageContent - object, which can be used as a prompt for image-based AI models. + This function takes a File object and converts it to an appropriate PromptMessageContent + object, which can be used as a prompt for image or audio-based AI models. Args: - file (File): The File object to convert. Must be of type FileType.IMAGE. + f (File): The File object to convert. + detail (Optional[ImagePromptMessageContent.DETAIL]): The detail level for image prompts. + If not provided, defaults to ImagePromptMessageContent.DETAIL.LOW. Returns: - ImagePromptMessageContent: An object containing the image data and detail level. + Union[ImagePromptMessageContent, AudioPromptMessageContent]: An object containing the file data and detail level Raises: - ValueError: If the file is not an image or if the file data is missing. - - Note: - The detail level of the image prompt is determined by the file's extra_config. - If not specified, it defaults to ImagePromptMessageContent.DETAIL.LOW. + ValueError: If the file type is not supported or if required data is missing. """ match f.type: case FileType.IMAGE: @@ -60,12 +63,7 @@ def to_prompt_message_content(f: File, /): else: data = _to_base64_data_string(f) - if f._extra_config and f._extra_config.image_config and f._extra_config.image_config.detail: - detail = f._extra_config.image_config.detail - else: - detail = ImagePromptMessageContent.DETAIL.LOW - - return ImagePromptMessageContent(data=data, detail=detail) + return ImagePromptMessageContent(data=data, detail=image_detail_config) case FileType.AUDIO: encoded_string = _file_to_encoded_string(f) if f.extension is None: @@ -78,7 +76,7 @@ def to_prompt_message_content(f: File, /): data = _to_base64_data_string(f) return VideoPromptMessageContent(data=data, format=f.extension.lstrip(".")) case _: - raise ValueError(f"file type {f.type} is not supported") + raise ValueError("file type f.type is not supported") def download(f: File, /): diff --git a/api/core/file/models.py b/api/core/file/models.py index 866ff3155b..0142893787 100644 --- a/api/core/file/models.py +++ b/api/core/file/models.py @@ -21,7 +21,7 @@ class ImageConfig(BaseModel): detail: ImagePromptMessageContent.DETAIL | None = None -class FileExtraConfig(BaseModel): +class FileUploadConfig(BaseModel): """ File Upload Entity. """ @@ -46,7 +46,6 @@ class File(BaseModel): extension: Optional[str] = Field(default=None, description="File extension, should contains dot") mime_type: Optional[str] = None size: int = -1 - _extra_config: FileExtraConfig | None = None def to_dict(self) -> Mapping[str, str | int | None]: data = self.model_dump(mode="json") @@ -107,34 +106,4 @@ class File(BaseModel): case FileTransferMethod.TOOL_FILE: if not self.related_id: raise ValueError("Missing file related_id") - - # Validate the extra config. - if not self._extra_config: - return self - - if self._extra_config.allowed_file_types: - if self.type not in self._extra_config.allowed_file_types and self.type != FileType.CUSTOM: - raise ValueError(f"Invalid file type: {self.type}") - - if self._extra_config.allowed_extensions and self.extension not in self._extra_config.allowed_extensions: - raise ValueError(f"Invalid file extension: {self.extension}") - - if ( - self._extra_config.allowed_upload_methods - and self.transfer_method not in self._extra_config.allowed_upload_methods - ): - raise ValueError(f"Invalid transfer method: {self.transfer_method}") - - match self.type: - case FileType.IMAGE: - # NOTE: This part of validation is deprecated, but still used in app features "Image Upload". - if not self._extra_config.image_config: - return self - # TODO: skip check if transfer_methods is empty, because many test cases are not setting this field - if ( - self._extra_config.image_config.transfer_methods - and self.transfer_method not in self._extra_config.image_config.transfer_methods - ): - raise ValueError(f"Invalid transfer method: {self.transfer_method}") - return self diff --git a/api/core/helper/code_executor/__init__.py b/api/core/helper/code_executor/__init__.py index e69de29bb2..ec885c2218 100644 --- a/api/core/helper/code_executor/__init__.py +++ b/api/core/helper/code_executor/__init__.py @@ -0,0 +1,3 @@ +from .code_executor import CodeExecutor, CodeLanguage + +__all__ = ["CodeExecutor", "CodeLanguage"] diff --git a/api/core/helper/code_executor/code_executor.py b/api/core/helper/code_executor/code_executor.py index 4932284540..03c4b8d49d 100644 --- a/api/core/helper/code_executor/code_executor.py +++ b/api/core/helper/code_executor/code_executor.py @@ -1,7 +1,8 @@ import logging +from collections.abc import Mapping from enum import Enum from threading import Lock -from typing import Optional +from typing import Any, Optional from httpx import Timeout, post from pydantic import BaseModel @@ -117,7 +118,7 @@ class CodeExecutor: return response.data.stdout or "" @classmethod - def execute_workflow_code_template(cls, language: CodeLanguage, code: str, inputs: dict) -> dict: + def execute_workflow_code_template(cls, language: CodeLanguage, code: str, inputs: Mapping[str, Any]) -> dict: """ Execute code :param language: code language diff --git a/api/core/helper/code_executor/template_transformer.py b/api/core/helper/code_executor/template_transformer.py index 6f016f27bc..b7a07b21e1 100644 --- a/api/core/helper/code_executor/template_transformer.py +++ b/api/core/helper/code_executor/template_transformer.py @@ -2,6 +2,8 @@ import json import re from abc import ABC, abstractmethod from base64 import b64encode +from collections.abc import Mapping +from typing import Any class TemplateTransformer(ABC): @@ -10,7 +12,7 @@ class TemplateTransformer(ABC): _result_tag: str = "<>" @classmethod - def transform_caller(cls, code: str, inputs: dict) -> tuple[str, str]: + def transform_caller(cls, code: str, inputs: Mapping[str, Any]) -> tuple[str, str]: """ Transform code to python runner :param code: code @@ -48,13 +50,13 @@ class TemplateTransformer(ABC): pass @classmethod - def serialize_inputs(cls, inputs: dict) -> str: + def serialize_inputs(cls, inputs: Mapping[str, Any]) -> str: inputs_json_str = json.dumps(inputs, ensure_ascii=False).encode() input_base64_encoded = b64encode(inputs_json_str).decode("utf-8") return input_base64_encoded @classmethod - def assemble_runner_script(cls, code: str, inputs: dict) -> str: + def assemble_runner_script(cls, code: str, inputs: Mapping[str, Any]) -> str: # assemble runner script script = cls.get_runner_script() script = script.replace(cls._code_placeholder, code) diff --git a/api/core/memory/token_buffer_memory.py b/api/core/memory/token_buffer_memory.py index d92c36a2df..688fb4776a 100644 --- a/api/core/memory/token_buffer_memory.py +++ b/api/core/memory/token_buffer_memory.py @@ -81,15 +81,18 @@ class TokenBufferMemory: db.session.query(WorkflowRun).filter(WorkflowRun.id == message.workflow_run_id).first() ) - if workflow_run: + if workflow_run and workflow_run.workflow: file_extra_config = FileUploadConfigManager.convert( workflow_run.workflow.features_dict, is_vision=False ) + detail = ImagePromptMessageContent.DETAIL.LOW if file_extra_config and app_record: file_objs = file_factory.build_from_message_files( message_files=files, tenant_id=app_record.tenant_id, config=file_extra_config ) + if file_extra_config.image_config and file_extra_config.image_config.detail: + detail = file_extra_config.image_config.detail else: file_objs = [] @@ -98,12 +101,16 @@ class TokenBufferMemory: else: prompt_message_contents: list[PromptMessageContent] = [] prompt_message_contents.append(TextPromptMessageContent(data=message.query)) - for file_obj in file_objs: - if file_obj.type in {FileType.IMAGE, FileType.AUDIO}: - prompt_message = file_manager.to_prompt_message_content(file_obj) + for file in file_objs: + if file.type in {FileType.IMAGE, FileType.AUDIO}: + prompt_message = file_manager.to_prompt_message_content( + file, + image_detail_config=detail, + ) prompt_message_contents.append(prompt_message) prompt_messages.append(UserPromptMessage(content=prompt_message_contents)) + else: prompt_messages.append(UserPromptMessage(content=message.query)) diff --git a/api/core/model_runtime/model_providers/azure_openai/llm/llm.py b/api/core/model_runtime/model_providers/azure_openai/llm/llm.py index 1cd4823e13..95c8f36271 100644 --- a/api/core/model_runtime/model_providers/azure_openai/llm/llm.py +++ b/api/core/model_runtime/model_providers/azure_openai/llm/llm.py @@ -113,7 +113,7 @@ class AzureOpenAILargeLanguageModel(_CommonAzureOpenAI, LargeLanguageModel): try: client = AzureOpenAI(**self._to_credential_kwargs(credentials)) - if model.startswith("o1"): + if "o1" in model: client.chat.completions.create( messages=[{"role": "user", "content": "ping"}], model=model, @@ -311,7 +311,7 @@ class AzureOpenAILargeLanguageModel(_CommonAzureOpenAI, LargeLanguageModel): prompt_messages = self._clear_illegal_prompt_messages(model, prompt_messages) block_as_stream = False - if model.startswith("o1"): + if "o1" in model: if stream: block_as_stream = True stream = False @@ -404,7 +404,7 @@ class AzureOpenAILargeLanguageModel(_CommonAzureOpenAI, LargeLanguageModel): ] ) - if model.startswith("o1"): + if "o1" in model: system_message_count = len([m for m in prompt_messages if isinstance(m, SystemPromptMessage)]) if system_message_count > 0: new_prompt_messages = [] @@ -653,7 +653,7 @@ class AzureOpenAILargeLanguageModel(_CommonAzureOpenAI, LargeLanguageModel): tokens_per_message = 4 # if there's a name, the role is omitted tokens_per_name = -1 - elif model.startswith("gpt-35-turbo") or model.startswith("gpt-4") or model.startswith("o1"): + elif model.startswith("gpt-35-turbo") or model.startswith("gpt-4") or "o1" in model: tokens_per_message = 3 tokens_per_name = 1 else: diff --git a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v2.yaml b/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v2.yaml index b1e5698375..d30dd5d6cf 100644 --- a/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v2.yaml +++ b/api/core/model_runtime/model_providers/bedrock/llm/anthropic.claude-3-sonnet-v2.yaml @@ -16,9 +16,9 @@ parameter_rules: use_template: max_tokens required: true type: int - default: 4096 + default: 8192 min: 1 - max: 4096 + max: 8192 help: zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. diff --git a/api/core/model_runtime/model_providers/gitee_ai/llm/Qwen2.5-72B-Instruct.yaml b/api/core/model_runtime/model_providers/gitee_ai/llm/Qwen2.5-72B-Instruct.yaml new file mode 100644 index 0000000000..cb300e5ddf --- /dev/null +++ b/api/core/model_runtime/model_providers/gitee_ai/llm/Qwen2.5-72B-Instruct.yaml @@ -0,0 +1,95 @@ +model: Qwen2.5-72B-Instruct +label: + zh_Hans: Qwen2.5-72B-Instruct + en_US: Qwen2.5-72B-Instruct +model_type: llm +features: + - agent-thought + - tool-call + - stream-tool-call +model_properties: + mode: chat + context_size: 32768 +parameter_rules: + - name: max_tokens + use_template: max_tokens + label: + en_US: "Max Tokens" + zh_Hans: "最大Token数" + type: int + default: 512 + min: 1 + required: true + help: + en_US: "The maximum number of tokens that can be generated by the model varies depending on the model." + zh_Hans: "模型可生成的最大 token 个数,不同模型上限不同。" + + - name: temperature + use_template: temperature + label: + en_US: "Temperature" + zh_Hans: "采样温度" + type: float + default: 0.7 + min: 0.0 + max: 1.0 + precision: 1 + required: true + help: + en_US: "The randomness of the sampling temperature control output. The temperature value is within the range of [0.0, 1.0]. The higher the value, the more random and creative the output; the lower the value, the more stable it is. It is recommended to adjust either top_p or temperature parameters according to your needs to avoid adjusting both at the same time." + zh_Hans: "采样温度控制输出的随机性。温度值在 [0.0, 1.0] 范围内,值越高,输出越随机和创造性;值越低,输出越稳定。建议根据需求调整 top_p 或 temperature 参数,避免同时调整两者。" + + - name: top_p + use_template: top_p + label: + en_US: "Top P" + zh_Hans: "Top P" + type: float + default: 0.7 + min: 0.0 + max: 1.0 + precision: 1 + required: true + help: + en_US: "The value range of the sampling method is [0.0, 1.0]. The top_p value determines that the model selects tokens from the top p% of candidate words with the highest probability; when top_p is 0, this parameter is invalid. It is recommended to adjust either top_p or temperature parameters according to your needs to avoid adjusting both at the same time." + zh_Hans: "采样方法的取值范围为 [0.0,1.0]。top_p 值确定模型从概率最高的前p%的候选词中选取 tokens;当 top_p 为 0 时,此参数无效。建议根据需求调整 top_p 或 temperature 参数,避免同时调整两者。" + + - name: top_k + use_template: top_k + label: + en_US: "Top K" + zh_Hans: "Top K" + type: int + default: 50 + min: 0 + max: 100 + required: true + help: + en_US: "The value range is [0,100], which limits the model to only select from the top k words with the highest probability when choosing the next word at each step. The larger the value, the more diverse text generation will be." + zh_Hans: "取值范围为 [0,100],限制模型在每一步选择下一个词时,只从概率最高的前 k 个词中选取。数值越大,文本生成越多样。" + + - name: frequency_penalty + use_template: frequency_penalty + label: + en_US: "Frequency Penalty" + zh_Hans: "频率惩罚" + type: float + default: 0 + min: -1.0 + max: 1.0 + precision: 1 + required: false + help: + en_US: "Used to adjust the frequency of repeated content in automatically generated text. Positive numbers reduce repetition, while negative numbers increase repetition. After setting this parameter, if a word has already appeared in the text, the model will decrease the probability of choosing that word for subsequent generation." + zh_Hans: "用于调整自动生成文本中重复内容的频率。正数减少重复,负数增加重复。设置此参数后,如果一个词在文本中已经出现过,模型在后续生成中选择该词的概率会降低。" + + - name: user + use_template: text + label: + en_US: "User" + zh_Hans: "用户" + type: string + required: false + help: + en_US: "Used to track and differentiate conversation requests from different users." + zh_Hans: "用于追踪和区分不同用户的对话请求。" diff --git a/api/core/model_runtime/model_providers/gitee_ai/llm/_position.yaml b/api/core/model_runtime/model_providers/gitee_ai/llm/_position.yaml index 21f6120742..13c31ad02b 100644 --- a/api/core/model_runtime/model_providers/gitee_ai/llm/_position.yaml +++ b/api/core/model_runtime/model_providers/gitee_ai/llm/_position.yaml @@ -1,3 +1,4 @@ +- Qwen2.5-72B-Instruct - Qwen2-7B-Instruct - Qwen2-72B-Instruct - Yi-1.5-34B-Chat diff --git a/api/core/model_runtime/model_providers/gitee_ai/llm/llm.py b/api/core/model_runtime/model_providers/gitee_ai/llm/llm.py index b65db6f665..1b85b7fced 100644 --- a/api/core/model_runtime/model_providers/gitee_ai/llm/llm.py +++ b/api/core/model_runtime/model_providers/gitee_ai/llm/llm.py @@ -6,6 +6,7 @@ from core.model_runtime.entities.message_entities import ( PromptMessage, PromptMessageTool, ) +from core.model_runtime.entities.model_entities import ModelFeature from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel @@ -28,14 +29,13 @@ class GiteeAILargeLanguageModel(OAIAPICompatLargeLanguageModel): user: Optional[str] = None, ) -> Union[LLMResult, Generator]: self._add_custom_parameters(credentials, model, model_parameters) - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream) + return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) def validate_credentials(self, model: str, credentials: dict) -> None: self._add_custom_parameters(credentials, model, None) super().validate_credentials(model, credentials) - @staticmethod - def _add_custom_parameters(credentials: dict, model: str, model_parameters: dict) -> None: + def _add_custom_parameters(self, credentials: dict, model: str, model_parameters: dict) -> None: if model is None: model = "bge-large-zh-v1.5" @@ -45,3 +45,7 @@ class GiteeAILargeLanguageModel(OAIAPICompatLargeLanguageModel): credentials["mode"] = LLMMode.COMPLETION.value else: credentials["mode"] = LLMMode.CHAT.value + + schema = self.get_model_schema(model, credentials) + if ModelFeature.TOOL_CALL in schema.features or ModelFeature.MULTI_TOOL_CALL in schema.features: + credentials["function_calling_type"] = "tool_call" diff --git a/api/core/model_runtime/model_providers/jina/rerank/rerank.py b/api/core/model_runtime/model_providers/jina/rerank/rerank.py index 0350207651..aacc8e75d3 100644 --- a/api/core/model_runtime/model_providers/jina/rerank/rerank.py +++ b/api/core/model_runtime/model_providers/jina/rerank/rerank.py @@ -55,6 +55,7 @@ class JinaRerankModel(RerankModel): base_url + "/rerank", json={"model": model, "query": query, "documents": docs, "top_n": top_n}, headers={"Authorization": f"Bearer {credentials.get('api_key')}"}, + timeout=20, ) response.raise_for_status() results = response.json() diff --git a/api/core/model_runtime/model_providers/minimax/llm/abab7-chat-preview.yaml b/api/core/model_runtime/model_providers/minimax/llm/abab7-chat-preview.yaml new file mode 100644 index 0000000000..bb9899d580 --- /dev/null +++ b/api/core/model_runtime/model_providers/minimax/llm/abab7-chat-preview.yaml @@ -0,0 +1,46 @@ +model: abab7-chat-preview +label: + en_US: Abab7-chat-preview +model_type: llm +features: + - agent-thought + - tool-call + - stream-tool-call +model_properties: + mode: chat + context_size: 245760 +parameter_rules: + - name: temperature + use_template: temperature + min: 0.01 + max: 1 + default: 0.1 + - name: top_p + use_template: top_p + min: 0.01 + max: 1 + default: 0.95 + - name: max_tokens + use_template: max_tokens + required: true + default: 2048 + min: 1 + max: 245760 + - name: mask_sensitive_info + type: boolean + default: true + label: + zh_Hans: 隐私保护 + en_US: Moderate + help: + zh_Hans: 对输出中易涉及隐私问题的文本信息进行打码,目前包括但不限于邮箱、域名、链接、证件号、家庭住址等,默认true,即开启打码 + en_US: Mask the sensitive info of the generated content, such as email/domain/link/address/phone/id.. + - name: presence_penalty + use_template: presence_penalty + - name: frequency_penalty + use_template: frequency_penalty +pricing: + input: '0.1' + output: '0.1' + unit: '0.001' + currency: RMB diff --git a/api/core/model_runtime/model_providers/minimax/llm/llm.py b/api/core/model_runtime/model_providers/minimax/llm/llm.py index 4250c40cfb..ce7c00f7da 100644 --- a/api/core/model_runtime/model_providers/minimax/llm/llm.py +++ b/api/core/model_runtime/model_providers/minimax/llm/llm.py @@ -34,6 +34,7 @@ from core.model_runtime.model_providers.minimax.llm.types import MinimaxMessage class MinimaxLargeLanguageModel(LargeLanguageModel): model_apis = { + "abab7-chat-preview": MinimaxChatCompletionPro, "abab6.5s-chat": MinimaxChatCompletionPro, "abab6.5-chat": MinimaxChatCompletionPro, "abab6-chat": MinimaxChatCompletionPro, diff --git a/api/core/model_runtime/model_providers/openai/llm/llm.py b/api/core/model_runtime/model_providers/openai/llm/llm.py index 922e5e1314..68317d7179 100644 --- a/api/core/model_runtime/model_providers/openai/llm/llm.py +++ b/api/core/model_runtime/model_providers/openai/llm/llm.py @@ -617,6 +617,10 @@ class OpenAILargeLanguageModel(_CommonOpenAI, LargeLanguageModel): # o1 compatibility block_as_stream = False if model.startswith("o1"): + if "max_tokens" in model_parameters: + model_parameters["max_completion_tokens"] = model_parameters["max_tokens"] + del model_parameters["max_tokens"] + if stream: block_as_stream = True stream = False diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/Internvl2-26b.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/Internvl2-26b.yaml new file mode 100644 index 0000000000..f7b03e1254 --- /dev/null +++ b/api/core/model_runtime/model_providers/siliconflow/llm/Internvl2-26b.yaml @@ -0,0 +1,84 @@ +model: OpenGVLab/InternVL2-26B +label: + en_US: OpenGVLab/InternVL2-26B +model_type: llm +features: + - vision +model_properties: + mode: chat + context_size: 32768 +parameter_rules: + - name: temperature + use_template: temperature + type: float + default: 0.3 + min: 0.0 + max: 2.0 + help: + zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 + en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. + - name: max_tokens + use_template: max_tokens + type: int + default: 2000 + min: 1 + max: 2000 + help: + zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 + en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. + - name: top_p + use_template: top_p + type: float + default: 0.8 + min: 0.1 + max: 0.9 + help: + zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 + en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. + - name: top_k + type: int + min: 0 + max: 99 + label: + zh_Hans: 取样数量 + en_US: Top k + help: + zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 + en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. + - name: seed + required: false + type: int + default: 1234 + label: + zh_Hans: 随机种子 + en_US: Random seed + help: + zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 + en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. + - name: repetition_penalty + required: false + type: float + default: 1.1 + label: + zh_Hans: 重复惩罚 + en_US: Repetition penalty + help: + zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 + en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object +pricing: + input: '21' + output: '21' + unit: '0.000001' + currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/Internvl2-8b.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/Internvl2-8b.yaml new file mode 100644 index 0000000000..1e858bb4be --- /dev/null +++ b/api/core/model_runtime/model_providers/siliconflow/llm/Internvl2-8b.yaml @@ -0,0 +1,84 @@ +model: Pro/OpenGVLab/InternVL2-8B +label: + en_US: Pro/OpenGVLab/InternVL2-8B +model_type: llm +features: + - vision +model_properties: + mode: chat + context_size: 32768 +parameter_rules: + - name: temperature + use_template: temperature + type: float + default: 0.3 + min: 0.0 + max: 2.0 + help: + zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 + en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. + - name: max_tokens + use_template: max_tokens + type: int + default: 2000 + min: 1 + max: 2000 + help: + zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 + en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. + - name: top_p + use_template: top_p + type: float + default: 0.8 + min: 0.1 + max: 0.9 + help: + zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 + en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. + - name: top_k + type: int + min: 0 + max: 99 + label: + zh_Hans: 取样数量 + en_US: Top k + help: + zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 + en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. + - name: seed + required: false + type: int + default: 1234 + label: + zh_Hans: 随机种子 + en_US: Random seed + help: + zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 + en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. + - name: repetition_penalty + required: false + type: float + default: 1.1 + label: + zh_Hans: 重复惩罚 + en_US: Repetition penalty + help: + zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 + en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object +pricing: + input: '21' + output: '21' + unit: '0.000001' + currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/_position.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/_position.yaml index 8d1df82140..f010e4c826 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/_position.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/_position.yaml @@ -1,16 +1,18 @@ +- Tencent/Hunyuan-A52B-Instruct - Qwen/Qwen2.5-72B-Instruct - Qwen/Qwen2.5-32B-Instruct - Qwen/Qwen2.5-14B-Instruct - Qwen/Qwen2.5-7B-Instruct +- Qwen/Qwen2.5-Coder-32B-Instruct - Qwen/Qwen2.5-Coder-7B-Instruct - Qwen/Qwen2.5-Math-72B-Instruct -- Qwen/Qwen2-72B-Instruct -- Qwen/Qwen2-57B-A14B-Instruct -- Qwen/Qwen2-7B-Instruct +- Qwen/Qwen2-VL-72B-Instruct - Qwen/Qwen2-1.5B-Instruct +- Pro/Qwen/Qwen2-VL-7B-Instruct +- OpenGVLab/InternVL2-Llama3-76B +- OpenGVLab/InternVL2-26B +- Pro/OpenGVLab/InternVL2-8B - deepseek-ai/DeepSeek-V2.5 -- deepseek-ai/DeepSeek-V2-Chat -- deepseek-ai/DeepSeek-Coder-V2-Instruct - THUDM/glm-4-9b-chat - 01-ai/Yi-1.5-34B-Chat-16K - 01-ai/Yi-1.5-9B-Chat-16K @@ -20,9 +22,6 @@ - meta-llama/Meta-Llama-3.1-405B-Instruct - meta-llama/Meta-Llama-3.1-70B-Instruct - meta-llama/Meta-Llama-3.1-8B-Instruct -- meta-llama/Meta-Llama-3-70B-Instruct -- meta-llama/Meta-Llama-3-8B-Instruct - google/gemma-2-27b-it - google/gemma-2-9b-it -- mistralai/Mistral-7B-Instruct-v0.2 -- mistralai/Mixtral-8x7B-Instruct-v0.1 +- deepseek-ai/DeepSeek-V2-Chat diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/deepdeek-coder-v2-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/deepdeek-coder-v2-instruct.yaml index d5f23776ea..b13a2a751c 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/deepdeek-coder-v2-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/deepdeek-coder-v2-instruct.yaml @@ -37,3 +37,4 @@ pricing: output: '1.33' unit: '0.000001' currency: RMB +deprecated: true diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2-chat.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2-chat.yaml index 7aa684ef38..00bdb0ddf5 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2-chat.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2-chat.yaml @@ -37,3 +37,4 @@ pricing: output: '1.33' unit: '0.000001' currency: RMB +deprecated: true diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2.5.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2.5.yaml index b30fa3e2d1..511cc1df9f 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2.5.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/deepseek-v2.5.yaml @@ -4,6 +4,8 @@ label: model_type: llm features: - agent-thought + - tool-call + - stream-tool-call model_properties: mode: chat context_size: 32768 @@ -32,6 +34,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '1.33' output: '1.33' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-27b-it.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-27b-it.yaml index f2a1f64bfb..7fd7adb753 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-27b-it.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-27b-it.yaml @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '1.26' output: '1.26' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-9b-it.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-9b-it.yaml index b096b9b647..ab548e4409 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-9b-it.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/gemma-2-9b-it.yaml @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '0' output: '0' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/glm4-9b-chat.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/glm4-9b-chat.yaml index 87acc557b7..43c5b344a0 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/glm4-9b-chat.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/glm4-9b-chat.yaml @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '0' output: '0' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/hunyuan-a52b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/hunyuan-a52b-instruct.yaml new file mode 100644 index 0000000000..c5489554a6 --- /dev/null +++ b/api/core/model_runtime/model_providers/siliconflow/llm/hunyuan-a52b-instruct.yaml @@ -0,0 +1,84 @@ +model: Tencent/Hunyuan-A52B-Instruct +label: + en_US: Tencent/Hunyuan-A52B-Instruct +model_type: llm +features: + - agent-thought +model_properties: + mode: chat + context_size: 32768 +parameter_rules: + - name: temperature + use_template: temperature + type: float + default: 0.3 + min: 0.0 + max: 2.0 + help: + zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 + en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. + - name: max_tokens + use_template: max_tokens + type: int + default: 2000 + min: 1 + max: 2000 + help: + zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 + en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. + - name: top_p + use_template: top_p + type: float + default: 0.8 + min: 0.1 + max: 0.9 + help: + zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 + en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. + - name: top_k + type: int + min: 0 + max: 99 + label: + zh_Hans: 取样数量 + en_US: Top k + help: + zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 + en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. + - name: seed + required: false + type: int + default: 1234 + label: + zh_Hans: 随机种子 + en_US: Random seed + help: + zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 + en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. + - name: repetition_penalty + required: false + type: float + default: 1.1 + label: + zh_Hans: 重复惩罚 + en_US: Repetition penalty + help: + zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 + en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object +pricing: + input: '21' + output: '21' + unit: '0.000001' + currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-20b-chat.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-20b-chat.yaml index 60157c2b46..a5ae3674c9 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-20b-chat.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-20b-chat.yaml @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '1' output: '1' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-7b-chat.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-7b-chat.yaml index faf4af7ea3..dec856ed2e 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-7b-chat.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/internlm2_5-7b-chat.yaml @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '0' output: '0' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/internvl2-llama3-76b.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/internvl2-llama3-76b.yaml new file mode 100644 index 0000000000..65386d3175 --- /dev/null +++ b/api/core/model_runtime/model_providers/siliconflow/llm/internvl2-llama3-76b.yaml @@ -0,0 +1,84 @@ +model: OpenGVLab/InternVL2-Llama3-76B +label: + en_US: OpenGVLab/InternVL2-Llama3-76B +model_type: llm +features: + - vision +model_properties: + mode: chat + context_size: 8192 +parameter_rules: + - name: temperature + use_template: temperature + type: float + default: 0.3 + min: 0.0 + max: 2.0 + help: + zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 + en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. + - name: max_tokens + use_template: max_tokens + type: int + default: 2000 + min: 1 + max: 2000 + help: + zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 + en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. + - name: top_p + use_template: top_p + type: float + default: 0.8 + min: 0.1 + max: 0.9 + help: + zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 + en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. + - name: top_k + type: int + min: 0 + max: 99 + label: + zh_Hans: 取样数量 + en_US: Top k + help: + zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 + en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. + - name: seed + required: false + type: int + default: 1234 + label: + zh_Hans: 随机种子 + en_US: Random seed + help: + zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 + en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. + - name: repetition_penalty + required: false + type: float + default: 1.1 + label: + zh_Hans: 重复惩罚 + en_US: Repetition penalty + help: + zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 + en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object +pricing: + input: '21' + output: '21' + unit: '0.000001' + currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/llm.py b/api/core/model_runtime/model_providers/siliconflow/llm/llm.py index 6015442c2b..e3a323a496 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/llm.py +++ b/api/core/model_runtime/model_providers/siliconflow/llm/llm.py @@ -29,6 +29,9 @@ class SiliconflowLargeLanguageModel(OAIAPICompatLargeLanguageModel): user: Optional[str] = None, ) -> Union[LLMResult, Generator]: self._add_custom_parameters(credentials) + # {"response_format": "json_object"} need convert to {"response_format": {"type": "json_object"}} + if "response_format" in model_parameters: + model_parameters["response_format"] = {"type": model_parameters.get("response_format")} return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream) def validate_credentials(self, model: str, credentials: dict) -> None: diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-70b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-70b-instruct.yaml index d01770cb01..9825090759 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-70b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-70b-instruct.yaml @@ -37,3 +37,4 @@ pricing: output: '4.13' unit: '0.000001' currency: RMB +deprecated: true diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-8b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-8b-instruct.yaml index 3cd75d89e8..0133fd15d4 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-8b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3-8b-instruct.yaml @@ -37,3 +37,4 @@ pricing: output: '0' unit: '0.000001' currency: RMB +deprecated: true diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-405b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-405b-instruct.yaml index 3506a70bcc..ceebbac515 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-405b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-405b-instruct.yaml @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '21' output: '21' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-70b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-70b-instruct.yaml index 994a754a82..f1fbb74cbd 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-70b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-70b-instruct.yaml @@ -6,7 +6,7 @@ features: - agent-thought model_properties: mode: chat - context_size: 32768 + context_size: 8192 parameter_rules: - name: temperature use_template: temperature @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '4.13' output: '4.13' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-8b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-8b-instruct.yaml index ebfa9aac9d..a9a43545e8 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-8b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/meta-mlama-3.1-8b-instruct.yaml @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '0' output: '0' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-57b-a14b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-57b-a14b-instruct.yaml index b2461335f8..0f56d16d9b 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-57b-a14b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-57b-a14b-instruct.yaml @@ -37,3 +37,4 @@ pricing: output: '1.26' unit: '0.000001' currency: RMB +deprecated: true diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-72b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-72b-instruct.yaml index e0f23bd89e..af65cfb8ed 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-72b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-72b-instruct.yaml @@ -37,3 +37,4 @@ pricing: output: '4.13' unit: '0.000001' currency: RMB +deprecated: true diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-7b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-7b-instruct.yaml index 47a9da8119..f0f10ae625 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-7b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-7b-instruct.yaml @@ -37,3 +37,4 @@ pricing: output: '0' unit: '0.000001' currency: RMB +deprecated: true diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-vl-72b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-vl-72b-instruct.yaml new file mode 100644 index 0000000000..1866a684b5 --- /dev/null +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-vl-72b-instruct.yaml @@ -0,0 +1,84 @@ +model: Qwen/Qwen2-VL-72B-Instruct +label: + en_US: Qwen/Qwen2-VL-72B-Instruct +model_type: llm +features: + - vision +model_properties: + mode: chat + context_size: 32768 +parameter_rules: + - name: temperature + use_template: temperature + type: float + default: 0.3 + min: 0.0 + max: 2.0 + help: + zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 + en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. + - name: max_tokens + use_template: max_tokens + type: int + default: 2000 + min: 1 + max: 2000 + help: + zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 + en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. + - name: top_p + use_template: top_p + type: float + default: 0.8 + min: 0.1 + max: 0.9 + help: + zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 + en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. + - name: top_k + type: int + min: 0 + max: 99 + label: + zh_Hans: 取样数量 + en_US: Top k + help: + zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 + en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. + - name: seed + required: false + type: int + default: 1234 + label: + zh_Hans: 随机种子 + en_US: Random seed + help: + zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 + en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. + - name: repetition_penalty + required: false + type: float + default: 1.1 + label: + zh_Hans: 重复惩罚 + en_US: Repetition penalty + help: + zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 + en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object +pricing: + input: '21' + output: '21' + unit: '0.000001' + currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-vl-7b-Instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-vl-7b-Instruct.yaml new file mode 100644 index 0000000000..a508344689 --- /dev/null +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2-vl-7b-Instruct.yaml @@ -0,0 +1,84 @@ +model: Pro/Qwen/Qwen2-VL-7B-Instruct +label: + en_US: Pro/Qwen/Qwen2-VL-7B-Instruct +model_type: llm +features: + - vision +model_properties: + mode: chat + context_size: 32768 +parameter_rules: + - name: temperature + use_template: temperature + type: float + default: 0.3 + min: 0.0 + max: 2.0 + help: + zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 + en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. + - name: max_tokens + use_template: max_tokens + type: int + default: 2000 + min: 1 + max: 2000 + help: + zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 + en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. + - name: top_p + use_template: top_p + type: float + default: 0.8 + min: 0.1 + max: 0.9 + help: + zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 + en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. + - name: top_k + type: int + min: 0 + max: 99 + label: + zh_Hans: 取样数量 + en_US: Top k + help: + zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 + en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. + - name: seed + required: false + type: int + default: 1234 + label: + zh_Hans: 随机种子 + en_US: Random seed + help: + zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 + en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. + - name: repetition_penalty + required: false + type: float + default: 1.1 + label: + zh_Hans: 重复惩罚 + en_US: Repetition penalty + help: + zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 + en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object +pricing: + input: '21' + output: '21' + unit: '0.000001' + currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-14b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-14b-instruct.yaml index 9cc5ac4c91..8a045b818a 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-14b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-14b-instruct.yaml @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '0.7' output: '0.7' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-32b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-32b-instruct.yaml index c7fb21e9e1..9c308d2ab4 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-32b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-32b-instruct.yaml @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '1.26' output: '1.26' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-72b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-72b-instruct.yaml index 03136c88a1..c80cd45dd3 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-72b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-72b-instruct.yaml @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '4.13' output: '4.13' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-7b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-7b-instruct.yaml index 99412adde7..bb85dbe948 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-7b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-7b-instruct.yaml @@ -32,6 +32,18 @@ parameter_rules: required: false - name: frequency_penalty use_template: frequency_penalty + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '0' output: '0' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-coder-32b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-coder-32b-instruct.yaml new file mode 100644 index 0000000000..de2224a67b --- /dev/null +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-coder-32b-instruct.yaml @@ -0,0 +1,84 @@ +model: Qwen/Qwen2.5-Coder-32B-Instruct +label: + en_US: Qwen/Qwen2.5-Coder-32B-Instruct +model_type: llm +features: + - agent-thought +model_properties: + mode: chat + context_size: 32768 +parameter_rules: + - name: temperature + use_template: temperature + type: float + default: 0.3 + min: 0.0 + max: 2.0 + help: + zh_Hans: 用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。 + en_US: Used to control the degree of randomness and diversity. Specifically, the temperature value controls the degree to which the probability distribution of each candidate word is smoothed when generating text. A higher temperature value will reduce the peak value of the probability distribution, allowing more low-probability words to be selected, and the generated results will be more diverse; while a lower temperature value will enhance the peak value of the probability distribution, making it easier for high-probability words to be selected. , the generated results are more certain. + - name: max_tokens + use_template: max_tokens + type: int + default: 8192 + min: 1 + max: 8192 + help: + zh_Hans: 用于指定模型在生成内容时token的最大数量,它定义了生成的上限,但不保证每次都会生成到这个数量。 + en_US: It is used to specify the maximum number of tokens when the model generates content. It defines the upper limit of generation, but does not guarantee that this number will be generated every time. + - name: top_p + use_template: top_p + type: float + default: 0.8 + min: 0.1 + max: 0.9 + help: + zh_Hans: 生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 + en_US: The probability threshold of the kernel sampling method during the generation process. For example, when the value is 0.8, only the smallest set of the most likely tokens with a sum of probabilities greater than or equal to 0.8 is retained as the candidate set. The value range is (0,1.0). The larger the value, the higher the randomness generated; the lower the value, the higher the certainty generated. + - name: top_k + type: int + min: 0 + max: 99 + label: + zh_Hans: 取样数量 + en_US: Top k + help: + zh_Hans: 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。 + en_US: The size of the sample candidate set when generated. For example, when the value is 50, only the 50 highest-scoring tokens in a single generation form a randomly sampled candidate set. The larger the value, the higher the randomness generated; the smaller the value, the higher the certainty generated. + - name: seed + required: false + type: int + default: 1234 + label: + zh_Hans: 随机种子 + en_US: Random seed + help: + zh_Hans: 生成时使用的随机数种子,用户控制模型生成内容的随机性。支持无符号64位整数,默认值为 1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 + en_US: The random number seed used when generating, the user controls the randomness of the content generated by the model. Supports unsigned 64-bit integers, default value is 1234. When using seed, the model will try its best to generate the same or similar results, but there is currently no guarantee that the results will be exactly the same every time. + - name: repetition_penalty + required: false + type: float + default: 1.1 + label: + zh_Hans: 重复惩罚 + en_US: Repetition penalty + help: + zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 + en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. + - name: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object +pricing: + input: '1.26' + output: '1.26' + unit: '0.000001' + currency: RMB diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-coder-7b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-coder-7b-instruct.yaml index 76526200cc..c31a338cdd 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-coder-7b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-coder-7b-instruct.yaml @@ -66,7 +66,17 @@ parameter_rules: zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - name: response_format - use_template: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '0' output: '0' diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-math-72b-instruct.yaml b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-math-72b-instruct.yaml index 90afa0cfd5..1b6f2603f5 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-math-72b-instruct.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/llm/qwen2.5-math-72b-instruct.yaml @@ -66,7 +66,17 @@ parameter_rules: zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。 en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment. - name: response_format - use_template: response_format + label: + zh_Hans: 回复格式 + en_US: Response Format + type: string + help: + zh_Hans: 指定模型必须输出的格式 + en_US: specifying the format that the model must output + required: false + options: + - text + - json_object pricing: input: '4.13' output: '4.13' diff --git a/api/core/model_runtime/model_providers/siliconflow/speech2text/funaudio-sense-voice-small.yaml b/api/core/model_runtime/model_providers/siliconflow/speech2text/funaudio-sense-voice-small.yaml new file mode 100644 index 0000000000..d4bc33c68e --- /dev/null +++ b/api/core/model_runtime/model_providers/siliconflow/speech2text/funaudio-sense-voice-small.yaml @@ -0,0 +1,5 @@ +model: FunAudioLLM/SenseVoiceSmall +model_type: speech2text +model_properties: + file_upload_limit: 1 + supported_file_extensions: mp3,wav diff --git a/api/core/model_runtime/model_providers/siliconflow/speech2text/sense-voice-small.yaml b/api/core/model_runtime/model_providers/siliconflow/speech2text/sense-voice-small.yaml index deceaf60f4..455b2ad467 100644 --- a/api/core/model_runtime/model_providers/siliconflow/speech2text/sense-voice-small.yaml +++ b/api/core/model_runtime/model_providers/siliconflow/speech2text/sense-voice-small.yaml @@ -3,3 +3,4 @@ model_type: speech2text model_properties: file_upload_limit: 1 supported_file_extensions: mp3,wav +deprecated: true diff --git a/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3.5-sonnet-v2.yaml b/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3.5-sonnet-v2.yaml index 0be3e26e7a..37b9f30cc3 100644 --- a/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3.5-sonnet-v2.yaml +++ b/api/core/model_runtime/model_providers/vertex_ai/llm/anthropic.claude-3.5-sonnet-v2.yaml @@ -13,9 +13,9 @@ parameter_rules: use_template: max_tokens required: true type: int - default: 4096 + default: 8192 min: 1 - max: 4096 + max: 8192 help: zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。 en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter. diff --git a/api/core/model_runtime/model_providers/vessl_ai/vessl_ai.yaml b/api/core/model_runtime/model_providers/vessl_ai/vessl_ai.yaml index 6052756cae..2138b281b9 100644 --- a/api/core/model_runtime/model_providers/vessl_ai/vessl_ai.yaml +++ b/api/core/model_runtime/model_providers/vessl_ai/vessl_ai.yaml @@ -1,6 +1,6 @@ provider: vessl_ai label: - en_US: vessl_ai + en_US: VESSL AI icon_small: en_US: icon_s_en.svg icon_large: @@ -20,28 +20,28 @@ model_credential_schema: label: en_US: Model Name placeholder: - en_US: Enter your model name + en_US: Enter model name credential_form_schemas: - variable: endpoint_url label: - en_US: endpoint url + en_US: Endpoint Url type: text-input required: true placeholder: - en_US: Enter the url of your endpoint url + en_US: Enter VESSL AI service endpoint url - variable: api_key required: true label: en_US: API Key type: secret-input placeholder: - en_US: Enter your VESSL AI secret key + en_US: Enter VESSL AI secret key - variable: mode show_on: - variable: __model_type value: llm label: - en_US: Completion mode + en_US: Completion Mode type: select required: false default: chat diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/common.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/common.py index 3825fd6574..0bfb18cda6 100644 --- a/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/common.py +++ b/api/core/model_runtime/model_providers/volcengine_maas/legacy/volc_sdk/common.py @@ -1,5 +1,6 @@ import json import random +from collections import UserDict from datetime import datetime @@ -10,9 +11,9 @@ class ChatRole: FUNCTION = "function" -class _Dict(dict): - __setattr__ = dict.__setitem__ - __getattr__ = dict.__getitem__ +class _Dict(UserDict): + __setattr__ = UserDict.__setitem__ + __getattr__ = UserDict.__getitem__ def __missing__(self, key): return None diff --git a/api/core/ops/entities/config_entity.py b/api/core/ops/entities/config_entity.py index 5c79867571..ef0f9c708f 100644 --- a/api/core/ops/entities/config_entity.py +++ b/api/core/ops/entities/config_entity.py @@ -54,3 +54,7 @@ class LangSmithConfig(BaseTracingConfig): raise ValueError("endpoint must start with https://") return v + + +OPS_FILE_PATH = "ops_trace/" +OPS_TRACE_FAILED_KEY = "FAILED_OPS_TRACE" diff --git a/api/core/ops/entities/trace_entity.py b/api/core/ops/entities/trace_entity.py index db6ce9d9c3..256595286f 100644 --- a/api/core/ops/entities/trace_entity.py +++ b/api/core/ops/entities/trace_entity.py @@ -23,6 +23,11 @@ class BaseTraceInfo(BaseModel): return v return "" + class Config: + json_encoders = { + datetime: lambda v: v.isoformat(), + } + class WorkflowTraceInfo(BaseTraceInfo): workflow_data: Any @@ -100,6 +105,12 @@ class GenerateNameTraceInfo(BaseTraceInfo): tenant_id: str +class TaskData(BaseModel): + app_id: str + trace_info_type: str + trace_info: Any + + trace_info_info_map = { "WorkflowTraceInfo": WorkflowTraceInfo, "MessageTraceInfo": MessageTraceInfo, diff --git a/api/core/ops/ops_trace_manager.py b/api/core/ops/ops_trace_manager.py index 986749f056..79704c115f 100644 --- a/api/core/ops/ops_trace_manager.py +++ b/api/core/ops/ops_trace_manager.py @@ -6,12 +6,13 @@ import threading import time from datetime import timedelta from typing import Any, Optional, Union -from uuid import UUID +from uuid import UUID, uuid4 from flask import current_app from core.helper.encrypter import decrypt_token, encrypt_token, obfuscated_token from core.ops.entities.config_entity import ( + OPS_FILE_PATH, LangfuseConfig, LangSmithConfig, TracingProviderEnum, @@ -22,6 +23,7 @@ from core.ops.entities.trace_entity import ( MessageTraceInfo, ModerationTraceInfo, SuggestedQuestionTraceInfo, + TaskData, ToolTraceInfo, TraceTaskName, WorkflowTraceInfo, @@ -30,6 +32,7 @@ from core.ops.langfuse_trace.langfuse_trace import LangFuseDataTrace from core.ops.langsmith_trace.langsmith_trace import LangSmithDataTrace from core.ops.utils import get_message_data from extensions.ext_database import db +from extensions.ext_storage import storage from models.model import App, AppModelConfig, Conversation, Message, MessageAgentThought, MessageFile, TraceAppConfig from models.workflow import WorkflowAppLog, WorkflowRun from tasks.ops_trace_task import process_trace_tasks @@ -740,10 +743,17 @@ class TraceQueueManager: def send_to_celery(self, tasks: list[TraceTask]): with self.flask_app.app_context(): for task in tasks: + file_id = uuid4().hex trace_info = task.execute() - task_data = { + task_data = TaskData( + app_id=task.app_id, + trace_info_type=type(trace_info).__name__, + trace_info=trace_info.model_dump() if trace_info else None, + ) + file_path = f"{OPS_FILE_PATH}{task.app_id}/{file_id}.json" + storage.save(file_path, task_data.model_dump_json().encode("utf-8")) + file_info = { + "file_id": file_id, "app_id": task.app_id, - "trace_info_type": type(trace_info).__name__, - "trace_info": trace_info.model_dump() if trace_info else {}, } - process_trace_tasks.delay(task_data) + process_trace_tasks.delay(file_info) diff --git a/api/core/prompt/advanced_prompt_transform.py b/api/core/prompt/advanced_prompt_transform.py index bbd9531b19..0f3f824966 100644 --- a/api/core/prompt/advanced_prompt_transform.py +++ b/api/core/prompt/advanced_prompt_transform.py @@ -15,6 +15,7 @@ from core.model_runtime.entities import ( TextPromptMessageContent, UserPromptMessage, ) +from core.model_runtime.entities.message_entities import ImagePromptMessageContent from core.prompt.entities.advanced_prompt_entities import ChatModelMessage, CompletionModelPromptTemplate, MemoryConfig from core.prompt.prompt_transform import PromptTransform from core.prompt.utils.prompt_template_parser import PromptTemplateParser @@ -26,8 +27,13 @@ class AdvancedPromptTransform(PromptTransform): Advanced Prompt Transform for Workflow LLM Node. """ - def __init__(self, with_variable_tmpl: bool = False) -> None: + def __init__( + self, + with_variable_tmpl: bool = False, + image_detail_config: ImagePromptMessageContent.DETAIL = ImagePromptMessageContent.DETAIL.LOW, + ) -> None: self.with_variable_tmpl = with_variable_tmpl + self.image_detail_config = image_detail_config def get_prompt( self, diff --git a/api/core/rag/datasource/vdb/elasticsearch/elasticsearch_vector.py b/api/core/rag/datasource/vdb/elasticsearch/elasticsearch_vector.py index c62042af80..b08811a021 100644 --- a/api/core/rag/datasource/vdb/elasticsearch/elasticsearch_vector.py +++ b/api/core/rag/datasource/vdb/elasticsearch/elasticsearch_vector.py @@ -178,6 +178,7 @@ class ElasticSearchVector(BaseVector): Field.VECTOR.value: { # Make sure the dimension is correct here "type": "dense_vector", "dims": dim, + "index": True, "similarity": "cosine", }, Field.METADATA_KEY.value: { diff --git a/api/core/rag/extractor/word_extractor.py b/api/core/rag/extractor/word_extractor.py index b59e7f94fd..8e084ab4ff 100644 --- a/api/core/rag/extractor/word_extractor.py +++ b/api/core/rag/extractor/word_extractor.py @@ -50,9 +50,9 @@ class WordExtractor(BaseExtractor): self.web_path = self.file_path # TODO: use a better way to handle the file - with tempfile.NamedTemporaryFile(delete=False) as self.temp_file: - self.temp_file.write(r.content) - self.file_path = self.temp_file.name + self.temp_file = tempfile.NamedTemporaryFile() # noqa: SIM115 + self.temp_file.write(r.content) + self.file_path = self.temp_file.name elif not os.path.isfile(self.file_path): raise ValueError(f"File path {self.file_path} is not a valid file or url") diff --git a/api/core/tools/entities/api_entities.py b/api/core/tools/entities/api_entities.py index b1db559441..ddb1481276 100644 --- a/api/core/tools/entities/api_entities.py +++ b/api/core/tools/entities/api_entities.py @@ -1,6 +1,6 @@ from typing import Literal, Optional -from pydantic import BaseModel +from pydantic import BaseModel, Field, field_validator from core.model_runtime.utils.encoders import jsonable_encoder from core.tools.entities.common_entities import I18nObject @@ -32,9 +32,14 @@ class UserToolProvider(BaseModel): original_credentials: Optional[dict] = None is_team_authorization: bool = False allow_delete: bool = True - tools: list[UserTool] | None = None + tools: list[UserTool] = Field(default_factory=list) labels: list[str] | None = None + @field_validator("tools", mode="before") + @classmethod + def convert_none_to_empty_list(cls, v): + return v if v is not None else [] + def to_dict(self) -> dict: # ------------- # overwrite tool parameter types for temp fix diff --git a/api/core/tools/provider/_position.yaml b/api/core/tools/provider/_position.yaml index d80974486d..937fb40774 100644 --- a/api/core/tools/provider/_position.yaml +++ b/api/core/tools/provider/_position.yaml @@ -78,3 +78,4 @@ - regex - trello - vanna +- fal diff --git a/api/core/tools/provider/builtin/audio/_assets/icon.svg b/api/core/tools/provider/builtin/audio/_assets/icon.svg new file mode 100644 index 0000000000..08cc4ede66 --- /dev/null +++ b/api/core/tools/provider/builtin/audio/_assets/icon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/api/core/tools/provider/builtin/audio/audio.py b/api/core/tools/provider/builtin/audio/audio.py new file mode 100644 index 0000000000..1f15386f78 --- /dev/null +++ b/api/core/tools/provider/builtin/audio/audio.py @@ -0,0 +1,6 @@ +from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController + + +class AudioToolProvider(BuiltinToolProviderController): + def _validate_credentials(self, credentials: dict) -> None: + pass diff --git a/api/core/tools/provider/builtin/audio/audio.yaml b/api/core/tools/provider/builtin/audio/audio.yaml new file mode 100644 index 0000000000..07db268dac --- /dev/null +++ b/api/core/tools/provider/builtin/audio/audio.yaml @@ -0,0 +1,11 @@ +identity: + author: hjlarry + name: audio + label: + en_US: Audio + description: + en_US: A tool for tts and asr. + zh_Hans: 一个用于文本转语音和语音转文本的工具。 + icon: icon.svg + tags: + - utilities diff --git a/api/core/tools/provider/builtin/audio/tools/asr.py b/api/core/tools/provider/builtin/audio/tools/asr.py new file mode 100644 index 0000000000..2aa409cd1a --- /dev/null +++ b/api/core/tools/provider/builtin/audio/tools/asr.py @@ -0,0 +1,69 @@ +import io +from typing import Any + +from core.file.enums import FileType +from core.file.file_manager import download +from core.model_manager import ModelManager +from core.model_runtime.entities.model_entities import ModelType +from core.tools.entities.common_entities import I18nObject +from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter, ToolParameterOption +from core.tools.tool.builtin_tool import BuiltinTool +from services.model_provider_service import ModelProviderService + + +class ASRTool(BuiltinTool): + def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> list[ToolInvokeMessage]: + file = tool_parameters.get("audio_file") + if file.type != FileType.AUDIO: + return [self.create_text_message("not a valid audio file")] + audio_binary = io.BytesIO(download(file)) + audio_binary.name = "temp.mp3" + provider, model = tool_parameters.get("model").split("#") + model_manager = ModelManager() + model_instance = model_manager.get_model_instance( + tenant_id=self.runtime.tenant_id, + provider=provider, + model_type=ModelType.SPEECH2TEXT, + model=model, + ) + text = model_instance.invoke_speech2text( + file=audio_binary, + user=user_id, + ) + return [self.create_text_message(text)] + + def get_available_models(self) -> list[tuple[str, str]]: + model_provider_service = ModelProviderService() + models = model_provider_service.get_models_by_model_type( + tenant_id=self.runtime.tenant_id, model_type="speech2text" + ) + items = [] + for provider_model in models: + provider = provider_model.provider + for model in provider_model.models: + items.append((provider, model.model)) + return items + + def get_runtime_parameters(self) -> list[ToolParameter]: + parameters = [] + + options = [] + for provider, model in self.get_available_models(): + option = ToolParameterOption(value=f"{provider}#{model}", label=I18nObject(en_US=f"{model}({provider})")) + options.append(option) + + parameters.append( + ToolParameter( + name="model", + label=I18nObject(en_US="Model", zh_Hans="Model"), + human_description=I18nObject( + en_US="All available ASR models. You can config model in the Model Provider of Settings.", + zh_Hans="所有可用的 ASR 模型。你可以在设置中的模型供应商里配置。", + ), + type=ToolParameter.ToolParameterType.SELECT, + form=ToolParameter.ToolParameterForm.FORM, + required=True, + options=options, + ) + ) + return parameters diff --git a/api/core/tools/provider/builtin/audio/tools/asr.yaml b/api/core/tools/provider/builtin/audio/tools/asr.yaml new file mode 100644 index 0000000000..b2c82f8086 --- /dev/null +++ b/api/core/tools/provider/builtin/audio/tools/asr.yaml @@ -0,0 +1,22 @@ +identity: + name: asr + author: hjlarry + label: + en_US: Speech To Text +description: + human: + en_US: Convert audio file to text. + zh_Hans: 将音频文件转换为文本。 + llm: Convert audio file to text. +parameters: + - name: audio_file + type: file + required: true + label: + en_US: Audio File + zh_Hans: 音频文件 + human_description: + en_US: The audio file to be converted. + zh_Hans: 要转换的音频文件。 + llm_description: The audio file to be converted. + form: llm diff --git a/api/core/tools/provider/builtin/audio/tools/tts.py b/api/core/tools/provider/builtin/audio/tools/tts.py new file mode 100644 index 0000000000..f83a64d041 --- /dev/null +++ b/api/core/tools/provider/builtin/audio/tools/tts.py @@ -0,0 +1,89 @@ +import io +from typing import Any + +from core.model_manager import ModelManager +from core.model_runtime.entities.model_entities import ModelPropertyKey, ModelType +from core.tools.entities.common_entities import I18nObject +from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter, ToolParameterOption +from core.tools.tool.builtin_tool import BuiltinTool +from services.model_provider_service import ModelProviderService + + +class TTSTool(BuiltinTool): + def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> list[ToolInvokeMessage]: + provider, model = tool_parameters.get("model").split("#") + voice = tool_parameters.get(f"voice#{provider}#{model}") + model_manager = ModelManager() + model_instance = model_manager.get_model_instance( + tenant_id=self.runtime.tenant_id, + provider=provider, + model_type=ModelType.TTS, + model=model, + ) + tts = model_instance.invoke_tts( + content_text=tool_parameters.get("text"), + user=user_id, + tenant_id=self.runtime.tenant_id, + voice=voice, + ) + buffer = io.BytesIO() + for chunk in tts: + buffer.write(chunk) + + wav_bytes = buffer.getvalue() + return [ + self.create_text_message("Audio generated successfully"), + self.create_blob_message( + blob=wav_bytes, + meta={"mime_type": "audio/x-wav"}, + save_as=self.VariableKey.AUDIO, + ), + ] + + def get_available_models(self) -> list[tuple[str, str, list[Any]]]: + model_provider_service = ModelProviderService() + models = model_provider_service.get_models_by_model_type(tenant_id=self.runtime.tenant_id, model_type="tts") + items = [] + for provider_model in models: + provider = provider_model.provider + for model in provider_model.models: + voices = model.model_properties.get(ModelPropertyKey.VOICES, []) + items.append((provider, model.model, voices)) + return items + + def get_runtime_parameters(self) -> list[ToolParameter]: + parameters = [] + + options = [] + for provider, model, voices in self.get_available_models(): + option = ToolParameterOption(value=f"{provider}#{model}", label=I18nObject(en_US=f"{model}({provider})")) + options.append(option) + parameters.append( + ToolParameter( + name=f"voice#{provider}#{model}", + label=I18nObject(en_US=f"Voice of {model}({provider})"), + type=ToolParameter.ToolParameterType.SELECT, + form=ToolParameter.ToolParameterForm.FORM, + options=[ + ToolParameterOption(value=voice.get("mode"), label=I18nObject(en_US=voice.get("name"))) + for voice in voices + ], + ) + ) + + parameters.insert( + 0, + ToolParameter( + name="model", + label=I18nObject(en_US="Model", zh_Hans="Model"), + human_description=I18nObject( + en_US="All available TTS models. You can config model in the Model Provider of Settings.", + zh_Hans="所有可用的 TTS 模型。你可以在设置中的模型供应商里配置。", + ), + type=ToolParameter.ToolParameterType.SELECT, + form=ToolParameter.ToolParameterForm.FORM, + required=True, + options=options, + ), + ) + return parameters diff --git a/api/core/tools/provider/builtin/audio/tools/tts.yaml b/api/core/tools/provider/builtin/audio/tools/tts.yaml new file mode 100644 index 0000000000..36f42bd689 --- /dev/null +++ b/api/core/tools/provider/builtin/audio/tools/tts.yaml @@ -0,0 +1,22 @@ +identity: + name: tts + author: hjlarry + label: + en_US: Text To Speech +description: + human: + en_US: Convert text to audio file. + zh_Hans: 将文本转换为音频文件。 + llm: Convert text to audio file. +parameters: + - name: text + type: string + required: true + label: + en_US: Text + zh_Hans: 文本 + human_description: + en_US: The text to be converted. + zh_Hans: 要转换的文本。 + llm_description: The text to be converted. + form: llm diff --git a/api/core/tools/provider/builtin/cogview/tools/cogvideo.py b/api/core/tools/provider/builtin/cogview/tools/cogvideo.py new file mode 100644 index 0000000000..7f69e833cb --- /dev/null +++ b/api/core/tools/provider/builtin/cogview/tools/cogvideo.py @@ -0,0 +1,24 @@ +from typing import Any, Union + +from zhipuai import ZhipuAI + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class CogVideoTool(BuiltinTool): + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + client = ZhipuAI( + base_url=self.runtime.credentials["zhipuai_base_url"], + api_key=self.runtime.credentials["zhipuai_api_key"], + ) + if not tool_parameters.get("prompt") and not tool_parameters.get("image_url"): + return self.create_text_message("require at least one of prompt and image_url") + + response = client.videos.generations( + model="cogvideox", prompt=tool_parameters.get("prompt"), image_url=tool_parameters.get("image_url") + ) + + return self.create_json_message(response.dict()) diff --git a/api/core/tools/provider/builtin/cogview/tools/cogvideo.yaml b/api/core/tools/provider/builtin/cogview/tools/cogvideo.yaml new file mode 100644 index 0000000000..3df0cfcea9 --- /dev/null +++ b/api/core/tools/provider/builtin/cogview/tools/cogvideo.yaml @@ -0,0 +1,32 @@ +identity: + name: cogvideo + author: hjlarry + label: + en_US: CogVideo + zh_Hans: CogVideo 视频生成 +description: + human: + en_US: Use the CogVideox model provided by ZhipuAI to generate videos based on user prompts and images. + zh_Hans: 使用智谱cogvideox模型,根据用户输入的提示词和图片,生成视频。 + llm: A tool for generating videos. The input is user's prompt or image url or both of them, the output is a task id. You can use another tool with this task id to check the status and get the video. +parameters: + - name: prompt + type: string + label: + en_US: prompt + zh_Hans: 提示词 + human_description: + en_US: The prompt text used to generate video. + zh_Hans: 用于生成视频的提示词。 + llm_description: The prompt text used to generate video. Optional. + form: llm + - name: image_url + type: string + label: + en_US: image url + zh_Hans: 图片链接 + human_description: + en_US: The image url used to generate video. + zh_Hans: 输入一个图片链接,生成的视频将基于该图片和提示词。 + llm_description: The image url used to generate video. Optional. + form: llm diff --git a/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.py b/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.py new file mode 100644 index 0000000000..a521f1c28a --- /dev/null +++ b/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.py @@ -0,0 +1,30 @@ +from typing import Any, Union + +import httpx +from zhipuai import ZhipuAI + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class CogVideoJobTool(BuiltinTool): + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + client = ZhipuAI( + api_key=self.runtime.credentials["zhipuai_api_key"], + base_url=self.runtime.credentials["zhipuai_base_url"], + ) + + response = client.videos.retrieve_videos_result(id=tool_parameters.get("id")) + result = [self.create_json_message(response.dict())] + if response.task_status == "SUCCESS": + for item in response.video_result: + video_cover_image = self.create_image_message(item.cover_image_url) + result.append(video_cover_image) + video = self.create_blob_message( + blob=httpx.get(item.url).content, meta={"mime_type": "video/mp4"}, save_as=self.VariableKey.VIDEO + ) + result.append(video) + + return result diff --git a/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.yaml b/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.yaml new file mode 100644 index 0000000000..fb2eb3ab13 --- /dev/null +++ b/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.yaml @@ -0,0 +1,21 @@ +identity: + name: cogvideo_job + author: hjlarry + label: + en_US: CogVideo Result + zh_Hans: CogVideo 结果获取 +description: + human: + en_US: Get the result of CogVideo tool generation. + zh_Hans: 根据 CogVideo 工具返回的 id 获取视频生成结果。 + llm: Get the result of CogVideo tool generation. The input is the id which is returned by the CogVideo tool. The output is the url of video and video cover image. +parameters: + - name: id + type: string + label: + en_US: id + human_description: + en_US: The id returned by the CogVideo. + zh_Hans: CogVideo 工具返回的 id。 + llm_description: The id returned by the cogvideo. + form: llm diff --git a/api/core/tools/provider/builtin/comfyui/tools/comfyui_client.py b/api/core/tools/provider/builtin/comfyui/tools/comfyui_client.py index 1aae7b2442..bed9cd1882 100644 --- a/api/core/tools/provider/builtin/comfyui/tools/comfyui_client.py +++ b/api/core/tools/provider/builtin/comfyui/tools/comfyui_client.py @@ -48,7 +48,6 @@ class ComfyUiClient: prompt = origin_prompt.copy() id_to_class_type = {id: details["class_type"] for id, details in prompt.items()} k_sampler = [key for key, value in id_to_class_type.items() if value == "KSampler"][0] - prompt.get(k_sampler)["inputs"]["seed"] = random.randint(10**14, 10**15 - 1) positive_input_id = prompt.get(k_sampler)["inputs"]["positive"][0] prompt.get(positive_input_id)["inputs"]["text"] = positive_prompt @@ -72,6 +71,18 @@ class ComfyUiClient: prompt.get(load_image)["inputs"]["image"] = image_name return prompt + def set_prompt_seed_by_id(self, origin_prompt: dict, seed_id: str) -> dict: + prompt = origin_prompt.copy() + if seed_id not in prompt: + raise Exception("Not a valid seed node") + if "seed" in prompt[seed_id]["inputs"]: + prompt[seed_id]["inputs"]["seed"] = random.randint(10**14, 10**15 - 1) + elif "noise_seed" in prompt[seed_id]["inputs"]: + prompt[seed_id]["inputs"]["noise_seed"] = random.randint(10**14, 10**15 - 1) + else: + raise Exception("Not a valid seed node") + return prompt + def track_progress(self, prompt: dict, ws: WebSocket, prompt_id: str): node_ids = list(prompt.keys()) finished_nodes = [] diff --git a/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.py b/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.py index d62772cda7..8783736277 100644 --- a/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.py +++ b/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.py @@ -70,6 +70,9 @@ class ComfyUIWorkflowTool(BuiltinTool): else: prompt = comfyui.set_prompt_images_by_default(prompt, image_names) + if seed_id := tool_parameters.get("seed_id"): + prompt = comfyui.set_prompt_seed_by_id(prompt, seed_id) + images = comfyui.generate_image_by_prompt(prompt) result = [] for img in images: diff --git a/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.yaml b/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.yaml index dc4e0d77b2..9428acbe94 100644 --- a/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.yaml +++ b/api/core/tools/provider/builtin/comfyui/tools/comfyui_workflow.yaml @@ -52,3 +52,12 @@ parameters: en_US: When the workflow has multiple image nodes, enter the ID list of these nodes, and the images will be passed to ComfyUI in the order of the list. zh_Hans: 当工作流有多个图片节点时,输入这些节点的ID列表,图片将按列表顺序传给ComfyUI form: form + - name: seed_id + type: string + label: + en_US: Seed Node Id + zh_Hans: 种子节点ID + human_description: + en_US: If you need to generate different images each time, you need to enter the ID of the seed node. + zh_Hans: 如果需要每次生成时使用不同的种子,需要输入包含种子的节点的ID + form: form diff --git a/api/core/tools/provider/builtin/email/_assets/icon.svg b/api/core/tools/provider/builtin/email/_assets/icon.svg new file mode 100644 index 0000000000..b34f333890 --- /dev/null +++ b/api/core/tools/provider/builtin/email/_assets/icon.svg @@ -0,0 +1 @@ + diff --git a/api/core/tools/provider/builtin/email/email.py b/api/core/tools/provider/builtin/email/email.py new file mode 100644 index 0000000000..182d8dac28 --- /dev/null +++ b/api/core/tools/provider/builtin/email/email.py @@ -0,0 +1,7 @@ +from core.tools.provider.builtin.email.tools.send_mail import SendMailTool +from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController + + +class SmtpProvider(BuiltinToolProviderController): + def _validate_credentials(self, credentials: dict) -> None: + SendMailTool() diff --git a/api/core/tools/provider/builtin/email/email.yaml b/api/core/tools/provider/builtin/email/email.yaml new file mode 100644 index 0000000000..bb1bb7f6f3 --- /dev/null +++ b/api/core/tools/provider/builtin/email/email.yaml @@ -0,0 +1,83 @@ +identity: + author: wakaka6 + name: email + label: + en_US: email + zh_Hans: 电子邮件 + description: + en_US: send email through smtp protocol + zh_Hans: 通过smtp协议发送电子邮件 + icon: icon.svg + tags: + - utilities +credentials_for_provider: + email_account: + type: text-input + required: true + label: + en_US: email account + zh_Hans: 邮件账号 + placeholder: + en_US: input you email account + zh_Hans: 输入你的邮箱账号 + help: + en_US: email account + zh_Hans: 邮件账号 + email_password: + type: secret-input + required: true + label: + en_US: email password + zh_Hans: 邮件密码 + placeholder: + en_US: email password + zh_Hans: 邮件密码 + help: + en_US: email password + zh_Hans: 邮件密码 + smtp_server: + type: text-input + required: true + label: + en_US: smtp server + zh_Hans: 发信smtp服务器地址 + placeholder: + en_US: smtp server + zh_Hans: 发信smtp服务器地址 + help: + en_US: smtp server + zh_Hans: 发信smtp服务器地址 + smtp_port: + type: text-input + required: true + label: + en_US: smtp server port + zh_Hans: 发信smtp服务器端口 + placeholder: + en_US: smtp server port + zh_Hans: 发信smtp服务器端口 + help: + en_US: smtp server port + zh_Hans: 发信smtp服务器端口 + encrypt_method: + type: select + required: true + options: + - value: NONE + label: + en_US: NONE + zh_Hans: 无加密 + - value: SSL + label: + en_US: SSL + zh_Hans: SSL加密 + - value: TLS + label: + en_US: START TLS + zh_Hans: START TLS加密 + label: + en_US: encrypt method + zh_Hans: 加密方式 + help: + en_US: smtp server encrypt method + zh_Hans: 发信smtp服务器加密方式 diff --git a/api/core/tools/provider/builtin/email/tools/send.py b/api/core/tools/provider/builtin/email/tools/send.py new file mode 100644 index 0000000000..35df574a41 --- /dev/null +++ b/api/core/tools/provider/builtin/email/tools/send.py @@ -0,0 +1,53 @@ +import logging +import smtplib +import ssl +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText + +from pydantic import BaseModel + + +class SendEmailToolParameters(BaseModel): + smtp_server: str + smtp_port: int + + email_account: str + email_password: str + + sender_to: str + subject: str + email_content: str + encrypt_method: str + + +def send_mail(parmas: SendEmailToolParameters): + timeout = 60 + msg = MIMEMultipart("alternative") + msg["From"] = parmas.email_account + msg["To"] = parmas.sender_to + msg["Subject"] = parmas.subject + msg.attach(MIMEText(parmas.email_content, "plain")) + msg.attach(MIMEText(parmas.email_content, "html")) + + ctx = ssl.create_default_context() + + if parmas.encrypt_method.upper() == "SSL": + try: + with smtplib.SMTP_SSL(parmas.smtp_server, parmas.smtp_port, context=ctx, timeout=timeout) as server: + server.login(parmas.email_account, parmas.email_password) + server.sendmail(parmas.email_account, parmas.sender_to, msg.as_string()) + return True + except Exception as e: + logging.exception("send email failed: %s", e) + return False + else: # NONE or TLS + try: + with smtplib.SMTP(parmas.smtp_server, parmas.smtp_port, timeout=timeout) as server: + if parmas.encrypt_method.upper() == "TLS": + server.starttls(context=ctx) + server.login(parmas.email_account, parmas.email_password) + server.sendmail(parmas.email_account, parmas.sender_to, msg.as_string()) + return True + except Exception as e: + logging.exception("send email failed: %s", e) + return False diff --git a/api/core/tools/provider/builtin/email/tools/send_mail.py b/api/core/tools/provider/builtin/email/tools/send_mail.py new file mode 100644 index 0000000000..d51d5439b7 --- /dev/null +++ b/api/core/tools/provider/builtin/email/tools/send_mail.py @@ -0,0 +1,66 @@ +import re +from typing import Any, Union + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.provider.builtin.email.tools.send import ( + SendEmailToolParameters, + send_mail, +) +from core.tools.tool.builtin_tool import BuiltinTool + + +class SendMailTool(BuiltinTool): + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + """ + invoke tools + """ + sender = self.runtime.credentials.get("email_account", "") + email_rgx = re.compile(r"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$") + password = self.runtime.credentials.get("email_password", "") + smtp_server = self.runtime.credentials.get("smtp_server", "") + if not smtp_server: + return self.create_text_message("please input smtp server") + smtp_port = self.runtime.credentials.get("smtp_port", "") + try: + smtp_port = int(smtp_port) + except ValueError: + return self.create_text_message("Invalid parameter smtp_port(should be int)") + + if not sender: + return self.create_text_message("please input sender") + if not email_rgx.match(sender): + return self.create_text_message("Invalid parameter userid, the sender is not a mailbox") + + receiver_email = tool_parameters["send_to"] + if not receiver_email: + return self.create_text_message("please input receiver email") + if not email_rgx.match(receiver_email): + return self.create_text_message("Invalid parameter receiver email, the receiver email is not a mailbox") + email_content = tool_parameters.get("email_content", "") + + if not email_content: + return self.create_text_message("please input email content") + + subject = tool_parameters.get("subject", "") + if not subject: + return self.create_text_message("please input email subject") + + encrypt_method = self.runtime.credentials.get("encrypt_method", "") + if not encrypt_method: + return self.create_text_message("please input encrypt method") + + send_email_params = SendEmailToolParameters( + smtp_server=smtp_server, + smtp_port=smtp_port, + email_account=sender, + email_password=password, + sender_to=receiver_email, + subject=subject, + email_content=email_content, + encrypt_method=encrypt_method, + ) + if send_mail(send_email_params): + return self.create_text_message("send email success") + return self.create_text_message("send email failed") diff --git a/api/core/tools/provider/builtin/email/tools/send_mail.yaml b/api/core/tools/provider/builtin/email/tools/send_mail.yaml new file mode 100644 index 0000000000..f54880bf3e --- /dev/null +++ b/api/core/tools/provider/builtin/email/tools/send_mail.yaml @@ -0,0 +1,46 @@ +identity: + name: send_mail + author: wakaka6 + label: + en_US: send email + zh_Hans: 发送邮件 + icon: icon.svg +description: + human: + en_US: A tool for sending email + zh_Hans: 用于发送邮件 + llm: A tool for sending email +parameters: + - name: send_to + type: string + required: true + label: + en_US: Recipient email account + zh_Hans: 收件人邮箱账号 + human_description: + en_US: Recipient email account + zh_Hans: 收件人邮箱账号 + llm_description: Recipient email account + form: llm + - name: subject + type: string + required: true + label: + en_US: email subject + zh_Hans: 邮件主题 + human_description: + en_US: email subject + zh_Hans: 邮件主题 + llm_description: email subject + form: llm + - name: email_content + type: string + required: true + label: + en_US: email content + zh_Hans: 邮件内容 + human_description: + en_US: email content + zh_Hans: 邮件内容 + llm_description: email content + form: llm diff --git a/api/core/tools/provider/builtin/email/tools/send_mail_batch.py b/api/core/tools/provider/builtin/email/tools/send_mail_batch.py new file mode 100644 index 0000000000..ff7e176990 --- /dev/null +++ b/api/core/tools/provider/builtin/email/tools/send_mail_batch.py @@ -0,0 +1,75 @@ +import json +import re +from typing import Any, Union + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.provider.builtin.email.tools.send import ( + SendEmailToolParameters, + send_mail, +) +from core.tools.tool.builtin_tool import BuiltinTool + + +class SendMailTool(BuiltinTool): + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + """ + invoke tools + """ + sender = self.runtime.credentials.get("email_account", "") + email_rgx = re.compile(r"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$") + password = self.runtime.credentials.get("email_password", "") + smtp_server = self.runtime.credentials.get("smtp_server", "") + if not smtp_server: + return self.create_text_message("please input smtp server") + smtp_port = self.runtime.credentials.get("smtp_port", "") + try: + smtp_port = int(smtp_port) + except ValueError: + return self.create_text_message("Invalid parameter smtp_port(should be int)") + + if not sender: + return self.create_text_message("please input sender") + if not email_rgx.match(sender): + return self.create_text_message("Invalid parameter userid, the sender is not a mailbox") + + receivers_email = tool_parameters["send_to"] + if not receivers_email: + return self.create_text_message("please input receiver email") + receivers_email = json.loads(receivers_email) + for receiver in receivers_email: + if not email_rgx.match(receiver): + return self.create_text_message( + f"Invalid parameter receiver email, the receiver email({receiver}) is not a mailbox" + ) + email_content = tool_parameters.get("email_content", "") + + if not email_content: + return self.create_text_message("please input email content") + + subject = tool_parameters.get("subject", "") + if not subject: + return self.create_text_message("please input email subject") + + encrypt_method = self.runtime.credentials.get("encrypt_method", "") + if not encrypt_method: + return self.create_text_message("please input encrypt method") + + msg = {} + for receiver in receivers_email: + send_email_params = SendEmailToolParameters( + smtp_server=smtp_server, + smtp_port=smtp_port, + email_account=sender, + email_password=password, + sender_to=receiver, + subject=subject, + email_content=email_content, + encrypt_method=encrypt_method, + ) + if send_mail(send_email_params): + msg[receiver] = "send email success" + else: + msg[receiver] = "send email failed" + return self.create_text_message(json.dumps(msg)) diff --git a/api/core/tools/provider/builtin/email/tools/send_mail_batch.yaml b/api/core/tools/provider/builtin/email/tools/send_mail_batch.yaml new file mode 100644 index 0000000000..6e4aa785cb --- /dev/null +++ b/api/core/tools/provider/builtin/email/tools/send_mail_batch.yaml @@ -0,0 +1,46 @@ +identity: + name: send_mail_batch + author: wakaka6 + label: + en_US: send email to multiple recipients + zh_Hans: 发送邮件给多个收件人 + icon: icon.svg +description: + human: + en_US: A tool for sending email to multiple recipients + zh_Hans: 用于发送邮件给多个收件人的工具 + llm: A tool for sending email to multiple recipients +parameters: + - name: send_to + type: string + required: true + label: + en_US: Recipient email account(json list) + zh_Hans: 收件人邮箱账号(json list) + human_description: + en_US: Recipient email account + zh_Hans: 收件人邮箱账号 + llm_description: A list of recipient email account(json format) + form: llm + - name: subject + type: string + required: true + label: + en_US: email subject + zh_Hans: 邮件主题 + human_description: + en_US: email subject + zh_Hans: 邮件主题 + llm_description: email subject + form: llm + - name: email_content + type: string + required: true + label: + en_US: email content + zh_Hans: 邮件内容 + human_description: + en_US: email content + zh_Hans: 邮件内容 + llm_description: email content + form: llm diff --git a/api/core/tools/provider/builtin/fal/_assets/icon.svg b/api/core/tools/provider/builtin/fal/_assets/icon.svg new file mode 100644 index 0000000000..bfb270774d --- /dev/null +++ b/api/core/tools/provider/builtin/fal/_assets/icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/api/core/tools/provider/builtin/fal/fal.py b/api/core/tools/provider/builtin/fal/fal.py new file mode 100644 index 0000000000..c68e202133 --- /dev/null +++ b/api/core/tools/provider/builtin/fal/fal.py @@ -0,0 +1,20 @@ +import requests + +from core.tools.errors import ToolProviderCredentialValidationError +from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController + + +class FalProvider(BuiltinToolProviderController): + def _validate_credentials(self, credentials: dict) -> None: + url = "https://fal.run/fal-ai/flux/dev" + headers = { + "Authorization": f"Key {credentials.get('fal_api_key')}", + "Content-Type": "application/json", + } + data = {"prompt": "Cat"} + + response = requests.post(url, json=data, headers=headers) + if response.status_code == 401: + raise ToolProviderCredentialValidationError("FAL API key is invalid") + elif response.status_code != 200: + raise ToolProviderCredentialValidationError(f"FAL API key validation failed: {response.text}") diff --git a/api/core/tools/provider/builtin/fal/fal.yaml b/api/core/tools/provider/builtin/fal/fal.yaml new file mode 100644 index 0000000000..050a73f626 --- /dev/null +++ b/api/core/tools/provider/builtin/fal/fal.yaml @@ -0,0 +1,21 @@ +identity: + author: Kalo Chin + name: fal + label: + en_US: FAL + zh_CN: FAL + description: + en_US: The image generation API provided by FAL. + zh_CN: FAL 提供的图像生成 API。 + icon: icon.svg + tags: + - image +credentials_for_provider: + fal_api_key: + type: secret-input + required: true + label: + en_US: FAL API Key + placeholder: + en_US: Please input your FAL API key + url: https://fal.ai/dashboard/keys diff --git a/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro.py b/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro.py new file mode 100644 index 0000000000..7b5f10a64d --- /dev/null +++ b/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro.py @@ -0,0 +1,46 @@ +from typing import Any, Union + +import requests + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class Flux11ProTool(BuiltinTool): + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + headers = { + "Authorization": f"Key {self.runtime.credentials['fal_api_key']}", + "Content-Type": "application/json", + } + + prompt = tool_parameters.get("prompt", "") + sanitized_prompt = prompt.replace("\\", "") # Remove backslashes from the prompt which may cause errors + + payload = { + "prompt": sanitized_prompt, + "image_size": tool_parameters.get("image_size", "landscape_4_3"), + "seed": tool_parameters.get("seed"), + "sync_mode": tool_parameters.get("sync_mode", False), + "num_images": tool_parameters.get("num_images", 1), + "enable_safety_checker": tool_parameters.get("enable_safety_checker", True), + "safety_tolerance": tool_parameters.get("safety_tolerance", "2"), + } + + url = "https://fal.run/fal-ai/flux-pro/v1.1" + + response = requests.post(url, json=payload, headers=headers) + + if response.status_code != 200: + return self.create_text_message(f"Got Error Response: {response.text}") + + res = response.json() + result = [self.create_json_message(res)] + + for image_info in res.get("images", []): + image_url = image_info.get("url") + if image_url: + result.append(self.create_image_message(image=image_url, save_as=self.VariableKey.IMAGE.value)) + + return result diff --git a/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro.yaml b/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro.yaml new file mode 100644 index 0000000000..237ee9937f --- /dev/null +++ b/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro.yaml @@ -0,0 +1,147 @@ +identity: + name: flux_1_1_pro + author: Kalo Chin + label: + en_US: FLUX 1.1 [pro] + zh_Hans: FLUX 1.1 [pro] + icon: icon.svg +description: + human: + en_US: FLUX 1.1 [pro] is an enhanced version of FLUX.1 [pro], improved image generation capabilities, delivering superior composition, detail, and artistic fidelity compared to its predecessor. + zh_Hans: FLUX 1.1 [pro] 是 FLUX.1 [pro] 的增强版,改进了图像生成能力,与其前身相比,提供了更出色的构图、细节和艺术保真度。 + llm: This tool generates images from prompts using FAL's FLUX 1.1 [pro] model. +parameters: + - name: prompt + type: string + required: true + label: + en_US: Prompt + zh_Hans: 提示词 + human_description: + en_US: The text prompt used to generate the image. + zh_Hans: 用于生成图片的文字提示词。 + llm_description: This prompt text will be used to generate the image. + form: llm + - name: image_size + type: select + required: false + options: + - value: square_hd + label: + en_US: Square HD + zh_Hans: 方形高清 + - value: square + label: + en_US: Square + zh_Hans: 方形 + - value: portrait_4_3 + label: + en_US: Portrait 4:3 + zh_Hans: 竖屏 4:3 + - value: portrait_16_9 + label: + en_US: Portrait 16:9 + zh_Hans: 竖屏 16:9 + - value: landscape_4_3 + label: + en_US: Landscape 4:3 + zh_Hans: 横屏 4:3 + - value: landscape_16_9 + label: + en_US: Landscape 16:9 + zh_Hans: 横屏 16:9 + default: landscape_4_3 + label: + en_US: Image Size + zh_Hans: 图片大小 + human_description: + en_US: The size of the generated image. + zh_Hans: 生成图像的尺寸。 + form: form + - name: num_images + type: number + required: false + default: 1 + min: 1 + max: 1 + label: + en_US: Number of Images + zh_Hans: 图片数量 + human_description: + en_US: The number of images to generate. + zh_Hans: 要生成的图片数量。 + form: form + - name: safety_tolerance + type: select + required: false + options: + - value: "1" + label: + en_US: "1 (Most strict)" + zh_Hans: "1(最严格)" + - value: "2" + label: + en_US: "2" + zh_Hans: "2" + - value: "3" + label: + en_US: "3" + zh_Hans: "3" + - value: "4" + label: + en_US: "4" + zh_Hans: "4" + - value: "5" + label: + en_US: "5" + zh_Hans: "5" + - value: "6" + label: + en_US: "6 (Most permissive)" + zh_Hans: "6(最宽松)" + default: "2" + label: + en_US: Safety Tolerance + zh_Hans: 安全容忍度 + human_description: + en_US: The safety tolerance level for the generated image. 1 being the most strict and 6 being the most permissive. + zh_Hans: 生成图像的安全容忍级别,1 为最严格,6 为最宽松。 + form: form + - name: seed + type: number + required: false + min: 0 + max: 9999999999 + label: + en_US: Seed + zh_Hans: 种子 + human_description: + en_US: The same seed and prompt can produce similar images. + zh_Hans: 相同的种子和提示词可以产生相似的图像。 + form: form + - name: enable_safety_checker + type: boolean + required: false + default: true + label: + en_US: Enable Safety Checker + zh_Hans: 启用安全检查器 + human_description: + en_US: Enable or disable the safety checker. + zh_Hans: 启用或禁用安全检查器。 + form: form + - name: sync_mode + type: boolean + required: false + default: false + label: + en_US: Sync Mode + zh_Hans: 同步模式 + human_description: + en_US: > + If set to true, the function will wait for the image to be generated and uploaded before returning the response. + This will increase the latency but allows you to get the image directly in the response without going through the CDN. + zh_Hans: > + 如果设置为 true,函数将在生成并上传图像后再返回响应。 + 这将增加函数的延迟,但可以让您直接在响应中获取图像,而无需通过 CDN。 + form: form diff --git a/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro_ultra.py b/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro_ultra.py new file mode 100644 index 0000000000..2fb1565e7c --- /dev/null +++ b/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro_ultra.py @@ -0,0 +1,47 @@ +from typing import Any, Union + +import requests + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class Flux11ProUltraTool(BuiltinTool): + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + headers = { + "Authorization": f"Key {self.runtime.credentials['fal_api_key']}", + "Content-Type": "application/json", + } + + prompt = tool_parameters.get("prompt", "") + sanitized_prompt = prompt.replace("\\", "") # Remove backslashes from the prompt which may cause errors + + payload = { + "prompt": sanitized_prompt, + "seed": tool_parameters.get("seed"), + "sync_mode": tool_parameters.get("sync_mode", False), + "num_images": tool_parameters.get("num_images", 1), + "enable_safety_checker": tool_parameters.get("enable_safety_checker", True), + "safety_tolerance": str(tool_parameters.get("safety_tolerance", "2")), + "aspect_ratio": tool_parameters.get("aspect_ratio", "16:9"), + "raw": tool_parameters.get("raw", False), + } + + url = "https://fal.run/fal-ai/flux-pro/v1.1-ultra" + + response = requests.post(url, json=payload, headers=headers) + + if response.status_code != 200: + return self.create_text_message(f"Got Error Response: {response.text}") + + res = response.json() + result = [self.create_json_message(res)] + + for image_info in res.get("images", []): + image_url = image_info.get("url") + if image_url: + result.append(self.create_image_message(image=image_url, save_as=self.VariableKey.IMAGE.value)) + + return result diff --git a/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro_ultra.yaml b/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro_ultra.yaml new file mode 100644 index 0000000000..d518e51929 --- /dev/null +++ b/api/core/tools/provider/builtin/fal/tools/flux_1_1_pro_ultra.yaml @@ -0,0 +1,162 @@ +identity: + name: flux_1_1_pro_ultra + author: Kalo Chin + label: + en_US: FLUX 1.1 [pro] ultra + zh_Hans: FLUX 1.1 [pro] ultra + icon: icon.svg +description: + human: + en_US: FLUX 1.1 [pro] ultra is the newest version of FLUX 1.1 [pro], maintaining professional-grade image quality while delivering up to 2K resolution with improved photo realism. + zh_Hans: FLUX 1.1 [pro] ultra 是 FLUX 1.1 [pro] 的最新版本,保持了专业级的图像质量,同时以改进的照片真实感提供高达 2K 的分辨率。 + llm: This tool generates images from prompts using FAL's FLUX 1.1 [pro] ultra model. +parameters: + - name: prompt + type: string + required: true + label: + en_US: Prompt + zh_Hans: 提示词 + human_description: + en_US: The text prompt used to generate the image. + zh_Hans: 用于生成图像的文本提示。 + llm_description: This prompt text will be used to generate the image. + form: llm + - name: aspect_ratio + type: select + required: false + options: + - value: '21:9' + label: + en_US: '21:9' + zh_Hans: '21:9' + - value: '16:9' + label: + en_US: '16:9' + zh_Hans: '16:9' + - value: '4:3' + label: + en_US: '4:3' + zh_Hans: '4:3' + - value: '1:1' + label: + en_US: '1:1' + zh_Hans: '1:1' + - value: '3:4' + label: + en_US: '3:4' + zh_Hans: '3:4' + - value: '9:16' + label: + en_US: '9:16' + zh_Hans: '9:16' + - value: '9:21' + label: + en_US: '9:21' + zh_Hans: '9:21' + default: '16:9' + label: + en_US: Aspect Ratio + zh_Hans: 纵横比 + human_description: + en_US: The aspect ratio of the generated image. + zh_Hans: 生成图像的宽高比。 + form: form + - name: num_images + type: number + required: false + default: 1 + min: 1 + max: 1 + label: + en_US: Number of Images + zh_Hans: 图片数量 + human_description: + en_US: The number of images to generate. + zh_Hans: 要生成的图像数量。 + form: form + - name: safety_tolerance + type: select + required: false + options: + - value: "1" + label: + en_US: "1 (Most strict)" + zh_Hans: "1(最严格)" + - value: "2" + label: + en_US: "2" + zh_Hans: "2" + - value: "3" + label: + en_US: "3" + zh_Hans: "3" + - value: "4" + label: + en_US: "4" + zh_Hans: "4" + - value: "5" + label: + en_US: "5" + zh_Hans: "5" + - value: "6" + label: + en_US: "6 (Most permissive)" + zh_Hans: "6(最宽松)" + default: '2' + label: + en_US: Safety Tolerance + zh_Hans: 安全容忍度 + human_description: + en_US: The safety tolerance level for the generated image. 1 being the most strict and 6 being the most permissive. + zh_Hans: 生成图像的安全容忍级别,1 为最严格,6 为最宽松。 + form: form + - name: seed + type: number + required: false + min: 0 + max: 9999999999 + label: + en_US: Seed + zh_Hans: 种子 + human_description: + en_US: The same seed and prompt can produce similar images. + zh_Hans: 相同的种子和提示词可以生成相似的图像。 + form: form + - name: raw + type: boolean + required: false + default: false + label: + en_US: Raw Mode + zh_Hans: 原始模式 + human_description: + en_US: Generate less processed, more natural-looking images. + zh_Hans: 生成较少处理、更自然的图像。 + form: form + - name: enable_safety_checker + type: boolean + required: false + default: true + label: + en_US: Enable Safety Checker + zh_Hans: 启用安全检查器 + human_description: + en_US: Enable or disable the safety checker. + zh_Hans: 启用或禁用安全检查器。 + form: form + - name: sync_mode + type: boolean + required: false + default: false + label: + en_US: Sync Mode + zh_Hans: 同步模式 + human_description: + en_US: > + If set to true, the function will wait for the image to be generated and uploaded before returning the response. + This will increase the latency but allows you to get the image directly in the response without going through the CDN. + zh_Hans: > + 如果设置为 true,函数将在生成并上传图像后才返回响应。 + 这将增加延迟,但允许您直接在响应中获取图像,而无需通过 CDN。 + form: form diff --git a/api/core/tools/provider/builtin/fal/tools/flux_1_dev.py b/api/core/tools/provider/builtin/fal/tools/flux_1_dev.py new file mode 100644 index 0000000000..b44d9fe752 --- /dev/null +++ b/api/core/tools/provider/builtin/fal/tools/flux_1_dev.py @@ -0,0 +1,47 @@ +from typing import Any, Union + +import requests + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class Flux1DevTool(BuiltinTool): + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + headers = { + "Authorization": f"Key {self.runtime.credentials['fal_api_key']}", + "Content-Type": "application/json", + } + + prompt = tool_parameters.get("prompt", "") + sanitized_prompt = prompt.replace("\\", "") # Remove backslashes from the prompt which may cause errors + + payload = { + "prompt": sanitized_prompt, + "image_size": tool_parameters.get("image_size", "landscape_4_3"), + "num_inference_steps": tool_parameters.get("num_inference_steps", 28), + "guidance_scale": tool_parameters.get("guidance_scale", 3.5), + "seed": tool_parameters.get("seed"), + "num_images": tool_parameters.get("num_images", 1), + "enable_safety_checker": tool_parameters.get("enable_safety_checker", True), + "sync_mode": tool_parameters.get("sync_mode", False), + } + + url = "https://fal.run/fal-ai/flux/dev" + + response = requests.post(url, json=payload, headers=headers) + + if response.status_code != 200: + return self.create_text_message(f"Got Error Response: {response.text}") + + res = response.json() + result = [self.create_json_message(res)] + + for image_info in res.get("images", []): + image_url = image_info.get("url") + if image_url: + result.append(self.create_image_message(image=image_url, save_as=self.VariableKey.IMAGE.value)) + + return result diff --git a/api/core/tools/provider/builtin/fal/tools/flux_1_dev.yaml b/api/core/tools/provider/builtin/fal/tools/flux_1_dev.yaml new file mode 100644 index 0000000000..3b22af941f --- /dev/null +++ b/api/core/tools/provider/builtin/fal/tools/flux_1_dev.yaml @@ -0,0 +1,137 @@ +identity: + name: flux_1_dev + author: Kalo Chin + label: + en_US: FLUX.1 [dev] + zh_Hans: FLUX.1 [dev] + icon: icon.svg +description: + human: + en_US: FLUX.1 [dev] is a 12 billion parameter flow transformer that generates high-quality images from text. It is suitable for personal and commercial use. + zh_Hans: FLUX.1 [dev] 是一个拥有120亿参数的流动变换模型,可以从文本生成高质量的图像。适用于个人和商业用途。 + llm: This tool generates images from prompts using FAL's FLUX.1 [dev] model. +parameters: + - name: prompt + type: string + required: true + label: + en_US: Prompt + zh_Hans: 提示词 + human_description: + en_US: The text prompt used to generate the image. + zh_Hans: 用于生成图片的文字提示词。 + llm_description: This prompt text will be used to generate the image. + form: llm + - name: image_size + type: select + required: false + options: + - value: square_hd + label: + en_US: Square HD + zh_Hans: 方形高清 + - value: square + label: + en_US: Square + zh_Hans: 方形 + - value: portrait_4_3 + label: + en_US: Portrait 4:3 + zh_Hans: 竖屏 4:3 + - value: portrait_16_9 + label: + en_US: Portrait 16:9 + zh_Hans: 竖屏 16:9 + - value: landscape_4_3 + label: + en_US: Landscape 4:3 + zh_Hans: 横屏 4:3 + - value: landscape_16_9 + label: + en_US: Landscape 16:9 + zh_Hans: 横屏 16:9 + default: landscape_4_3 + label: + en_US: Image Size + zh_Hans: 图片大小 + human_description: + en_US: The size of the generated image. + zh_Hans: 生成图像的尺寸。 + form: form + - name: num_images + type: number + required: false + default: 1 + min: 1 + max: 4 + label: + en_US: Number of Images + zh_Hans: 图片数量 + human_description: + en_US: The number of images to generate. + zh_Hans: 要生成的图片数量。 + form: form + - name: num_inference_steps + type: number + required: false + default: 28 + min: 1 + max: 50 + label: + en_US: Num Inference Steps + zh_Hans: 推理步数 + human_description: + en_US: The number of inference steps to perform. More steps produce higher quality but take longer. + zh_Hans: 执行的推理步骤数量。更多的步骤可以产生更高质量的结果,但需要更长的时间。 + form: form + - name: guidance_scale + type: number + required: false + default: 3.5 + min: 0 + max: 20 + label: + en_US: Guidance Scale + zh_Hans: 指导强度 + human_description: + en_US: How closely the model should follow the prompt. + zh_Hans: 模型对提示词的遵循程度。 + form: form + - name: seed + type: number + required: false + min: 0 + max: 9999999999 + label: + en_US: Seed + zh_Hans: 种子 + human_description: + en_US: The same seed and prompt can produce similar images. + zh_Hans: 相同的种子和提示可以产生相似的图像。 + form: form + - name: enable_safety_checker + type: boolean + required: false + default: true + label: + en_US: Enable Safety Checker + zh_Hans: 启用安全检查器 + human_description: + en_US: Enable or disable the safety checker. + zh_Hans: 启用或禁用安全检查器。 + form: form + - name: sync_mode + type: boolean + required: false + default: false + label: + en_US: Sync Mode + zh_Hans: 同步模式 + human_description: + en_US: > + If set to true, the function will wait for the image to be generated and uploaded before returning the response. + This will increase the latency but allows you to get the image directly in the response without going through the CDN. + zh_Hans: > + 如果设置为 true,函数将在生成并上传图像后再返回响应。 + 这将增加函数的延迟,但可以让您直接在响应中获取图像,而无需通过 CDN。 + form: form diff --git a/api/core/tools/provider/builtin/fal/tools/flux_1_pro_new.py b/api/core/tools/provider/builtin/fal/tools/flux_1_pro_new.py new file mode 100644 index 0000000000..be60366155 --- /dev/null +++ b/api/core/tools/provider/builtin/fal/tools/flux_1_pro_new.py @@ -0,0 +1,47 @@ +from typing import Any, Union + +import requests + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class Flux1ProNewTool(BuiltinTool): + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + headers = { + "Authorization": f"Key {self.runtime.credentials['fal_api_key']}", + "Content-Type": "application/json", + } + + prompt = tool_parameters.get("prompt", "") + sanitized_prompt = prompt.replace("\\", "") # Remove backslashes that may cause errors + + payload = { + "prompt": sanitized_prompt, + "image_size": tool_parameters.get("image_size", "landscape_4_3"), + "num_inference_steps": tool_parameters.get("num_inference_steps", 28), + "guidance_scale": tool_parameters.get("guidance_scale", 3.5), + "seed": tool_parameters.get("seed"), + "num_images": tool_parameters.get("num_images", 1), + "safety_tolerance": tool_parameters.get("safety_tolerance", "2"), + "sync_mode": tool_parameters.get("sync_mode", False), + } + + url = "https://fal.run/fal-ai/flux-pro/new" + + response = requests.post(url, json=payload, headers=headers) + + if response.status_code != 200: + return self.create_text_message(f"Got Error Response: {response.text}") + + res = response.json() + result = [self.create_json_message(res)] + + for image_info in res.get("images", []): + image_url = image_info.get("url") + if image_url: + result.append(self.create_image_message(image=image_url, save_as=self.VariableKey.IMAGE.value)) + + return result diff --git a/api/core/tools/provider/builtin/fal/tools/flux_1_pro_new.yaml b/api/core/tools/provider/builtin/fal/tools/flux_1_pro_new.yaml new file mode 100644 index 0000000000..6f8dbb3a54 --- /dev/null +++ b/api/core/tools/provider/builtin/fal/tools/flux_1_pro_new.yaml @@ -0,0 +1,164 @@ +identity: + name: flux_1_pro_new + author: Kalo Chin + label: + en_US: FLUX.1 [pro] new + zh_Hans: FLUX.1 [pro] new + icon: icon.svg +description: + human: + en_US: FLUX.1 [pro] new is an accelerated version of FLUX.1 [pro], maintaining professional-grade image quality while delivering significantly faster generation speeds. + zh_Hans: FLUX.1 [pro] new 是 FLUX.1 [pro] 的加速版本,在保持专业级图像质量的同时,大大提高了生成速度。 + llm: This tool generates images from prompts using FAL's FLUX.1 [pro] new model. +parameters: + - name: prompt + type: string + required: true + label: + en_US: Prompt + zh_Hans: 提示词 + human_description: + en_US: The text prompt used to generate the image. + zh_Hans: 用于生成图像的文本提示。 + llm_description: This prompt text will be used to generate the image. + form: llm + - name: image_size + type: select + required: false + options: + - value: square_hd + label: + en_US: Square HD + zh_Hans: 正方形高清 + - value: square + label: + en_US: Square + zh_Hans: 正方形 + - value: portrait_4_3 + label: + en_US: Portrait 4:3 + zh_Hans: 竖屏 4:3 + - value: portrait_16_9 + label: + en_US: Portrait 16:9 + zh_Hans: 竖屏 16:9 + - value: landscape_4_3 + label: + en_US: Landscape 4:3 + zh_Hans: 横屏 4:3 + - value: landscape_16_9 + label: + en_US: Landscape 16:9 + zh_Hans: 横屏 16:9 + default: landscape_4_3 + label: + en_US: Image Size + zh_Hans: 图像尺寸 + human_description: + en_US: The size of the generated image. + zh_Hans: 生成图像的尺寸。 + form: form + - name: num_images + type: number + required: false + default: 1 + min: 1 + max: 1 + label: + en_US: Number of Images + zh_Hans: 图像数量 + human_description: + en_US: The number of images to generate. + zh_Hans: 要生成的图像数量。 + form: form + - name: num_inference_steps + type: number + required: false + default: 28 + min: 1 + max: 50 + label: + en_US: Num Inference Steps + zh_Hans: 推理步数 + human_description: + en_US: The number of inference steps to perform. More steps produce higher quality but take longer. + zh_Hans: 执行的推理步数。步数越多,质量越高,但所需时间也更长。 + form: form + - name: guidance_scale + type: number + required: false + default: 3.5 + min: 0 + max: 20 + label: + en_US: Guidance Scale + zh_Hans: 指导强度 + human_description: + en_US: How closely the model should follow the prompt. + zh_Hans: 模型对提示词的遵循程度。 + form: form + - name: safety_tolerance + type: select + required: false + options: + - value: "1" + label: + en_US: "1 (Most strict)" + zh_Hans: "1(最严格)" + - value: "2" + label: + en_US: "2" + zh_Hans: "2" + - value: "3" + label: + en_US: "3" + zh_Hans: "3" + - value: "4" + label: + en_US: "4" + zh_Hans: "4" + - value: "5" + label: + en_US: "5" + zh_Hans: "5" + - value: "6" + label: + en_US: "6 (Most permissive)" + zh_Hans: "6(最宽松)" + default: "2" + label: + en_US: Safety Tolerance + zh_Hans: 安全容忍度 + human_description: + en_US: > + The safety tolerance level for the generated image. 1 being the most strict and 5 being the most permissive. + zh_Hans: > + 生成图像的安全容忍级别。1 是最严格,6 是最宽松。 + form: form + - name: seed + type: number + required: false + min: 0 + max: 9999999999 + label: + en_US: Seed + zh_Hans: 种子 + human_description: + en_US: The same seed and prompt can produce similar images. + zh_Hans: 相同的种子和提示词可以生成相似的图像。 + form: form + - name: sync_mode + type: boolean + required: false + default: false + label: + en_US: Sync Mode + zh_Hans: 同步模式 + human_description: + en_US: > + If set to true, the function will wait for the image to be generated and uploaded before returning the response. + This will increase the latency but allows you to get the image directly in the response without going through the CDN. + zh_Hans: > + 如果设置为 true,函数将在生成并上传图像后才返回响应。 + 这将增加延迟,但允许您直接在响应中获取图像,而无需通过 CDN。 + form: form diff --git a/api/core/tools/provider/builtin/fal/tools/wizper.py b/api/core/tools/provider/builtin/fal/tools/wizper.py new file mode 100644 index 0000000000..67a5e45118 --- /dev/null +++ b/api/core/tools/provider/builtin/fal/tools/wizper.py @@ -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) diff --git a/api/core/tools/provider/builtin/fal/tools/wizper.yaml b/api/core/tools/provider/builtin/fal/tools/wizper.yaml new file mode 100644 index 0000000000..5742efcc1b --- /dev/null +++ b/api/core/tools/provider/builtin/fal/tools/wizper.yaml @@ -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 diff --git a/api/core/tools/provider/builtin/gitee_ai/gitee_ai.yaml b/api/core/tools/provider/builtin/gitee_ai/gitee_ai.yaml index 2e18f8a7fc..d0475665dd 100644 --- a/api/core/tools/provider/builtin/gitee_ai/gitee_ai.yaml +++ b/api/core/tools/provider/builtin/gitee_ai/gitee_ai.yaml @@ -5,7 +5,7 @@ identity: en_US: Gitee AI zh_Hans: Gitee AI description: - en_US: 快速体验大模型,领先探索 AI 开源世界 + en_US: Quickly experience large models and explore the leading AI open source world zh_Hans: 快速体验大模型,领先探索 AI 开源世界 icon: icon.svg tags: diff --git a/api/core/tools/provider/builtin/podcast_generator/podcast_generator.py b/api/core/tools/provider/builtin/podcast_generator/podcast_generator.py index 0b9c025834..a7f7ad2e78 100644 --- a/api/core/tools/provider/builtin/podcast_generator/podcast_generator.py +++ b/api/core/tools/provider/builtin/podcast_generator/podcast_generator.py @@ -1,6 +1,7 @@ from typing import Any import openai +from yarl import URL from core.tools.errors import ToolProviderCredentialValidationError from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController @@ -10,6 +11,7 @@ class PodcastGeneratorProvider(BuiltinToolProviderController): def _validate_credentials(self, credentials: dict[str, Any]) -> None: tts_service = credentials.get("tts_service") api_key = credentials.get("api_key") + base_url = credentials.get("openai_base_url") if not tts_service: raise ToolProviderCredentialValidationError("TTS service is not specified") @@ -17,13 +19,16 @@ class PodcastGeneratorProvider(BuiltinToolProviderController): if not api_key: raise ToolProviderCredentialValidationError("API key is missing") + if base_url: + base_url = str(URL(base_url) / "v1") + if tts_service == "openai": - self._validate_openai_credentials(api_key) + self._validate_openai_credentials(api_key, base_url) else: raise ToolProviderCredentialValidationError(f"Unsupported TTS service: {tts_service}") - def _validate_openai_credentials(self, api_key: str) -> None: - client = openai.OpenAI(api_key=api_key) + def _validate_openai_credentials(self, api_key: str, base_url: str | None) -> None: + client = openai.OpenAI(api_key=api_key, base_url=base_url) try: # We're using a simple API call to validate the credentials client.models.list() diff --git a/api/core/tools/provider/builtin/podcast_generator/podcast_generator.yaml b/api/core/tools/provider/builtin/podcast_generator/podcast_generator.yaml index bd02b32020..d4edb17b28 100644 --- a/api/core/tools/provider/builtin/podcast_generator/podcast_generator.yaml +++ b/api/core/tools/provider/builtin/podcast_generator/podcast_generator.yaml @@ -32,3 +32,15 @@ credentials_for_provider: placeholder: en_US: Enter your TTS service API key zh_Hans: 输入您的 TTS 服务 API 密钥 + openai_base_url: + type: text-input + required: false + label: + en_US: OpenAI base URL + zh_Hans: OpenAI base URL + help: + en_US: Please input your OpenAI base URL + zh_Hans: 请输入你的 OpenAI base URL + placeholder: + en_US: Please input your OpenAI base URL + zh_Hans: 请输入你的 OpenAI base URL diff --git a/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.py b/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.py index 476e2d01e1..165e93956e 100644 --- a/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.py +++ b/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.py @@ -5,6 +5,7 @@ import warnings from typing import Any, Literal, Optional, Union import openai +from yarl import URL from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.errors import ToolParameterValidationError, ToolProviderCredentialValidationError @@ -53,15 +54,24 @@ class PodcastAudioGeneratorTool(BuiltinTool): if not host1_voice or not host2_voice: raise ToolParameterValidationError("Host voices are required") - # Get OpenAI API key from credentials + # Ensure runtime and credentials if not self.runtime or not self.runtime.credentials: raise ToolProviderCredentialValidationError("Tool runtime or credentials are missing") + + # Get OpenAI API key from credentials api_key = self.runtime.credentials.get("api_key") if not api_key: raise ToolProviderCredentialValidationError("OpenAI API key is missing") + # Get OpenAI base URL + openai_base_url = self.runtime.credentials.get("openai_base_url", None) + openai_base_url = str(URL(openai_base_url) / "v1") if openai_base_url else None + # Initialize OpenAI client - client = openai.OpenAI(api_key=api_key) + client = openai.OpenAI( + api_key=api_key, + base_url=openai_base_url, + ) # Create a thread pool max_workers = 5 diff --git a/api/core/tools/provider/builtin/vanna/tools/vanna.yaml b/api/core/tools/provider/builtin/vanna/tools/vanna.yaml index 12ca8a862e..309681321b 100644 --- a/api/core/tools/provider/builtin/vanna/tools/vanna.yaml +++ b/api/core/tools/provider/builtin/vanna/tools/vanna.yaml @@ -32,7 +32,7 @@ parameters: en_US: RAG Model for your database DDL zh_Hans: 存储数据库训练数据的RAG模型 llm_description: RAG Model for generating SQL - form: form + form: llm - name: db_type type: select required: true @@ -136,7 +136,7 @@ parameters: human_description: en_US: DDL statements for training data zh_Hans: 用于训练RAG Model的建表语句 - form: form + form: llm - name: question type: string required: false @@ -146,7 +146,7 @@ parameters: human_description: en_US: Question-SQL Pairs zh_Hans: Question-SQL中的问题 - form: form + form: llm - name: sql type: string required: false @@ -156,7 +156,7 @@ parameters: human_description: en_US: SQL queries to your training data zh_Hans: 用于训练RAG Model的SQL语句 - form: form + form: llm - name: memos type: string required: false @@ -166,7 +166,7 @@ parameters: human_description: en_US: Sometimes you may want to add documentation about your business terminology or definitions zh_Hans: 添加更多关于数据库的业务说明 - form: form + form: llm - name: enable_training type: boolean required: false diff --git a/api/core/tools/provider/builtin/vectorizer/vectorizer.py b/api/core/tools/provider/builtin/vectorizer/vectorizer.py index 8140348723..211ec78f4d 100644 --- a/api/core/tools/provider/builtin/vectorizer/vectorizer.py +++ b/api/core/tools/provider/builtin/vectorizer/vectorizer.py @@ -1,19 +1,23 @@ from typing import Any -from core.file import File -from core.file.enums import FileTransferMethod, FileType +from core.file import FileTransferMethod, FileType from core.tools.errors import ToolProviderCredentialValidationError from core.tools.provider.builtin.vectorizer.tools.vectorizer import VectorizerTool from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController +from factories import file_factory class VectorizerProvider(BuiltinToolProviderController): def _validate_credentials(self, credentials: dict[str, Any]) -> None: - test_img = File( + mapping = { + "transfer_method": FileTransferMethod.TOOL_FILE, + "type": FileType.IMAGE, + "id": "test_id", + "url": "https://cloud.dify.ai/logo/logo-site.png", + } + test_img = file_factory.build_from_mapping( + mapping=mapping, tenant_id="__test_123", - remote_url="https://cloud.dify.ai/logo/logo-site.png", - type=FileType.IMAGE, - transfer_method=FileTransferMethod.REMOTE_URL, ) try: VectorizerTool().fork_tool_runtime( diff --git a/api/core/tools/tool_manager.py b/api/core/tools/tool_manager.py index bf2ad13620..d2723df7b2 100644 --- a/api/core/tools/tool_manager.py +++ b/api/core/tools/tool_manager.py @@ -555,6 +555,7 @@ class ToolManager: """ get tool provider """ + provider_name = provider provider: ApiToolProvider = ( db.session.query(ApiToolProvider) .filter( @@ -565,7 +566,7 @@ class ToolManager: ) if provider is None: - raise ValueError(f"you have not added provider {provider}") + raise ValueError(f"you have not added provider {provider_name}") try: credentials = json.loads(provider.credentials_str) or {} diff --git a/api/core/tools/utils/web_reader_tool.py b/api/core/tools/utils/web_reader_tool.py index 5807d61b94..3aae31e93a 100644 --- a/api/core/tools/utils/web_reader_tool.py +++ b/api/core/tools/utils/web_reader_tool.py @@ -356,3 +356,19 @@ def content_digest(element): digest.update(child.encode("utf-8")) digest = digest.hexdigest() return digest + + +def get_image_upload_file_ids(content): + pattern = r"!\[image\]\((http?://.*?(file-preview|image-preview))\)" + matches = re.findall(pattern, content) + image_upload_file_ids = [] + for match in matches: + if match[1] == "file-preview": + content_pattern = r"files/([^/]+)/file-preview" + else: + content_pattern = r"files/([^/]+)/image-preview" + content_match = re.search(content_pattern, match[0]) + if content_match: + image_upload_file_id = content_match.group(1) + image_upload_file_ids.append(image_upload_file_id) + return image_upload_file_ids diff --git a/api/core/variables/__init__.py b/api/core/variables/__init__.py index 87f9e3ed45..144c1b899f 100644 --- a/api/core/variables/__init__.py +++ b/api/core/variables/__init__.py @@ -17,6 +17,7 @@ from .segments import ( from .types import SegmentType from .variables import ( ArrayAnyVariable, + ArrayFileVariable, ArrayNumberVariable, ArrayObjectVariable, ArrayStringVariable, @@ -58,4 +59,5 @@ __all__ = [ "ArrayStringSegment", "FileSegment", "FileVariable", + "ArrayFileVariable", ] diff --git a/api/core/variables/variables.py b/api/core/variables/variables.py index ddc6914192..c902303eef 100644 --- a/api/core/variables/variables.py +++ b/api/core/variables/variables.py @@ -1,9 +1,13 @@ +from collections.abc import Sequence +from uuid import uuid4 + from pydantic import Field from core.helper import encrypter from .segments import ( ArrayAnySegment, + ArrayFileSegment, ArrayNumberSegment, ArrayObjectSegment, ArrayStringSegment, @@ -24,11 +28,12 @@ class Variable(Segment): """ id: str = Field( - default="", - description="Unique identity for variable. It's only used by environment variables now.", + default=lambda _: str(uuid4()), + description="Unique identity for variable.", ) name: str description: str = Field(default="", description="Description of the variable.") + selector: Sequence[str] = Field(default_factory=list) class StringVariable(StringSegment, Variable): @@ -78,3 +83,7 @@ class NoneVariable(NoneSegment, Variable): class FileVariable(FileSegment, Variable): pass + + +class ArrayFileVariable(ArrayFileSegment, Variable): + pass diff --git a/api/core/workflow/entities/node_entities.py b/api/core/workflow/entities/node_entities.py index 7e10cddc71..a747266661 100644 --- a/api/core/workflow/entities/node_entities.py +++ b/api/core/workflow/entities/node_entities.py @@ -24,6 +24,7 @@ class NodeRunMetadataKey(str, Enum): PARENT_PARALLEL_ID = "parent_parallel_id" PARENT_PARALLEL_START_NODE_ID = "parent_parallel_start_node_id" PARALLEL_MODE_RUN_ID = "parallel_mode_run_id" + ITERATION_DURATION_MAP = "iteration_duration_map" # single iteration duration if iteration node runs class NodeRunResult(BaseModel): diff --git a/api/core/workflow/entities/variable_pool.py b/api/core/workflow/entities/variable_pool.py index 3dc3395da1..844b46f352 100644 --- a/api/core/workflow/entities/variable_pool.py +++ b/api/core/workflow/entities/variable_pool.py @@ -95,13 +95,16 @@ class VariablePool(BaseModel): if len(selector) < 2: raise ValueError("Invalid selector") + if isinstance(value, Variable): + variable = value if isinstance(value, Segment): - v = value + variable = variable_factory.segment_to_variable(segment=value, selector=selector) else: - v = variable_factory.build_segment(value) + segment = variable_factory.build_segment(value) + variable = variable_factory.segment_to_variable(segment=segment, selector=selector) hash_key = hash(tuple(selector[1:])) - self.variable_dictionary[selector[0]][hash_key] = v + self.variable_dictionary[selector[0]][hash_key] = variable def get(self, selector: Sequence[str], /) -> Segment | None: """ diff --git a/api/core/workflow/graph_engine/entities/event.py b/api/core/workflow/graph_engine/entities/event.py index bacea191dd..3736e632c3 100644 --- a/api/core/workflow/graph_engine/entities/event.py +++ b/api/core/workflow/graph_engine/entities/event.py @@ -148,6 +148,7 @@ class IterationRunStartedEvent(BaseIterationEvent): class IterationRunNextEvent(BaseIterationEvent): index: int = Field(..., description="index") pre_iteration_output: Optional[Any] = Field(None, description="pre iteration output") + duration: Optional[float] = Field(None, description="duration") class IterationRunSucceededEvent(BaseIterationEvent): @@ -156,6 +157,7 @@ class IterationRunSucceededEvent(BaseIterationEvent): outputs: Optional[dict[str, Any]] = None metadata: Optional[dict[str, Any]] = None steps: int = 0 + iteration_duration_map: Optional[dict[str, float]] = None class IterationRunFailedEvent(BaseIterationEvent): diff --git a/api/core/workflow/nodes/code/code_node.py b/api/core/workflow/nodes/code/code_node.py index de70af58dd..ce283e38ec 100644 --- a/api/core/workflow/nodes/code/code_node.py +++ b/api/core/workflow/nodes/code/code_node.py @@ -49,13 +49,7 @@ class CodeNode(BaseNode[CodeNodeData]): for variable_selector in self.node_data.variables: variable_name = variable_selector.variable variable = self.graph_runtime_state.variable_pool.get(variable_selector.value_selector) - if variable is None: - return NodeRunResult( - status=WorkflowNodeExecutionStatus.FAILED, - inputs=variables, - error=f"Variable `{variable_selector.value_selector}` not found", - ) - variables[variable_name] = variable.to_object() + variables[variable_name] = variable.to_object() if variable else None # Run code try: result = CodeExecutor.execute_workflow_code_template( diff --git a/api/core/workflow/nodes/document_extractor/node.py b/api/core/workflow/nodes/document_extractor/node.py index c90017d5e1..c3cacdab7f 100644 --- a/api/core/workflow/nodes/document_extractor/node.py +++ b/api/core/workflow/nodes/document_extractor/node.py @@ -143,14 +143,14 @@ def _extract_text_by_file_extension(*, file_content: bytes, file_extension: str) def _extract_text_from_plain_text(file_content: bytes) -> str: try: - return file_content.decode("utf-8") + return file_content.decode("utf-8", "ignore") except UnicodeDecodeError as e: raise TextExtractionError("Failed to decode plain text file") from e def _extract_text_from_json(file_content: bytes) -> str: try: - json_data = json.loads(file_content.decode("utf-8")) + json_data = json.loads(file_content.decode("utf-8", "ignore")) return json.dumps(json_data, indent=2, ensure_ascii=False) except (UnicodeDecodeError, json.JSONDecodeError) as e: raise TextExtractionError(f"Failed to decode or parse JSON file: {e}") from e @@ -159,7 +159,7 @@ def _extract_text_from_json(file_content: bytes) -> str: def _extract_text_from_yaml(file_content: bytes) -> str: """Extract the content from yaml file""" try: - yaml_data = yaml.safe_load_all(file_content.decode("utf-8")) + yaml_data = yaml.safe_load_all(file_content.decode("utf-8", "ignore")) return yaml.dump_all(yaml_data, allow_unicode=True, sort_keys=False) except (UnicodeDecodeError, yaml.YAMLError) as e: raise TextExtractionError(f"Failed to decode or parse YAML file: {e}") from e @@ -217,7 +217,7 @@ def _extract_text_from_file(file: File): def _extract_text_from_csv(file_content: bytes) -> str: try: - csv_file = io.StringIO(file_content.decode("utf-8")) + csv_file = io.StringIO(file_content.decode("utf-8", "ignore")) csv_reader = csv.reader(csv_file) rows = list(csv_reader) diff --git a/api/core/workflow/nodes/http_request/node.py b/api/core/workflow/nodes/http_request/node.py index 61c661e587..5b399bed63 100644 --- a/api/core/workflow/nodes/http_request/node.py +++ b/api/core/workflow/nodes/http_request/node.py @@ -13,6 +13,7 @@ from core.workflow.nodes.base import BaseNode from core.workflow.nodes.enums import NodeType from core.workflow.nodes.http_request.executor import Executor from core.workflow.utils import variable_template_parser +from factories import file_factory from models.workflow import WorkflowNodeExecutionStatus from .entities import ( @@ -161,16 +162,15 @@ class HttpRequestNode(BaseNode[HttpRequestNodeData]): mimetype=content_type, ) - files.append( - File( - tenant_id=self.tenant_id, - type=FileType.IMAGE, - transfer_method=FileTransferMethod.TOOL_FILE, - related_id=tool_file.id, - filename=filename, - extension=extension, - mime_type=content_type, - ) + mapping = { + "tool_file_id": tool_file.id, + "type": FileType.IMAGE.value, + "transfer_method": FileTransferMethod.TOOL_FILE.value, + } + file = file_factory.build_from_mapping( + mapping=mapping, + tenant_id=self.tenant_id, ) + files.append(file) return files diff --git a/api/core/workflow/nodes/iteration/iteration_node.py b/api/core/workflow/nodes/iteration/iteration_node.py index e1d2b88360..d5428f0286 100644 --- a/api/core/workflow/nodes/iteration/iteration_node.py +++ b/api/core/workflow/nodes/iteration/iteration_node.py @@ -156,7 +156,8 @@ class IterationNode(BaseNode[IterationNodeData]): index=0, pre_iteration_output=None, ) - outputs: list[Any] = [] + iter_run_map: dict[str, float] = {} + outputs: list[Any] = [None] * len(iterator_list_value) try: if self.node_data.is_parallel: futures: list[Future] = [] @@ -175,6 +176,7 @@ class IterationNode(BaseNode[IterationNodeData]): iteration_graph, index, item, + iter_run_map, ) future.add_done_callback(thread_pool.task_done_callback) futures.append(future) @@ -213,7 +215,10 @@ class IterationNode(BaseNode[IterationNodeData]): start_at, graph_engine, iteration_graph, + iter_run_map, ) + if self.node_data.error_handle_mode == ErrorHandleMode.REMOVE_ABNORMAL_OUTPUT: + outputs = [output for output in outputs if output is not None] yield IterationRunSucceededEvent( iteration_id=self.id, iteration_node_id=self.node_id, @@ -228,7 +233,9 @@ class IterationNode(BaseNode[IterationNodeData]): yield RunCompletedEvent( run_result=NodeRunResult( - status=WorkflowNodeExecutionStatus.SUCCEEDED, outputs={"output": jsonable_encoder(outputs)} + status=WorkflowNodeExecutionStatus.SUCCEEDED, + outputs={"output": jsonable_encoder(outputs)}, + metadata={NodeRunMetadataKey.ITERATION_DURATION_MAP: iter_run_map}, ) ) except IterationNodeError as e: @@ -354,15 +361,19 @@ class IterationNode(BaseNode[IterationNodeData]): start_at: datetime, graph_engine: "GraphEngine", iteration_graph: Graph, + iter_run_map: dict[str, float], parallel_mode_run_id: Optional[str] = None, ) -> Generator[NodeEvent | InNodeEvent, None, None]: """ run single iteration """ + iter_start_at = datetime.now(timezone.utc).replace(tzinfo=None) + try: rst = graph_engine.run() # get current iteration index current_index = variable_pool.get([self.node_id, "index"]).value + iteration_run_id = parallel_mode_run_id if parallel_mode_run_id is not None else f"{current_index}" next_index = int(current_index) + 1 if current_index is None: @@ -425,10 +436,12 @@ class IterationNode(BaseNode[IterationNodeData]): yield NodeInIterationFailedEvent( **metadata_event.model_dump(), ) - outputs.insert(current_index, None) + outputs[current_index] = None variable_pool.add([self.node_id, "index"], next_index) if next_index < len(iterator_list_value): variable_pool.add([self.node_id, "item"], iterator_list_value[next_index]) + duration = (datetime.now(timezone.utc).replace(tzinfo=None) - iter_start_at).total_seconds() + iter_run_map[iteration_run_id] = duration yield IterationRunNextEvent( iteration_id=self.id, iteration_node_id=self.node_id, @@ -437,6 +450,7 @@ class IterationNode(BaseNode[IterationNodeData]): index=next_index, parallel_mode_run_id=parallel_mode_run_id, pre_iteration_output=None, + duration=duration, ) return elif self.node_data.error_handle_mode == ErrorHandleMode.REMOVE_ABNORMAL_OUTPUT: @@ -447,6 +461,8 @@ class IterationNode(BaseNode[IterationNodeData]): if next_index < len(iterator_list_value): variable_pool.add([self.node_id, "item"], iterator_list_value[next_index]) + duration = (datetime.now(timezone.utc).replace(tzinfo=None) - iter_start_at).total_seconds() + iter_run_map[iteration_run_id] = duration yield IterationRunNextEvent( iteration_id=self.id, iteration_node_id=self.node_id, @@ -455,6 +471,7 @@ class IterationNode(BaseNode[IterationNodeData]): index=next_index, parallel_mode_run_id=parallel_mode_run_id, pre_iteration_output=None, + duration=duration, ) return elif self.node_data.error_handle_mode == ErrorHandleMode.TERMINATED: @@ -472,8 +489,11 @@ class IterationNode(BaseNode[IterationNodeData]): ) yield metadata_event - current_iteration_output = variable_pool.get(self.node_data.output_selector).value - outputs.insert(current_index, current_iteration_output) + current_output_segment = variable_pool.get(self.node_data.output_selector) + if current_output_segment is None: + raise IterationNodeError("iteration output selector not found") + current_iteration_output = current_output_segment.value + outputs[current_index] = current_iteration_output # remove all nodes outputs from variable pool for node_id in iteration_graph.node_ids: variable_pool.remove([node_id]) @@ -483,6 +503,8 @@ class IterationNode(BaseNode[IterationNodeData]): if next_index < len(iterator_list_value): variable_pool.add([self.node_id, "item"], iterator_list_value[next_index]) + duration = (datetime.now(timezone.utc).replace(tzinfo=None) - iter_start_at).total_seconds() + iter_run_map[iteration_run_id] = duration yield IterationRunNextEvent( iteration_id=self.id, iteration_node_id=self.node_id, @@ -491,6 +513,7 @@ class IterationNode(BaseNode[IterationNodeData]): index=next_index, parallel_mode_run_id=parallel_mode_run_id, pre_iteration_output=jsonable_encoder(current_iteration_output) if current_iteration_output else None, + duration=duration, ) except IterationNodeError as e: @@ -526,6 +549,7 @@ class IterationNode(BaseNode[IterationNodeData]): iteration_graph: Graph, index: int, item: Any, + iter_run_map: dict[str, float], ) -> Generator[NodeEvent | InNodeEvent, None, None]: """ run single iteration in parallel mode @@ -544,6 +568,7 @@ class IterationNode(BaseNode[IterationNodeData]): start_at=start_at, graph_engine=graph_engine_copy, iteration_graph=iteration_graph, + iter_run_map=iter_run_map, parallel_mode_run_id=parallel_mode_run_id, ): q.put(event) diff --git a/api/core/workflow/nodes/list_operator/entities.py b/api/core/workflow/nodes/list_operator/entities.py index 79cef1c27a..75df784a92 100644 --- a/api/core/workflow/nodes/list_operator/entities.py +++ b/api/core/workflow/nodes/list_operator/entities.py @@ -49,8 +49,14 @@ class Limit(BaseModel): size: int = -1 +class ExtractConfig(BaseModel): + enabled: bool = False + serial: str = "1" + + class ListOperatorNodeData(BaseNodeData): variable: Sequence[str] = Field(default_factory=list) filter_by: FilterBy order_by: OrderBy limit: Limit + extract_by: ExtractConfig = Field(default_factory=ExtractConfig) diff --git a/api/core/workflow/nodes/list_operator/node.py b/api/core/workflow/nodes/list_operator/node.py index 49e7ca85fd..79066cece4 100644 --- a/api/core/workflow/nodes/list_operator/node.py +++ b/api/core/workflow/nodes/list_operator/node.py @@ -58,6 +58,10 @@ class ListOperatorNode(BaseNode[ListOperatorNodeData]): if self.node_data.filter_by.enabled: variable = self._apply_filter(variable) + # Extract + if self.node_data.extract_by.enabled: + variable = self._extract_slice(variable) + # Order if self.node_data.order_by.enabled: variable = self._apply_order(variable) @@ -140,6 +144,16 @@ class ListOperatorNode(BaseNode[ListOperatorNodeData]): result = variable.value[: self.node_data.limit.size] return variable.model_copy(update={"value": result}) + def _extract_slice( + self, variable: Union[ArrayFileSegment, ArrayNumberSegment, ArrayStringSegment] + ) -> Union[ArrayFileSegment, ArrayNumberSegment, ArrayStringSegment]: + value = int(self.graph_runtime_state.variable_pool.convert_template(self.node_data.extract_by.serial).text) - 1 + if len(variable.value) > int(value): + result = variable.value[value] + else: + result = "" + return variable.model_copy(update={"value": [result]}) + def _get_file_extract_number_func(*, key: str) -> Callable[[File], int]: match key: diff --git a/api/core/workflow/nodes/template_transform/template_transform_node.py b/api/core/workflow/nodes/template_transform/template_transform_node.py index 0ee66784c5..22a1b21888 100644 --- a/api/core/workflow/nodes/template_transform/template_transform_node.py +++ b/api/core/workflow/nodes/template_transform/template_transform_node.py @@ -34,12 +34,7 @@ class TemplateTransformNode(BaseNode[TemplateTransformNodeData]): for variable_selector in self.node_data.variables: variable_name = variable_selector.variable value = self.graph_runtime_state.variable_pool.get(variable_selector.value_selector) - if value is None: - return NodeRunResult( - status=WorkflowNodeExecutionStatus.FAILED, - error=f"Variable {variable_name} not found in variable pool", - ) - variables[variable_name] = value.to_object() + variables[variable_name] = value.to_object() if value else None # Run code try: result = CodeExecutor.execute_workflow_code_template( diff --git a/api/core/workflow/nodes/tool/tool_node.py b/api/core/workflow/nodes/tool/tool_node.py index 42e870c46c..5560f26456 100644 --- a/api/core/workflow/nodes/tool/tool_node.py +++ b/api/core/workflow/nodes/tool/tool_node.py @@ -1,5 +1,4 @@ from collections.abc import Mapping, Sequence -from os import path from typing import Any from sqlalchemy import select @@ -17,6 +16,7 @@ from core.workflow.nodes.base import BaseNode from core.workflow.nodes.enums import NodeType from core.workflow.utils.variable_template_parser import VariableTemplateParser from extensions.ext_database import db +from factories import file_factory from models import ToolFile from models.workflow import WorkflowNodeExecutionStatus @@ -179,7 +179,6 @@ class ToolNode(BaseNode[ToolNodeData]): for response in tool_response: if response.type in {ToolInvokeMessage.MessageType.IMAGE_LINK, ToolInvokeMessage.MessageType.IMAGE}: 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] transfer_method = response.meta.get("transfer_method", FileTransferMethod.TOOL_FILE) @@ -189,39 +188,33 @@ class ToolNode(BaseNode[ToolNodeData]): if tool_file is None: raise ToolFileError(f"Tool file {tool_file_id} does not exist") - result.append( - File( - tenant_id=self.tenant_id, - type=FileType.IMAGE, - transfer_method=transfer_method, - remote_url=url, - related_id=tool_file.id, - filename=tool_file.name, - extension=ext, - mime_type=tool_file.mimetype, - size=tool_file.size, - ) + mapping = { + "tool_file_id": tool_file_id, + "type": FileType.IMAGE, + "transfer_method": transfer_method, + "url": url, + } + file = file_factory.build_from_mapping( + mapping=mapping, + tenant_id=self.tenant_id, ) + result.append(file) elif response.type == ToolInvokeMessage.MessageType.BLOB: - # get tool file id tool_file_id = str(response.message).split("/")[-1].split(".")[0] with Session(db.engine) as session: stmt = select(ToolFile).where(ToolFile.id == tool_file_id) tool_file = session.scalar(stmt) if tool_file is None: - raise ToolFileError(f"Tool file {tool_file_id} does not exist") - result.append( - File( - tenant_id=self.tenant_id, - type=FileType.IMAGE, - transfer_method=FileTransferMethod.TOOL_FILE, - related_id=tool_file.id, - filename=tool_file.name, - extension=path.splitext(response.save_as)[1], - mime_type=tool_file.mimetype, - size=tool_file.size, - ) + raise ValueError(f"tool file {tool_file_id} not exists") + mapping = { + "tool_file_id": tool_file_id, + "transfer_method": FileTransferMethod.TOOL_FILE, + } + file = file_factory.build_from_mapping( + mapping=mapping, + tenant_id=self.tenant_id, ) + result.append(file) elif response.type == ToolInvokeMessage.MessageType.LINK: url = str(response.message) transfer_method = FileTransferMethod.TOOL_FILE @@ -231,20 +224,14 @@ class ToolNode(BaseNode[ToolNodeData]): tool_file = session.scalar(stmt) if tool_file is None: raise ToolFileError(f"Tool file {tool_file_id} does not exist") - if "." in url: - extension = "." + url.split("/")[-1].split(".")[1] - else: - extension = ".bin" - file = File( + mapping = { + "tool_file_id": tool_file_id, + "transfer_method": transfer_method, + "url": url, + } + file = file_factory.build_from_mapping( + mapping=mapping, tenant_id=self.tenant_id, - type=FileType(response.save_as), - transfer_method=transfer_method, - remote_url=url, - filename=tool_file.name, - related_id=tool_file.id, - extension=extension, - mime_type=tool_file.mimetype, - size=tool_file.size, ) result.append(file) diff --git a/api/core/workflow/workflow_entry.py b/api/core/workflow/workflow_entry.py index eb812bad21..84b251223f 100644 --- a/api/core/workflow/workflow_entry.py +++ b/api/core/workflow/workflow_entry.py @@ -5,10 +5,10 @@ from collections.abc import Generator, Mapping, Sequence from typing import Any, Optional, cast from configs import dify_config -from core.app.app_config.entities import FileExtraConfig +from core.app.app_config.entities import FileUploadConfig from core.app.apps.base_app_queue_manager import GenerateTaskStoppedError from core.app.entities.app_invoke_entities import InvokeFrom -from core.file.models import File, FileTransferMethod, FileType, ImageConfig +from core.file.models import File, FileTransferMethod, ImageConfig from core.workflow.callbacks import WorkflowCallback from core.workflow.entities.variable_pool import VariablePool from core.workflow.errors import WorkflowNodeRunFailedError @@ -22,6 +22,7 @@ from core.workflow.nodes.base import BaseNode, BaseNodeData from core.workflow.nodes.event import NodeEvent from core.workflow.nodes.llm import LLMNodeData from core.workflow.nodes.node_mapping import node_type_classes_mapping +from factories import file_factory from models.enums import UserFrom from models.workflow import ( Workflow, @@ -271,19 +272,17 @@ class WorkflowEntry: for item in input_value: if isinstance(item, dict) and "type" in item and item["type"] == "image": transfer_method = FileTransferMethod.value_of(item.get("transfer_method")) - file = File( + mapping = { + "id": item.get("id"), + "transfer_method": transfer_method, + "upload_file_id": item.get("upload_file_id"), + "url": item.get("url"), + } + config = FileUploadConfig(image_config=ImageConfig(detail=detail) if detail else None) + file = file_factory.build_from_mapping( + mapping=mapping, tenant_id=tenant_id, - type=FileType.IMAGE, - transfer_method=transfer_method, - remote_url=item.get("url") - if transfer_method == FileTransferMethod.REMOTE_URL - else None, - related_id=item.get("upload_file_id") - if transfer_method == FileTransferMethod.LOCAL_FILE - else None, - _extra_config=FileExtraConfig( - image_config=ImageConfig(detail=detail) if detail else None - ), + config=config, ) new_value.append(file) diff --git a/api/docker/entrypoint.sh b/api/docker/entrypoint.sh index 1edc558676..2b6a8dd3d0 100755 --- a/api/docker/entrypoint.sh +++ b/api/docker/entrypoint.sh @@ -21,7 +21,7 @@ if [[ "${MODE}" == "worker" ]]; then fi exec celery -A app.celery worker -P ${CELERY_WORKER_CLASS:-gevent} $CONCURRENCY_OPTION --loglevel ${LOG_LEVEL} \ - -Q ${CELERY_QUEUES:-dataset,generation,mail,ops_trace,app_deletion} + -Q ${CELERY_QUEUES:-dataset,mail,ops_trace,app_deletion} elif [[ "${MODE}" == "beat" ]]; then exec celery -A app.celery beat --loglevel ${LOG_LEVEL} diff --git a/api/extensions/ext_celery.py b/api/extensions/ext_celery.py index 504899c276..42012eee8e 100644 --- a/api/extensions/ext_celery.py +++ b/api/extensions/ext_celery.py @@ -1,5 +1,6 @@ from datetime import timedelta +import pytz from celery import Celery, Task from celery.schedules import crontab from flask import Flask @@ -43,6 +44,10 @@ def init_app(app: Flask) -> Celery: result_backend=dify_config.CELERY_RESULT_BACKEND, broker_transport_options=broker_transport_options, broker_connection_retry_on_startup=True, + worker_log_format=dify_config.LOG_FORMAT, + worker_task_log_format=dify_config.LOG_FORMAT, + worker_hijack_root_logger=False, + timezone=pytz.timezone(dify_config.LOG_TZ), ) if dify_config.BROKER_USE_SSL: @@ -50,6 +55,11 @@ def init_app(app: Flask) -> Celery: broker_use_ssl=ssl_options, # Add the SSL options to the broker configuration ) + if dify_config.LOG_FILE: + celery_app.conf.update( + worker_logfile=dify_config.LOG_FILE, + ) + celery_app.set_default() app.extensions["celery"] = celery_app diff --git a/api/extensions/ext_logging.py b/api/extensions/ext_logging.py index 56b1d6bd28..a15c73bd71 100644 --- a/api/extensions/ext_logging.py +++ b/api/extensions/ext_logging.py @@ -9,19 +9,21 @@ from configs import dify_config def init_app(app: Flask): - log_handlers = None + log_handlers = [] log_file = dify_config.LOG_FILE if log_file: log_dir = os.path.dirname(log_file) os.makedirs(log_dir, exist_ok=True) - log_handlers = [ + log_handlers.append( RotatingFileHandler( filename=log_file, maxBytes=dify_config.LOG_FILE_MAX_SIZE * 1024 * 1024, backupCount=dify_config.LOG_FILE_BACKUP_COUNT, - ), - logging.StreamHandler(sys.stdout), - ] + ) + ) + + # Always add StreamHandler to log to console + log_handlers.append(logging.StreamHandler(sys.stdout)) logging.basicConfig( level=dify_config.LOG_LEVEL, diff --git a/api/factories/file_factory.py b/api/factories/file_factory.py index 1066dc8862..15e9d7f34f 100644 --- a/api/factories/file_factory.py +++ b/api/factories/file_factory.py @@ -1,23 +1,21 @@ import mimetypes -from collections.abc import Mapping, Sequence +from collections.abc import Callable, Mapping, Sequence from typing import Any import httpx from sqlalchemy import select -from constants import AUDIO_EXTENSIONS, DOCUMENT_EXTENSIONS, IMAGE_EXTENSIONS, VIDEO_EXTENSIONS -from core.file import File, FileBelongsTo, FileExtraConfig, FileTransferMethod, FileType +from core.file import File, FileBelongsTo, FileTransferMethod, FileType, FileUploadConfig from core.helper import ssrf_proxy from extensions.ext_database import db from models import MessageFile, ToolFile, UploadFile -from models.enums import CreatedByRole def build_from_message_files( *, message_files: Sequence["MessageFile"], tenant_id: str, - config: FileExtraConfig, + config: FileUploadConfig, ) -> Sequence[File]: results = [ build_from_message_file(message_file=file, tenant_id=tenant_id, config=config) @@ -31,7 +29,7 @@ def build_from_message_file( *, message_file: "MessageFile", tenant_id: str, - config: FileExtraConfig, + config: FileUploadConfig, ): mapping = { "transfer_method": message_file.transfer_method, @@ -43,8 +41,6 @@ def build_from_message_file( return build_from_mapping( mapping=mapping, tenant_id=tenant_id, - user_id=message_file.created_by, - role=CreatedByRole(message_file.created_by_role), config=config, ) @@ -53,38 +49,30 @@ def build_from_mapping( *, mapping: Mapping[str, Any], tenant_id: str, - user_id: str, - role: "CreatedByRole", - config: FileExtraConfig, -): + config: FileUploadConfig | None = None, +) -> File: + config = config or FileUploadConfig() + transfer_method = FileTransferMethod.value_of(mapping.get("transfer_method")) - match transfer_method: - case FileTransferMethod.REMOTE_URL: - file = _build_from_remote_url( - mapping=mapping, - tenant_id=tenant_id, - config=config, - transfer_method=transfer_method, - ) - case FileTransferMethod.LOCAL_FILE: - file = _build_from_local_file( - mapping=mapping, - tenant_id=tenant_id, - user_id=user_id, - role=role, - config=config, - transfer_method=transfer_method, - ) - case FileTransferMethod.TOOL_FILE: - file = _build_from_tool_file( - mapping=mapping, - tenant_id=tenant_id, - user_id=user_id, - config=config, - transfer_method=transfer_method, - ) - case _: - raise ValueError(f"Invalid file transfer method: {transfer_method}") + + build_functions: dict[FileTransferMethod, Callable] = { + FileTransferMethod.LOCAL_FILE: _build_from_local_file, + FileTransferMethod.REMOTE_URL: _build_from_remote_url, + FileTransferMethod.TOOL_FILE: _build_from_tool_file, + } + + build_func = build_functions.get(transfer_method) + if not build_func: + raise ValueError(f"Invalid file transfer method: {transfer_method}") + + file = build_func( + mapping=mapping, + tenant_id=tenant_id, + transfer_method=transfer_method, + ) + + if not _is_file_valid_with_config(file=file, config=config): + raise ValueError(f"File validation failed for file: {file.filename}") return file @@ -92,10 +80,8 @@ def build_from_mapping( def build_from_mappings( *, mappings: Sequence[Mapping[str, Any]], - config: FileExtraConfig | None, + config: FileUploadConfig | None, tenant_id: str, - user_id: str, - role: "CreatedByRole", ) -> Sequence[File]: if not config: return [] @@ -104,8 +90,6 @@ def build_from_mappings( build_from_mapping( mapping=mapping, tenant_id=tenant_id, - user_id=user_id, - role=role, config=config, ) for mapping in mappings @@ -128,31 +112,20 @@ def _build_from_local_file( *, mapping: Mapping[str, Any], tenant_id: str, - user_id: str, - role: "CreatedByRole", - config: FileExtraConfig, transfer_method: FileTransferMethod, -): - # check if the upload file exists. +) -> File: file_type = FileType.value_of(mapping.get("type")) stmt = select(UploadFile).where( UploadFile.id == mapping.get("upload_file_id"), UploadFile.tenant_id == tenant_id, - UploadFile.created_by == user_id, - UploadFile.created_by_role == role, ) - if file_type == FileType.IMAGE: - stmt = stmt.where(UploadFile.extension.in_(IMAGE_EXTENSIONS)) - elif file_type == FileType.VIDEO: - stmt = stmt.where(UploadFile.extension.in_(VIDEO_EXTENSIONS)) - elif file_type == FileType.AUDIO: - stmt = stmt.where(UploadFile.extension.in_(AUDIO_EXTENSIONS)) - elif file_type == FileType.DOCUMENT: - stmt = stmt.where(UploadFile.extension.in_(DOCUMENT_EXTENSIONS)) + row = db.session.scalar(stmt) + if row is None: raise ValueError("Invalid upload file") - file = File( + + return File( id=mapping.get("id"), filename=row.name, extension="." + row.extension, @@ -162,23 +135,37 @@ def _build_from_local_file( transfer_method=transfer_method, remote_url=row.source_url, related_id=mapping.get("upload_file_id"), - _extra_config=config, size=row.size, ) - return file def _build_from_remote_url( *, mapping: Mapping[str, Any], tenant_id: str, - config: FileExtraConfig, transfer_method: FileTransferMethod, -): +) -> File: url = mapping.get("url") if not url: raise ValueError("Invalid file url") + mime_type, filename, file_size = _get_remote_file_info(url) + extension = mimetypes.guess_extension(mime_type) or "." + filename.split(".")[-1] if "." in filename else ".bin" + + return File( + id=mapping.get("id"), + filename=filename, + tenant_id=tenant_id, + type=FileType.value_of(mapping.get("type")), + transfer_method=transfer_method, + remote_url=url, + mime_type=mime_type, + extension=extension, + size=file_size, + ) + + +def _get_remote_file_info(url: str): mime_type = mimetypes.guess_type(url)[0] or "" file_size = -1 filename = url.split("/")[-1].split("?")[0] or "unknown_file" @@ -186,66 +173,74 @@ def _build_from_remote_url( resp = ssrf_proxy.head(url, follow_redirects=True) if resp.status_code == httpx.codes.OK: if content_disposition := resp.headers.get("Content-Disposition"): - filename = content_disposition.split("filename=")[-1].strip('"') + filename = str(content_disposition.split("filename=")[-1].strip('"')) file_size = int(resp.headers.get("Content-Length", file_size)) mime_type = mime_type or str(resp.headers.get("Content-Type", "")) - # Determine file extension - extension = mimetypes.guess_extension(mime_type) or "." + filename.split(".")[-1] if "." in filename else ".bin" + return mime_type, filename, file_size - if not mime_type: - mime_type, _ = mimetypes.guess_type(url) - file = File( - id=mapping.get("id"), - filename=filename, - tenant_id=tenant_id, - type=FileType.value_of(mapping.get("type")), - transfer_method=transfer_method, - remote_url=url, - _extra_config=config, - mime_type=mime_type, - extension=extension, - size=file_size, - ) - return file + +def _get_file_type_by_mimetype(mime_type: str) -> FileType: + if "image" in mime_type: + file_type = FileType.IMAGE + elif "video" in mime_type: + file_type = FileType.VIDEO + elif "audio" in mime_type: + file_type = FileType.AUDIO + elif "text" in mime_type or "pdf" in mime_type: + file_type = FileType.DOCUMENT + else: + file_type = FileType.CUSTOM + return file_type def _build_from_tool_file( *, mapping: Mapping[str, Any], tenant_id: str, - user_id: str, - config: FileExtraConfig, transfer_method: FileTransferMethod, -): +) -> File: tool_file = ( db.session.query(ToolFile) .filter( ToolFile.id == mapping.get("tool_file_id"), ToolFile.tenant_id == tenant_id, - ToolFile.user_id == user_id, ) .first() ) + if tool_file is None: raise ValueError(f"ToolFile {mapping.get('tool_file_id')} not found") - path = tool_file.file_key - if "." in path: - extension = "." + path.split("/")[-1].split(".")[-1] - else: - extension = ".bin" - file = File( + extension = "." + tool_file.file_key.split(".")[-1] if "." in tool_file.file_key else ".bin" + file_type = mapping.get("type", _get_file_type_by_mimetype(tool_file.mimetype)) + + return File( id=mapping.get("id"), tenant_id=tenant_id, filename=tool_file.name, - type=FileType.value_of(mapping.get("type")), + type=file_type, transfer_method=transfer_method, remote_url=tool_file.original_url, related_id=tool_file.id, extension=extension, mime_type=tool_file.mimetype, size=tool_file.size, - _extra_config=config, ) - return file + + +def _is_file_valid_with_config(*, file: File, config: FileUploadConfig) -> bool: + if config.allowed_file_types and file.type not in config.allowed_file_types and file.type != FileType.CUSTOM: + return False + + if config.allowed_extensions and file.extension not in config.allowed_extensions: + return False + + if config.allowed_upload_methods and file.transfer_method not in config.allowed_upload_methods: + return False + + if file.type == FileType.IMAGE and config.image_config: + if config.image_config.transfer_methods and file.transfer_method not in config.image_config.transfer_methods: + return False + + return True diff --git a/api/factories/variable_factory.py b/api/factories/variable_factory.py index 0191102b90..5b004405b4 100644 --- a/api/factories/variable_factory.py +++ b/api/factories/variable_factory.py @@ -1,34 +1,65 @@ -from collections.abc import Mapping +from collections.abc import Mapping, Sequence from typing import Any +from uuid import uuid4 from configs import dify_config from core.file import File -from core.variables import ( +from core.variables.exc import VariableError +from core.variables.segments import ( ArrayAnySegment, ArrayFileSegment, ArrayNumberSegment, - ArrayNumberVariable, ArrayObjectSegment, - ArrayObjectVariable, ArraySegment, ArrayStringSegment, - ArrayStringVariable, FileSegment, FloatSegment, - FloatVariable, IntegerSegment, - IntegerVariable, NoneSegment, ObjectSegment, + Segment, + StringSegment, +) +from core.variables.types import SegmentType +from core.variables.variables import ( + ArrayAnyVariable, + ArrayFileVariable, + ArrayNumberVariable, + ArrayObjectVariable, + ArrayStringVariable, + FileVariable, + FloatVariable, + IntegerVariable, + NoneVariable, ObjectVariable, SecretVariable, - Segment, - SegmentType, - StringSegment, StringVariable, Variable, ) -from core.variables.exc import VariableError + + +class InvalidSelectorError(ValueError): + pass + + +class UnsupportedSegmentTypeError(Exception): + pass + + +# Define the constant +SEGMENT_TO_VARIABLE_MAP = { + StringSegment: StringVariable, + IntegerSegment: IntegerVariable, + FloatSegment: FloatVariable, + ObjectSegment: ObjectVariable, + FileSegment: FileVariable, + ArrayStringSegment: ArrayStringVariable, + ArrayNumberSegment: ArrayNumberVariable, + ArrayObjectSegment: ArrayObjectVariable, + ArrayFileSegment: ArrayFileVariable, + ArrayAnySegment: ArrayAnyVariable, + NoneSegment: NoneVariable, +} def build_variable_from_mapping(mapping: Mapping[str, Any], /) -> Variable: @@ -96,3 +127,30 @@ def build_segment(value: Any, /) -> Segment: case _: raise ValueError(f"not supported value {value}") raise ValueError(f"not supported value {value}") + + +def segment_to_variable( + *, + segment: Segment, + selector: Sequence[str], + id: str | None = None, + name: str | None = None, + description: str = "", +) -> Variable: + if isinstance(segment, Variable): + return segment + name = name or selector[-1] + id = id or str(uuid4()) + + segment_type = type(segment) + if segment_type not in SEGMENT_TO_VARIABLE_MAP: + raise UnsupportedSegmentTypeError(f"not supported segment type {segment_type}") + + variable_class = SEGMENT_TO_VARIABLE_MAP[segment_type] + return variable_class( + id=id, + name=name, + description=description, + value=segment.value, + selector=selector, + ) diff --git a/api/migrations/versions/2024_10_09_1329-d8e744d88ed6_fix_wrong_service_api_history.py b/api/migrations/versions/2024_10_09_1329-d8e744d88ed6_fix_wrong_service_api_history.py index b3b8dfa7d4..38a5cdf8e5 100644 --- a/api/migrations/versions/2024_10_09_1329-d8e744d88ed6_fix_wrong_service_api_history.py +++ b/api/migrations/versions/2024_10_09_1329-d8e744d88ed6_fix_wrong_service_api_history.py @@ -23,7 +23,7 @@ v0_9_0_release_date= '2024-09-29 12:00:00' def upgrade(): # ### commands auto generated by Alembic - please adjust! ### sql = f"""UPDATE - public.messages + messages SET parent_message_id = '{UUID_NIL}' WHERE @@ -37,7 +37,7 @@ WHERE def downgrade(): # ### commands auto generated by Alembic - please adjust! ### sql = f"""UPDATE - public.messages + messages SET parent_message_id = NULL WHERE diff --git a/api/models/model.py b/api/models/model.py index d049cd373d..e909d53e3e 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -13,7 +13,7 @@ from sqlalchemy import Float, func, text from sqlalchemy.orm import Mapped, mapped_column from configs import dify_config -from core.file import FILE_MODEL_IDENTITY, File, FileExtraConfig, FileTransferMethod, FileType +from core.file import FILE_MODEL_IDENTITY, File, FileTransferMethod, FileType from core.file import helpers as file_helpers from core.file.tool_file_parser import ToolFileParser from extensions.ext_database import db @@ -949,9 +949,6 @@ class Message(db.Model): "type": message_file.type, }, tenant_id=current_app.tenant_id, - user_id=self.from_account_id or self.from_end_user_id or "", - role=CreatedByRole(message_file.created_by_role), - config=FileExtraConfig(), ) elif message_file.transfer_method == "remote_url": if message_file.url is None: @@ -964,9 +961,6 @@ class Message(db.Model): "url": message_file.url, }, tenant_id=current_app.tenant_id, - user_id=self.from_account_id or self.from_end_user_id or "", - role=CreatedByRole(message_file.created_by_role), - config=FileExtraConfig(), ) elif message_file.transfer_method == "tool_file": if message_file.upload_file_id is None: @@ -981,9 +975,6 @@ class Message(db.Model): file = file_factory.build_from_mapping( mapping=mapping, tenant_id=current_app.tenant_id, - user_id=self.from_account_id or self.from_end_user_id or "", - role=CreatedByRole(message_file.created_by_role), - config=FileExtraConfig(), ) else: raise ValueError( diff --git a/api/poetry.lock b/api/poetry.lock index 6cd5e24dec..74c2ef5dc6 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -125,13 +125,13 @@ speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] [[package]] name = "aiohttp-retry" -version = "2.8.3" +version = "2.9.0" description = "Simple retry client for aiohttp" optional = false python-versions = ">=3.7" files = [ - {file = "aiohttp_retry-2.8.3-py3-none-any.whl", hash = "sha256:3aeeead8f6afe48272db93ced9440cf4eda8b6fd7ee2abb25357b7eb28525b45"}, - {file = "aiohttp_retry-2.8.3.tar.gz", hash = "sha256:9a8e637e31682ad36e1ff9f8bcba912fcfc7d7041722bc901a4b948da4d71ea9"}, + {file = "aiohttp_retry-2.9.0-py3-none-any.whl", hash = "sha256:7661af92471e9a96c69d9b8f32021360272073397e6a15bc44c1726b12f46056"}, + {file = "aiohttp_retry-2.9.0.tar.gz", hash = "sha256:92c47f1580040208bac95d9a8389a87227ef22758530f2e3f4683395e42c41b5"}, ] [package.dependencies] @@ -172,12 +172,12 @@ tz = ["backports.zoneinfo"] [[package]] name = "alibabacloud-credentials" -version = "0.3.5" +version = "0.3.6" description = "The alibabacloud credentials module of alibabaCloud Python SDK." optional = false python-versions = ">=3.6" files = [ - {file = "alibabacloud_credentials-0.3.5.tar.gz", hash = "sha256:ad065ec95921eaf51939195485d0e5cc9e0ea050282059c7d8bf74bdb5496177"}, + {file = "alibabacloud_credentials-0.3.6.tar.gz", hash = "sha256:caa82cf258648dcbe1ca14aeba50ba21845567d6ac3cd48d318e0a445fff7f96"}, ] [package.dependencies] @@ -847,13 +847,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.47" +version = "1.35.52" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.47-py3-none-any.whl", hash = "sha256:05f4493119a96799ff84d43e78691efac3177e1aec8840cca99511de940e342a"}, - {file = "botocore-1.35.47.tar.gz", hash = "sha256:f8f703463d3cd8b6abe2bedc443a7ab29f0e2ff1588a2e83164b108748645547"}, + {file = "botocore-1.35.52-py3-none-any.whl", hash = "sha256:cdbb5e43c9c3a977763e2a10d3b8b9c405d51279f9fcfd4ca4800763b22acba5"}, + {file = "botocore-1.35.52.tar.gz", hash = "sha256:1fe7485ea13d638b089103addd818c12984ff1e4d208de15f180b1e25ad944c5"}, ] [package.dependencies] @@ -932,6 +932,10 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -944,8 +948,14 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -956,8 +966,24 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, + {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, + {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -967,6 +993,10 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -978,6 +1008,10 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -990,6 +1024,10 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -1002,6 +1040,10 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -1098,13 +1140,13 @@ files = [ [[package]] name = "celery" -version = "5.3.6" +version = "5.4.0" description = "Distributed Task Queue." optional = false python-versions = ">=3.8" files = [ - {file = "celery-5.3.6-py3-none-any.whl", hash = "sha256:9da4ea0118d232ce97dff5ed4974587fb1c0ff5c10042eb15278487cdd27d1af"}, - {file = "celery-5.3.6.tar.gz", hash = "sha256:870cc71d737c0200c397290d730344cc991d13a057534353d124c9380267aab9"}, + {file = "celery-5.4.0-py3-none-any.whl", hash = "sha256:369631eb580cf8c51a82721ec538684994f8277637edde2dfc0dacd73ed97f64"}, + {file = "celery-5.4.0.tar.gz", hash = "sha256:504a19140e8d3029d5acad88330c541d4c3f64c789d85f94756762d8bca7e706"}, ] [package.dependencies] @@ -1120,7 +1162,7 @@ vine = ">=5.1.0,<6.0" [package.extras] arangodb = ["pyArango (>=2.0.2)"] -auth = ["cryptography (==41.0.5)"] +auth = ["cryptography (==42.0.5)"] azureblockblob = ["azure-storage-blob (>=12.15.0)"] brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] cassandra = ["cassandra-driver (>=3.25.0,<4)"] @@ -1130,22 +1172,23 @@ couchbase = ["couchbase (>=3.0.0)"] couchdb = ["pycouchdb (==1.14.2)"] django = ["Django (>=2.2.28)"] dynamodb = ["boto3 (>=1.26.143)"] -elasticsearch = ["elastic-transport (<=8.10.0)", "elasticsearch (<=8.11.0)"] +elasticsearch = ["elastic-transport (<=8.13.0)", "elasticsearch (<=8.13.0)"] eventlet = ["eventlet (>=0.32.0)"] +gcs = ["google-cloud-storage (>=2.10.0)"] gevent = ["gevent (>=1.5.0)"] librabbitmq = ["librabbitmq (>=2.0.0)"] memcache = ["pylibmc (==1.6.3)"] mongodb = ["pymongo[srv] (>=4.0.2)"] -msgpack = ["msgpack (==1.0.7)"] -pymemcache = ["python-memcached (==1.59)"] +msgpack = ["msgpack (==1.0.8)"] +pymemcache = ["python-memcached (>=1.61)"] pyro = ["pyro4 (==4.82)"] -pytest = ["pytest-celery (==0.0.0)"] +pytest = ["pytest-celery[all] (>=1.0.0)"] redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] s3 = ["boto3 (>=1.26.143)"] slmq = ["softlayer-messaging (>=1.0.3)"] solar = ["ephem (==4.1.5)"] sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] -sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.0)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] +sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.4)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] yaml = ["PyYAML (>=3.10)"] zookeeper = ["kazoo (>=1.3.1)"] @@ -2257,18 +2300,18 @@ files = [ [[package]] name = "duckduckgo-search" -version = "6.3.2" +version = "6.3.3" description = "Search for words, documents, images, news, maps and text translation using the DuckDuckGo.com search engine." optional = false python-versions = ">=3.8" files = [ - {file = "duckduckgo_search-6.3.2-py3-none-any.whl", hash = "sha256:cd631275292460d590d1d496995d002bf2fe6db9752713fab17b9e95924ced98"}, - {file = "duckduckgo_search-6.3.2.tar.gz", hash = "sha256:53dbf45f8749bfc67483eb9f281f2e722a5fe644d61c54ed9e551d26cb6bcbf2"}, + {file = "duckduckgo_search-6.3.3-py3-none-any.whl", hash = "sha256:63e5d6b958bd532016bc8a53e8b18717751bf7ef51b1c83e59b9f5780c79e64c"}, + {file = "duckduckgo_search-6.3.3.tar.gz", hash = "sha256:4d49508f01f85c8675765fdd4cc25eedbb3450e129b35209897fded874f6568f"}, ] [package.dependencies] click = ">=8.1.7" -primp = ">=0.6.4" +primp = ">=0.6.5" [package.extras] dev = ["mypy (>=1.11.1)", "pytest (>=8.3.1)", "pytest-asyncio (>=0.23.8)", "ruff (>=0.6.1)"] @@ -2373,13 +2416,13 @@ pycryptodome = ">=3.10.1" [[package]] name = "et-xmlfile" -version = "1.1.0" +version = "2.0.0" description = "An implementation of lxml.xmlfile for the standard library" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"}, - {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, + {file = "et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa"}, + {file = "et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54"}, ] [[package]] @@ -2410,15 +2453,35 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "fal-client" +version = "0.5.6" +description = "Python client for fal.ai" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fal_client-0.5.6-py3-none-any.whl", hash = "sha256:631fd857a3c44753ee46a2eea1e7276471453aca58faac9c3702f744c7c84050"}, + {file = "fal_client-0.5.6.tar.gz", hash = "sha256:d3afc4b6250023d0ee8437ec504558231d3b106d7aabc12cda8c39883faddecb"}, +] + +[package.dependencies] +httpx = ">=0.21.0,<1" +httpx-sse = ">=0.4.0,<0.5" + +[package.extras] +dev = ["fal-client[docs,test]"] +docs = ["sphinx", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] +test = ["pillow", "pytest", "pytest-asyncio"] + [[package]] name = "fastapi" -version = "0.115.3" +version = "0.115.4" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.3-py3-none-any.whl", hash = "sha256:8035e8f9a2b0aa89cea03b6c77721178ed5358e1aea4cd8570d9466895c0638c"}, - {file = "fastapi-0.115.3.tar.gz", hash = "sha256:c091c6a35599c036d676fa24bd4a6e19fa30058d93d950216cdc672881f6f7db"}, + {file = "fastapi-0.115.4-py3-none-any.whl", hash = "sha256:0b504a063ffb3cf96a5e27dc1bc32c80ca743a2528574f9cdc77daa2d31b4742"}, + {file = "fastapi-0.115.4.tar.gz", hash = "sha256:db653475586b091cb8b2fec2ac54a680ac6a158e07406e1abae31679e8826349"}, ] [package.dependencies] @@ -3336,13 +3399,13 @@ grpc = ["grpcio (>=1.38.0,<2.0dev)", "grpcio-status (>=1.38.0,<2.0.dev0)"] [[package]] name = "google-cloud-resource-manager" -version = "1.12.5" +version = "1.13.0" description = "Google Cloud Resource Manager API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_resource_manager-1.12.5-py2.py3-none-any.whl", hash = "sha256:2708a718b45c79464b7b21559c701b5c92e6b0b1ab2146d0a256277a623dc175"}, - {file = "google_cloud_resource_manager-1.12.5.tar.gz", hash = "sha256:b7af4254401ed4efa3aba3a929cb3ddb803fa6baf91a78485e45583597de5891"}, + {file = "google_cloud_resource_manager-1.13.0-py2.py3-none-any.whl", hash = "sha256:33beb4528c2b7aee7a97ed843710581a7b4a27f3dd1fa41a0bf3359b3d68853f"}, + {file = "google_cloud_resource_manager-1.13.0.tar.gz", hash = "sha256:ae4bf69443f14b37007d4d84150115b0942e8b01650fd7a1fc6ff4dc1760e5c4"}, ] [package.dependencies] @@ -3606,70 +3669,70 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4 [[package]] name = "grpcio" -version = "1.67.0" +version = "1.67.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.67.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:bd79929b3bb96b54df1296cd3bf4d2b770bd1df6c2bdf549b49bab286b925cdc"}, - {file = "grpcio-1.67.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:16724ffc956ea42967f5758c2f043faef43cb7e48a51948ab593570570d1e68b"}, - {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:2b7183c80b602b0ad816315d66f2fb7887614ead950416d60913a9a71c12560d"}, - {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:efe32b45dd6d118f5ea2e5deaed417d8a14976325c93812dd831908522b402c9"}, - {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe89295219b9c9e47780a0f1c75ca44211e706d1c598242249fe717af3385ec8"}, - {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa8d025fae1595a207b4e47c2e087cb88d47008494db258ac561c00877d4c8f8"}, - {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f95e15db43e75a534420e04822df91f645664bf4ad21dfaad7d51773c80e6bb4"}, - {file = "grpcio-1.67.0-cp310-cp310-win32.whl", hash = "sha256:a6b9a5c18863fd4b6624a42e2712103fb0f57799a3b29651c0e5b8119a519d65"}, - {file = "grpcio-1.67.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6eb68493a05d38b426604e1dc93bfc0137c4157f7ab4fac5771fd9a104bbaa6"}, - {file = "grpcio-1.67.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:e91d154689639932305b6ea6f45c6e46bb51ecc8ea77c10ef25aa77f75443ad4"}, - {file = "grpcio-1.67.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cb204a742997277da678611a809a8409657b1398aaeebf73b3d9563b7d154c13"}, - {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:ae6de510f670137e755eb2a74b04d1041e7210af2444103c8c95f193340d17ee"}, - {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74b900566bdf68241118f2918d312d3bf554b2ce0b12b90178091ea7d0a17b3d"}, - {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4e95e43447a02aa603abcc6b5e727d093d161a869c83b073f50b9390ecf0fa8"}, - {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0bb94e66cd8f0baf29bd3184b6aa09aeb1a660f9ec3d85da615c5003154bc2bf"}, - {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:82e5bd4b67b17c8c597273663794a6a46a45e44165b960517fe6d8a2f7f16d23"}, - {file = "grpcio-1.67.0-cp311-cp311-win32.whl", hash = "sha256:7fc1d2b9fd549264ae585026b266ac2db53735510a207381be509c315b4af4e8"}, - {file = "grpcio-1.67.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac11ecb34a86b831239cc38245403a8de25037b448464f95c3315819e7519772"}, - {file = "grpcio-1.67.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:227316b5631260e0bef8a3ce04fa7db4cc81756fea1258b007950b6efc90c05d"}, - {file = "grpcio-1.67.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d90cfdafcf4b45a7a076e3e2a58e7bc3d59c698c4f6470b0bb13a4d869cf2273"}, - {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:77196216d5dd6f99af1c51e235af2dd339159f657280e65ce7e12c1a8feffd1d"}, - {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15c05a26a0f7047f720da41dc49406b395c1470eef44ff7e2c506a47ac2c0591"}, - {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3840994689cc8cbb73d60485c594424ad8adb56c71a30d8948d6453083624b52"}, - {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5a1e03c3102b6451028d5dc9f8591131d6ab3c8a0e023d94c28cb930ed4b5f81"}, - {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:682968427a63d898759474e3b3178d42546e878fdce034fd7474ef75143b64e3"}, - {file = "grpcio-1.67.0-cp312-cp312-win32.whl", hash = "sha256:d01793653248f49cf47e5695e0a79805b1d9d4eacef85b310118ba1dfcd1b955"}, - {file = "grpcio-1.67.0-cp312-cp312-win_amd64.whl", hash = "sha256:985b2686f786f3e20326c4367eebdaed3e7aa65848260ff0c6644f817042cb15"}, - {file = "grpcio-1.67.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:8c9a35b8bc50db35ab8e3e02a4f2a35cfba46c8705c3911c34ce343bd777813a"}, - {file = "grpcio-1.67.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:42199e704095b62688998c2d84c89e59a26a7d5d32eed86d43dc90e7a3bd04aa"}, - {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c4c425f440fb81f8d0237c07b9322fc0fb6ee2b29fbef5f62a322ff8fcce240d"}, - {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:323741b6699cd2b04a71cb38f502db98f90532e8a40cb675393d248126a268af"}, - {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:662c8e105c5e5cee0317d500eb186ed7a93229586e431c1bf0c9236c2407352c"}, - {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f6bd2ab135c64a4d1e9e44679a616c9bc944547357c830fafea5c3caa3de5153"}, - {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:2f55c1e0e2ae9bdd23b3c63459ee4c06d223b68aeb1961d83c48fb63dc29bc03"}, - {file = "grpcio-1.67.0-cp313-cp313-win32.whl", hash = "sha256:fd6bc27861e460fe28e94226e3673d46e294ca4673d46b224428d197c5935e69"}, - {file = "grpcio-1.67.0-cp313-cp313-win_amd64.whl", hash = "sha256:cf51d28063338608cd8d3cd64677e922134837902b70ce00dad7f116e3998210"}, - {file = "grpcio-1.67.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:7f200aca719c1c5dc72ab68be3479b9dafccdf03df530d137632c534bb6f1ee3"}, - {file = "grpcio-1.67.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0892dd200ece4822d72dd0952f7112c542a487fc48fe77568deaaa399c1e717d"}, - {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f4d613fbf868b2e2444f490d18af472ccb47660ea3df52f068c9c8801e1f3e85"}, - {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c69bf11894cad9da00047f46584d5758d6ebc9b5950c0dc96fec7e0bce5cde9"}, - {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9bca3ca0c5e74dea44bf57d27e15a3a3996ce7e5780d61b7c72386356d231db"}, - {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:014dfc020e28a0d9be7e93a91f85ff9f4a87158b7df9952fe23cc42d29d31e1e"}, - {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d4ea4509d42c6797539e9ec7496c15473177ce9abc89bc5c71e7abe50fc25737"}, - {file = "grpcio-1.67.0-cp38-cp38-win32.whl", hash = "sha256:9d75641a2fca9ae1ae86454fd25d4c298ea8cc195dbc962852234d54a07060ad"}, - {file = "grpcio-1.67.0-cp38-cp38-win_amd64.whl", hash = "sha256:cff8e54d6a463883cda2fab94d2062aad2f5edd7f06ae3ed030f2a74756db365"}, - {file = "grpcio-1.67.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:62492bd534979e6d7127b8a6b29093161a742dee3875873e01964049d5250a74"}, - {file = "grpcio-1.67.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eef1dce9d1a46119fd09f9a992cf6ab9d9178b696382439446ca5f399d7b96fe"}, - {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f623c57a5321461c84498a99dddf9d13dac0e40ee056d884d6ec4ebcab647a78"}, - {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54d16383044e681f8beb50f905249e4e7261dd169d4aaf6e52eab67b01cbbbe2"}, - {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2a44e572fb762c668e4812156b81835f7aba8a721b027e2d4bb29fb50ff4d33"}, - {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:391df8b0faac84d42f5b8dfc65f5152c48ed914e13c522fd05f2aca211f8bfad"}, - {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfd9306511fdfc623a1ba1dc3bc07fbd24e6cfbe3c28b4d1e05177baa2f99617"}, - {file = "grpcio-1.67.0-cp39-cp39-win32.whl", hash = "sha256:30d47dbacfd20cbd0c8be9bfa52fdb833b395d4ec32fe5cff7220afc05d08571"}, - {file = "grpcio-1.67.0-cp39-cp39-win_amd64.whl", hash = "sha256:f55f077685f61f0fbd06ea355142b71e47e4a26d2d678b3ba27248abfe67163a"}, - {file = "grpcio-1.67.0.tar.gz", hash = "sha256:e090b2553e0da1c875449c8e75073dd4415dd71c9bde6a406240fdf4c0ee467c"}, + {file = "grpcio-1.67.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:8b0341d66a57f8a3119b77ab32207072be60c9bf79760fa609c5609f2deb1f3f"}, + {file = "grpcio-1.67.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:f5a27dddefe0e2357d3e617b9079b4bfdc91341a91565111a21ed6ebbc51b22d"}, + {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:43112046864317498a33bdc4797ae6a268c36345a910de9b9c17159d8346602f"}, + {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9b929f13677b10f63124c1a410994a401cdd85214ad83ab67cc077fc7e480f0"}, + {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7d1797a8a3845437d327145959a2c0c47c05947c9eef5ff1a4c80e499dcc6fa"}, + {file = "grpcio-1.67.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0489063974d1452436139501bf6b180f63d4977223ee87488fe36858c5725292"}, + {file = "grpcio-1.67.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9fd042de4a82e3e7aca44008ee2fb5da01b3e5adb316348c21980f7f58adc311"}, + {file = "grpcio-1.67.1-cp310-cp310-win32.whl", hash = "sha256:638354e698fd0c6c76b04540a850bf1db27b4d2515a19fcd5cf645c48d3eb1ed"}, + {file = "grpcio-1.67.1-cp310-cp310-win_amd64.whl", hash = "sha256:608d87d1bdabf9e2868b12338cd38a79969eaf920c89d698ead08f48de9c0f9e"}, + {file = "grpcio-1.67.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:7818c0454027ae3384235a65210bbf5464bd715450e30a3d40385453a85a70cb"}, + {file = "grpcio-1.67.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ea33986b70f83844cd00814cee4451055cd8cab36f00ac64a31f5bb09b31919e"}, + {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c7a01337407dd89005527623a4a72c5c8e2894d22bead0895306b23c6695698f"}, + {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b866f73224b0634f4312a4674c1be21b2b4afa73cb20953cbbb73a6b36c3cc"}, + {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fff78ba10d4250bfc07a01bd6254a6d87dc67f9627adece85c0b2ed754fa96"}, + {file = "grpcio-1.67.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8a23cbcc5bb11ea7dc6163078be36c065db68d915c24f5faa4f872c573bb400f"}, + {file = "grpcio-1.67.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1a65b503d008f066e994f34f456e0647e5ceb34cfcec5ad180b1b44020ad4970"}, + {file = "grpcio-1.67.1-cp311-cp311-win32.whl", hash = "sha256:e29ca27bec8e163dca0c98084040edec3bc49afd10f18b412f483cc68c712744"}, + {file = "grpcio-1.67.1-cp311-cp311-win_amd64.whl", hash = "sha256:786a5b18544622bfb1e25cc08402bd44ea83edfb04b93798d85dca4d1a0b5be5"}, + {file = "grpcio-1.67.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:267d1745894200e4c604958da5f856da6293f063327cb049a51fe67348e4f953"}, + {file = "grpcio-1.67.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:85f69fdc1d28ce7cff8de3f9c67db2b0ca9ba4449644488c1e0303c146135ddb"}, + {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f26b0b547eb8d00e195274cdfc63ce64c8fc2d3e2d00b12bf468ece41a0423a0"}, + {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4422581cdc628f77302270ff839a44f4c24fdc57887dc2a45b7e53d8fc2376af"}, + {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d7616d2ded471231c701489190379e0c311ee0a6c756f3c03e6a62b95a7146e"}, + {file = "grpcio-1.67.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8a00efecde9d6fcc3ab00c13f816313c040a28450e5e25739c24f432fc6d3c75"}, + {file = "grpcio-1.67.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:699e964923b70f3101393710793289e42845791ea07565654ada0969522d0a38"}, + {file = "grpcio-1.67.1-cp312-cp312-win32.whl", hash = "sha256:4e7b904484a634a0fff132958dabdb10d63e0927398273917da3ee103e8d1f78"}, + {file = "grpcio-1.67.1-cp312-cp312-win_amd64.whl", hash = "sha256:5721e66a594a6c4204458004852719b38f3d5522082be9061d6510b455c90afc"}, + {file = "grpcio-1.67.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:aa0162e56fd10a5547fac8774c4899fc3e18c1aa4a4759d0ce2cd00d3696ea6b"}, + {file = "grpcio-1.67.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:beee96c8c0b1a75d556fe57b92b58b4347c77a65781ee2ac749d550f2a365dc1"}, + {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:a93deda571a1bf94ec1f6fcda2872dad3ae538700d94dc283c672a3b508ba3af"}, + {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6f255980afef598a9e64a24efce87b625e3e3c80a45162d111a461a9f92955"}, + {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e838cad2176ebd5d4a8bb03955138d6589ce9e2ce5d51c3ada34396dbd2dba8"}, + {file = "grpcio-1.67.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a6703916c43b1d468d0756c8077b12017a9fcb6a1ef13faf49e67d20d7ebda62"}, + {file = "grpcio-1.67.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:917e8d8994eed1d86b907ba2a61b9f0aef27a2155bca6cbb322430fc7135b7bb"}, + {file = "grpcio-1.67.1-cp313-cp313-win32.whl", hash = "sha256:e279330bef1744040db8fc432becc8a727b84f456ab62b744d3fdb83f327e121"}, + {file = "grpcio-1.67.1-cp313-cp313-win_amd64.whl", hash = "sha256:fa0c739ad8b1996bd24823950e3cb5152ae91fca1c09cc791190bf1627ffefba"}, + {file = "grpcio-1.67.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:178f5db771c4f9a9facb2ab37a434c46cb9be1a75e820f187ee3d1e7805c4f65"}, + {file = "grpcio-1.67.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f3e49c738396e93b7ba9016e153eb09e0778e776df6090c1b8c91877cc1c426"}, + {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:24e8a26dbfc5274d7474c27759b54486b8de23c709d76695237515bc8b5baeab"}, + {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b6c16489326d79ead41689c4b84bc40d522c9a7617219f4ad94bc7f448c5085"}, + {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e6a4dcf5af7bbc36fd9f81c9f372e8ae580870a9e4b6eafe948cd334b81cf3"}, + {file = "grpcio-1.67.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:95b5f2b857856ed78d72da93cd7d09b6db8ef30102e5e7fe0961fe4d9f7d48e8"}, + {file = "grpcio-1.67.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b49359977c6ec9f5d0573ea4e0071ad278ef905aa74e420acc73fd28ce39e9ce"}, + {file = "grpcio-1.67.1-cp38-cp38-win32.whl", hash = "sha256:f5b76ff64aaac53fede0cc93abf57894ab2a7362986ba22243d06218b93efe46"}, + {file = "grpcio-1.67.1-cp38-cp38-win_amd64.whl", hash = "sha256:804c6457c3cd3ec04fe6006c739579b8d35c86ae3298ffca8de57b493524b771"}, + {file = "grpcio-1.67.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:a25bdea92b13ff4d7790962190bf6bf5c4639876e01c0f3dda70fc2769616335"}, + {file = "grpcio-1.67.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cdc491ae35a13535fd9196acb5afe1af37c8237df2e54427be3eecda3653127e"}, + {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:85f862069b86a305497e74d0dc43c02de3d1d184fc2c180993aa8aa86fbd19b8"}, + {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec74ef02010186185de82cc594058a3ccd8d86821842bbac9873fd4a2cf8be8d"}, + {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01f616a964e540638af5130469451cf580ba8c7329f45ca998ab66e0c7dcdb04"}, + {file = "grpcio-1.67.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:299b3d8c4f790c6bcca485f9963b4846dd92cf6f1b65d3697145d005c80f9fe8"}, + {file = "grpcio-1.67.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:60336bff760fbb47d7e86165408126f1dded184448e9a4c892189eb7c9d3f90f"}, + {file = "grpcio-1.67.1-cp39-cp39-win32.whl", hash = "sha256:5ed601c4c6008429e3d247ddb367fe8c7259c355757448d7c1ef7bd4a6739e8e"}, + {file = "grpcio-1.67.1-cp39-cp39-win_amd64.whl", hash = "sha256:5db70d32d6703b89912af16d6d45d78406374a8b8ef0d28140351dd0ec610e98"}, + {file = "grpcio-1.67.1.tar.gz", hash = "sha256:3dc2ed4cabea4dc14d5e708c2b426205956077cc5de419b4d4079315017e9732"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.67.0)"] +protobuf = ["grpcio-tools (>=1.67.1)"] [[package]] name = "grpcio-status" @@ -4048,6 +4111,17 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "httpx-sse" +version = "0.4.0" +description = "Consume Server-Sent Event (SSE) messages with HTTPX." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721"}, + {file = "httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f"}, +] + [[package]] name = "huggingface-hub" version = "0.16.4" @@ -4644,13 +4718,13 @@ openai = ["openai (>=0.27.8)"] [[package]] name = "langsmith" -version = "0.1.137" +version = "0.1.138" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.137-py3-none-any.whl", hash = "sha256:4256d5c61133749890f7b5c88321dbb133ce0f440c621ea28e76513285859b81"}, - {file = "langsmith-0.1.137.tar.gz", hash = "sha256:56cdfcc6c74cb20a3f437d5bd144feb5bf93f54c5a2918d1e568cbd084a372d4"}, + {file = "langsmith-0.1.138-py3-none-any.whl", hash = "sha256:5c2bd5c11c75f7b3d06a0f06b115186e7326ca969fd26d66ffc65a0669012aee"}, + {file = "langsmith-0.1.138.tar.gz", hash = "sha256:1ecf613bb52f6bf17f1510e24ad8b70d4b0259bc9d3dbfd69b648c66d4644f0b"}, ] [package.dependencies] @@ -5611,12 +5685,12 @@ twitter = ["twython"] [[package]] name = "nomic" -version = "3.1.2" +version = "3.1.3" description = "The official Nomic python client." optional = false python-versions = "*" files = [ - {file = "nomic-3.1.2.tar.gz", hash = "sha256:2de1ab1dcf2429011c92987bb2f1eafe1a3a4901c3185b18f994bf89616f606d"}, + {file = "nomic-3.1.3.tar.gz", hash = "sha256:b06744b79fbe47451874ca7b272cafa1bb272cfb82acc79c64abfc943a98e035"}, ] [package.dependencies] @@ -5636,7 +5710,7 @@ tqdm = "*" [package.extras] all = ["nomic[aws,local]"] aws = ["boto3", "sagemaker"] -dev = ["black (==24.3.0)", "cairosvg", "coverage", "isort", "mkautodoc", "mkdocs-jupyter", "mkdocs-material", "mkdocstrings[python]", "myst-parser", "nomic[all]", "pandas", "pillow", "pylint", "pyright", "pytest", "pytorch-lightning", "twine"] +dev = ["black (==24.3.0)", "cairosvg", "coverage", "isort", "mkautodoc", "mkdocs-jupyter", "mkdocs-material", "mkdocstrings[python]", "myst-parser", "nomic[all]", "pandas", "pillow", "pylint", "pyright (<=1.1.377)", "pytest", "pytorch-lightning", "twine"] local = ["gpt4all (>=2.5.0,<3)"] [[package]] @@ -6705,19 +6779,19 @@ dill = ["dill (>=0.3.9)"] [[package]] name = "primp" -version = "0.6.4" +version = "0.6.5" description = "HTTP client that can impersonate web browsers, mimicking their headers and `TLS/JA3/JA4/HTTP2` fingerprints" optional = false python-versions = ">=3.8" files = [ - {file = "primp-0.6.4-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e627330c1f2b723b523dc2e47caacbc5b5d0cd51ca11583b42fb8cde4da60d7d"}, - {file = "primp-0.6.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:e0cb7c05dd56c8b9741042fd568c0983fc19b0f3aa209a3940ecc04b4fd60314"}, - {file = "primp-0.6.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4adc200ccb39e130c478d8b1a94f43a5b359068c6cb65b7c848812f96d96992"}, - {file = "primp-0.6.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:0ebae2d3aa36b04028e4accf2609d31d2e6981659e8e2effb09ee8ba960192e1"}, - {file = "primp-0.6.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:77f5fa5b34eaf251815622258419a484a2a9179dcbae2a1e702a254d91f613f1"}, - {file = "primp-0.6.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:14cddf535cd2c4987412e90ca3ca35ae52cddbee6e0f0953d26b33a652a95692"}, - {file = "primp-0.6.4-cp38-abi3-win_amd64.whl", hash = "sha256:96177ec2dadc47eaecbf0b22d2e93aeaf964a1be9a71e6e318d2ffb9e4242743"}, - {file = "primp-0.6.4.tar.gz", hash = "sha256:0a3de63e46a50664bcdc76e7aaf7060bf8443698efa902864669c5fca0d1abdd"}, + {file = "primp-0.6.5-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b2bab0250d38c02a437c75ed94b99e3a8c03a281ba9a4c33780ccd04999c741b"}, + {file = "primp-0.6.5-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:0aedb33515d86df4c1f91b9d5772e1b74d1593dfe8978c258b136c171f8ab94c"}, + {file = "primp-0.6.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8850be30fbfefeb76c1eb5859a55c5f11c8c285a4a03ebf99c73fea964b2a"}, + {file = "primp-0.6.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9b71ac07a79cbb401390e2ee5a5767d0bf202a956a533fd084957020fcb2a64"}, + {file = "primp-0.6.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:79c65fcb07b36bd0f8c3966a4a18c4f6a6d624a33a0b0133b0f0cc8d0050c351"}, + {file = "primp-0.6.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5a55e450bb52a88f4a2891db50577c8f20b134d17d37e93361ee51de1a6fe8c8"}, + {file = "primp-0.6.5-cp38-abi3-win_amd64.whl", hash = "sha256:cbe584de5c177b9f0656b77e88721296ae6151b6c4565e2e0a342b6473990f27"}, + {file = "primp-0.6.5.tar.gz", hash = "sha256:abb46c579ae682f34c1f339faac38709c85ab76c056ec3711a26823334ab8124"}, ] [package.extras] @@ -6904,52 +6978,55 @@ files = [ [[package]] name = "pyarrow" -version = "17.0.0" +version = "18.0.0" description = "Python library for Apache Arrow" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pyarrow-17.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a5c8b238d47e48812ee577ee20c9a2779e6a5904f1708ae240f53ecbee7c9f07"}, - {file = "pyarrow-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db023dc4c6cae1015de9e198d41250688383c3f9af8f565370ab2b4cb5f62655"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1e060b3876faa11cee287839f9cc7cdc00649f475714b8680a05fd9071d545"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c06d4624c0ad6674364bb46ef38c3132768139ddec1c56582dbac54f2663e2"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:fa3c246cc58cb5a4a5cb407a18f193354ea47dd0648194e6265bd24177982fe8"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f7ae2de664e0b158d1607699a16a488de3d008ba99b3a7aa5de1cbc13574d047"}, - {file = "pyarrow-17.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5984f416552eea15fd9cee03da53542bf4cddaef5afecefb9aa8d1010c335087"}, - {file = "pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977"}, - {file = "pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4"}, - {file = "pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03"}, - {file = "pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22"}, - {file = "pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b"}, - {file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"}, - {file = "pyarrow-17.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:af5ff82a04b2171415f1410cff7ebb79861afc5dae50be73ce06d6e870615204"}, - {file = "pyarrow-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:edca18eaca89cd6382dfbcff3dd2d87633433043650c07375d095cd3517561d8"}, - {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c7916bff914ac5d4a8fe25b7a25e432ff921e72f6f2b7547d1e325c1ad9d155"}, - {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f553ca691b9e94b202ff741bdd40f6ccb70cdd5fbf65c187af132f1317de6145"}, - {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0cdb0e627c86c373205a2f94a510ac4376fdc523f8bb36beab2e7f204416163c"}, - {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:d7d192305d9d8bc9082d10f361fc70a73590a4c65cf31c3e6926cd72b76bc35c"}, - {file = "pyarrow-17.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:02dae06ce212d8b3244dd3e7d12d9c4d3046945a5933d28026598e9dbbda1fca"}, - {file = "pyarrow-17.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:13d7a460b412f31e4c0efa1148e1d29bdf18ad1411eb6757d38f8fbdcc8645fb"}, - {file = "pyarrow-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b564a51fbccfab5a04a80453e5ac6c9954a9c5ef2890d1bcf63741909c3f8df"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32503827abbc5aadedfa235f5ece8c4f8f8b0a3cf01066bc8d29de7539532687"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a155acc7f154b9ffcc85497509bcd0d43efb80d6f733b0dc3bb14e281f131c8b"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:dec8d129254d0188a49f8a1fc99e0560dc1b85f60af729f47de4046015f9b0a5"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a48ddf5c3c6a6c505904545c25a4ae13646ae1f8ba703c4df4a1bfe4f4006bda"}, - {file = "pyarrow-17.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204"}, - {file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"}, + {file = "pyarrow-18.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:2333f93260674e185cfbf208d2da3007132572e56871f451ba1a556b45dae6e2"}, + {file = "pyarrow-18.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:4c381857754da44326f3a49b8b199f7f87a51c2faacd5114352fc78de30d3aba"}, + {file = "pyarrow-18.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:603cd8ad4976568954598ef0a6d4ed3dfb78aff3d57fa8d6271f470f0ce7d34f"}, + {file = "pyarrow-18.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58a62549a3e0bc9e03df32f350e10e1efb94ec6cf63e3920c3385b26663948ce"}, + {file = "pyarrow-18.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bc97316840a349485fbb137eb8d0f4d7057e1b2c1272b1a20eebbbe1848f5122"}, + {file = "pyarrow-18.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:2e549a748fa8b8715e734919923f69318c953e077e9c02140ada13e59d043310"}, + {file = "pyarrow-18.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:606e9a3dcb0f52307c5040698ea962685fb1c852d72379ee9412be7de9c5f9e2"}, + {file = "pyarrow-18.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d5795e37c0a33baa618c5e054cd61f586cf76850a251e2b21355e4085def6280"}, + {file = "pyarrow-18.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:5f0510608ccd6e7f02ca8596962afb8c6cc84c453e7be0da4d85f5f4f7b0328a"}, + {file = "pyarrow-18.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616ea2826c03c16e87f517c46296621a7c51e30400f6d0a61be645f203aa2b93"}, + {file = "pyarrow-18.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1824f5b029ddd289919f354bc285992cb4e32da518758c136271cf66046ef22"}, + {file = "pyarrow-18.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6dd1b52d0d58dd8f685ced9971eb49f697d753aa7912f0a8f50833c7a7426319"}, + {file = "pyarrow-18.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:320ae9bd45ad7ecc12ec858b3e8e462578de060832b98fc4d671dee9f10d9954"}, + {file = "pyarrow-18.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:2c992716cffb1088414f2b478f7af0175fd0a76fea80841b1706baa8fb0ebaad"}, + {file = "pyarrow-18.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:e7ab04f272f98ebffd2a0661e4e126036f6936391ba2889ed2d44c5006237802"}, + {file = "pyarrow-18.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:03f40b65a43be159d2f97fd64dc998f769d0995a50c00f07aab58b0b3da87e1f"}, + {file = "pyarrow-18.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be08af84808dff63a76860847c48ec0416928a7b3a17c2f49a072cac7c45efbd"}, + {file = "pyarrow-18.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c70c1965cde991b711a98448ccda3486f2a336457cf4ec4dca257a926e149c9"}, + {file = "pyarrow-18.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:00178509f379415a3fcf855af020e3340254f990a8534294ec3cf674d6e255fd"}, + {file = "pyarrow-18.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:a71ab0589a63a3e987beb2bc172e05f000a5c5be2636b4b263c44034e215b5d7"}, + {file = "pyarrow-18.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe92efcdbfa0bcf2fa602e466d7f2905500f33f09eb90bf0bcf2e6ca41b574c8"}, + {file = "pyarrow-18.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:907ee0aa8ca576f5e0cdc20b5aeb2ad4d3953a3b4769fc4b499e00ef0266f02f"}, + {file = "pyarrow-18.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:66dcc216ebae2eb4c37b223feaf82f15b69d502821dde2da138ec5a3716e7463"}, + {file = "pyarrow-18.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc1daf7c425f58527900876354390ee41b0ae962a73ad0959b9d829def583bb1"}, + {file = "pyarrow-18.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:871b292d4b696b09120ed5bde894f79ee2a5f109cb84470546471df264cae136"}, + {file = "pyarrow-18.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:082ba62bdcb939824ba1ce10b8acef5ab621da1f4c4805e07bfd153617ac19d4"}, + {file = "pyarrow-18.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:2c664ab88b9766413197733c1720d3dcd4190e8fa3bbdc3710384630a0a7207b"}, + {file = "pyarrow-18.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:dc892be34dbd058e8d189b47db1e33a227d965ea8805a235c8a7286f7fd17d3a"}, + {file = "pyarrow-18.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:28f9c39a56d2c78bf6b87dcc699d520ab850919d4a8c7418cd20eda49874a2ea"}, + {file = "pyarrow-18.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:f1a198a50c409ab2d009fbf20956ace84567d67f2c5701511d4dd561fae6f32e"}, + {file = "pyarrow-18.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5bd7fd32e3ace012d43925ea4fc8bd1b02cc6cc1e9813b518302950e89b5a22"}, + {file = "pyarrow-18.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:336addb8b6f5208be1b2398442c703a710b6b937b1a046065ee4db65e782ff5a"}, + {file = "pyarrow-18.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:45476490dd4adec5472c92b4d253e245258745d0ccaabe706f8d03288ed60a79"}, + {file = "pyarrow-18.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:b46591222c864e7da7faa3b19455196416cd8355ff6c2cc2e65726a760a3c420"}, + {file = "pyarrow-18.0.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:eb7e3abcda7e1e6b83c2dc2909c8d045881017270a119cc6ee7fdcfe71d02df8"}, + {file = "pyarrow-18.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:09f30690b99ce34e0da64d20dab372ee54431745e4efb78ac938234a282d15f9"}, + {file = "pyarrow-18.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5ca5d707e158540312e09fd907f9f49bacbe779ab5236d9699ced14d2293b8"}, + {file = "pyarrow-18.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6331f280c6e4521c69b201a42dd978f60f7e129511a55da9e0bfe426b4ebb8d"}, + {file = "pyarrow-18.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3ac24b2be732e78a5a3ac0b3aa870d73766dd00beba6e015ea2ea7394f8b4e55"}, + {file = "pyarrow-18.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b30a927c6dff89ee702686596f27c25160dd6c99be5bcc1513a763ae5b1bfc03"}, + {file = "pyarrow-18.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:8f40ec677e942374e3d7f2fad6a67a4c2811a8b975e8703c6fd26d3b168a90e2"}, + {file = "pyarrow-18.0.0.tar.gz", hash = "sha256:a6aa027b1a9d2970cf328ccd6dbe4a996bc13c39fd427f502782f5bdb9ca20f5"}, ] -[package.dependencies] -numpy = ">=1.16.6" - [package.extras] test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"] @@ -7257,13 +7334,13 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pymilvus" -version = "2.4.8" +version = "2.4.9" description = "Python Sdk for Milvus" optional = false python-versions = ">=3.8" files = [ - {file = "pymilvus-2.4.8-py3-none-any.whl", hash = "sha256:5824f8ef4ecb14cfd4b205bf976aa52576c3a83c3cd848d21c8f5f9bb99b29e1"}, - {file = "pymilvus-2.4.8.tar.gz", hash = "sha256:0ddd18a060635fc8f1d1ab5635d9cc340ef29a97783b73db186df6334fa31ee2"}, + {file = "pymilvus-2.4.9-py3-none-any.whl", hash = "sha256:45313607d2c164064bdc44e0f933cb6d6afa92e9efcc7f357c5240c57db58fbe"}, + {file = "pymilvus-2.4.9.tar.gz", hash = "sha256:0937663700007c23a84cfc0656160b301f6ff9247aaec4c96d599a6b43572136"}, ] [package.dependencies] @@ -7372,23 +7449,24 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pypdf" -version = "5.0.1" +version = "5.1.0" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" optional = false python-versions = ">=3.8" files = [ - {file = "pypdf-5.0.1-py3-none-any.whl", hash = "sha256:ff8a32da6c7a63fea9c32fa4dd837cdd0db7966adf6c14f043e3f12592e992db"}, - {file = "pypdf-5.0.1.tar.gz", hash = "sha256:a361c3c372b4a659f9c8dd438d5ce29a753c79c620dc6e1fd66977651f5547ea"}, + {file = "pypdf-5.1.0-py3-none-any.whl", hash = "sha256:3bd4f503f4ebc58bae40d81e81a9176c400cbbac2ba2d877367595fb524dfdfc"}, + {file = "pypdf-5.1.0.tar.gz", hash = "sha256:425a129abb1614183fd1aca6982f650b47f8026867c0ce7c4b9f281c443d2740"}, ] [package.dependencies] typing_extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] -crypto = ["PyCryptodome", "cryptography"] +crypto = ["cryptography"] +cryptodome = ["PyCryptodome"] dev = ["black", "flit", "pip-tools", "pre-commit (<2.18.0)", "pytest-cov", "pytest-socket", "pytest-timeout", "pytest-xdist", "wheel"] docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"] -full = ["Pillow (>=8.0.0)", "PyCryptodome", "cryptography"] +full = ["Pillow (>=8.0.0)", "cryptography"] image = ["Pillow (>=8.0.0)"] [[package]] @@ -7924,99 +8002,99 @@ dev = ["pytest"] [[package]] name = "rapidfuzz" -version = "3.10.0" +version = "3.10.1" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.9" files = [ - {file = "rapidfuzz-3.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:884453860de029380dded8f3c1918af2d8eb5adf8010261645c7e5c88c2b5428"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718c9bd369288aca5fa929df6dbf66fdbe9768d90940a940c0b5cdc96ade4309"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a68e3724b7dab761c01816aaa64b0903734d999d5589daf97c14ef5cc0629a8e"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1af60988d47534246d9525f77288fdd9de652608a4842815d9018570b959acc6"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3084161fc3e963056232ef8d937449a2943852e07101f5a136c8f3cfa4119217"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cd67d3d017296d98ff505529104299f78433e4b8af31b55003d901a62bbebe9"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b11a127ac590fc991e8a02c2d7e1ac86e8141c92f78546f18b5c904064a0552c"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aadce42147fc09dcef1afa892485311e824c050352e1aa6e47f56b9b27af4cf0"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b54853c2371bf0e38d67da379519deb6fbe70055efb32f6607081641af3dc752"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ce19887268e90ee81a3957eef5e46a70ecc000713796639f83828b950343f49e"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f39a2a5ded23b9b9194ec45740dce57177b80f86c6d8eba953d3ff1a25c97766"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0ec338d5f4ad8d9339a88a08db5c23e7f7a52c2b2a10510c48a0cef1fb3f0ddc"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-win32.whl", hash = "sha256:56fd15ea8f4c948864fa5ebd9261c67cf7b89a1c517a0caef4df75446a7af18c"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:43dfc5e733808962a822ff6d9c29f3039a3cfb3620706f5953e17cfe4496724c"}, - {file = "rapidfuzz-3.10.0-cp310-cp310-win_arm64.whl", hash = "sha256:ae7966f205b5a7fde93b44ca8fed37c1c8539328d7f179b1197de34eceaceb5f"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bb0013795b40db5cf361e6f21ee7cda09627cf294977149b50e217d7fe9a2f03"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:69ef5b363afff7150a1fbe788007e307b9802a2eb6ad92ed51ab94e6ad2674c6"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c582c46b1bb0b19f1a5f4c1312f1b640c21d78c371a6615c34025b16ee56369b"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:288f6f6e7410cacb115fb851f3f18bf0e4231eb3f6cb5bd1cec0e7b25c4d039d"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9e29a13d2fd9be3e7d8c26c7ef4ba60b5bc7efbc9dbdf24454c7e9ebba31768"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea2da0459b951ee461bd4e02b8904890bd1c4263999d291c5cd01e6620177ad4"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:457827ba82261aa2ae6ac06a46d0043ab12ba7216b82d87ae1434ec0f29736d6"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5d350864269d56f51ab81ab750c9259ae5cad3152c0680baef143dcec92206a1"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a9b8f51e08c3f983d857c3889930af9ddecc768453822076683664772d87e374"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7f3a6aa6e70fc27e4ff5c479f13cc9fc26a56347610f5f8b50396a0d344c5f55"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:803f255f10d63420979b1909ef976e7d30dec42025c9b067fc1d2040cc365a7e"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2026651761bf83a0f31495cc0f70840d5c0d54388f41316e3f9cb51bd85e49a5"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-win32.whl", hash = "sha256:4df75b3ebbb8cfdb9bf8b213b168620b88fd92d0c16a8bc9f9234630b282db59"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f9f0bbfb6787b97c51516f3ccf97737d504db5d239ad44527673b81f598b84ab"}, - {file = "rapidfuzz-3.10.0-cp311-cp311-win_arm64.whl", hash = "sha256:10fdad800441b9c97d471a937ba7d42625f1b530db05e572f1cb7d401d95c893"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7dc87073ba3a40dd65591a2100aa71602107443bf10770579ff9c8a3242edb94"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a425a0a868cf8e9c6e93e1cda4b758cdfd314bb9a4fc916c5742c934e3613480"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a86d5d1d75e61df060c1e56596b6b0a4422a929dff19cc3dbfd5eee762c86b61"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34f213d59219a9c3ca14e94a825f585811a68ac56b4118b4dc388b5b14afc108"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96ad46f5f56f70fab2be9e5f3165a21be58d633b90bf6e67fc52a856695e4bcf"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9178277f72d144a6c7704d7ae7fa15b7b86f0f0796f0e1049c7b4ef748a662ef"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76a35e9e19a7c883c422ffa378e9a04bc98cb3b29648c5831596401298ee51e6"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a6405d34c394c65e4f73a1d300c001f304f08e529d2ed6413b46ee3037956eb"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bd393683129f446a75d8634306aed7e377627098a1286ff3af2a4f1736742820"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b0445fa9880ead81f5a7d0efc0b9c977a947d8052c43519aceeaf56eabaf6843"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c50bc308fa29767ed8f53a8d33b7633a9e14718ced038ed89d41b886e301da32"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e89605afebbd2d4b045bccfdc12a14b16fe8ccbae05f64b4b4c64a97dad1c891"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-win32.whl", hash = "sha256:2db9187f3acf3cd33424ecdbaad75414c298ecd1513470df7bda885dcb68cc15"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:50e3d0c72ea15391ba9531ead7f2068a67c5b18a6a365fef3127583aaadd1725"}, - {file = "rapidfuzz-3.10.0-cp312-cp312-win_arm64.whl", hash = "sha256:9eac95b4278bd53115903d89118a2c908398ee8bdfd977ae844f1bd2b02b917c"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fe5231e8afd069c742ac5b4f96344a0fe4aff52df8e53ef87faebf77f827822c"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:886882367dbc985f5736356105798f2ae6e794e671fc605476cbe2e73838a9bb"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b33e13e537e3afd1627d421a142a12bbbe601543558a391a6fae593356842f6e"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:094c26116d55bf9c53abd840d08422f20da78ec4c4723e5024322321caedca48"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:545fc04f2d592e4350f59deb0818886c1b444ffba3bec535b4fbb97191aaf769"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:916a6abf3632e592b937c3d04c00a6efadd8fd30539cdcd4e6e4d92be7ca5d90"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6ec40cef63b1922083d33bfef2f91fc0b0bc07b5b09bfee0b0f1717d558292"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c77a7330dd15c7eb5fd3631dc646fc96327f98db8181138766bd14d3e905f0ba"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:949b5e9eeaa4ecb4c7e9c2a4689dddce60929dd1ff9c76a889cdbabe8bbf2171"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b5363932a5aab67010ae1a6205c567d1ef256fb333bc23c27582481606be480c"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5dd6eec15b13329abe66cc241b484002ecb0e17d694491c944a22410a6a9e5e2"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79e7f98525b60b3c14524e0a4e1fedf7654657b6e02eb25f1be897ab097706f3"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-win32.whl", hash = "sha256:d29d1b9857c65f8cb3a29270732e1591b9bacf89de9d13fa764f79f07d8f1fd2"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:fa9720e56663cc3649d62b4b5f3145e94b8f5611e8a8e1b46507777249d46aad"}, - {file = "rapidfuzz-3.10.0-cp313-cp313-win_arm64.whl", hash = "sha256:eda4c661e68dddd56c8fbfe1ca35e40dd2afd973f7ebb1605f4d151edc63dff8"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cffbc50e0767396ed483900900dd58ce4351bc0d40e64bced8694bd41864cc71"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c038b9939da3035afb6cb2f465f18163e8f070aba0482923ecff9443def67178"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca366c2e2a54e2f663f4529b189fdeb6e14d419b1c78b754ec1744f3c01070d4"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c4c82b1689b23b1b5e6a603164ed2be41b6f6de292a698b98ba2381e889eb9d"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98f6ebe28831a482981ecfeedc8237047878424ad0c1add2c7f366ba44a20452"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bd1a7676ee2a4c8e2f7f2550bece994f9f89e58afb96088964145a83af7408b"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec9139baa3f85b65adc700eafa03ed04995ca8533dd56c924f0e458ffec044ab"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:26de93e6495078b6af4c4d93a42ca067b16cc0e95699526c82ab7d1025b4d3bf"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f3a0bda83c18195c361b5500377d0767749f128564ca95b42c8849fd475bb327"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:63e4c175cbce8c3adc22dca5e6154588ae673f6c55374d156f3dac732c88d7de"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4dd3d8443970eaa02ab5ae45ce584b061f2799cd9f7e875190e2617440c1f9d4"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e5ddb2388610799fc46abe389600625058f2a73867e63e20107c5ad5ffa57c47"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-win32.whl", hash = "sha256:2e9be5d05cd960914024412b5406fb75a82f8562f45912ff86255acbfdbfb78e"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:47aca565a39c9a6067927871973ca827023e8b65ba6c5747f4c228c8d7ddc04f"}, - {file = "rapidfuzz-3.10.0-cp39-cp39-win_arm64.whl", hash = "sha256:b0732343cdc4273b5921268026dd7266f75466eb21873cb7635a200d9d9c3fac"}, - {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f744b5eb1469bf92dd143d36570d2bdbbdc88fe5cb0b5405e53dd34f479cbd8a"}, - {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b67cc21a14327a0eb0f47bc3d7e59ec08031c7c55220ece672f9476e7a8068d3"}, - {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fe5783676f0afba4a522c80b15e99dbf4e393c149ab610308a8ef1f04c6bcc8"}, - {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4688862f957c8629d557d084f20b2d803f8738b6c4066802a0b1cc472e088d9"}, - {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20bd153aacc244e4c907d772c703fea82754c4db14f8aa64d75ff81b7b8ab92d"}, - {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:50484d563f8bfa723c74c944b0bb15b9e054db9c889348c8c307abcbee75ab92"}, - {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5897242d455461f2c5b82d7397b29341fd11e85bf3608a522177071044784ee8"}, - {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:116c71a81e046ba56551d8ab68067ca7034d94b617545316d460a452c5c3c289"}, - {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0a547e4350d1fa32624d3eab51eff8cf329f4cae110b4ea0402486b1da8be40"}, - {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:399b9b79ccfcf50ca3bad7692bc098bb8eade88d7d5e15773b7f866c91156d0c"}, - {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7947a425d1be3e744707ee58c6cb318b93a56e08f080722dcc0347e0b7a1bb9a"}, - {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:94c48b4a2a4b1d22246f48e2b11cae01ec7d23f0c9123f8bb822839ad79d0a88"}, - {file = "rapidfuzz-3.10.0.tar.gz", hash = "sha256:6b62af27e65bb39276a66533655a2fa3c60a487b03935721c45b7809527979be"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f17d9f21bf2f2f785d74f7b0d407805468b4c173fa3e52c86ec94436b338e74a"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b31f358a70efc143909fb3d75ac6cd3c139cd41339aa8f2a3a0ead8315731f2b"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f4f43f2204b56a61448ec2dd061e26fd344c404da99fb19f3458200c5874ba2"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d81bf186a453a2757472133b24915768abc7c3964194406ed93e170e16c21cb"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3611c8f45379a12063d70075c75134f2a8bd2e4e9b8a7995112ddae95ca1c982"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c3b537b97ac30da4b73930fa8a4fe2f79c6d1c10ad535c5c09726612cd6bed9"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:231ef1ec9cf7b59809ce3301006500b9d564ddb324635f4ea8f16b3e2a1780da"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed4f3adc1294834955b7e74edd3c6bd1aad5831c007f2d91ea839e76461a5879"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7b6015da2e707bf632a71772a2dbf0703cff6525732c005ad24987fe86e8ec32"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1b35a118d61d6f008e8e3fb3a77674d10806a8972c7b8be433d6598df4d60b01"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bc308d79a7e877226f36bdf4e149e3ed398d8277c140be5c1fd892ec41739e6d"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f017dbfecc172e2d0c37cf9e3d519179d71a7f16094b57430dffc496a098aa17"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-win32.whl", hash = "sha256:36c0e1483e21f918d0f2f26799fe5ac91c7b0c34220b73007301c4f831a9c4c7"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:10746c1d4c8cd8881c28a87fd7ba0c9c102346dfe7ff1b0d021cdf093e9adbff"}, + {file = "rapidfuzz-3.10.1-cp310-cp310-win_arm64.whl", hash = "sha256:dfa64b89dcb906835e275187569e51aa9d546a444489e97aaf2cc84011565fbe"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:92958ae075c87fef393f835ed02d4fe8d5ee2059a0934c6c447ea3417dfbf0e8"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba7521e072c53e33c384e78615d0718e645cab3c366ecd3cc8cb732befd94967"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d02cbd75d283c287471b5b3738b3e05c9096150f93f2d2dfa10b3d700f2db9"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:efa1582a397da038e2f2576c9cd49b842f56fde37d84a6b0200ffebc08d82350"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f12912acee1f506f974f58de9fdc2e62eea5667377a7e9156de53241c05fdba8"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666d5d8b17becc3f53447bcb2b6b33ce6c2df78792495d1fa82b2924cd48701a"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26f71582c0d62445067ee338ddad99b655a8f4e4ed517a90dcbfbb7d19310474"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8a2ef08b27167bcff230ffbfeedd4c4fa6353563d6aaa015d725dd3632fc3de7"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:365e4fc1a2b95082c890f5e98489b894e6bf8c338c6ac89bb6523c2ca6e9f086"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1996feb7a61609fa842e6b5e0c549983222ffdedaf29644cc67e479902846dfe"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:cf654702f144beaa093103841a2ea6910d617d0bb3fccb1d1fd63c54dde2cd49"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec108bf25de674781d0a9a935030ba090c78d49def3d60f8724f3fc1e8e75024"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-win32.whl", hash = "sha256:031f8b367e5d92f7a1e27f7322012f3c321c3110137b43cc3bf678505583ef48"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:f98f36c6a1bb9a6c8bbec99ad87c8c0e364f34761739b5ea9adf7b48129ae8cf"}, + {file = "rapidfuzz-3.10.1-cp311-cp311-win_arm64.whl", hash = "sha256:f1da2028cb4e41be55ee797a82d6c1cf589442504244249dfeb32efc608edee7"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1340b56340896bede246f612b6ecf685f661a56aabef3d2512481bfe23ac5835"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2316515169b7b5a453f0ce3adbc46c42aa332cae9f2edb668e24d1fc92b2f2bb"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e06fe6a12241ec1b72c0566c6b28cda714d61965d86569595ad24793d1ab259"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d99c1cd9443b19164ec185a7d752f4b4db19c066c136f028991a480720472e23"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1d9aa156ed52d3446388ba4c2f335e312191d1ca9d1f5762ee983cf23e4ecf6"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54bcf4efaaee8e015822be0c2c28214815f4f6b4f70d8362cfecbd58a71188ac"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0c955e32afdbfdf6e9ee663d24afb25210152d98c26d22d399712d29a9b976b"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:191633722203f5b7717efcb73a14f76f3b124877d0608c070b827c5226d0b972"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:195baad28057ec9609e40385991004e470af9ef87401e24ebe72c064431524ab"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0fff4a6b87c07366662b62ae994ffbeadc472e72f725923f94b72a3db49f4671"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4ffed25f9fdc0b287f30a98467493d1e1ce5b583f6317f70ec0263b3c97dbba6"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d02cf8e5af89a9ac8f53c438ddff6d773f62c25c6619b29db96f4aae248177c0"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-win32.whl", hash = "sha256:f3bb81d4fe6a5d20650f8c0afcc8f6e1941f6fecdb434f11b874c42467baded0"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:aaf83e9170cb1338922ae42d320699dccbbdca8ffed07faeb0b9257822c26e24"}, + {file = "rapidfuzz-3.10.1-cp312-cp312-win_arm64.whl", hash = "sha256:c5da802a0d085ad81b0f62828fb55557996c497b2d0b551bbdfeafd6d447892f"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fc22d69a1c9cccd560a5c434c0371b2df0f47c309c635a01a913e03bbf183710"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38b0dac2c8e057562b8f0d8ae5b663d2d6a28c5ab624de5b73cef9abb6129a24"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fde3bbb14e92ce8fcb5c2edfff72e474d0080cadda1c97785bf4822f037a309"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9141fb0592e55f98fe9ac0f3ce883199b9c13e262e0bf40c5b18cdf926109d16"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:237bec5dd1bfc9b40bbd786cd27949ef0c0eb5fab5eb491904c6b5df59d39d3c"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18123168cba156ab5794ea6de66db50f21bb3c66ae748d03316e71b27d907b95"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b75fe506c8e02769cc47f5ab21ce3e09b6211d3edaa8f8f27331cb6988779be"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9da82aa4b46973aaf9e03bb4c3d6977004648c8638febfc0f9d237e865761270"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c34c022d5ad564f1a5a57a4a89793bd70d7bad428150fb8ff2760b223407cdcf"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e96c84d6c2a0ca94e15acb5399118fff669f4306beb98a6d8ec6f5dccab4412"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e8e154b84a311263e1aca86818c962e1fa9eefdd643d1d5d197fcd2738f88cb9"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:335fee93188f8cd585552bb8057228ce0111bd227fa81bfd40b7df6b75def8ab"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-win32.whl", hash = "sha256:6729b856166a9e95c278410f73683957ea6100c8a9d0a8dbe434c49663689255"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:0e06d99ad1ad97cb2ef7f51ec6b1fedd74a3a700e4949353871cf331d07b382a"}, + {file = "rapidfuzz-3.10.1-cp313-cp313-win_arm64.whl", hash = "sha256:8d1b7082104d596a3eb012e0549b2634ed15015b569f48879701e9d8db959dbb"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:779027d3307e1a2b1dc0c03c34df87a470a368a1a0840a9d2908baf2d4067956"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:440b5608ab12650d0390128d6858bc839ae77ffe5edf0b33a1551f2fa9860651"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82cac41a411e07a6f3dc80dfbd33f6be70ea0abd72e99c59310819d09f07d945"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:958473c9f0bca250590200fd520b75be0dbdbc4a7327dc87a55b6d7dc8d68552"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ef60dfa73749ef91cb6073be1a3e135f4846ec809cc115f3cbfc6fe283a5584"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7fbac18f2c19fc983838a60611e67e3262e36859994c26f2ee85bb268de2355"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a0d519ff39db887cd73f4e297922786d548f5c05d6b51f4e6754f452a7f4296"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bebb7bc6aeb91cc57e4881b222484c26759ca865794187217c9dcea6c33adae6"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fe07f8b9c3bb5c5ad1d2c66884253e03800f4189a60eb6acd6119ebaf3eb9894"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bfa48a4a2d45a41457f0840c48e579db157a927f4e97acf6e20df8fc521c79de"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2cf44d01bfe8ee605b7eaeecbc2b9ca64fc55765f17b304b40ed8995f69d7716"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e6bbca9246d9eedaa1c84e04a7f555493ba324d52ae4d9f3d9ddd1b740dcd87"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-win32.whl", hash = "sha256:567f88180f2c1423b4fe3f3ad6e6310fc97b85bdba574801548597287fc07028"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:6b2cd7c29d6ecdf0b780deb587198f13213ac01c430ada6913452fd0c40190fc"}, + {file = "rapidfuzz-3.10.1-cp39-cp39-win_arm64.whl", hash = "sha256:9f912d459e46607ce276128f52bea21ebc3e9a5ccf4cccfef30dd5bddcf47be8"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ac4452f182243cfab30ba4668ef2de101effaedc30f9faabb06a095a8c90fd16"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:565c2bd4f7d23c32834652b27b51dd711814ab614b4e12add8476be4e20d1cf5"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:187d9747149321607be4ccd6f9f366730078bed806178ec3eeb31d05545e9e8f"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:616290fb9a8fa87e48cb0326d26f98d4e29f17c3b762c2d586f2b35c1fd2034b"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:073a5b107e17ebd264198b78614c0206fa438cce749692af5bc5f8f484883f50"}, + {file = "rapidfuzz-3.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:39c4983e2e2ccb9732f3ac7d81617088822f4a12291d416b09b8a1eadebb3e29"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ac7adee6bcf0c6fee495d877edad1540a7e0f5fc208da03ccb64734b43522d7a"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:425f4ac80b22153d391ee3f94bc854668a0c6c129f05cf2eaf5ee74474ddb69e"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65a2fa13e8a219f9b5dcb9e74abe3ced5838a7327e629f426d333dfc8c5a6e66"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75561f3df9a906aaa23787e9992b228b1ab69007932dc42070f747103e177ba8"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edd062490537e97ca125bc6c7f2b7331c2b73d21dc304615afe61ad1691e15d5"}, + {file = "rapidfuzz-3.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfcc8feccf63245a22dfdd16e222f1a39771a44b870beb748117a0e09cbb4a62"}, + {file = "rapidfuzz-3.10.1.tar.gz", hash = "sha256:5a15546d847a915b3f42dc79ef9b0c78b998b4e2c53b252e7166284066585979"}, ] [package.extras] @@ -8461,29 +8539,29 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.6.9" +version = "0.7.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.9-py3-none-linux_armv6l.whl", hash = "sha256:064df58d84ccc0ac0fcd63bc3090b251d90e2a372558c0f057c3f75ed73e1ccd"}, - {file = "ruff-0.6.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:140d4b5c9f5fc7a7b074908a78ab8d384dd7f6510402267bc76c37195c02a7ec"}, - {file = "ruff-0.6.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53fd8ca5e82bdee8da7f506d7b03a261f24cd43d090ea9db9a1dc59d9313914c"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645d7d8761f915e48a00d4ecc3686969761df69fb561dd914a773c1a8266e14e"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eae02b700763e3847595b9d2891488989cac00214da7f845f4bcf2989007d577"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d5ccc9e58112441de8ad4b29dcb7a86dc25c5f770e3c06a9d57e0e5eba48829"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:417b81aa1c9b60b2f8edc463c58363075412866ae4e2b9ab0f690dc1e87ac1b5"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c866b631f5fbce896a74a6e4383407ba7507b815ccc52bcedabb6810fdb3ef7"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b118afbb3202f5911486ad52da86d1d52305b59e7ef2031cea3425142b97d6f"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a67267654edc23c97335586774790cde402fb6bbdb3c2314f1fc087dee320bfa"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3ef0cc774b00fec123f635ce5c547dac263f6ee9fb9cc83437c5904183b55ceb"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:12edd2af0c60fa61ff31cefb90aef4288ac4d372b4962c2864aeea3a1a2460c0"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:55bb01caeaf3a60b2b2bba07308a02fca6ab56233302406ed5245180a05c5625"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:925d26471fa24b0ce5a6cdfab1bb526fb4159952385f386bdcc643813d472039"}, - {file = "ruff-0.6.9-py3-none-win32.whl", hash = "sha256:eb61ec9bdb2506cffd492e05ac40e5bc6284873aceb605503d8494180d6fc84d"}, - {file = "ruff-0.6.9-py3-none-win_amd64.whl", hash = "sha256:785d31851c1ae91f45b3d8fe23b8ae4b5170089021fbb42402d811135f0b7117"}, - {file = "ruff-0.6.9-py3-none-win_arm64.whl", hash = "sha256:a9641e31476d601f83cd602608739a0840e348bda93fec9f1ee816f8b6798b93"}, - {file = "ruff-0.6.9.tar.gz", hash = "sha256:b076ef717a8e5bc819514ee1d602bbdca5b4420ae13a9cf61a0c0a4f53a2baa2"}, + {file = "ruff-0.7.3-py3-none-linux_armv6l.whl", hash = "sha256:34f2339dc22687ec7e7002792d1f50712bf84a13d5152e75712ac08be565d344"}, + {file = "ruff-0.7.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fb397332a1879b9764a3455a0bb1087bda876c2db8aca3a3cbb67b3dbce8cda0"}, + {file = "ruff-0.7.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:37d0b619546103274e7f62643d14e1adcbccb242efda4e4bdb9544d7764782e9"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59f0c3ee4d1a6787614e7135b72e21024875266101142a09a61439cb6e38a5"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:44eb93c2499a169d49fafd07bc62ac89b1bc800b197e50ff4633aed212569299"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d0242ce53f3a576c35ee32d907475a8d569944c0407f91d207c8af5be5dae4e"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6b6224af8b5e09772c2ecb8dc9f3f344c1aa48201c7f07e7315367f6dd90ac29"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c50f95a82b94421c964fae4c27c0242890a20fe67d203d127e84fbb8013855f5"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f3eff9961b5d2644bcf1616c606e93baa2d6b349e8aa8b035f654df252c8c67"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8963cab06d130c4df2fd52c84e9f10d297826d2e8169ae0c798b6221be1d1d2"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:61b46049d6edc0e4317fb14b33bd693245281a3007288b68a3f5b74a22a0746d"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:10ebce7696afe4644e8c1a23b3cf8c0f2193a310c18387c06e583ae9ef284de2"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3f36d56326b3aef8eeee150b700e519880d1aab92f471eefdef656fd57492aa2"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5d024301109a0007b78d57ab0ba190087b43dce852e552734ebf0b0b85e4fb16"}, + {file = "ruff-0.7.3-py3-none-win32.whl", hash = "sha256:4ba81a5f0c5478aa61674c5a2194de8b02652f17addf8dfc40c8937e6e7d79fc"}, + {file = "ruff-0.7.3-py3-none-win_amd64.whl", hash = "sha256:588a9ff2fecf01025ed065fe28809cd5a53b43505f48b69a1ac7707b1b7e4088"}, + {file = "ruff-0.7.3-py3-none-win_arm64.whl", hash = "sha256:1713e2c5545863cdbfe2cbce21f69ffaf37b813bfd1fb3b90dc9a6f1963f5a8c"}, + {file = "ruff-0.7.3.tar.gz", hash = "sha256:e1d1ba2e40b6e71a61b063354d04be669ab0d39c352461f3d789cac68b54a313"}, ] [[package]] @@ -8680,13 +8758,13 @@ test = ["accelerate (>=0.24.1,<=0.27.0)", "apache-airflow (==2.9.3)", "apache-ai [[package]] name = "sagemaker-core" -version = "1.0.10" +version = "1.0.11" description = "An python package for sagemaker core functionalities" optional = false python-versions = ">=3.8" files = [ - {file = "sagemaker_core-1.0.10-py3-none-any.whl", hash = "sha256:0bdcf6a467db988919cc6b6d0077f74871ee24c24adf7f759f9cb98460e08953"}, - {file = "sagemaker_core-1.0.10.tar.gz", hash = "sha256:6d34a9b6dc5e17e8bfffd1d0650726865779c92b3b8f1b59fc15d42061a0dd29"}, + {file = "sagemaker_core-1.0.11-py3-none-any.whl", hash = "sha256:d8ee3db83759073aa8c9f2bd4899113088a7c2acf340597e76cf9934e384d915"}, + {file = "sagemaker_core-1.0.11.tar.gz", hash = "sha256:fb48a5dcb859a54de7461c71cf58562a3be259294dcd39c317020a9b018f5016"}, ] [package.dependencies] @@ -8735,6 +8813,11 @@ files = [ {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f60021ec1574e56632be2a36b946f8143bf4e5e6af4a06d85281adc22938e0dd"}, {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:394397841449853c2290a32050382edaec3da89e35b3e03d6cc966aebc6a8ae6"}, {file = "scikit_learn-1.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:57cc1786cfd6bd118220a92ede80270132aa353647684efa385a74244a41e3b1"}, + {file = "scikit_learn-1.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9a702e2de732bbb20d3bad29ebd77fc05a6b427dc49964300340e4c9328b3f5"}, + {file = "scikit_learn-1.5.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:b0768ad641981f5d3a198430a1d31c3e044ed2e8a6f22166b4d546a5116d7908"}, + {file = "scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:178ddd0a5cb0044464fc1bfc4cca5b1833bfc7bb022d70b05db8530da4bb3dd3"}, + {file = "scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7284ade780084d94505632241bf78c44ab3b6f1e8ccab3d2af58e0e950f9c12"}, + {file = "scikit_learn-1.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:b7b0f9a0b1040830d38c39b91b3a44e1b643f4b36e36567b80b7c6bd2202a27f"}, {file = "scikit_learn-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:757c7d514ddb00ae249832fe87100d9c73c6ea91423802872d9e74970a0e40b9"}, {file = "scikit_learn-1.5.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:52788f48b5d8bca5c0736c175fa6bdaab2ef00a8f536cda698db61bd89c551c1"}, {file = "scikit_learn-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:643964678f4b5fbdc95cbf8aec638acc7aa70f5f79ee2cdad1eec3df4ba6ead8"}, @@ -8860,23 +8943,23 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "75.2.0" +version = "75.3.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, - {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, + {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, + {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, ] [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"] [[package]] name = "sgmllib3k" @@ -9625,13 +9708,13 @@ six = "*" [[package]] name = "tqdm" -version = "4.66.5" +version = "4.66.6" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, - {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, + {file = "tqdm-4.66.6-py3-none-any.whl", hash = "sha256:223e8b5359c2efc4b30555531f09e9f2f3589bcd7fdd389271191031b49b7a63"}, + {file = "tqdm-4.66.6.tar.gz", hash = "sha256:4bdd694238bef1485ce839d67967ab50af8f9272aab687c0d7702a01da0be090"}, ] [package.dependencies] @@ -9885,13 +9968,13 @@ files = [ [[package]] name = "unstructured" -version = "0.16.1" +version = "0.16.3" description = "A library that prepares raw documents for downstream ML tasks." optional = false python-versions = "<3.13,>=3.9.0" files = [ - {file = "unstructured-0.16.1-py3-none-any.whl", hash = "sha256:7512281a2917809a563cbb186876b77d5a361e1f3089eca61e9219aecd1218f9"}, - {file = "unstructured-0.16.1.tar.gz", hash = "sha256:03608b5189a004412cd618ce2d083ff926c56dbbca41b41c92e08ffa9e2bac3a"}, + {file = "unstructured-0.16.3-py3-none-any.whl", hash = "sha256:e0e3b56531b44e62154d17cbfdae7fd7fa1d795b7cf510fb654c6714d4257655"}, + {file = "unstructured-0.16.3.tar.gz", hash = "sha256:f9528636773c910a53c8a34e32d4733ea54b79cbd507d0e956e299ab1da3003f"}, ] [package.dependencies] @@ -9922,19 +10005,19 @@ unstructured-client = "*" wrapt = "*" [package.extras] -all-docs = ["effdet", "google-cloud-vision", "markdown", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypandoc", "pypdf", "python-docx (>=1.1.2)", "python-pptx (>=1.0.1)", "unstructured-inference (==0.8.0)", "unstructured.pytesseract (>=0.3.12)", "xlrd"] +all-docs = ["effdet", "google-cloud-vision", "markdown", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypandoc", "pypdf", "python-docx (>=1.1.2)", "python-pptx (>=1.0.1)", "unstructured-inference (==0.8.1)", "unstructured.pytesseract (>=0.3.12)", "xlrd"] csv = ["pandas"] doc = ["python-docx (>=1.1.2)"] docx = ["python-docx (>=1.1.2)"] epub = ["pypandoc"] huggingface = ["langdetect", "sacremoses", "sentencepiece", "torch", "transformers"] -image = ["effdet", "google-cloud-vision", "onnx", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypdf", "unstructured-inference (==0.8.0)", "unstructured.pytesseract (>=0.3.12)"] -local-inference = ["effdet", "google-cloud-vision", "markdown", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypandoc", "pypdf", "python-docx (>=1.1.2)", "python-pptx (>=1.0.1)", "unstructured-inference (==0.8.0)", "unstructured.pytesseract (>=0.3.12)", "xlrd"] +image = ["effdet", "google-cloud-vision", "onnx", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypdf", "unstructured-inference (==0.8.1)", "unstructured.pytesseract (>=0.3.12)"] +local-inference = ["effdet", "google-cloud-vision", "markdown", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypandoc", "pypdf", "python-docx (>=1.1.2)", "python-pptx (>=1.0.1)", "unstructured-inference (==0.8.1)", "unstructured.pytesseract (>=0.3.12)", "xlrd"] md = ["markdown"] odt = ["pypandoc", "python-docx (>=1.1.2)"] org = ["pypandoc"] paddleocr = ["paddlepaddle (==3.0.0b1)", "unstructured.paddleocr (==2.8.1.0)"] -pdf = ["effdet", "google-cloud-vision", "onnx", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypdf", "unstructured-inference (==0.8.0)", "unstructured.pytesseract (>=0.3.12)"] +pdf = ["effdet", "google-cloud-vision", "onnx", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypdf", "unstructured-inference (==0.8.1)", "unstructured.pytesseract (>=0.3.12)"] ppt = ["python-pptx (>=1.0.1)"] pptx = ["python-pptx (>=1.0.1)"] rst = ["pypandoc"] @@ -9944,13 +10027,13 @@ xlsx = ["networkx", "openpyxl", "pandas", "xlrd"] [[package]] name = "unstructured-client" -version = "0.26.1" +version = "0.26.2" description = "Python Client SDK for Unstructured API" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "unstructured_client-0.26.1-py3-none-any.whl", hash = "sha256:b8b839d477122bab3f37242cbe44b39f7eb7b564b07b53500321f953710119b6"}, - {file = "unstructured_client-0.26.1.tar.gz", hash = "sha256:907cceb470529b45b0fddb2d0f1bbf4d6568f347c757ab68639a7bb620ec2484"}, + {file = "unstructured_client-0.26.2-py3-none-any.whl", hash = "sha256:0adb22b7d175814f333ee2425a279005f253220a55f459fd5830a6779b679780"}, + {file = "unstructured_client-0.26.2.tar.gz", hash = "sha256:02f7183ab16db6ec48ad1ac75c01b05967c87c561a89e96d9ffb836baed902d7"}, ] [package.dependencies] @@ -10445,13 +10528,13 @@ files = [ [[package]] name = "werkzeug" -version = "3.0.4" +version = "3.0.6" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.8" files = [ - {file = "werkzeug-3.0.4-py3-none-any.whl", hash = "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c"}, - {file = "werkzeug-3.0.4.tar.gz", hash = "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306"}, + {file = "werkzeug-3.0.6-py3-none-any.whl", hash = "sha256:1bc0c2310d2fbb07b1dd1105eba2f7af72f322e1e455f2f93c993bee8c8a5f17"}, + {file = "werkzeug-3.0.6.tar.gz", hash = "sha256:a8dd59d4de28ca70471a34cba79bed5f7ef2e036a76b3ab0835474246eb41f8d"}, ] [package.dependencies] @@ -10745,13 +10828,13 @@ multidict = ">=4.0" [[package]] name = "yfinance" -version = "0.2.46" +version = "0.2.48" description = "Download market data from Yahoo! Finance API" optional = false python-versions = "*" files = [ - {file = "yfinance-0.2.46-py2.py3-none-any.whl", hash = "sha256:371860d532cae76605195678a540e29382bfd0607f8aa61695f753e714916ffc"}, - {file = "yfinance-0.2.46.tar.gz", hash = "sha256:a6e2a128915532a54b8f6614cfdb7a8c242d2386e05f95c89b15865b5d9c0352"}, + {file = "yfinance-0.2.48-py2.py3-none-any.whl", hash = "sha256:eda797145faa4536595eb629f869d3616e58ed7e71de36856b19f1abaef71a5b"}, + {file = "yfinance-0.2.48.tar.gz", hash = "sha256:1434cd8bf22f345fa27ef1ed82bfdd291c1bb5b6fe3067118a94e256aa90c4eb"}, ] [package.dependencies] @@ -10995,4 +11078,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "bb8385625eb61de086b7a7156745066b4fb171d9ca67afd1d092fa7e872f3abd" +content-hash = "2ba4b464eebc26598f290fa94713acc44c588f902176e6efa80622911d40f0ac" diff --git a/api/pyproject.toml b/api/pyproject.toml index 928dee975b..0633e9dd90 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -118,10 +118,11 @@ beautifulsoup4 = "4.12.2" boto3 = "1.35.17" bs4 = "~0.0.1" cachetools = "~5.3.0" -celery = "~5.3.6" +celery = "~5.4.0" chardet = "~5.1.0" cohere = "~5.2.4" dashscope = { version = "~1.17.0", extras = ["tokenizer"] } +fal-client = "0.5.6" flask = "~3.0.1" flask-compress = "~1.14" flask-cors = "~4.0.0" @@ -278,4 +279,4 @@ pytest-mock = "~3.14.0" optional = true [tool.poetry.group.lint.dependencies] dotenv-linter = "~0.5.0" -ruff = "~0.6.9" +ruff = "~0.7.3" diff --git a/api/schedule/create_tidb_serverless_task.py b/api/schedule/create_tidb_serverless_task.py index 42d6c04beb..a20b500308 100644 --- a/api/schedule/create_tidb_serverless_task.py +++ b/api/schedule/create_tidb_serverless_task.py @@ -12,6 +12,8 @@ from models.dataset import TidbAuthBinding @app.celery.task(queue="dataset") def create_tidb_serverless_task(): click.echo(click.style("Start create tidb serverless task.", fg="green")) + if not dify_config.CREATE_TIDB_SERVICE_JOB_ENABLED: + return tidb_serverless_number = dify_config.TIDB_SERVERLESS_NUMBER start_at = time.perf_counter() while True: diff --git a/api/services/dataset_service.py b/api/services/dataset_service.py index 50da547fd8..806dbdf8c5 100644 --- a/api/services/dataset_service.py +++ b/api/services/dataset_service.py @@ -14,8 +14,6 @@ from configs import dify_config from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError from core.model_manager import ModelManager from core.model_runtime.entities.model_entities import ModelType -from core.rag.datasource.keyword.keyword_factory import Keyword -from core.rag.models.document import Document as RAGDocument from core.rag.retrieval.retrieval_methods import RetrievalMethod from events.dataset_event import dataset_was_deleted from events.document_event import document_was_deleted @@ -37,6 +35,7 @@ from models.dataset import ( ) from models.model import UploadFile from models.source import DataSourceOauthBinding +from services.entities.knowledge_entities.knowledge_entities import SegmentUpdateEntity from services.errors.account import NoPermissionError from services.errors.dataset import DatasetNameDuplicateError from services.errors.document import DocumentIndexingError @@ -1415,9 +1414,13 @@ class SegmentService: created_by=current_user.id, ) if document.doc_form == "qa_model": + segment_document.word_count += len(args["answer"]) segment_document.answer = args["answer"] db.session.add(segment_document) + # update document word count + document.word_count += segment_document.word_count + db.session.add(document) db.session.commit() # save vector index @@ -1436,6 +1439,7 @@ class SegmentService: @classmethod def multi_create_segment(cls, segments: list, document: Document, dataset: Dataset): lock_name = "multi_add_segment_lock_document_id_{}".format(document.id) + increment_word_count = 0 with redis_client.lock(lock_name, timeout=600): embedding_model = None if dataset.indexing_technique == "high_quality": @@ -1454,6 +1458,7 @@ class SegmentService: pre_segment_data_list = [] segment_data_list = [] keywords_list = [] + position = max_position + 1 if max_position else 1 for segment_item in segments: content = segment_item["content"] doc_id = str(uuid.uuid4()) @@ -1461,14 +1466,17 @@ class SegmentService: tokens = 0 if dataset.indexing_technique == "high_quality" and embedding_model: # calc embedding use tokens - tokens = embedding_model.get_text_embedding_num_tokens(texts=[content]) + if document.doc_form == "qa_model": + tokens = embedding_model.get_text_embedding_num_tokens(texts=[content + segment_item["answer"]]) + else: + tokens = embedding_model.get_text_embedding_num_tokens(texts=[content]) segment_document = DocumentSegment( tenant_id=current_user.current_tenant_id, dataset_id=document.dataset_id, document_id=document.id, index_node_id=doc_id, index_node_hash=segment_hash, - position=max_position + 1 if max_position else 1, + position=position, content=content, word_count=len(content), tokens=tokens, @@ -1479,15 +1487,20 @@ class SegmentService: ) if document.doc_form == "qa_model": segment_document.answer = segment_item["answer"] + segment_document.word_count += len(segment_item["answer"]) + increment_word_count += segment_document.word_count db.session.add(segment_document) segment_data_list.append(segment_document) + position += 1 pre_segment_data_list.append(segment_document) if "keywords" in segment_item: keywords_list.append(segment_item["keywords"]) else: keywords_list.append(None) - + # update document word count + document.word_count += increment_word_count + db.session.add(document) try: # save vector index VectorService.create_segments_vector(keywords_list, pre_segment_data_list, dataset) @@ -1503,12 +1516,13 @@ class SegmentService: @classmethod def update_segment(cls, args: dict, segment: DocumentSegment, document: Document, dataset: Dataset): + segment_update_entity = SegmentUpdateEntity(**args) indexing_cache_key = "segment_{}_indexing".format(segment.id) cache_result = redis_client.get(indexing_cache_key) if cache_result is not None: raise ValueError("Segment is indexing, please try again later") - if "enabled" in args and args["enabled"] is not None: - action = args["enabled"] + if segment_update_entity.enabled is not None: + action = segment_update_entity.enabled if segment.enabled != action: if not action: segment.enabled = action @@ -1521,37 +1535,34 @@ class SegmentService: disable_segment_from_index_task.delay(segment.id) return segment if not segment.enabled: - if "enabled" in args and args["enabled"] is not None: - if not args["enabled"]: + if segment_update_entity.enabled is not None: + if not segment_update_entity.enabled: raise ValueError("Can't update disabled segment") else: raise ValueError("Can't update disabled segment") try: - content = args["content"] + word_count_change = segment.word_count + content = segment_update_entity.content if segment.content == content: + segment.word_count = len(content) if document.doc_form == "qa_model": - segment.answer = args["answer"] - if args.get("keywords"): - segment.keywords = args["keywords"] + segment.answer = segment_update_entity.answer + segment.word_count += len(segment_update_entity.answer) + word_count_change = segment.word_count - word_count_change + if segment_update_entity.keywords: + segment.keywords = segment_update_entity.keywords segment.enabled = True segment.disabled_at = None segment.disabled_by = None db.session.add(segment) db.session.commit() + # update document word count + if word_count_change != 0: + document.word_count = max(0, document.word_count + word_count_change) + db.session.add(document) # update segment index task - if "keywords" in args: - keyword = Keyword(dataset) - keyword.delete_by_ids([segment.index_node_id]) - document = RAGDocument( - page_content=segment.content, - metadata={ - "doc_id": segment.index_node_id, - "doc_hash": segment.index_node_hash, - "document_id": segment.document_id, - "dataset_id": segment.dataset_id, - }, - ) - keyword.add_texts([document], keywords_list=[args["keywords"]]) + if segment_update_entity.enabled: + VectorService.create_segments_vector([segment_update_entity.keywords], [segment], dataset) else: segment_hash = helper.generate_text_hash(content) tokens = 0 @@ -1565,7 +1576,10 @@ class SegmentService: ) # calc embedding use tokens - tokens = embedding_model.get_text_embedding_num_tokens(texts=[content]) + if document.doc_form == "qa_model": + tokens = embedding_model.get_text_embedding_num_tokens(texts=[content + segment.answer]) + else: + tokens = embedding_model.get_text_embedding_num_tokens(texts=[content]) segment.content = content segment.index_node_hash = segment_hash segment.word_count = len(content) @@ -1579,11 +1593,17 @@ class SegmentService: segment.disabled_at = None segment.disabled_by = None if document.doc_form == "qa_model": - segment.answer = args["answer"] + segment.answer = segment_update_entity.answer + segment.word_count += len(segment_update_entity.answer) + word_count_change = segment.word_count - word_count_change + # update document word count + if word_count_change != 0: + document.word_count = max(0, document.word_count + word_count_change) + db.session.add(document) db.session.add(segment) db.session.commit() # update segment vector index - VectorService.update_segment_vector(args["keywords"], segment, dataset) + VectorService.update_segment_vector(segment_update_entity.keywords, segment, dataset) except Exception as e: logging.exception("update segment index failed") @@ -1608,6 +1628,9 @@ class SegmentService: redis_client.setex(indexing_cache_key, 600, 1) delete_segment_from_index_task.delay(segment.id, segment.index_node_id, dataset.id, document.id) db.session.delete(segment) + # update document word count + document.word_count -= segment.word_count + db.session.add(document) db.session.commit() diff --git a/api/services/entities/knowledge_entities/knowledge_entities.py b/api/services/entities/knowledge_entities/knowledge_entities.py new file mode 100644 index 0000000000..449b79f339 --- /dev/null +++ b/api/services/entities/knowledge_entities/knowledge_entities.py @@ -0,0 +1,10 @@ +from typing import Optional + +from pydantic import BaseModel + + +class SegmentUpdateEntity(BaseModel): + content: str + answer: Optional[str] = None + keywords: Optional[list[str]] = None + enabled: Optional[bool] = None diff --git a/api/services/tools/api_tools_manage_service.py b/api/services/tools/api_tools_manage_service.py index 4a93891855..b6b0143fac 100644 --- a/api/services/tools/api_tools_manage_service.py +++ b/api/services/tools/api_tools_manage_service.py @@ -113,8 +113,10 @@ class ApiToolManageService: if schema_type not in [member.value for member in ApiProviderSchemaType]: raise ValueError(f"invalid schema type {schema}") + provider_name = provider_name.strip() + # check if the provider exists - provider: ApiToolProvider = ( + provider = ( db.session.query(ApiToolProvider) .filter( ApiToolProvider.tenant_id == tenant_id, @@ -199,21 +201,21 @@ class ApiToolManageService: return {"schema": schema} @staticmethod - def list_api_tool_provider_tools(user_id: str, tenant_id: str, provider: str) -> list[UserTool]: + def list_api_tool_provider_tools(user_id: str, tenant_id: str, provider_name: str) -> list[UserTool]: """ list api tool provider tools """ - provider: ApiToolProvider = ( + provider = ( db.session.query(ApiToolProvider) .filter( ApiToolProvider.tenant_id == tenant_id, - ApiToolProvider.name == provider, + ApiToolProvider.name == provider_name, ) .first() ) if provider is None: - raise ValueError(f"you have not added provider {provider}") + raise ValueError(f"you have not added provider {provider_name}") controller = ToolTransformService.api_provider_to_controller(db_provider=provider) labels = ToolLabelManager.get_tool_labels(controller) @@ -246,8 +248,10 @@ class ApiToolManageService: if schema_type not in [member.value for member in ApiProviderSchemaType]: raise ValueError(f"invalid schema type {schema}") + provider_name = provider_name.strip() + # check if the provider exists - provider: ApiToolProvider = ( + provider = ( db.session.query(ApiToolProvider) .filter( ApiToolProvider.tenant_id == tenant_id, @@ -314,7 +318,7 @@ class ApiToolManageService: """ delete tool provider """ - provider: ApiToolProvider = ( + provider = ( db.session.query(ApiToolProvider) .filter( ApiToolProvider.tenant_id == tenant_id, @@ -364,7 +368,7 @@ class ApiToolManageService: if tool_bundle is None: raise ValueError(f"invalid tool name {tool_name}") - db_provider: ApiToolProvider = ( + db_provider = ( db.session.query(ApiToolProvider) .filter( ApiToolProvider.tenant_id == tenant_id, diff --git a/api/services/workflow/workflow_converter.py b/api/services/workflow/workflow_converter.py index 75c11afa94..90b5cc4836 100644 --- a/api/services/workflow/workflow_converter.py +++ b/api/services/workflow/workflow_converter.py @@ -13,7 +13,7 @@ from core.app.app_config.entities import ( from core.app.apps.agent_chat.app_config_manager import AgentChatAppConfigManager from core.app.apps.chat.app_config_manager import ChatAppConfigManager from core.app.apps.completion.app_config_manager import CompletionAppConfigManager -from core.file.models import FileExtraConfig +from core.file.models import FileUploadConfig from core.helper import encrypter from core.model_runtime.entities.llm_entities import LLMMode from core.model_runtime.utils.encoders import jsonable_encoder @@ -381,7 +381,7 @@ class WorkflowConverter: graph: dict, model_config: ModelConfigEntity, prompt_template: PromptTemplateEntity, - file_upload: Optional[FileExtraConfig] = None, + file_upload: Optional[FileUploadConfig] = None, external_data_variable_node_mapping: dict[str, str] | None = None, ) -> dict: """ diff --git a/api/tasks/batch_create_segment_to_index_task.py b/api/tasks/batch_create_segment_to_index_task.py index de7f0ddec1..d1b41f2675 100644 --- a/api/tasks/batch_create_segment_to_index_task.py +++ b/api/tasks/batch_create_segment_to_index_task.py @@ -57,7 +57,7 @@ def batch_create_segment_to_index_task( model_type=ModelType.TEXT_EMBEDDING, model=dataset.embedding_model, ) - + word_count_change = 0 for segment in content: content = segment["content"] doc_id = str(uuid.uuid4()) @@ -86,8 +86,13 @@ def batch_create_segment_to_index_task( ) if dataset_document.doc_form == "qa_model": segment_document.answer = segment["answer"] + segment_document.word_count += len(segment["answer"]) + word_count_change += segment_document.word_count db.session.add(segment_document) document_segments.append(segment_document) + # update document word count + dataset_document.word_count += word_count_change + db.session.add(dataset_document) # add index to db indexing_runner = IndexingRunner() indexing_runner.batch_add_segments(document_segments, dataset) diff --git a/api/tasks/clean_dataset_task.py b/api/tasks/clean_dataset_task.py index 3624903801..4d45df4d2a 100644 --- a/api/tasks/clean_dataset_task.py +++ b/api/tasks/clean_dataset_task.py @@ -5,6 +5,7 @@ import click from celery import shared_task from core.rag.index_processor.index_processor_factory import IndexProcessorFactory +from core.tools.utils.web_reader_tool import get_image_upload_file_ids from extensions.ext_database import db from extensions.ext_storage import storage from models.dataset import ( @@ -67,6 +68,16 @@ def clean_dataset_task( db.session.delete(document) for segment in segments: + image_upload_file_ids = get_image_upload_file_ids(segment.content) + for upload_file_id in image_upload_file_ids: + image_file = db.session.query(UploadFile).filter(UploadFile.id == upload_file_id).first() + try: + storage.delete(image_file.key) + except Exception: + logging.exception( + "Delete image_files failed when storage deleted, \ + image_upload_file_is: {}".format(upload_file_id) + ) db.session.delete(segment) db.session.query(DatasetProcessRule).filter(DatasetProcessRule.dataset_id == dataset_id).delete() diff --git a/api/tasks/clean_document_task.py b/api/tasks/clean_document_task.py index ae2855aa2e..54c89450c9 100644 --- a/api/tasks/clean_document_task.py +++ b/api/tasks/clean_document_task.py @@ -6,6 +6,7 @@ import click from celery import shared_task from core.rag.index_processor.index_processor_factory import IndexProcessorFactory +from core.tools.utils.web_reader_tool import get_image_upload_file_ids from extensions.ext_database import db from extensions.ext_storage import storage from models.dataset import Dataset, DocumentSegment @@ -40,6 +41,16 @@ def clean_document_task(document_id: str, dataset_id: str, doc_form: str, file_i index_processor.clean(dataset, index_node_ids) for segment in segments: + image_upload_file_ids = get_image_upload_file_ids(segment.content) + for upload_file_id in image_upload_file_ids: + image_file = db.session.query(UploadFile).filter(UploadFile.id == upload_file_id).first() + try: + storage.delete(image_file.key) + except Exception: + logging.exception( + "Delete image_files failed when storage deleted, \ + image_upload_file_is: {}".format(upload_file_id) + ) db.session.delete(segment) db.session.commit() diff --git a/api/tasks/document_indexing_task.py b/api/tasks/document_indexing_task.py index 72c4674e0f..df1177d578 100644 --- a/api/tasks/document_indexing_task.py +++ b/api/tasks/document_indexing_task.py @@ -25,7 +25,9 @@ def document_indexing_task(dataset_id: str, document_ids: list): start_at = time.perf_counter() dataset = db.session.query(Dataset).filter(Dataset.id == dataset_id).first() - + if not dataset: + logging.info(click.style("Dataset is not found: {}".format(dataset_id), fg="yellow")) + return # check document limit features = FeatureService.get_features(dataset.tenant_id) try: diff --git a/api/tasks/ops_trace_task.py b/api/tasks/ops_trace_task.py index 260069c6e2..34c62dc923 100644 --- a/api/tasks/ops_trace_task.py +++ b/api/tasks/ops_trace_task.py @@ -1,17 +1,20 @@ +import json import logging -import time from celery import shared_task from flask import current_app +from core.ops.entities.config_entity import OPS_FILE_PATH, OPS_TRACE_FAILED_KEY from core.ops.entities.trace_entity import trace_info_info_map from core.rag.models.document import Document +from extensions.ext_redis import redis_client +from extensions.ext_storage import storage from models.model import Message from models.workflow import WorkflowRun @shared_task(queue="ops_trace") -def process_trace_tasks(tasks_data): +def process_trace_tasks(file_info): """ Async process trace tasks :param tasks_data: List of dictionaries containing task data @@ -20,9 +23,12 @@ def process_trace_tasks(tasks_data): """ from core.ops.ops_trace_manager import OpsTraceManager - trace_info = tasks_data.get("trace_info") - app_id = tasks_data.get("app_id") - trace_info_type = tasks_data.get("trace_info_type") + app_id = file_info.get("app_id") + file_id = file_info.get("file_id") + file_path = f"{OPS_FILE_PATH}{app_id}/{file_id}.json" + file_data = json.loads(storage.load(file_path)) + trace_info = file_data.get("trace_info") + trace_info_type = file_data.get("trace_info_type") trace_instance = OpsTraceManager.get_ops_trace_instance(app_id) if trace_info.get("message_data"): @@ -39,6 +45,10 @@ def process_trace_tasks(tasks_data): if trace_type: trace_info = trace_type(**trace_info) trace_instance.trace(trace_info) - end_at = time.perf_counter() + logging.info(f"Processing trace tasks success, app_id: {app_id}") except Exception: - logging.exception("Processing trace tasks failed") + failed_key = f"{OPS_TRACE_FAILED_KEY}_{app_id}" + redis_client.incr(failed_key) + logging.info(f"Processing trace tasks failed, app_id: {app_id}") + finally: + storage.delete(file_path) diff --git a/api/tests/integration_tests/vdb/__mock/baiduvectordb.py b/api/tests/integration_tests/vdb/__mock/baiduvectordb.py index f20833ea56..0ea61369c0 100644 --- a/api/tests/integration_tests/vdb/__mock/baiduvectordb.py +++ b/api/tests/integration_tests/vdb/__mock/baiduvectordb.py @@ -1,4 +1,5 @@ import os +from collections import UserDict from unittest.mock import MagicMock import pytest @@ -11,7 +12,7 @@ from pymochow.model.table import Table from requests.adapters import HTTPAdapter -class AttrDict(dict): +class AttrDict(UserDict): def __getattr__(self, item): return self.get(item) diff --git a/api/tests/integration_tests/vdb/__mock/upstashvectordb.py b/api/tests/integration_tests/vdb/__mock/upstashvectordb.py index c93292bd8a..4b251ba836 100644 --- a/api/tests/integration_tests/vdb/__mock/upstashvectordb.py +++ b/api/tests/integration_tests/vdb/__mock/upstashvectordb.py @@ -1,4 +1,5 @@ import os +from collections import UserDict from typing import Optional import pytest @@ -50,7 +51,7 @@ class MockIndex: return AttrDict({"dimension": 1024}) -class AttrDict(dict): +class AttrDict(UserDict): def __getattr__(self, item): return self.get(item) diff --git a/api/tests/integration_tests/workflow/nodes/test_http.py b/api/tests/integration_tests/workflow/nodes/test_http.py index 0da6622658..9eea63f722 100644 --- a/api/tests/integration_tests/workflow/nodes/test_http.py +++ b/api/tests/integration_tests/workflow/nodes/test_http.py @@ -430,37 +430,3 @@ def test_multi_colons_parse(setup_http_mock): assert urlencode({"Redirect": "http://example2.com"}) in result.process_data.get("request", "") assert 'form-data; name="Redirect"\r\n\r\nhttp://example6.com' in result.process_data.get("request", "") # assert "http://example3.com" == resp.get("headers", {}).get("referer") - - -def test_image_file(monkeypatch): - from types import SimpleNamespace - - monkeypatch.setattr( - "core.tools.tool_file_manager.ToolFileManager.create_file_by_raw", - lambda *args, **kwargs: SimpleNamespace(id="1"), - ) - - node = init_http_node( - config={ - "id": "1", - "data": { - "title": "http", - "desc": "", - "method": "get", - "url": "https://cloud.dify.ai/logo/logo-site.png", - "authorization": { - "type": "no-auth", - "config": None, - }, - "params": "", - "headers": "", - "body": None, - }, - } - ) - - result = node._run() - assert result.process_data is not None - assert result.outputs is not None - resp = result.outputs - assert len(resp.get("files", [])) == 1 diff --git a/api/tests/unit_tests/core/app/app_config/features/file_upload/test_manager.py b/api/tests/unit_tests/core/app/app_config/features/file_upload/test_manager.py new file mode 100644 index 0000000000..50a612ec5f --- /dev/null +++ b/api/tests/unit_tests/core/app/app_config/features/file_upload/test_manager.py @@ -0,0 +1,61 @@ +from core.app.app_config.features.file_upload.manager import FileUploadConfigManager +from core.file.models import FileTransferMethod, FileUploadConfig, ImageConfig +from core.model_runtime.entities.message_entities import ImagePromptMessageContent + + +def test_convert_with_vision(): + config = { + "file_upload": { + "enabled": True, + "number_limits": 5, + "allowed_file_upload_methods": [FileTransferMethod.REMOTE_URL], + "image": {"detail": "high"}, + } + } + result = FileUploadConfigManager.convert(config, is_vision=True) + expected = FileUploadConfig( + image_config=ImageConfig( + number_limits=5, + transfer_methods=[FileTransferMethod.REMOTE_URL], + detail=ImagePromptMessageContent.DETAIL.HIGH, + ) + ) + assert result == expected + + +def test_convert_without_vision(): + config = { + "file_upload": { + "enabled": True, + "number_limits": 5, + "allowed_file_upload_methods": [FileTransferMethod.REMOTE_URL], + } + } + result = FileUploadConfigManager.convert(config, is_vision=False) + expected = FileUploadConfig( + image_config=ImageConfig(number_limits=5, transfer_methods=[FileTransferMethod.REMOTE_URL]) + ) + assert result == expected + + +def test_validate_and_set_defaults(): + config = {} + result, keys = FileUploadConfigManager.validate_and_set_defaults(config) + assert "file_upload" in result + assert keys == ["file_upload"] + + +def test_validate_and_set_defaults_with_existing_config(): + config = { + "file_upload": { + "enabled": True, + "number_limits": 5, + "allowed_file_upload_methods": [FileTransferMethod.REMOTE_URL], + } + } + result, keys = FileUploadConfigManager.validate_and_set_defaults(config) + assert "file_upload" in result + assert keys == ["file_upload"] + assert result["file_upload"]["enabled"] is True + assert result["file_upload"]["number_limits"] == 5 + assert result["file_upload"]["allowed_file_upload_methods"] == [FileTransferMethod.REMOTE_URL] diff --git a/api/tests/unit_tests/core/app/segments/test_segment.py b/api/tests/unit_tests/core/app/segments/test_segment.py index 3b1715ab45..1b035d01a7 100644 --- a/api/tests/unit_tests/core/app/segments/test_segment.py +++ b/api/tests/unit_tests/core/app/segments/test_segment.py @@ -1,5 +1,5 @@ from core.helper import encrypter -from core.variables import SecretVariable, StringSegment +from core.variables import SecretVariable, StringVariable from core.workflow.entities.variable_pool import VariablePool from core.workflow.enums import SystemVariableKey @@ -54,4 +54,5 @@ def test_convert_variable_to_segment_group(): segments_group = variable_pool.convert_template(template) assert segments_group.text == "fake-user-id" assert segments_group.log == "fake-user-id" - assert segments_group.value == [StringSegment(value="fake-user-id")] + assert isinstance(segments_group.value[0], StringVariable) + assert segments_group.value[0].value == "fake-user-id" diff --git a/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py b/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py index ece2173090..7d19cff3e8 100644 --- a/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py +++ b/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py @@ -3,7 +3,7 @@ from unittest.mock import MagicMock, patch import pytest from core.app.app_config.entities import ModelConfigEntity -from core.file import File, FileExtraConfig, FileTransferMethod, FileType, ImageConfig +from core.file import File, FileTransferMethod, FileType, FileUploadConfig, ImageConfig from core.memory.token_buffer_memory import TokenBufferMemory from core.model_runtime.entities.message_entities import ( AssistantPromptMessage, @@ -134,7 +134,6 @@ def test__get_chat_model_prompt_messages_with_files_no_memory(get_chat_model_arg type=FileType.IMAGE, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="https://example.com/image1.jpg", - _extra_config=FileExtraConfig(image_config=ImageConfig(detail=ImagePromptMessageContent.DETAIL.HIGH)), ) ] diff --git a/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py b/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py index 4f1f8f05c8..1a550ec530 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py @@ -140,6 +140,17 @@ def test_extract_text_from_plain_text(): assert text == "Hello, world!" +def test_extract_text_from_plain_text_non_utf8(): + import tempfile + + non_utf8_content = b"Hello, world\xa9." # \xA9 represents © in Latin-1 + with tempfile.NamedTemporaryFile(delete=True) as temp_file: + temp_file.write(non_utf8_content) + temp_file.seek(0) + text = _extract_text_from_plain_text(temp_file.read()) + assert text == "Hello, world." + + @patch("pypdfium2.PdfDocument") def test_extract_text_from_pdf(mock_pdf_document): mock_page = Mock() diff --git a/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py b/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py index 0f5c8bf51b..d20dfc5b31 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py @@ -4,7 +4,14 @@ import pytest from core.file import File, FileTransferMethod, FileType from core.variables import ArrayFileSegment -from core.workflow.nodes.list_operator.entities import FilterBy, FilterCondition, Limit, ListOperatorNodeData, OrderBy +from core.workflow.nodes.list_operator.entities import ( + ExtractConfig, + FilterBy, + FilterCondition, + Limit, + ListOperatorNodeData, + OrderBy, +) from core.workflow.nodes.list_operator.exc import InvalidKeyError from core.workflow.nodes.list_operator.node import ListOperatorNode, _get_file_extract_string_func from models.workflow import WorkflowNodeExecutionStatus @@ -22,6 +29,7 @@ def list_operator_node(): ), "order_by": OrderBy(enabled=False, value="asc"), "limit": Limit(enabled=False, size=0), + "extract_by": ExtractConfig(enabled=False, serial="1"), "title": "Test Title", } node_data = ListOperatorNodeData(**config) diff --git a/api/tests/unit_tests/oss/__mock/volcengine_tos.py b/api/tests/unit_tests/oss/__mock/volcengine_tos.py index 1194a03258..649d93a202 100644 --- a/api/tests/unit_tests/oss/__mock/volcengine_tos.py +++ b/api/tests/unit_tests/oss/__mock/volcengine_tos.py @@ -1,4 +1,5 @@ import os +from collections import UserDict from unittest.mock import MagicMock import pytest @@ -14,7 +15,7 @@ from tests.unit_tests.oss.__mock.base import ( ) -class AttrDict(dict): +class AttrDict(UserDict): def __getattr__(self, item): return self.get(item) diff --git a/docker-legacy/docker-compose.yaml b/docker-legacy/docker-compose.yaml index 88650194ec..9c2a1fe980 100644 --- a/docker-legacy/docker-compose.yaml +++ b/docker-legacy/docker-compose.yaml @@ -2,7 +2,7 @@ version: '3' services: # API service api: - image: langgenius/dify-api:0.11.0 + image: langgenius/dify-api:0.11.1 restart: always environment: # Startup mode, 'api' starts the API server. @@ -227,7 +227,7 @@ services: # worker service # The Celery worker for processing the queue. worker: - image: langgenius/dify-api:0.11.0 + image: langgenius/dify-api:0.11.1 restart: always environment: CONSOLE_WEB_URL: '' @@ -384,6 +384,7 @@ services: NOTION_INTERNAL_SECRET: you-internal-secret # Indexing configuration INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: 1000 + CREATE_TIDB_SERVICE_JOB_ENABLED: false depends_on: - db - redis @@ -396,7 +397,7 @@ services: # Frontend web application. web: - image: langgenius/dify-web:0.11.0 + image: langgenius/dify-web:0.11.1 restart: always environment: # The base URL of console application api server, refers to the Console base URL of WEB service if console domain is diff --git a/docker/.env.example b/docker/.env.example index 9a178dc44c..cf09f72bce 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -54,6 +54,10 @@ LOG_FILE= LOG_FILE_MAX_SIZE=20 # Log file max backup count LOG_FILE_BACKUP_COUNT=5 +# Log dateformat +LOG_DATEFORMAT=%Y-%m-%d %H:%M:%S +# Log Timezone +LOG_TZ=UTC # Debug mode, default is false. # It is recommended to turn on this configuration for local development @@ -583,12 +587,13 @@ CODE_GENERATION_MAX_TOKENS=1024 # Multi-modal Configuration # ------------------------------ -# The format of the image sent when the multi-modal model is input, +# The format of the image/video sent when the multi-modal model is input, # the default is base64, optional url. # The delay of the call in url mode will be lower than that in base64 mode. # It is generally recommended to use the more compatible base64 mode. -# If configured as url, you need to configure FILES_URL as an externally accessible address so that the multi-modal model can access the image. +# If configured as url, you need to configure FILES_URL as an externally accessible address so that the multi-modal model can access the image/video. MULTIMODAL_SEND_IMAGE_FORMAT=base64 +MULTIMODAL_SEND_VIDEO_FORMAT=base64 # Upload image file size limit, default 10M. UPLOAD_IMAGE_FILE_SIZE_LIMIT=10 @@ -684,6 +689,9 @@ TEMPLATE_TRANSFORM_MAX_LENGTH=80000 CODE_MAX_STRING_ARRAY_LENGTH=30 CODE_MAX_OBJECT_ARRAY_LENGTH=30 CODE_MAX_NUMBER_ARRAY_LENGTH=1000 +CODE_EXECUTION_CONNECT_TIMEOUT=10 +CODE_EXECUTION_READ_TIMEOUT=60 +CODE_EXECUTION_WRITE_TIMEOUT=10 # Workflow runtime configuration WORKFLOW_MAX_EXECUTION_STEPS=500 @@ -906,3 +914,6 @@ POSITION_PROVIDER_EXCLUDES= # CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP CSP_WHITELIST= + +# Enable or disable create tidb service job +CREATE_TIDB_SERVICE_JOB_ENABLED=false \ No newline at end of file diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 5a963b3beb..0de68c5299 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -4,6 +4,10 @@ x-shared-env: &shared-api-worker-env LOG_FILE: ${LOG_FILE:-} LOG_FILE_MAX_SIZE: ${LOG_FILE_MAX_SIZE:-20} LOG_FILE_BACKUP_COUNT: ${LOG_FILE_BACKUP_COUNT:-5} + # Log dateformat + LOG_DATEFORMAT: ${LOG_DATEFORMAT:-%Y-%m-%d %H:%M:%S} + # Log Timezone + LOG_TZ: ${LOG_TZ:-UTC} DEBUG: ${DEBUG:-false} FLASK_DEBUG: ${FLASK_DEBUG:-false} SECRET_KEY: ${SECRET_KEY:-sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U} @@ -214,6 +218,7 @@ x-shared-env: &shared-api-worker-env PROMPT_GENERATION_MAX_TOKENS: ${PROMPT_GENERATION_MAX_TOKENS:-512} CODE_GENERATION_MAX_TOKENS: ${CODE_GENERATION_MAX_TOKENS:-1024} MULTIMODAL_SEND_IMAGE_FORMAT: ${MULTIMODAL_SEND_IMAGE_FORMAT:-base64} + MULTIMODAL_SEND_VIDEO_FORMAT: ${MULTIMODAL_SEND_VIDEO_FORMAT:-base64} UPLOAD_IMAGE_FILE_SIZE_LIMIT: ${UPLOAD_IMAGE_FILE_SIZE_LIMIT:-10} UPLOAD_VIDEO_FILE_SIZE_LIMIT: ${UPLOAD_VIDEO_FILE_SIZE_LIMIT:-100} UPLOAD_AUDIO_FILE_SIZE_LIMIT: ${UPLOAD_AUDIO_FILE_SIZE_LIMIT:-50} @@ -239,6 +244,9 @@ x-shared-env: &shared-api-worker-env RESET_PASSWORD_TOKEN_EXPIRY_MINUTES: ${RESET_PASSWORD_TOKEN_EXPIRY_MINUTES:-5} CODE_EXECUTION_ENDPOINT: ${CODE_EXECUTION_ENDPOINT:-http://sandbox:8194} CODE_EXECUTION_API_KEY: ${SANDBOX_API_KEY:-dify-sandbox} + CODE_EXECUTION_CONNECT_TIMEOUT: ${CODE_EXECUTION_CONNECT_TIMEOUT:-10} + CODE_EXECUTION_READ_TIMEOUT: ${CODE_EXECUTION_READ_TIMEOUT:-60} + CODE_EXECUTION_WRITE_TIMEOUT: ${CODE_EXECUTION_WRITE_TIMEOUT:-10} CODE_MAX_NUMBER: ${CODE_MAX_NUMBER:-9223372036854775807} CODE_MIN_NUMBER: ${CODE_MIN_NUMBER:--9223372036854775808} CODE_MAX_DEPTH: ${CODE_MAX_DEPTH:-5} @@ -270,11 +278,12 @@ x-shared-env: &shared-api-worker-env OCEANBASE_VECTOR_DATABASE: ${OCEANBASE_VECTOR_DATABASE:-test} OCEANBASE_CLUSTER_NAME: ${OCEANBASE_CLUSTER_NAME:-difyai} OCEANBASE_MEMORY_LIMIT: ${OCEANBASE_MEMORY_LIMIT:-6G} + CREATE_TIDB_SERVICE_JOB_ENABLED: ${CREATE_TIDB_SERVICE_JOB_ENABLED:-false} services: # API service api: - image: langgenius/dify-api:0.11.0 + image: langgenius/dify-api:0.11.1 restart: always environment: # Use the shared environment variables. @@ -294,7 +303,7 @@ services: # worker service # The Celery worker for processing the queue. worker: - image: langgenius/dify-api:0.11.0 + image: langgenius/dify-api:0.11.1 restart: always environment: # Use the shared environment variables. @@ -313,7 +322,7 @@ services: # Frontend web application. web: - image: langgenius/dify-web:0.11.0 + image: langgenius/dify-web:0.11.1 restart: always environment: CONSOLE_API_URL: ${CONSOLE_API_URL:-} diff --git a/web/Dockerfile b/web/Dockerfile index 256e601e11..ba8148805d 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,5 +1,5 @@ # base image -FROM node:20.11-alpine3.19 AS base +FROM node:20-alpine3.20 AS base LABEL maintainer="takatost@gmail.com" # if you located in China, you can use aliyun mirror to speed up diff --git a/web/app/components/app/configuration/dataset-config/index.tsx b/web/app/components/app/configuration/dataset-config/index.tsx index 0d9d575c1e..78b49f81d0 100644 --- a/web/app/components/app/configuration/dataset-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/index.tsx @@ -47,12 +47,16 @@ const DatasetConfig: FC = () => { const { currentModel: currentRerankModel, + currentProvider: currentRerankProvider, } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank) const onRemove = (id: string) => { const filteredDataSets = dataSet.filter(item => item.id !== id) setDataSet(filteredDataSets) - const retrievalConfig = getMultipleRetrievalConfig(datasetConfigs as any, filteredDataSets, dataSet, !!currentRerankModel) + const retrievalConfig = getMultipleRetrievalConfig(datasetConfigs as any, filteredDataSets, dataSet, { + provider: currentRerankProvider?.provider, + model: currentRerankModel?.model, + }) setDatasetConfigs({ ...(datasetConfigs as any), ...retrievalConfig, diff --git a/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx b/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx index 75f0c33349..dcb2b1a3fd 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx @@ -25,7 +25,7 @@ import { useSelectedDatasetsMode } from '@/app/components/workflow/nodes/knowled import Switch from '@/app/components/base/switch' import Toast from '@/app/components/base/toast' -interface Props { +type Props = { datasetConfigs: DatasetConfigs onChange: (configs: DatasetConfigs, isRetrievalModeChange?: boolean) => void isInWorkflow?: boolean @@ -172,7 +172,7 @@ const ConfigContent: FC = ({ return false return datasetConfigs.reranking_enable - }, [canManuallyToggleRerank, datasetConfigs.reranking_enable]) + }, [canManuallyToggleRerank, datasetConfigs.reranking_enable, isRerankDefaultModelValid]) const handleDisabledSwitchClick = useCallback(() => { if (!currentRerankModel && !showRerankModel) diff --git a/web/app/components/app/configuration/dataset-config/params-config/index.tsx b/web/app/components/app/configuration/dataset-config/params-config/index.tsx index 7dd091e1c7..701b7053f5 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/index.tsx @@ -19,7 +19,7 @@ import { getMultipleRetrievalConfig, } from '@/app/components/workflow/nodes/knowledge-retrieval/utils' -interface ParamsConfigProps { +type ParamsConfigProps = { disabled?: boolean selectedDatasets: DataSet[] } @@ -43,6 +43,7 @@ const ParamsConfig = ({ const { defaultModel: rerankDefaultModel, currentModel: isRerankDefaultModelValid, + currentProvider: rerankDefaultProvider, } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank) const isValid = () => { @@ -91,7 +92,10 @@ const ParamsConfig = ({ reranking_mode: restConfigs.reranking_mode, weights: restConfigs.weights, reranking_enable: restConfigs.reranking_enable, - }, selectedDatasets, selectedDatasets, !!isRerankDefaultModelValid) + }, selectedDatasets, selectedDatasets, { + provider: rerankDefaultProvider?.provider, + model: isRerankDefaultModelValid?.model, + }) setTempDataSetConfigs({ ...retrievalConfig, @@ -135,11 +139,11 @@ const ParamsConfig = ({ />
- - +
) diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index 0474887f32..6bbab4a6e0 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -228,6 +228,7 @@ const Configuration: FC = () => { const [rerankSettingModalOpen, setRerankSettingModalOpen] = useState(false) const { currentModel: currentRerankModel, + currentProvider: currentRerankProvider, } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank) const handleSelect = (data: DataSet[]) => { if (isEqual(data.map(item => item.id), dataSets.map(item => item.id))) { @@ -281,7 +282,10 @@ const Configuration: FC = () => { reranking_mode: restConfigs.reranking_mode, weights: restConfigs.weights, reranking_enable: restConfigs.reranking_enable, - }, newDatasets, dataSets, !!currentRerankModel) + }, newDatasets, dataSets, { + provider: currentRerankProvider?.provider, + model: currentRerankModel?.model, + }) setDatasetConfigs({ ...retrievalConfig, @@ -636,7 +640,10 @@ const Configuration: FC = () => { syncToPublishedConfig(config) setPublishedConfig(config) - const retrievalConfig = getMultipleRetrievalConfig(modelConfig.dataset_configs, datasets, datasets, !!currentRerankModel) + const retrievalConfig = getMultipleRetrievalConfig(modelConfig.dataset_configs, datasets, datasets, { + provider: currentRerankProvider?.provider, + model: currentRerankModel?.model, + }) setDatasetConfigs({ retrieval_model: RETRIEVE_TYPE.multiWay, ...modelConfig.dataset_configs, diff --git a/web/app/components/app/overview/settings/index.tsx b/web/app/components/app/overview/settings/index.tsx index 989f786585..f23100a796 100644 --- a/web/app/components/app/overview/settings/index.tsx +++ b/web/app/components/app/overview/settings/index.tsx @@ -22,7 +22,7 @@ import AppContext, { useAppContext } from '@/context/app-context' import type { AppIconSelection } from '@/app/components/base/app-icon-picker' import AppIconPicker from '@/app/components/base/app-icon-picker' -export interface ISettingsModalProps { +export type ISettingsModalProps = { isChat: boolean appInfo: AppDetailResponse & Partial isShow: boolean @@ -31,7 +31,7 @@ export interface ISettingsModalProps { onSave?: (params: ConfigParams) => Promise } -export interface ConfigParams { +export type ConfigParams = { title: string description: string default_language: string @@ -243,7 +243,7 @@ const SettingsModal: FC = ({

{t(`${prefixSettings}.workflow.title`)}

-
{t(`${prefixSettings}.workflow.subTitle`)}
+
{t(`${prefixSettings}.workflow.subTitle`)}
= ({ onChange={onChange('chatColorTheme')} placeholder='E.g #A020F0' /> +
+

{t(`${prefixSettings}.chatColorThemeInverted`)}

+ setInputInfo({ ...inputInfo, chatColorThemeInverted: v })}> +
} {systemFeatures.enable_web_sso_switch_component &&

{t(`${prefixSettings}.sso.label`)}

-
{t(`${prefixSettings}.sso.title`)}
+
{t(`${prefixSettings}.sso.title`)}
= ({
} {!isShowMore &&
setIsShowMore(true)}>
-
{t(`${prefixSettings}.more.entry`)}
-
+
{t(`${prefixSettings}.more.entry`)}
+
diff --git a/web/app/components/base/chat/chat-with-history/hooks.tsx b/web/app/components/base/chat/chat-with-history/hooks.tsx index d4fa170e4c..a67cc3cd88 100644 --- a/web/app/components/base/chat/chat-with-history/hooks.tsx +++ b/web/app/components/base/chat/chat-with-history/hooks.tsx @@ -173,7 +173,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { const conversationInputs: Record = {} inputsForms.forEach((item: any) => { - conversationInputs[item.variable] = item.default || '' + conversationInputs[item.variable] = item.default || null }) handleNewConversationInputsChange(conversationInputs) }, [handleNewConversationInputsChange, inputsForms]) diff --git a/web/app/components/base/chat/embedded-chatbot/hooks.tsx b/web/app/components/base/chat/embedded-chatbot/hooks.tsx index 631d3b56bc..0a8bc0993f 100644 --- a/web/app/components/base/chat/embedded-chatbot/hooks.tsx +++ b/web/app/components/base/chat/embedded-chatbot/hooks.tsx @@ -159,7 +159,7 @@ export const useEmbeddedChatbot = () => { const conversationInputs: Record = {} inputsForms.forEach((item: any) => { - conversationInputs[item.variable] = item.default || '' + conversationInputs[item.variable] = item.default || null }) handleNewConversationInputsChange(conversationInputs) }, [handleNewConversationInputsChange, inputsForms]) diff --git a/web/app/components/base/markdown-blocks/form.tsx b/web/app/components/base/markdown-blocks/form.tsx index f87f2dcd91..7ce3e82b1d 100644 --- a/web/app/components/base/markdown-blocks/form.tsx +++ b/web/app/components/base/markdown-blocks/form.tsx @@ -1,3 +1,4 @@ +import React, { useEffect, useState } from 'react' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import Textarea from '@/app/components/base/textarea' @@ -32,20 +33,31 @@ const MarkdownForm = ({ node }: any) => { // const { onSend } = useChatContext() - const getFormValues = (children: any) => { - const formValues: { [key: string]: any } = {} - children.forEach((child: any) => { - if (child.tagName === SUPPORTED_TAGS.INPUT) - formValues[child.properties.name] = child.properties.value - if (child.tagName === SUPPORTED_TAGS.TEXTAREA) - formValues[child.properties.name] = child.properties.value + const [formValues, setFormValues] = useState<{ [key: string]: any }>({}) + + useEffect(() => { + const initialValues: { [key: string]: any } = {} + node.children.forEach((child: any) => { + if ([SUPPORTED_TAGS.INPUT, SUPPORTED_TAGS.TEXTAREA].includes(child.tagName)) + initialValues[child.properties.name] = child.properties.value }) - return formValues + setFormValues(initialValues) + }, [node.children]) + + const getFormValues = (children: any) => { + const values: { [key: string]: any } = {} + children.forEach((child: any) => { + if ([SUPPORTED_TAGS.INPUT, SUPPORTED_TAGS.TEXTAREA].includes(child.tagName)) + values[child.properties.name] = formValues[child.properties.name] + }) + return values } + const onSubmit = (e: any) => { e.preventDefault() const format = node.properties.dataFormat || DATA_FORMAT.TEXT const result = getFormValues(node.children) + if (format === DATA_FORMAT.JSON) { onSend?.(JSON.stringify(result)) } @@ -77,25 +89,22 @@ const MarkdownForm = ({ node }: any) => { ) } - if (child.tagName === SUPPORTED_TAGS.INPUT) { - if (Object.values(SUPPORTED_TYPES).includes(child.properties.type)) { - return ( - { - e.preventDefault() - child.properties.value = e.target.value - }} - /> - ) - } - else { - return

Unsupported input type: {child.properties.type}

- } + if (child.tagName === SUPPORTED_TAGS.INPUT && Object.values(SUPPORTED_TYPES).includes(child.properties.type)) { + return ( + { + setFormValues(prevValues => ({ + ...prevValues, + [child.properties.name]: e.target.value, + })) + }} + /> + ) } if (child.tagName === SUPPORTED_TAGS.TEXTAREA) { return ( @@ -103,10 +112,12 @@ const MarkdownForm = ({ node }: any) => { key={index} name={child.properties.name} placeholder={child.properties.placeholder} - value={child.properties.value} + value={formValues[child.properties.name]} onChange={(e) => { - e.preventDefault() - child.properties.value = e.target.value + setFormValues(prevValues => ({ + ...prevValues, + [child.properties.name]: e.target.value, + })) }} /> ) diff --git a/web/app/components/base/prompt-editor/constants.tsx b/web/app/components/base/prompt-editor/constants.tsx index 00360c7bc1..78568aba7b 100644 --- a/web/app/components/base/prompt-editor/constants.tsx +++ b/web/app/components/base/prompt-editor/constants.tsx @@ -53,6 +53,6 @@ export const getInputVars = (text: string): ValueSelector[] => { export const FILE_EXTS: Record = { [SupportUploadFileTypes.image]: ['JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'], [SupportUploadFileTypes.document]: ['TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'], - [SupportUploadFileTypes.audio]: ['MP3', 'M4A', 'WAV', 'WEBM', 'AMR'], + [SupportUploadFileTypes.audio]: ['MP3', 'M4A', 'WAV', 'WEBM', 'AMR', 'MPGA'], [SupportUploadFileTypes.video]: ['MP4', 'MOV', 'MPEG', 'MPGA'], } diff --git a/web/app/components/develop/template/template_advanced_chat.en.mdx b/web/app/components/develop/template/template_advanced_chat.en.mdx index 6642c5cedc..c3c3f7c6f3 100644 --- a/web/app/components/develop/template/template_advanced_chat.en.mdx +++ b/web/app/components/develop/template/template_advanced_chat.en.mdx @@ -774,6 +774,10 @@ Chat applications support session persistence, allowing previous chat history to ### Request Body + Rename the session, the session name is used for display on clients that support multiple sessions. + + ### Path + - `conversation_id` (string) Conversation ID @@ -796,10 +800,10 @@ Chat applications support session persistence, allowing previous chat history to - + ```bash {{ title: 'cURL' }} - curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \ + curl -X POST '${props.appDetail.api_base_url}/conversations/{conversation_id}/name' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer {api_key}' \ --data-raw '{ diff --git a/web/app/components/develop/template/template_advanced_chat.zh.mdx b/web/app/components/develop/template/template_advanced_chat.zh.mdx index 8e64d63ac5..f3ddd6933c 100755 --- a/web/app/components/develop/template/template_advanced_chat.zh.mdx +++ b/web/app/components/develop/template/template_advanced_chat.zh.mdx @@ -810,6 +810,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' 对会话进行重命名,会话名称用于显示在支持多会话的客户端上。 + ### Path + - `conversation_id` (string) 会话 ID + ### Request Body @@ -833,10 +836,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - + ```bash {{ title: 'cURL' }} - curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \ + curl -X POST '${props.appDetail.api_base_url}/conversations/{conversation_id}/name' \ --header 'Authorization: Bearer {api_key}' \ --header 'Content-Type: application/json' \ --data-raw '{ diff --git a/web/app/components/develop/template/template_chat.en.mdx b/web/app/components/develop/template/template_chat.en.mdx index a94016ca3a..f44f991b89 100644 --- a/web/app/components/develop/template/template_chat.en.mdx +++ b/web/app/components/develop/template/template_chat.en.mdx @@ -808,6 +808,10 @@ Chat applications support session persistence, allowing previous chat history to ### Request Body + Rename the session, the session name is used for display on clients that support multiple sessions. + + ### Path + - `conversation_id` (string) Conversation ID @@ -830,10 +834,10 @@ Chat applications support session persistence, allowing previous chat history to - + ```bash {{ title: 'cURL' }} - curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \ + curl -X POST '${props.appDetail.api_base_url}/conversations/{conversation_id}/name' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer {api_key}' \ --data-raw '{ diff --git a/web/app/components/develop/template/template_chat.zh.mdx b/web/app/components/develop/template/template_chat.zh.mdx index 92b13b2c7d..c786d56980 100644 --- a/web/app/components/develop/template/template_chat.zh.mdx +++ b/web/app/components/develop/template/template_chat.zh.mdx @@ -824,6 +824,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' 对会话进行重命名,会话名称用于显示在支持多会话的客户端上。 + ### Path + - `conversation_id` (string) 会话 ID + ### Request Body @@ -847,10 +850,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - + ```bash {{ title: 'cURL' }} - curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \ + curl -X POST '${props.appDetail.api_base_url}/conversations/{conversation_id}/name' \ --header 'Authorization: Bearer {api_key}' \ --header 'Content-Type: application/json' \ --data-raw '{ diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index 5e9b2a7a67..5bfb6c0516 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -46,17 +46,17 @@ enum TaskStatus { failed = 'failed', } -interface TaskParam { +type TaskParam = { inputs: Record } -interface Task { +type Task = { id: number status: TaskStatus params: TaskParam } -export interface IMainProps { +export type IMainProps = { isInstalledApp?: boolean installedAppInfo?: InstalledApp isWorkflow?: boolean @@ -94,6 +94,7 @@ const TextGeneration: FC = ({ const [isCallBatchAPI, setIsCallBatchAPI] = useState(false) const isInBatchTab = currentTab === 'batch' const [inputs, setInputs] = useState>({}) + const inputsRef = useRef(inputs) const [appId, setAppId] = useState('') const [siteInfo, setSiteInfo] = useState(null) const [canReplaceLogo, setCanReplaceLogo] = useState(false) @@ -133,8 +134,10 @@ const TextGeneration: FC = ({ setIsCallBatchAPI(false) setControlSend(Date.now()) + // eslint-disable-next-line ts/no-use-before-define setAllTaskList([]) // clear batch task running status + // eslint-disable-next-line ts/no-use-before-define showResSidebar() } @@ -315,6 +318,7 @@ const TextGeneration: FC = ({ // clear run once task status setControlStopResponding(Date.now()) + // eslint-disable-next-line ts/no-use-before-define showResSidebar() } const handleCompleted = (completionRes: string, taskId?: number, isSuccess?: boolean) => { @@ -604,6 +608,7 @@ const TextGeneration: FC = ({ + inputsRef: React.MutableRefObject> onInputsChange: (inputs: Record) => void onSend: () => void visionConfig: VisionSettings @@ -27,6 +28,7 @@ export interface IRunOnceProps { const RunOnce: FC = ({ promptConfig, inputs, + inputsRef, onInputsChange, onSend, visionConfig, @@ -47,6 +49,11 @@ const RunOnce: FC = ({ onSend() } + const handleInputsChange = useCallback((newInputs: Record) => { + onInputsChange(newInputs) + inputsRef.current = newInputs + }, [onInputsChange, inputsRef]) + return (
@@ -60,7 +67,7 @@ const RunOnce: FC = ({ +
+ ) +} +export default React.memo(ExtractInput) diff --git a/web/app/components/workflow/nodes/list-operator/default.ts b/web/app/components/workflow/nodes/list-operator/default.ts index a7d411420c..fe8773a914 100644 --- a/web/app/components/workflow/nodes/list-operator/default.ts +++ b/web/app/components/workflow/nodes/list-operator/default.ts @@ -12,6 +12,10 @@ const nodeDefault: NodeDefault = { enabled: false, conditions: [], }, + extract_by: { + enabled: false, + serial: '1', + }, order_by: { enabled: false, key: '', diff --git a/web/app/components/workflow/nodes/list-operator/panel.tsx b/web/app/components/workflow/nodes/list-operator/panel.tsx index 0930727f1b..f04cb2b7b8 100644 --- a/web/app/components/workflow/nodes/list-operator/panel.tsx +++ b/web/app/components/workflow/nodes/list-operator/panel.tsx @@ -13,6 +13,7 @@ import FilterCondition from './components/filter-condition' import Field from '@/app/components/workflow/nodes/_base/components/field' import type { NodePanelProps } from '@/app/components/workflow/types' import Switch from '@/app/components/base/switch' +import ExtractInput from '@/app/components/workflow/nodes/list-operator/components/extract-input' const i18nPrefix = 'workflow.nodes.listFilter' @@ -32,6 +33,8 @@ const Panel: FC> = ({ filterVar, handleFilterEnabledChange, handleFilterChange, + handleExtractsEnabledChange, + handleExtractsChange, handleLimitChange, handleOrderByEnabledChange, handleOrderByKeyChange, @@ -79,6 +82,41 @@ const Panel: FC> = ({ : null} + + } + > + {inputs.extract_by?.enabled + ? ( +
+ {hasSubVariable && ( +
+ +
+ )} +
+ ) + : null} +
+ + + > = ({ : null} -
-
<> diff --git a/web/app/components/workflow/nodes/list-operator/types.ts b/web/app/components/workflow/nodes/list-operator/types.ts index dcd71b6956..770590329a 100644 --- a/web/app/components/workflow/nodes/list-operator/types.ts +++ b/web/app/components/workflow/nodes/list-operator/types.ts @@ -25,6 +25,10 @@ export type ListFilterNodeType = CommonNodeType & { enabled: boolean conditions: Condition[] } + extract_by: { + enabled: boolean + serial?: string + } order_by: { enabled: boolean key: ValueSelector | string diff --git a/web/app/components/workflow/nodes/list-operator/use-config.ts b/web/app/components/workflow/nodes/list-operator/use-config.ts index 694ce9d49a..00defe7a84 100644 --- a/web/app/components/workflow/nodes/list-operator/use-config.ts +++ b/web/app/components/workflow/nodes/list-operator/use-config.ts @@ -119,6 +119,22 @@ const useConfig = (id: string, payload: ListFilterNodeType) => { setInputs(newInputs) }, [inputs, setInputs]) + const handleExtractsEnabledChange = useCallback((enabled: boolean) => { + const newInputs = produce(inputs, (draft) => { + draft.extract_by.enabled = enabled + if (enabled) + draft.extract_by.serial = '1' + }) + setInputs(newInputs) + }, [inputs, setInputs]) + + const handleExtractsChange = useCallback((value: string) => { + const newInputs = produce(inputs, (draft) => { + draft.extract_by.serial = value + }) + setInputs(newInputs) + }, [inputs, setInputs]) + const handleOrderByEnabledChange = useCallback((enabled: boolean) => { const newInputs = produce(inputs, (draft) => { draft.order_by.enabled = enabled @@ -162,6 +178,8 @@ const useConfig = (id: string, payload: ListFilterNodeType) => { handleOrderByEnabledChange, handleOrderByKeyChange, handleOrderByTypeChange, + handleExtractsEnabledChange, + handleExtractsChange, } } diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx index 361f9d6bf4..d560c0b2cb 100644 --- a/web/app/components/workflow/panel/workflow-preview.tsx +++ b/web/app/components/workflow/panel/workflow-preview.tsx @@ -28,7 +28,7 @@ import IterationResultPanel from '../run/iteration-result-panel' import InputsPanel from './inputs-panel' import cn from '@/utils/classnames' import Loading from '@/app/components/base/loading' -import type { NodeTracing } from '@/types/workflow' +import type { IterationDurationMap, NodeTracing } from '@/types/workflow' const WorkflowPreview = () => { const { t } = useTranslation() @@ -53,12 +53,14 @@ const WorkflowPreview = () => { }, [workflowRunningData]) const [iterationRunResult, setIterationRunResult] = useState([]) + const [iterDurationMap, setIterDurationMap] = useState({}) const [isShowIterationDetail, { setTrue: doShowIterationDetail, setFalse: doHideIterationDetail, }] = useBoolean(false) - const handleShowIterationDetail = useCallback((detail: NodeTracing[][]) => { + const handleShowIterationDetail = useCallback((detail: NodeTracing[][], iterationDurationMap: IterationDurationMap) => { + setIterDurationMap(iterationDurationMap) setIterationRunResult(detail) doShowIterationDetail() }, [doShowIterationDetail]) @@ -72,6 +74,7 @@ const WorkflowPreview = () => { list={iterationRunResult} onHide={doHideIterationDetail} onBack={doHideIterationDetail} + iterDurationMap={iterDurationMap} />
) @@ -94,6 +97,7 @@ const WorkflowPreview = () => { list={iterationRunResult} onHide={doHideIterationDetail} onBack={doHideIterationDetail} + iterDurationMap={iterDurationMap} /> ) : ( diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx index 6e269c2714..5267cf257d 100644 --- a/web/app/components/workflow/run/index.tsx +++ b/web/app/components/workflow/run/index.tsx @@ -13,11 +13,11 @@ import cn from '@/utils/classnames' import { ToastContext } from '@/app/components/base/toast' import Loading from '@/app/components/base/loading' import { fetchRunDetail, fetchTracingList } from '@/service/log' -import type { NodeTracing } from '@/types/workflow' +import type { IterationDurationMap, NodeTracing } from '@/types/workflow' import type { WorkflowRunDetailResponse } from '@/models/log' import { useStore as useAppStore } from '@/app/components/app/store' -export interface RunProps { +export type RunProps = { hideResult?: boolean activeTab?: 'RESULT' | 'DETAIL' | 'TRACING' runID: string @@ -172,15 +172,17 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe }, [loading]) const [iterationRunResult, setIterationRunResult] = useState([]) + const [iterDurationMap, setIterDurationMap] = useState({}) const [isShowIterationDetail, { setTrue: doShowIterationDetail, setFalse: doHideIterationDetail, }] = useBoolean(false) - const handleShowIterationDetail = useCallback((detail: NodeTracing[][]) => { + const handleShowIterationDetail = useCallback((detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => { setIterationRunResult(detail) doShowIterationDetail() - }, [doShowIterationDetail]) + setIterDurationMap(iterDurationMap) + }, [doShowIterationDetail, setIterationRunResult, setIterDurationMap]) if (isShowIterationDetail) { return ( @@ -189,6 +191,7 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe list={iterationRunResult} onHide={doHideIterationDetail} onBack={doHideIterationDetail} + iterDurationMap={iterDurationMap} />
) diff --git a/web/app/components/workflow/run/iteration-result-panel.tsx b/web/app/components/workflow/run/iteration-result-panel.tsx index 44b8ac6b84..f71adbbece 100644 --- a/web/app/components/workflow/run/iteration-result-panel.tsx +++ b/web/app/components/workflow/run/iteration-result-panel.tsx @@ -6,19 +6,22 @@ import { RiArrowRightSLine, RiCloseLine, RiErrorWarningLine, + RiLoader2Line, } from '@remixicon/react' import { ArrowNarrowLeft } from '../../base/icons/src/vender/line/arrows' +import { NodeRunningStatus } from '../types' import TracingPanel from './tracing-panel' import { Iteration } from '@/app/components/base/icons/src/vender/workflow' import cn from '@/utils/classnames' -import type { NodeTracing } from '@/types/workflow' +import type { IterationDurationMap, NodeTracing } from '@/types/workflow' const i18nPrefix = 'workflow.singleRun' -interface Props { +type Props = { list: NodeTracing[][] onHide: () => void onBack: () => void noWrap?: boolean + iterDurationMap?: IterationDurationMap } const IterationResultPanel: FC = ({ @@ -26,6 +29,7 @@ const IterationResultPanel: FC = ({ onHide, onBack, noWrap, + iterDurationMap, }) => { const { t } = useTranslation() const [expandedIterations, setExpandedIterations] = useState>({}) @@ -36,6 +40,40 @@ const IterationResultPanel: FC = ({ [index]: !prev[index], })) }, []) + const countIterDuration = (iteration: NodeTracing[], iterDurationMap: IterationDurationMap): string => { + const IterRunIndex = iteration[0].execution_metadata.iteration_index as number + const iterRunId = iteration[0].execution_metadata.parallel_mode_run_id + const iterItem = iterDurationMap[iterRunId || IterRunIndex] + const duration = iterItem + return `${(duration && duration > 0.01) ? duration.toFixed(2) : 0.01}s` + } + const iterationStatusShow = (index: number, iteration: NodeTracing[], iterDurationMap?: IterationDurationMap) => { + const hasFailed = iteration.some(item => item.status === NodeRunningStatus.Failed) + const isRunning = iteration.some(item => item.status === NodeRunningStatus.Running) + const hasDurationMap = iterDurationMap && Object.keys(iterDurationMap).length !== 0 + + if (hasFailed) + return + + if (isRunning) + return + + return ( + <> + {hasDurationMap && ( +
+ {countIterDuration(iteration, iterDurationMap)} +
+ )} + + + ) + } const main = ( <> @@ -66,29 +104,17 @@ const IterationResultPanel: FC = ({ onClick={() => toggleIteration(index)} >
-
+
- + {t(`${i18nPrefix}.iteration`)} {index + 1} - { - iteration.some(item => item.status === 'failed') - ? ( - - ) - : (< RiArrowRightSLine className={ - cn( - 'w-4 h-4 text-text-tertiary transition-transform duration-200 flex-shrink-0', - expandedIterations[index] && 'transform rotate-90', - )} /> - ) - } - + {iterationStatusShow(index, iteration, iterDurationMap)}
{expandedIterations[index] &&
}
void + onShowIterationDetail?: (detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => void notShowIterationNav?: boolean justShowIterationNavArrow?: boolean } @@ -90,7 +90,7 @@ const NodePanel: FC = ({ const handleOnShowIterationDetail = (e: React.MouseEvent) => { e.stopPropagation() e.nativeEvent.stopImmediatePropagation() - onShowIterationDetail?.(nodeInfo.details || []) + onShowIterationDetail?.(nodeInfo.details || [], nodeInfo?.iterDurationMap || nodeInfo.execution_metadata?.iteration_duration_map || {}) } return (
@@ -144,7 +144,7 @@ const NodePanel: FC = ({ className='flex items-center w-full self-stretch gap-2 px-3 py-2 bg-components-button-tertiary-bg-hover hover:bg-components-button-tertiary-bg-hover rounded-lg cursor-pointer border-none' onClick={handleOnShowIterationDetail} > - +
{t('workflow.nodes.iteration.iteration', { count: getCount(nodeInfo.details?.length, nodeInfo.metadata?.iterator_length) })}{getErrorCount(nodeInfo.details) > 0 && ( <> {t('workflow.nodes.iteration.comma')} @@ -153,12 +153,12 @@ const NodePanel: FC = ({ )}
{justShowIterationNavArrow ? ( - + ) : (
{t('workflow.common.viewDetailInTracingPanel')}
- +
)} diff --git a/web/app/components/workflow/run/tracing-panel.tsx b/web/app/components/workflow/run/tracing-panel.tsx index 613c10198d..3f1c2465cf 100644 --- a/web/app/components/workflow/run/tracing-panel.tsx +++ b/web/app/components/workflow/run/tracing-panel.tsx @@ -16,11 +16,11 @@ import NodePanel from './node' import { BlockEnum, } from '@/app/components/workflow/types' -import type { NodeTracing } from '@/types/workflow' +import type { IterationDurationMap, NodeTracing } from '@/types/workflow' type TracingPanelProps = { list: NodeTracing[] - onShowIterationDetail?: (detail: NodeTracing[][]) => void + onShowIterationDetail?: (detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => void className?: string hideNodeInfo?: boolean hideNodeProcessDetail?: boolean @@ -57,7 +57,7 @@ function buildLogTree(nodes: NodeTracing[], t: (key: string) => string): Tracing levelCounts[levelKey]++ const parentTitle = parentId ? parallelStacks[parentId]?.parallelTitle : '' - const levelNumber = parentTitle ? parseInt(parentTitle.split('-')[1]) + 1 : 1 + const levelNumber = parentTitle ? Number.parseInt(parentTitle.split('-')[1]) + 1 : 1 const letter = parallelChildCounts[levelKey]?.size > 1 ? String.fromCharCode(64 + levelCounts[levelKey]) : '' return `${t('workflow.common.parallel')}-${levelNumber}${letter}` } @@ -65,7 +65,7 @@ function buildLogTree(nodes: NodeTracing[], t: (key: string) => string): Tracing const getBranchTitle = (parentId: string | null, branchNum: number): string => { const levelKey = parentId || 'root' const parentTitle = parentId ? parallelStacks[parentId]?.parallelTitle : '' - const levelNumber = parentTitle ? parseInt(parentTitle.split('-')[1]) + 1 : 1 + const levelNumber = parentTitle ? Number.parseInt(parentTitle.split('-')[1]) + 1 : 1 const letter = parallelChildCounts[levelKey]?.size > 1 ? String.fromCharCode(64 + levelCounts[levelKey]) : '' const branchLetter = String.fromCharCode(64 + branchNum) return `${t('workflow.common.branch')}-${levelNumber}${letter}-${branchLetter}` @@ -227,7 +227,7 @@ const TracingPanel: FC = ({ {node.parallelTitle}
diff --git a/web/i18n/README.md b/web/i18n/README.md index 6e79be6b9b..9384ffc519 100644 --- a/web/i18n/README.md +++ b/web/i18n/README.md @@ -72,7 +72,7 @@ export type I18nText = { } ``` -4. Add the new language to the `language.ts` file. +4. Add the new language to the `language.json` file. ```typescript diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 7f237b1a49..ed20adbfc9 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -369,6 +369,7 @@ const translation = { inputVars: 'Input Variables', api: 'API', apiPlaceholder: 'Enter URL, type ‘/’ insert variable', + extractListPlaceholder: 'Enter list item index, type ‘/’ insert variable', notStartWithHttp: 'API should start with http:// or https://', key: 'Key', type: 'Type', @@ -568,9 +569,9 @@ const translation = { MaxParallelismDesc: 'The maximum parallelism is used to control the number of tasks executed simultaneously in a single iteration.', errorResponseMethod: 'Error response method', ErrorMethod: { - operationTerminated: 'terminated', - continueOnError: 'continue-on-error', - removeAbnormalOutput: 'remove-abnormal-output', + operationTerminated: 'Terminated', + continueOnError: 'Continue on Error', + removeAbnormalOutput: 'Remove Abnormal Output', }, answerNodeWarningDesc: 'Parallel mode warning: Answer nodes, conversation variable assignments, and persistent read/write operations within iterations may cause exceptions.', }, @@ -605,6 +606,7 @@ const translation = { inputVar: 'Input Variable', filterCondition: 'Filter Condition', filterConditionKey: 'Filter Condition Key', + extractsCondition: 'Extract the N item', filterConditionComparisonOperator: 'Filter Condition Comparison Operator', filterConditionComparisonValue: 'Filter Condition value', selectVariableKeyPlaceholder: 'Select sub variable key', diff --git a/web/i18n/language.ts b/web/i18n/language.ts index fde69328bd..e83088346c 100644 --- a/web/i18n/language.ts +++ b/web/i18n/language.ts @@ -24,6 +24,7 @@ export type I18nText = { 'pl-PL': string 'hi-IN': string 'fa-IR': string + 'sl-SI': string } export const languages = data.languages @@ -53,6 +54,7 @@ export const NOTICE_I18N = { vi_VN: 'Thông báo quan trọng', it_IT: 'Avviso Importante', fa_IR: 'هشدار مهم', + sl_SI: 'Pomembno obvestilo', }, desc: { en_US: @@ -83,6 +85,8 @@ export const NOTICE_I18N = { 'Sistemimiz, 28 Ağustos\'ta 19:00 ile 24:00 UTC saatleri arasında güncelleme nedeniyle kullanılamayacaktır. Sorularınız için lütfen destek ekibimizle iletişime geçin (support@dify.ai). Sabrınız için teşekkür ederiz.', fa_IR: 'سیستم ما از ساعت 19:00 تا 24:00 UTC در تاریخ 28 اوت برای ارتقاء در دسترس نخواهد بود. برای سؤالات، لطفاً با تیم پشتیبانی ما (support@dify.ai) تماس بگیرید. ما برای صبر شما ارزش قائلیم.', + sl_SI: + 'Naš sistem ne bo na voljo od 19:00 do 24:00 UTC 28. avgusta zaradi nadgradnje. Za vprašanja se obrnite na našo skupino za podporo (support@dify.ai). Cenimo vašo potrpežljivost.', }, href: '#', } diff --git a/web/i18n/languages.json b/web/i18n/languages.json index a70963b067..c29d0280a1 100644 --- a/web/i18n/languages.json +++ b/web/i18n/languages.json @@ -139,6 +139,13 @@ "prompt_name": "Farsi", "example": "سلام, دیفای!", "supported": true + }, + { + "value": "sl-SI", + "name": "Slovensko (Slovenija)", + "prompt_name": "Slovensko", + "example": "Zdravo, Dify!", + "supported": true } ] } diff --git a/web/i18n/pt-BR/app-log.ts b/web/i18n/pt-BR/app-log.ts index 96e604c49e..03f5bc981f 100644 --- a/web/i18n/pt-BR/app-log.ts +++ b/web/i18n/pt-BR/app-log.ts @@ -86,8 +86,8 @@ const translation = { agenteLogDetail: { agentMode: 'Modo Agente', toolUsed: 'Ferramenta usada', - iterações: 'Iterações', - iteração: 'Iteração', + iterations: 'Iterações', + iteration: 'Iteração', finalProcessing: 'Processamento Final', }, agentLogDetail: { diff --git a/web/i18n/sl-SI/app-annotation.ts b/web/i18n/sl-SI/app-annotation.ts new file mode 100644 index 0000000000..07b175a8e7 --- /dev/null +++ b/web/i18n/sl-SI/app-annotation.ts @@ -0,0 +1,87 @@ +const translation = { + title: 'Opombe', + name: 'Odgovor na opombo', + editBy: 'Odgovor je uredil {{author}}', + noData: { + title: 'Brez opomb', + description: 'Opombe lahko urejate med odpravljanjem napak v aplikaciji ali jih množično uvozite tukaj za visokokakovosten odgovor.', + }, + table: { + header: { + question: 'vprašanje', + answer: 'odgovor', + createdAt: 'ustvarjeno ob', + hits: 'zadetki', + actions: 'dejanja', + addAnnotation: 'Dodaj opombo', + bulkImport: 'Množični uvoz', + bulkExport: 'Množični izvoz', + clearAll: 'Počisti vse opombe', + }, + }, + editModal: { + title: 'Uredi odgovor na opombo', + queryName: 'Uporabniško vprašanje', + answerName: 'Pripovedovalec Bot', + yourAnswer: 'Vaš odgovor', + answerPlaceholder: 'Vnesite svoj odgovor tukaj', + yourQuery: 'Vaše vprašanje', + queryPlaceholder: 'Vnesite svoje vprašanje tukaj', + removeThisCache: 'Odstrani to opombo', + createdAt: 'Ustvarjeno ob', + }, + addModal: { + title: 'Dodaj odgovor na opombo', + queryName: 'Vprašanje', + answerName: 'Odgovor', + answerPlaceholder: 'Vnesite odgovor tukaj', + queryPlaceholder: 'Vnesite vprašanje tukaj', + createNext: 'Dodaj še en odgovor z opombo', + }, + batchModal: { + title: 'Množični uvoz', + csvUploadTitle: 'Povlecite in spustite svoj CSV datoteko tukaj ali ', + browse: 'poiščite', + tip: 'CSV datoteka mora ustrezati naslednji strukturi:', + question: 'vprašanje', + answer: 'odgovor', + contentTitle: 'vsebina fragmenta', + content: 'vsebina', + template: 'Prenesite predlogo tukaj', + cancel: 'Prekliči', + run: 'Zaženi množično obdelavo', + runError: 'Napaka pri množičnem zagonu', + processing: 'V množični obdelavi', + completed: 'Uvoz zaključen', + error: 'Napaka pri uvozu', + ok: 'V redu', + }, + errorMessage: { + answerRequired: 'Odgovor je obvezen', + queryRequired: 'Vprašanje je obvezno', + }, + viewModal: { + annotatedResponse: 'Odgovor na opombo', + hitHistory: 'Zgodovina zadetkov', + hit: 'Zadetek', + hits: 'Zadetki', + noHitHistory: 'Ni zgodovine zadetkov', + }, + hitHistoryTable: { + query: 'Vprašanje', + match: 'Ujemanje', + response: 'Odgovor', + source: 'Vir', + score: 'Rezultat', + time: 'Čas', + }, + initSetup: { + title: 'Začetna nastavitev odgovora na opombo', + configTitle: 'Nastavitev odgovora na opombo', + confirmBtn: 'Shrani in omogoči', + configConfirmBtn: 'Shrani', + }, + embeddingModelSwitchTip: 'Model za vektorizacijo besedila opomb, preklapljanje modelov bo ponovno vektoriziralo, kar bo povzročilo dodatne stroške.', +} + +export default translation diff --git a/web/i18n/sl-SI/app-api.ts b/web/i18n/sl-SI/app-api.ts new file mode 100644 index 0000000000..17d0645193 --- /dev/null +++ b/web/i18n/sl-SI/app-api.ts @@ -0,0 +1,84 @@ +const translation = { + apiServer: 'API Strežnik', + apiKey: 'API Ključ', + status: 'Status', + disabled: 'Onemogočeno', + ok: 'V uporabi', + copy: 'Kopiraj', + copied: 'Kopirano', + regenerate: 'Regeneriraj', + play: 'Predvajaj', + pause: 'Premor', + playing: 'Predvajanje', + loading: 'Nalaganje', + merMaid: { + rerender: 'Ponovno izrisi', + }, + never: 'Nikoli', + apiKeyModal: { + apiSecretKey: 'API Skrivni ključ', + apiSecretKeyTips: 'Da bi preprečili zlorabo API-ja, zaščitite svoj API ključ. Izogibajte se uporabi v navadnem besedilu v sprednji kodi. :)', + createNewSecretKey: 'Ustvari nov skrivni ključ', + secretKey: 'Skrivni ključ', + created: 'USTVARJENO', + lastUsed: 'ZADNJA UPORABA', + generateTips: 'Hranite ta ključ na varnem in dostopnem mestu.', + }, + actionMsg: { + deleteConfirmTitle: 'Izbrisati ta skrivni ključ?', + deleteConfirmTips: 'To dejanje ni mogoče razveljaviti.', + ok: 'V redu', + }, + completionMode: { + title: 'API za dokončanje aplikacije', + info: 'Za visokokakovostno generiranje besedil, kot so članki, povzetki in prevodi, uporabite API za dokončanje sporočil z vnosom uporabnika. Generiranje besedil temelji na parametrih modela in predlogah pozivov, določenih v Dify Prompt Engineering.', + createCompletionApi: 'Ustvari sporočilo o dokončanju', + createCompletionApiTip: 'Ustvari sporočilo o dokončanju za podporo načinu vprašanj in odgovorov.', + inputsTips: '(Neobvezno) Navedite vnosna polja uporabnikov kot ključ-vrednost pare, ki ustrezajo spremenljivkam v Prompt Eng. Ključ je ime spremenljivke, vrednost pa vrednost parametra. Če je vrsta polja Izberi, mora biti posredovana vrednost ena izmed vnaprej določenih možnosti.', + queryTips: 'Vsebina besedila vnosa uporabnika.', + blocking: 'Vrsta blokiranja, čakanje na dokončanje izvajanja in vračanje rezultatov. (Zahteve se lahko prekinejo, če je postopek dolg)', + streaming: 'streaming povratki. Implementacija povratkov pretakanja na podlagi SSE (Server-Sent Events).', + messageFeedbackApi: 'Povratne informacije o sporočilih (všeč)', + messageFeedbackApiTip: 'Ocenite prejeta sporočila v imenu končnih uporabnikov z všečki ali nevšečki. Ti podatki so vidni na strani Dnevniki in opombe ter se uporabljajo za nadaljnje fino prilagajanje modela.', + messageIDTip: 'ID sporočila', + ratingTip: 'všeč ali nevšeč, null je preklic', + parametersApi: 'Pridobite informacije o parametrih aplikacije', + parametersApiTip: 'Pridobite konfigurirane vhodne parametre, vključno z imeni spremenljivk, imeni polj, vrstami in privzetimi vrednostmi. Običajno se uporablja za prikaz teh polj v obrazcu ali izpolnjevanje privzetih vrednosti po nalaganju odjemalca.', + }, + chatMode: { + title: 'API za klepet aplikacije', + info: 'Za vsestranske pogovorne aplikacije, ki uporabljajo obliko vprašanj in odgovorov, pokličite API za klepetna sporočila, da začnete dialog. Ohranite tekoče pogovore tako, da prenesete vrnjeni conversation_id. Parametri odgovorov in predloge so odvisni od nastavitev Dify Prompt Eng.', + createChatApi: 'Ustvari klepetno sporočilo', + createChatApiTip: 'Ustvari novo pogovorno sporočilo ali nadaljuj obstoječi pogovor.', + inputsTips: '(Neobvezno) Navedite vnosna polja uporabnikov kot ključ-vrednost pare, ki ustrezajo spremenljivkam v Prompt Eng. Ključ je ime spremenljivke, vrednost pa vrednost parametra. Če je vrsta polja Izberi, mora biti posredovana vrednost ena izmed vnaprej določenih možnosti.', + queryTips: 'Vsebina vnosa/uporabniškega vprašanja', + blocking: 'Vrsta blokiranja, čakanje na dokončanje izvajanja in vračanje rezultatov. (Zahteve se lahko prekinejo, če je postopek dolg)', + streaming: 'streaming povratki. Implementacija povratkov pretakanja na podlagi SSE (Server-Sent Events).', + conversationIdTip: '(Neobvezno) ID pogovora: pustite prazno za prvi pogovor; prenesite conversation_id iz konteksta, da nadaljujete dialog.', + messageFeedbackApi: 'Povratne informacije končnih uporabnikov o sporočilu, všeč', + messageFeedbackApiTip: 'Ocenite prejeta sporočila v imenu končnih uporabnikov z všečki ali nevšečki. Ti podatki so vidni na strani Dnevniki in opombe ter se uporabljajo za nadaljnje fino prilagajanje modela.', + messageIDTip: 'ID sporočila', + ratingTip: 'všeč ali nevšeč, null je preklic', + chatMsgHistoryApi: 'Pridobi zgodovino klepetnih sporočil', + chatMsgHistoryApiTip: 'Prva stran vrne najnovejše `limit` zapise, ki so v obratnem vrstnem redu.', + chatMsgHistoryConversationIdTip: 'ID pogovora', + chatMsgHistoryFirstId: 'ID prvega klepeta na trenutni strani. Privzeto ni.', + chatMsgHistoryLimit: 'Koliko klepetov je vrnjenih na eno zahtevo', + conversationsListApi: 'Pridobi seznam pogovorov', + conversationsListApiTip: 'Pridobi seznam sej trenutnega uporabnika. Privzeto je vrnjenih zadnjih 20 sej.', + conversationsListFirstIdTip: 'ID zadnjega zapisa na trenutni strani, privzeto ni.', + conversationsListLimitTip: 'Koliko klepetov je vrnjenih na eno zahtevo', + conversationRenamingApi: 'Preimenovanje pogovora', + conversationRenamingApiTip: 'Preimenujte pogovore; ime je prikazano v večsejnih odjemalskih vmesnikih.', + conversationRenamingNameTip: 'Novo ime', + parametersApi: 'Pridobite informacije o parametrih aplikacije', + parametersApiTip: 'Pridobite konfigurirane vhodne parametre, vključno z imeni spremenljivk, imeni polj, vrstami in privzetimi vrednostmi. Običajno se uporablja za prikaz teh polj v obrazcu ali izpolnjevanje privzetih vrednosti po nalaganju odjemalca.', + }, + develop: { + requestBody: 'Telo zahteve', + pathParams: 'Parametri poti', + query: 'Poizvedba', + }, +} + +export default translation diff --git a/web/i18n/sl-SI/app-debug.ts b/web/i18n/sl-SI/app-debug.ts new file mode 100644 index 0000000000..8672fa5c58 --- /dev/null +++ b/web/i18n/sl-SI/app-debug.ts @@ -0,0 +1,242 @@ +const translation = { + pageTitle: { + line1: 'PROMPT', + line2: 'Inženiring', + }, + orchestrate: 'Orkestriraj', + promptMode: { + simple: 'Preklopi na strokovni način, da urejaš celoten PROMPT', + advanced: 'Strokovni način', + switchBack: 'Preklopi nazaj', + advancedWarning: { + title: 'Preklopil si na strokovni način. Ko spremeniš PROMPT, ne moreš več preklopiti nazaj v osnovni način.', + description: 'V strokovnem načinu lahko urejaš celoten PROMPT.', + learnMore: 'Preberi več', + ok: 'V redu', + }, + operation: { + addMessage: 'Dodaj sporočilo', + }, + contextMissing: 'Manjka komponenta konteksta, zato učinkovitost PROMPT-a morda ne bo najboljša.', + }, + operation: { + applyConfig: 'Objavi', + resetConfig: 'Ponastavi', + debugConfig: 'Odpravljanje napak', + addFeature: 'Dodaj funkcionalnost', + automatic: 'Generiraj', + stopResponding: 'Prenehaj odgovarjati', + agree: 'všeč', + disagree: 'ni všeč', + cancelAgree: 'Prekliči všeček', + cancelDisagree: 'Prekliči nevšečnost', + userAction: 'Uporabnik ', + }, + notSetAPIKey: { + title: 'Ključ ponudnika LLM ni nastavljen', + trailFinished: 'Preizkus končan', + description: 'Ključ ponudnika LLM ni nastavljen. Pred odpravljanjem napak je treba nastaviti ključ.', + settingBtn: 'Pojdi v nastavitve', + }, + trailUseGPT4Info: { + title: 'GPT-4 trenutno ni podprt', + description: 'Za uporabo GPT-4 je treba nastaviti API ključ.', + }, + feature: { + groupChat: { + title: 'Izboljšanje klepeta', + description: 'Dodajanje prednastavitev klepeta lahko izboljša uporabniško izkušnjo.', + }, + groupExperience: { + title: 'Izboljšanje izkušnje', + }, + conversationOpener: { + title: 'Začetek pogovora', + description: 'V klepetu AI običajno začne pogovor z uporabnikom z dobrodošlico.', + }, + suggestedQuestionsAfterAnswer: { + title: 'Nadaljnja vprašanja', + description: 'Nastavitev predlogov za naslednja vprašanja lahko uporabnikom izboljša klepet.', + resDes: '3 predlogi za naslednje vprašanje uporabnika.', + tryToAsk: 'Poskusi vprašati', + }, + moreLikeThis: { + title: 'Več takšnih', + description: 'Ustvari več besedil naenkrat, nato pa jih urejaj in nadaljuj z ustvarjanjem.', + generateNumTip: 'Število generacij vsakič', + tip: 'Uporaba te funkcije povzroča dodatno porabo žetonov.', + }, + speechToText: { + title: 'Govor v besedilo', + description: 'Ko je omogočeno, lahko uporabljaš glasovni vnos.', + resDes: 'Glasovni vnos je omogočen.', + }, + textToSpeech: { + title: 'Besedilo v govor', + description: 'Ko je omogočeno, lahko besedilo pretvoriš v govor.', + resDes: 'Pretvorba besedila v zvok je omogočena.', + }, + citation: { + title: 'Citati in pripisovanja', + description: 'Ko je omogočeno, prikaži izvorni dokument in pripisani del generirane vsebine.', + resDes: 'Citati in pripisovanja so omogočeni.', + }, + annotation: { + title: 'Odgovor z opombami', + description: 'Ročno lahko dodate visokokakovostne odgovore v predpomnilnik za prednostno ujemanje s podobnimi vprašanji uporabnikov.', + resDes: 'Odgovor z opombami je omogočen.', + scoreThreshold: { + title: 'Prag ujemanja', + description: 'Uporabljeno za nastavitev praga podobnosti za odgovor z opombami.', + easyMatch: 'Lahko ujemanje', + accurateMatch: 'Natančno ujemanje', + }, + matchVariable: { + title: 'Spremenljivka za ujemanje', + choosePlaceholder: 'Izberi spremenljivko za ujemanje', + }, + cacheManagement: 'Upravljanje opomb', + cached: 'Z opombo', + remove: 'Odstrani', + removeConfirm: 'Izbrisati to opombo?', + add: 'Dodaj opombo', + edit: 'Uredi opombo', + }, + dataSet: { + title: 'Kontekst', + noData: 'Uvozi znanje kot kontekst', + words: 'Besede', + textBlocks: 'Bloki besedila', + selectTitle: 'Izberi referenčno znanje', + selected: 'Izbrano znanje', + noDataSet: 'Znanje ni bilo najdeno', + toCreate: 'Pojdi na ustvarjanje', + notSupportSelectMulti: 'Trenutno je podprto le eno znanje', + queryVariable: { + title: 'Spremenljivka poizvedbe', + tip: 'Ta spremenljivka bo uporabljena kot vnos poizvedbe za pridobitev kontekstnih informacij.', + choosePlaceholder: 'Izberi spremenljivko poizvedbe', + noVar: 'Ni spremenljivk', + noVarTip: 'ustvari spremenljivko v razdelku Spremenljivke', + unableToQueryDataSet: 'Neuspešna poizvedba po znanju', + unableToQueryDataSetTip: 'Neuspešna poizvedba po znanju, izberi spremenljivko poizvedbe v razdelku kontekst.', + ok: 'V redu', + contextVarNotEmpty: 'Spremenljivka poizvedbe ne sme biti prazna', + deleteContextVarTitle: 'Izbrisati spremenljivko “{{varName}}”?', + deleteContextVarTip: 'Ta spremenljivka je nastavljena kot spremenljivka za poizvedbo po kontekstu. Če jo odstraniš, bo to vplivalo na uporabo znanja. Če jo želiš izbrisati, ponovno izberi v razdelku kontekst.', + }, + }, + tools: { + title: 'Orodja', + tips: 'Orodja nudijo standardiziran način klicanja API-jev, pri čemer se uporabniški vnos ali spremenljivke uporabijo kot parametri za poizvedovanje zunanjih podatkov.', + toolsInUse: '{{count}} orodij v uporabi', + modal: { + title: 'Orodje', + toolType: { + title: 'Tip orodja', + placeholder: 'Izberi tip orodja', + }, + name: { + title: 'Ime', + placeholder: 'Vnesi ime', + }, + variableName: { + title: 'Ime spremenljivke', + placeholder: 'Vnesi ime spremenljivke', + }, + }, + }, + conversationHistory: { + title: 'Zgodovina pogovorov', + description: 'Nastavi predpone imen za vloge v pogovoru', + tip: 'Zgodovina pogovorov ni omogočena. Dodaj v zgornji PROMPT.', + learnMore: 'Preberi več', + editModal: { + title: 'Uredi imena vlog v pogovoru', + userPrefix: 'Predpona uporabnika', + assistantPrefix: 'Predpona pomočnika', + }, + }, + toolbox: { + title: 'ORODJA', + }, + moderation: { + title: 'Moderiranje vsebine', + description: 'Zagotovi varno izhodno vsebino s pomočjo API-ja za moderiranje ali vzdrževanja seznama občutljivih besed.', + allEnabled: 'VSEBINA VNOSA/IZHODA omogočena', + inputEnabled: 'VSEBINA VNOSA omogočena', + outputEnabled: 'VSEBINA IZHODA omogočena', + modal: { + title: 'Nastavitve moderiranja vsebine', + provider: { + title: 'Ponudnik', + openai: 'OpenAI Moderiranje', + openaiTip: { + prefix: 'OpenAI Moderiranje zahteva nastavljen API ključ pri ', + suffix: '.', + }, + keywords: 'Ključne besede', + }, + keywords: { + tip: 'Vsaka beseda na lastni vrstici, ločena z vrsticami. Največ 100 znakov na vrstico.', + placeholder: 'Vsaka beseda na lastni vrstici, ločena z vrsticami', + line: 'Vrstica', + }, + content: { + input: 'Moderiraj VSEBINO VNOSA', + output: 'Moderiraj VSEBINO IZHODA', + }, + }, + }, + debug: { + title: 'Odpravljanje napak', + description: 'Debugiranje omogoča pregled podrobnih informacij, kot so podatki API-jev, vklop dnevnikov, opozorila in še več.', + }, + agent: { + title: 'Pomočnik', + description: 'Osnovne informacije in odgovorne naloge pomočnika.', + prompts: 'Temeljni PROMPT', + message: { + title: 'Vrstice sporočila', + user: 'Uporabnik', + assistant: 'Pomočnik', + }, + }, + history: { + title: 'Zgodovina', + notFound: 'Zgodovina ni bila najdena', + notOpen: 'Zgodovina ni odprta', + }, + prompt: { + title: 'Vsebina PROMPT-a', + }, + message: { + title: 'Sporočilo', + description: 'Način nastavitve formatiranega pogovora.', + tryChat: 'Preizkusi klepet', + }, + theme: { + title: 'Tema', + themes: { + default: 'Osnovna tema', + light: 'Svetla tema', + dark: 'Temna tema', + custom: 'Prilagodi temo', + }, + modal: { + title: 'Nastavitve teme', + primaryColor: { + title: 'Primarna barva', + placeholder: 'Izberi primarno barvo', + }, + textColor: { + title: 'Barva besedila', + placeholder: 'Izberi barvo besedila', + }, + ok: 'V redu', + }, + }, + }, +} + +module.exports = translation diff --git a/web/i18n/sl-SI/app-log.ts b/web/i18n/sl-SI/app-log.ts new file mode 100644 index 0000000000..03efc594ca --- /dev/null +++ b/web/i18n/sl-SI/app-log.ts @@ -0,0 +1,95 @@ +const translation = { + title: 'Dnevniki', + description: 'Dnevniki beležijo stanje delovanja aplikacije, vključno z vnosi uporabnikov in odgovori umetne inteligence.', + dateTimeFormat: 'DD.MM.YYYY hh:mm A', + table: { + header: { + updatedTime: 'Čas posodobitve', + time: 'Čas ustvarjanja', + endUser: 'Končni uporabnik ali račun', + input: 'Vnos', + output: 'Izhod', + summary: 'Naslov', + messageCount: 'Število sporočil', + userRate: 'Ocena uporabnika', + adminRate: 'Ocena operaterja', + startTime: 'ZAČETNI ČAS', + status: 'STATUS', + runtime: 'ČAS DELOVANJA', + tokens: 'ŽETONI', + user: 'Končni uporabnik ali račun', + version: 'VERZIJA', + }, + pagination: { + previous: 'Prejšnja', + next: 'Naslednja', + }, + empty: { + noChat: 'Še ni pogovora', + noOutput: 'Ni izhoda', + element: { + title: 'Je kdo tam?', + content: 'Opazujte in označite interakcije med končnimi uporabniki in aplikacijami umetne inteligence, da stalno izboljšujete natančnost AI. Lahko delite ali preizkusite spletno aplikacijo sami, nato pa se vrnete na to stran.', + }, + }, + }, + detail: { + time: 'Čas', + conversationId: 'ID pogovora', + promptTemplate: 'Predloga PROMPT-a', + promptTemplateBeforeChat: 'Predloga PROMPT-a pred pogovorom · Kot sistemsko sporočilo', + annotationTip: 'Izboljšave, ki jih je označil {{user}}', + timeConsuming: 'Porabljen čas', + second: 's', + tokenCost: 'Porabljeni žetoni', + loading: 'nalaganje', + operation: { + like: 'všeč', + dislike: 'ni všeč', + addAnnotation: 'Dodaj izboljšavo', + editAnnotation: 'Uredi izboljšavo', + annotationPlaceholder: 'Vnesite pričakovan odgovor, ki ga želite, da AI odgovori, kar se lahko uporabi za izboljšanje modela in kakovosti generiranja besedil v prihodnje.', + }, + variables: 'Spremenljivke', + uploadImages: 'Naložene slike', + }, + filter: { + period: { + today: 'Danes', + last7days: 'Zadnjih 7 dni', + last4weeks: 'Zadnje 4 tedne', + last3months: 'Zadnji 3 meseci', + last12months: 'Zadnjih 12 mesecev', + monthToDate: 'Mesec do danes', + quarterToDate: 'Četrtletje do danes', + yearToDate: 'Leto do danes', + allTime: 'Vse obdobje', + }, + annotation: { + all: 'Vse', + annotated: 'Označene izboljšave ({{count}} elementov)', + not_annotated: 'Neoznačene', + }, + sortBy: 'Razvrsti po:', + descending: 'padajoče', + ascending: 'naraščajoče', + }, + workflowTitle: 'Dnevniki poteka dela', + workflowSubtitle: 'Dnevnik beleži delovanje avtomatizacije.', + runDetail: { + title: 'Dnevnik pogovora', + workflowTitle: 'Podrobnosti dnevnika', + }, + promptLog: 'Dnevnik PROMPT-ov', + agentLog: 'Dnevnik pomočnika', + viewLog: 'Ogled dnevnika', + agentLogDetail: { + agentMode: 'Način pomočnika', + toolUsed: 'Uporabljeno orodje', + iterations: 'Iteracije', + iteration: 'Iteracija', + finalProcessing: 'Končna obdelava', + }, +} + +export default translation diff --git a/web/i18n/sl-SI/app-overview.ts b/web/i18n/sl-SI/app-overview.ts new file mode 100644 index 0000000000..ced27cc779 --- /dev/null +++ b/web/i18n/sl-SI/app-overview.ts @@ -0,0 +1,168 @@ +const translation = { + welcome: { + firstStepTip: 'Začnite s tem, da', + enterKeyTip: 'vnesete svoj OpenAI API ključ spodaj', + getKeyTip: 'Pridobite svoj API ključ na nadzorni plošči OpenAI', + placeholder: 'Vaš OpenAI API ključ (npr. sk-xxxx)', + }, + apiKeyInfo: { + cloud: { + trial: { + title: 'Uporabljate {{providerName}} poskusno kvoto.', + description: 'Poskusna kvota je namenjena vašemu testiranju. Preden se kvota izčrpa, nastavite lastnega ponudnika modela ali kupite dodatno kvoto.', + }, + exhausted: { + title: 'Vaša poskusna kvota je bila porabljena, nastavite API ključ.', + description: 'Porabili ste svojo poskusno kvoto. Prosimo, nastavite lastnega ponudnika modela ali kupite dodatno kvoto.', + }, + }, + selfHost: { + title: { + row1: 'Za začetek,', + row2: 'najprej nastavite svojega ponudnika modela.', + }, + }, + callTimes: 'Število klicev', + usedToken: 'Porabljeni žetoni', + setAPIBtn: 'Pojdi na nastavitev ponudnika modela', + tryCloud: 'Ali preizkusite oblačno različico Dify s prosto kvoto', + }, + overview: { + title: 'Pregled', + appInfo: { + explanation: 'Pripravljena AI spletna aplikacija', + accessibleAddress: 'Javni URL', + preview: 'Predogled', + regenerate: 'Ustvari ponovno', + regenerateNotice: 'Ali želite ponovno ustvariti javni URL?', + preUseReminder: 'Pred nadaljevanjem omogočite spletno aplikacijo.', + settings: { + entry: 'Nastavitve', + title: 'Nastavitve spletne aplikacije', + webName: 'Ime spletne aplikacije', + webDesc: 'Opis spletne aplikacije', + webDescTip: 'Besedilo bo prikazano na strani za stranke in bo zagotavljalo osnovna navodila za uporabo aplikacije', + webDescPlaceholder: 'Vnesite opis spletne aplikacije', + language: 'Jezik', + workflow: { + title: 'Potek dela', + subTitle: 'Podrobnosti poteka dela', + show: 'Prikaži', + hide: 'Skrij', + showDesc: 'Pokažite ali skrijte podrobnosti poteka dela v spletni aplikaciji', + }, + chatColorTheme: 'Barvna tema klepeta', + chatColorThemeDesc: 'Nastavite barvno temo klepetalnega bota', + chatColorThemeInverted: 'Inverzna', + invalidHexMessage: 'Neveljavna vrednost heksa', + sso: { + label: 'SSO avtentikacija', + title: 'SSO spletne aplikacije', + description: 'Vsi uporabniki morajo pred uporabo spletne aplikacije opraviti prijavo preko SSO', + tooltip: 'Za omogočitev SSO za spletno aplikacijo se obrnite na skrbnika', + }, + more: { + entry: 'Prikaži več nastavitev', + copyright: 'Avtorske pravice', + copyRightPlaceholder: 'Vnesite ime avtorja ali organizacije', + privacyPolicy: 'Politika zasebnosti', + privacyPolicyPlaceholder: 'Vnesite povezavo do politike zasebnosti', + privacyPolicyTip: 'Pomaga obiskovalcem razumeti, katere podatke aplikacija zbira, glejte politiko zasebnosti Dify.', + customDisclaimer: 'Prilagojena izjava o omejitvi odgovornosti', + customDisclaimerPlaceholder: 'Vnesite prilagojeno izjavo o omejitvi odgovornosti', + customDisclaimerTip: 'Prilagojeno izjavo o omejitvi odgovornosti bo prikazano na strani za stranke, ki bo zagotavljala dodatne informacije o aplikaciji', + }, + }, + embedded: { + entry: 'Vdelano', + title: 'Vdelava na spletno stran', + explanation: 'Izberite način vdelave klepeta na svojo spletno stran', + iframe: 'Za dodajanje klepeta kjerkoli na vaši spletni strani dodajte to iframe v vašo HTML kodo.', + scripts: 'Za dodajanje klepeta na spodnji desni del vaše spletne strani dodajte to kodo v vašo HTML kodo.', + chromePlugin: 'Namestite Dify Chatbot razširitev za Chrome', + copied: 'Kopirano', + copy: 'Kopiraj', + }, + qrcode: { + title: 'Povezava QR koda', + scan: 'Skeniraj za deljenje', + download: 'Prenesi QR kodo', + }, + customize: { + way: 'način', + entry: 'Prilagodi', + title: 'Prilagodi AI spletno aplikacijo', + explanation: 'Lahko prilagodite sprednji del spletne aplikacije, da ustreza vašim scenarijem in potrebam po slogu.', + way1: { + name: 'Forkajte kodo stranke, jo spremenite in namestite na Vercel (priporočeno)', + step1: 'Forkajte kodo stranke in jo spremenite', + step1Tip: 'Kliknite tukaj, da forknite izvorno kodo v svoj GitHub račun in spremenite kodo', + step1Operation: 'Dify-WebClient', + step2: 'Namestite na Vercel', + step2Tip: 'Kliknite tukaj, da uvozite repozitorij v Vercel in namestite', + step2Operation: 'Uvoz repozitorija', + step3: 'Konfigurirajte spremenljivke okolja', + step3Tip: 'Dodajte naslednje spremenljivke okolja v Vercel', + }, + way2: { + name: 'Napišite kodo na strani stranke za klic API-ja in jo namestite na strežnik', + operation: 'Dokumentacija', + }, + }, + }, + apiInfo: { + title: 'API storitev v ozadju', + explanation: 'Enostavna integracija v vašo aplikacijo', + accessibleAddress: 'API končna točka storitve', + doc: 'API referenca', + }, + status: { + running: 'V storitvi', + disable: 'Onemogočeno', + }, + }, + analysis: { + title: 'Analiza', + ms: 'ms', + tokenPS: 'Žetoni/s', + totalMessages: { + title: 'Skupno število sporočil', + explanation: 'Število dnevnih AI interakcij.', + }, + totalConversations: { + title: 'Skupno število pogovorov', + explanation: 'Število dnevnih AI pogovorov; inženiring promptov/debugging izključeno.', + }, + activeUsers: { + title: 'Aktivni uporabniki', + explanation: 'Unikatni uporabniki, ki sodelujejo v vprašanjih in odgovorih z AI; inženiring promptov/debugging izključeno.', + }, + tokenUsage: { + title: 'Poraba žetonov', + explanation: 'Odzrcaljuje dnevno porabo žetonov jezikovnega modela za aplikacijo, uporabno za namene nadzora stroškov.', + consumed: 'Porabljeni', + }, + avgSessionInteractions: { + title: 'Povprečne interakcije v seji', + explanation: 'Število neprekinjenih komunikacij med uporabnikom in AI; za aplikacije, ki temeljijo na pogovoru.', + }, + avgUserInteractions: { + title: 'Povprečne interakcije uporabnika', + explanation: 'Odzrcaljuje dnevno pogostost uporabe uporabnikov. Ta metrika odraža vezanost uporabnikov.', + }, + userSatisfactionRate: { + title: 'Stopnja zadovoljstva uporabnikov', + explanation: 'Število všečkov na 1.000 sporočil. To kaže delež odgovorov, s katerimi so uporabniki zelo zadovoljni.', + }, + avgResponseTime: { + title: 'Povprečni odzivni čas', + explanation: 'Čas (v ms) za obdelavo/odgovor AI; za aplikacije, ki temeljijo na besedilu.', + }, + tps: { + title: 'Hitrost izhoda žetonov', + explanation: 'Merite učinkovitost LLM. Šteje hitrost izhoda žetonov od začetka zahteve do zaključka izhoda.', + }, + }, +} + +export default translation diff --git a/web/i18n/sl-SI/app.ts b/web/i18n/sl-SI/app.ts new file mode 100644 index 0000000000..d111316668 --- /dev/null +++ b/web/i18n/sl-SI/app.ts @@ -0,0 +1,138 @@ +const translation = { + createApp: 'USTVARI APLIKACIJO', + types: { + all: 'Vse', + chatbot: 'Klepetalnik', + agent: 'Agent', + workflow: 'Potek dela', + completion: 'Dopolnjevanje', + }, + duplicate: 'Podvoji', + duplicateTitle: 'Podvoji aplikacijo', + export: 'Izvozi DSL', + exportFailed: 'Izvoz DSL ni uspel.', + importDSL: 'Uvozi datoteko DSL', + createFromConfigFile: 'Ustvari iz datoteke DSL', + importFromDSL: 'Uvozi iz DSL', + importFromDSLFile: 'Iz datoteke DSL', + importFromDSLUrl: 'Iz URL-ja', + importFromDSLUrlPlaceholder: 'Tukaj prilepi povezavo DSL', + deleteAppConfirmTitle: 'Izbrišem to aplikacijo?', + deleteAppConfirmContent: + 'Brisanje aplikacije je nepopravljivo. Uporabniki ne bodo več imeli dostopa do vaše aplikacije, vse konfiguracije in dnevniki pa bodo trajno izbrisani.', + appDeleted: 'Aplikacija izbrisana', + appDeleteFailed: 'Brisanje aplikacije ni uspelo', + join: 'Pridruži se skupnosti', + communityIntro: + 'Pogovarjajte se s člani ekipe, sodelavci in razvijalci na različnih kanalih.', + roadmap: 'Oglejte si naš načrt', + newApp: { + startFromBlank: 'Ustvari iz nič', + startFromTemplate: 'Ustvari iz predloge', + captionAppType: 'Kakšno aplikacijo želite ustvariti?', + chatbotDescription: 'Zgradite aplikacijo, ki temelji na klepetu. Ta aplikacija uporablja format vprašanj in odgovorov, ki omogoča več krogov neprekinjenega pogovora.', + completionDescription: 'Zgradite aplikacijo, ki na podlagi pozivov generira visokokakovostno besedilo, kot je ustvarjanje člankov, povzetkov, prevodov in več.', + completionWarning: 'Ta vrsta aplikacije ne bo več podprta.', + agentDescription: 'Zgradite inteligentnega agenta, ki lahko samostojno izbere orodja za dokončanje nalog.', + workflowDescription: 'Zgradite aplikacijo, ki generira visokokakovostno besedilo na podlagi orkestracije poteka dela z visoko stopnjo prilagodljivosti. Primerna je za izkušene uporabnike.', + workflowWarning: 'Trenutno v beta različici', + chatbotType: 'Metoda orkestracije klepetalnika', + basic: 'Osnovno', + basicTip: 'Za začetnike, lahko kasneje preklopite na Chatflow', + basicFor: 'ZA ZAČETNIKE', + basicDescription: 'Osnovna orkestracija omogoča orkestracijo aplikacije klepetalnika z enostavnimi nastavitvami, brez možnosti spreminjanja vgrajenih pozivov. Primerna je za začetnike.', + advanced: 'Chatflow', + advancedFor: 'Za napredne uporabnike', + advancedDescription: 'Orkestracija poteka dela orkestrira klepetalnike v obliki potekov dela, ki ponuja visoko stopnjo prilagodljivosti, vključno z možnostjo urejanja vgrajenih pozivov. Primerna je za izkušene uporabnike.', + captionName: 'Ikona in ime aplikacije', + appNamePlaceholder: 'Poimenujte svojo aplikacijo', + captionDescription: 'Opis', + appDescriptionPlaceholder: 'Vnesite opis aplikacije', + useTemplate: 'Uporabi to predlogo', + previewDemo: 'Predogled demo različice', + chatApp: 'Pomočnik', + chatAppIntro: + 'Želim zgraditi aplikacijo, ki temelji na klepetu. Ta aplikacija uporablja format vprašanj in odgovorov, ki omogoča več krogov neprekinjenega pogovora.', + agentAssistant: 'Novi pomočnik agenta', + completeApp: 'Generator besedila', + completeAppIntro: + 'Želim ustvariti aplikacijo, ki na podlagi pozivov generira visokokakovostno besedilo, kot je ustvarjanje člankov, povzetkov, prevodov in več.', + showTemplates: 'Želim izbrati iz predloge', + hideTemplates: 'Vrni se na izbiro načina', + Create: 'Ustvari', + Cancel: 'Prekliči', + nameNotEmpty: 'Ime ne sme biti prazno', + appTemplateNotSelected: 'Izberite predlogo', + appTypeRequired: 'Izberite vrsto aplikacije', + appCreated: 'Aplikacija ustvarjena', + appCreateFailed: 'Ustvarjanje aplikacije ni uspelo', + }, + editApp: 'Uredi informacije', + editAppTitle: 'Uredi informacije o aplikaciji', + editDone: 'Informacije o aplikaciji posodobljene', + editFailed: 'Posodobitev informacij o aplikaciji ni uspela', + iconPicker: { + ok: 'V redu', + cancel: 'Prekliči', + emoji: 'Emoji', + image: 'Slika', + }, + answerIcon: { + title: 'Uporabite ikono WebApp za zamenjavo 🤖', + description: 'Ali uporabiti ikono WebApp za zamenjavo 🤖 v deljeni aplikaciji', + descriptionInExplore: 'Ali uporabiti ikono WebApp za zamenjavo 🤖 v razdelku Razišči', + }, + switch: 'Preklopi na Workflow Orchestrate', + switchTipStart: 'Za vas bo ustvarjena nova kopija aplikacije, ki bo preklopila na Workflow Orchestrate. Nova kopija ne bo ', + switchTip: 'dovolila', + switchTipEnd: ' preklopa nazaj na Basic Orchestrate.', + switchLabel: 'Kopija aplikacije, ki bo ustvarjena', + removeOriginal: 'Izbriši izvirno aplikacijo', + switchStart: 'Začni preklop', + typeSelector: { + all: 'VSE VRSTE', + chatbot: 'Klepetalnik', + agent: 'Agent', + workflow: 'Potek dela', + completion: 'Dopolnjevanje', + }, + tracing: { + title: 'Sledenje uspešnosti aplikacije', + description: 'Konfiguracija ponudnika LLMOps tretje osebe in sledenje uspešnosti aplikacije.', + config: 'Konfiguracija', + view: 'Ogled', + collapse: 'Strni', + expand: 'Razširi', + tracing: 'Sledenje', + disabled: 'Onemogočeno', + disabledTip: 'Najprej konfigurirajte ponudnika', + enabled: 'V storitvi', + tracingDescription: 'Zajem celotnega konteksta izvajanja aplikacije, vključno s klici LLM, kontekstom, pozivi, zahtevami HTTP in še več, na platformo za sledenje tretje osebe.', + configProviderTitle: { + configured: 'Konfigurirano', + notConfigured: 'Konfigurirajte ponudnika za omogočanje sledenja', + moreProvider: 'Več ponudnikov', + }, + langsmith: { + title: 'LangSmith', + description: 'Vse-v-enem razvijalska platforma za vsak korak življenjskega cikla aplikacije, ki jo poganja LLM.', + }, + langfuse: { + title: 'Langfuse', + description: 'Sledi, vrednoti, upravlja pozive in meri za odpravljanje napak in izboljšanje vaše aplikacije LLM.', + }, + inUse: 'V uporabi', + configProvider: { + title: 'Konfiguracija', + placeholder: 'Vnesite vaš {{key}}', + project: 'Projekt', + publicKey: 'Javni ključ', + secretKey: 'Skrivni ključ', + viewDocsLink: 'Ogled dokumentov {{key}}', + removeConfirmTitle: 'Odstraniti konfiguracijo {{key}}?', + removeConfirmContent: 'Trenutna konfiguracija je v uporabi, odstranitev bo onemogočila funkcijo sledenja.', + }, + }, +} + +export default translation diff --git a/web/i18n/sl-SI/billing.ts b/web/i18n/sl-SI/billing.ts new file mode 100644 index 0000000000..edbf03ee9a --- /dev/null +++ b/web/i18n/sl-SI/billing.ts @@ -0,0 +1,118 @@ +const translation = { + currentPlan: 'Trenutni načrt', + upgradeBtn: { + plain: 'Nadgradi načrt', + encourage: 'Nadgradi zdaj', + encourageShort: 'Nadgradi', + }, + viewBilling: 'Upravljanje s plačili in naročninami', + buyPermissionDeniedTip: 'Za naročnino kontaktirajte svojega skrbnika podjetja', + plansCommon: { + title: 'Izberite načrt, ki vam ustreza', + yearlyTip: 'Z letno naročnino pridobite 2 meseca brezplačno!', + mostPopular: 'Najbolj priljubljeno', + planRange: { + monthly: 'Mesečno', + yearly: 'Letno', + }, + month: 'mesec', + year: 'leto', + save: 'Prihranite ', + free: 'Brezplačno', + currentPlan: 'Trenutni načrt', + contractSales: 'Kontaktirajte prodajo', + contractOwner: 'Kontaktirajte upravitelja ekipe', + startForFree: 'Začnite brezplačno', + getStartedWith: 'Začnite z ', + contactSales: 'Kontaktirajte prodajo', + talkToSales: 'Pogovorite se s prodajo', + modelProviders: 'Ponudniki modelov', + teamMembers: 'Člani ekipe', + annotationQuota: 'Kvote za označevanje', + buildApps: 'Gradite aplikacije', + vectorSpace: 'Prostor za vektorje', + vectorSpaceBillingTooltip: 'Vsak 1 MB lahko shrani približno 1,2 milijona znakov vektoriziranih podatkov (ocenjeno z uporabo OpenAI Embeddings, odvisno od modelov).', + vectorSpaceTooltip: 'Prostor za vektorje je dolgoročni pomnilniški sistem, potreben za to, da LLM-ji razumejo vaše podatke.', + documentsUploadQuota: 'Kvote za nalaganje dokumentov', + documentProcessingPriority: 'Prioriteta obdelave dokumentov', + documentProcessingPriorityTip: 'Za višjo prioriteto obdelave dokumentov nadgradite svoj načrt.', + documentProcessingPriorityUpgrade: 'Obdelujte več podatkov z večjo natančnostjo in hitrostjo.', + priority: { + 'standard': 'Standard', + 'priority': 'Prioriteta', + 'top-priority': 'Najvišja prioriteta', + }, + logsHistory: 'Zgodovina dnevnikov', + customTools: 'Prilagojena orodja', + unavailable: 'Ni na voljo', + days: 'dni', + unlimited: 'Neomejeno', + support: 'Podpora', + supportItems: { + communityForums: 'Skupnostni forumi', + emailSupport: 'Podpora preko e-pošte', + priorityEmail: 'Prioritetna podpora preko e-pošte in klepeta', + logoChange: 'Sprememba logotipa', + SSOAuthentication: 'SSO avtentikacija', + personalizedSupport: 'Osebna podpora', + dedicatedAPISupport: 'Namenska podpora API-ju', + customIntegration: 'Prilagojena integracija in podpora', + ragAPIRequest: 'RAG API zahtevki', + bulkUpload: 'Masovni prenos dokumentov', + agentMode: 'Način agenta', + workflow: 'Potek dela', + llmLoadingBalancing: 'LLM uravnoteženje obremenitve', + llmLoadingBalancingTooltip: 'Dodajte več API ključev modelom, kar učinkovito preseže omejitve hitrosti API-ja.', + }, + comingSoon: 'Kmalu na voljo', + member: 'Član', + memberAfter: 'Član', + messageRequest: { + title: 'Krediti za sporočila', + tooltip: 'Kvota za klice sporočil pri različnih načrtih z uporabo modelov OpenAI (razen GPT-4). Sporočila preko omejitve bodo uporabljala vaš OpenAI API ključ.', + }, + annotatedResponse: { + title: 'Omejitve kvote za označevanje', + tooltip: 'Ročno urejanje in označevanje odgovorov omogoča prilagojeno visoko kakovostno odgovarjanje na vprašanja v aplikacijah. (Velja samo za klepetalne aplikacije)', + }, + ragAPIRequestTooltip: 'Nanaša se na število API klicev, ki vključujejo samo sposobnosti obdelave baze znanja Dify.', + receiptInfo: 'Le lastnik ekipe in skrbnik ekipe lahko naročita in si ogledate podatke o plačilih', + }, + plans: { + sandbox: { + name: 'Peskovnik', + description: '200 brezplačnih poskusov GPT', + includesTitle: 'Vključuje:', + }, + professional: { + name: 'Profesionalni', + description: 'Za posameznike in male ekipe, da odklenete več zmogljivosti po ugodni ceni.', + includesTitle: 'Vse v brezplačnem načrtu, plus:', + }, + team: { + name: 'Ekipa', + description: 'Sodelujte brez omejitev in uživajte v vrhunski zmogljivosti.', + includesTitle: 'Vse v profesionalnem načrtu, plus:', + }, + enterprise: { + name: 'Podjetje', + description: 'Pridobite vse zmogljivosti in podporo za velike sisteme kritične za misijo.', + includesTitle: 'Vse v načrtu Ekipa, plus:', + }, + }, + vectorSpace: { + fullTip: 'Prostor za vektorje je poln.', + fullSolution: 'Nadgradite svoj načrt za več prostora.', + }, + apps: { + fullTipLine1: 'Nadgradite svoj načrt, da', + fullTipLine2: 'gradite več aplikacij.', + }, + annotatedResponse: { + fullTipLine1: 'Nadgradite svoj načrt, da', + fullTipLine2: 'označite več pogovorov.', + quotaTitle: 'Kvote za odgovor z označevanjem', + }, +} + +export default translation diff --git a/web/i18n/sl-SI/common.ts b/web/i18n/sl-SI/common.ts new file mode 100644 index 0000000000..c780fbb6c2 --- /dev/null +++ b/web/i18n/sl-SI/common.ts @@ -0,0 +1,580 @@ +const translation = { + api: { + success: 'Uspeh', + actionSuccess: 'Dejanje je uspelo', + saved: 'Shranjeno', + create: 'Ustvarjeno', + remove: 'Odstranjeno', + }, + operation: { + create: 'Ustvari', + confirm: 'Potrdi', + cancel: 'Prekliči', + clear: 'Počisti', + save: 'Shrani', + saveAndEnable: 'Shrani in omogoči', + edit: 'Uredi', + add: 'Dodaj', + added: 'Dodano', + refresh: 'Osveži', + reset: 'Ponastavi', + search: 'Išči', + change: 'Spremeni', + remove: 'Odstrani', + send: 'Pošlji', + copy: 'Kopiraj', + lineBreak: 'Prelom vrstice', + sure: 'Prepričan sem', + download: 'Prenesi', + delete: 'Izbriši', + settings: 'Nastavitve', + setup: 'Nastavitev', + getForFree: 'Dobite brezplačno', + reload: 'Ponovno naloži', + ok: 'V redu', + log: 'Dnevnik', + learnMore: 'Izvedi več', + params: 'Parametri', + duplicate: 'Podvoji', + rename: 'Preimenuj', + audioSourceUnavailable: 'Zvočni vir ni na voljo', + }, + errorMsg: { + fieldRequired: '{{field}} je obvezno', + urlError: 'url mora začeti z http:// ali https://', + }, + placeholder: { + input: 'Vnesite prosim', + select: 'Izberite prosim', + }, + voice: { + language: { + zhHans: 'Kitajščina (poenostavljena)', + zhHant: 'Kitajščina (tradicionalna)', + enUS: 'Angleščina', + deDE: 'Nemščina', + frFR: 'Francoščina', + esES: 'Španščina', + itIT: 'Italijanščina', + thTH: 'Tajščina', + idID: 'Indonezijščina', + jaJP: 'Japonščina', + koKR: 'Korejščina', + ptBR: 'Portugalščina', + ruRU: 'Ruščina', + ukUA: 'Ukrajinščina', + viVN: 'Vietnamščina', + plPL: 'Poljščina', + roRO: 'Romunščina', + hiIN: 'Hindujščina', + trTR: 'Turščina', + faIR: 'Farsi', + }, + }, + unit: { + char: 'znaki', + }, + actionMsg: { + noModification: 'Trenutno ni sprememb.', + modifiedSuccessfully: 'Spremenjeno uspešno', + modifiedUnsuccessfully: 'Spremenjeno neuspešno', + copySuccessfully: 'Kopirano uspešno', + paySucceeded: 'Plačilo je uspelo', + payCancelled: 'Plačilo preklicano', + generatedSuccessfully: 'Generirano uspešno', + generatedUnsuccessfully: 'Generirano neuspešno', + }, + model: { + params: { + temperature: 'Temperatura', + temperatureTip: + 'Nadzoruje naključnost: Znižanje temperature vodi do manj naključnih zaključkov. Ko se temperatura približa ničli, bo model postal determinističen in ponavljajoč.', + top_p: 'Top P', + top_pTip: + 'Nadzoruje raznolikost preko vzorčenja jedra: 0.5 pomeni, da je upoštevanih polovica vseh možnosti glede na njihovo verjetnost.', + presence_penalty: 'Kaznovanje za prisotnost', + presence_penaltyTip: + 'Kako močno kaznovati nove besede, glede na to, ali so se že pojavile v besedilu.\nPovečuje verjetnost, da bo model obravnaval nove teme.', + frequency_penalty: 'Kaznovanje za frekvenco', + frequency_penaltyTip: + 'Kako močno kaznovati nove besede glede na njihovo že obstoječo frekvenco v besedilu.\nZmanjšuje verjetnost, da bo model ponavljal iste vrstice.', + max_tokens: 'Največje število žetonov', + max_tokensTip: + 'Uporabljeno za omejitev največje dolžine odgovora, v žetonih.\nVečje vrednosti lahko omejijo prostor za besede, zgodovino pogovorov in znanje. \nPriporočljivo je nastaviti pod dve tretjini.\ngpt-4-1106-preview, gpt-4-vision-preview maksimalno število žetonov (vnos 128k, izhod 4k)', + maxTokenSettingTip: 'Vaša nastavitev za največje število žetonov je visoka, kar lahko omeji prostor za pozive, poizvedbe in podatke. Razmislite o nastavitvi pod 2/3.', + setToCurrentModelMaxTokenTip: 'Največje število žetonov je posodobljeno na 80 % največjega števila žetonov trenutnega modela {{maxToken}}.', + stop_sequences: 'Zaporedja ustavljanja', + stop_sequencesTip: 'Do štiri zaporedja, kjer bo API prenehal generirati nadaljnje žetone. Vrnjen tekst ne bo vseboval zaporedja ustavitve.', + stop_sequencesPlaceholder: 'Vnesite zaporedje in pritisnite Tab', + }, + tone: { + Creative: 'Kreativno', + Balanced: 'Uravnoteženo', + Precise: 'Natančno', + Custom: 'Po meri', + }, + addMoreModel: 'Pojdite v nastavitve, da dodate več modelov', + }, + menus: { + status: 'beta', + explore: 'Raziskuj', + apps: 'Studio', + plugins: 'Vtičniki', + pluginsTips: 'Integrirajte vtičnike tretjih oseb ali ustvarite vtičnike, združljive s ChatGPT.', + datasets: 'Znanje', + datasetsTips: 'KMALU: Uvozite svoje besedilne podatke ali pišite podatke v realnem času preko spletnih kljuk za izboljšanje konteksta LLM.', + newApp: 'Nova aplikacija', + newDataset: 'Ustvari znanje', + tools: 'Orodja', + }, + userProfile: { + settings: 'Nastavitve', + emailSupport: 'Podpora po e-pošti', + workspace: 'Delovni prostor', + createWorkspace: 'Ustvari delovni prostor', + helpCenter: 'Pomoč', + communityFeedback: 'Povratne informacije', + roadmap: 'Načrt razvoja', + community: 'Skupnost', + about: 'O nas', + logout: 'Odjava', + }, + settings: { + accountGroup: 'SPLOŠNO', + workplaceGroup: 'DELOVNI PROSTOR', + account: 'Moj račun', + members: 'Člani', + billing: 'Zaračunavanje', + integrations: 'Integracije', + language: 'Jezik', + provider: 'Ponudnik modelov', + dataSource: 'Vir podatkov', + plugin: 'Vtičniki', + apiBasedExtension: 'Razširitev API-ja', + }, + account: { + account: 'Račun', + myAccount: 'Moj račun', + studio: 'Dify Studio', + avatar: 'Avatar', + name: 'Ime', + email: 'E-pošta', + password: 'Geslo', + passwordTip: 'Lahko nastavite stalno geslo, če ne želite uporabljati začasnih prijavnih kod', + setPassword: 'Nastavi geslo', + resetPassword: 'Ponastavi geslo', + currentPassword: 'Trenutno geslo', + newPassword: 'Novo geslo', + confirmPassword: 'Potrdi geslo', + notEqual: 'Gesli se ne ujemata.', + langGeniusAccount: 'Dify račun', + langGeniusAccountTip: 'Vaš Dify račun in povezani uporabniški podatki.', + editName: 'Uredi ime', + showAppLength: 'Prikaz {{length}} aplikacij', + delete: 'Izbriši račun', + deleteTip: 'Brisanje vašega računa bo trajno izbrisalo vse vaše podatke in jih ne bo mogoče obnoviti.', + deleteConfirmTip: 'Za potrditev pošljite naslednje s svojega registriranega e-poštnega naslova na ', + }, + members: { + team: 'Ekipa', + invite: 'Dodaj', + name: 'IME', + lastActive: 'NAZADNJE AKTIVEN', + role: 'VLOGE', + pending: 'V teku...', + owner: 'Lastnik', + admin: 'Administrator', + adminTip: 'Lahko ustvarja aplikacije in upravlja nastavitve ekipe', + normal: 'Običajni uporabnik', + normalTip: 'Lahko uporablja samo aplikacije, ne more ustvarjati aplikacij', + builder: 'Graditelj', + builderTip: 'Lahko ustvarja in ureja lastne aplikacije', + editor: 'Urednik', + editorTip: 'Lahko ustvarja in ureja aplikacije', + datasetOperator: 'Skrbnik znanja', + datasetOperatorTip: 'Lahko upravlja samo bazo znanja', + inviteTeamMember: 'Dodaj člana ekipe', + inviteTeamMemberTip: 'Do vaših podatkov bo lahko dostopal takoj po prijavi.', + email: 'E-pošta', + emailInvalid: 'Neveljaven format e-pošte', + emailPlaceholder: 'Vnesite e-poštne naslove', + sendInvite: 'Pošlji povabilo', + invitedAsRole: 'Povabljen kot uporabnik {{role}}', + invitationSent: 'Povabilo poslano', + invitationSentTip: 'Povabilo poslano, in po prijavi v Dify bodo imeli dostop do vaših podatkov ekipe.', + invitationLink: 'Povezava za povabilo', + failedInvitationEmails: 'Spodnji uporabniki niso bili uspešno povabljeni', + ok: 'V redu', + removeFromTeam: 'Odstrani iz ekipe', + removeFromTeamTip: 'Odstranjen bo dostop do ekipe', + setAdmin: 'Nastavi za administratorja', + setMember: 'Nastavi za običajnega člana', + setBuilder: 'Nastavi za graditelja', + setEditor: 'Nastavi za urednika', + disInvite: 'Prekliči povabilo', + deleteMember: 'Izbriši člana', + you: '(Vi)', + }, + integrations: { + connected: 'Povezano', + google: 'Google', + googleAccount: 'Prijavite se z Google računom', + github: 'GitHub', + githubAccount: 'Prijavite se z GitHub računom', + connect: 'Poveži', + }, + language: { + displayLanguage: 'Jezik prikaza', + timezone: 'Časovni pas', + }, + provider: { + apiKey: 'API ključ', + enterYourKey: 'Vnesite svoj API ključ tukaj', + invalidKey: 'Neveljaven OpenAI API ključ', + validatedError: 'Preverjanje ni uspelo: ', + validating: 'Preverjam ključ...', + saveFailed: 'Shranjevanje API ključa ni uspelo', + apiKeyExceedBill: 'Ta API ključ nima več na voljo kvote, preberite', + addKey: 'Dodaj ključ', + comingSoon: 'Kmalu', + editKey: 'Uredi', + invalidApiKey: 'Neveljaven API ključ', + azure: { + apiBase: 'API Osnova', + apiBasePlaceholder: 'URL API osnove vašega Azure OpenAI končnega mesta.', + apiKey: 'API ključ', + apiKeyPlaceholder: 'Vnesite svoj API ključ tukaj', + helpTip: 'Spoznajte Azure OpenAI storitev', + }, + openaiHosted: { + openaiHosted: 'Gostovani OpenAI', + onTrial: 'NA PREIZKUSU', + exhausted: 'KVOTA PORABLJENA', + desc: 'Gostitvena storitev OpenAI, ki jo ponuja Dify, vam omogoča uporabo modelov, kot je GPT-3.5. Preden porabite kvoto za preizkus, morate nastaviti druge ponudnike modelov.', + callTimes: 'Časi klicev', + usedUp: 'Kvota za preizkus porabljena. Dodajte svojega ponudnika modelov.', + useYourModel: 'Trenutno uporabljate svojega ponudnika modelov.', + close: 'Zapri', + }, + anthropicHosted: { + anthropicHosted: 'Anthropic Claude', + onTrial: 'NA PREIZKUSU', + exhausted: 'KVOTA PORABLJENA', + desc: 'Zmogljiv model, ki se odlično obnese pri različnih nalogah, od sofisticiranega dialoga in ustvarjanja kreativnih vsebin do podrobnih navodil.', + callTimes: 'Časi klicev', + usedUp: 'Kvota za preizkus porabljena. Dodajte svojega ponudnika modelov.', + useYourModel: 'Trenutno uporabljate svojega ponudnika modelov.', + close: 'Zapri', + }, + anthropic: { + using: 'Zmožnost vdelave uporablja', + enableTip: 'Za omogočitev modela Anthropic morate najprej povezati OpenAI ali Azure OpenAI storitev.', + notEnabled: 'Ni omogočeno', + keyFrom: 'Pridobite svoj API ključ pri Anthropic', + }, + encrypted: { + front: 'Vaš API ključ bo šifriran in shranjen z uporabo', + back: ' tehnologije.', + }, + }, + modelProvider: { + notConfigured: 'Sistemski model še ni popolnoma konfiguriran, nekatere funkcije morda ne bodo na voljo.', + systemModelSettings: 'Nastavitve sistemskega modela', + systemModelSettingsLink: 'Zakaj je potrebno nastaviti sistemski model?', + selectModel: 'Izberite svoj model', + setupModelFirst: 'Najprej nastavite svoj model', + systemReasoningModel: { + key: 'Sistemski model za sklepanja', + tip: 'Nastavite privzeti model za sklepanja, ki se bo uporabljal za ustvarjanje aplikacij, kot tudi funkcije, kot so generiranje imen dialogov in predlaganje naslednjih vprašanj.', + }, + embeddingModel: { + key: 'Model za vdelavo', + tip: 'Nastavite privzeti model za obdelavo vdelave dokumentov znanja, tako pri iskanju kot pri uvozu znanja se uporablja ta model za vektorizacijo. Preklop bo povzročil neusklajenost vektorske dimenzije med uvoženim znanjem in vprašanjem, kar bo povzročilo neuspešno iskanje. Da bi se izognili neuspehu pri iskanju, ne preklapljajte tega modela brez potrebe.', + required: 'Model za vdelavo je obvezen', + }, + speechToTextModel: { + key: 'Model za pretvorbo govora v besedilo', + tip: 'Nastavite privzeti model za vnos govora v besedilo v pogovoru.', + }, + ttsModel: { + key: 'Model za pretvorbo besedila v govor', + tip: 'Nastavite privzeti model za pretvorbo besedila v govor v pogovoru.', + }, + rerankModel: { + key: 'Model za prerazvrstitev', + tip: 'Model za prerazvrstitev bo prerazporedil seznam kandidatskih dokumentov na podlagi semantične ujemanja z uporabniško poizvedbo, s čimer se izboljšajo rezultati semantičnega razvrščanja.', + }, + apiKey: 'API-KEY', + quota: 'Kvote', + searchModel: 'Model iskanja', + noModelFound: 'Za {{model}} ni najden noben model', + models: 'Modeli', + showMoreModelProvider: 'Prikaži več ponudnikov modelov', + selector: { + tip: 'Ta model je bil odstranjen. Prosimo, dodajte model ali izberite drugega.', + emptyTip: 'Ni razpoložljivih modelov', + emptySetting: 'Prosimo, pojdite v nastavitve za konfiguracijo', + rerankTip: 'Prosimo, nastavite model za prerazvrstitev', + }, + card: { + quota: 'KVOTE', + onTrial: 'Na preizkusu', + paid: 'Plačano', + quotaExhausted: 'Kvote porabljene', + callTimes: 'Časi klicev', + tokens: 'Žetoni', + buyQuota: 'Kupi kvoto', + priorityUse: 'Prednostna uporaba', + removeKey: 'Odstrani API ključ', + tip: 'Prednostno se bo uporabila plačana kvota. Kvota za preizkus se bo uporabila, ko bo plačana kvota porabljena.', + }, + item: { + deleteDesc: '{{modelName}} se uporablja kot sistemski model za sklepanja. Nekatere funkcije ne bodo na voljo po odstranitvi. Prosimo, potrdite.', + freeQuota: 'BREZPLAČNA KVOTA', + }, + addApiKey: 'Dodaj svoj API ključ', + invalidApiKey: 'Neveljaven API ključ', + encrypted: { + front: 'Vaš API ključ bo šifriran in shranjen z uporabo', + back: ' tehnologije.', + }, + freeQuota: { + howToEarn: 'Kako zaslužiti', + }, + addMoreModelProvider: 'DODAJ VEČ PONUDNIKOV MODELOV', + addModel: 'Dodaj model', + modelsNum: '{{num}} modelov', + showModels: 'Prikaži modele', + showModelsNum: 'Prikaži {{num}} modelov', + collapse: 'Strni', + config: 'Konfiguracija', + modelAndParameters: 'Model in parametri', + model: 'Model', + featureSupported: '{{feature}} podprto', + callTimes: 'Število klicev', + credits: 'Sporočilni krediti', + buyQuota: 'Kupi kvoto', + getFreeTokens: 'Pridobi brezplačne žetone', + priorityUsing: 'Prednostna uporaba', + deprecated: 'Zastarelo', + confirmDelete: 'Potrdite izbris?', + quotaTip: 'Preostali razpoložljivi brezplačni žetoni', + loadPresets: 'Naloži prednastavitve', + parameters: 'PARAMETRI', + loadBalancing: 'Uravnoteženje obremenitev', + loadBalancingDescription: 'Zmanjšajte pritisk s pomočjo več sklopov poverilnic.', + loadBalancingHeadline: 'Uravnoteženje obremenitev', + configLoadBalancing: 'Konfiguracija uravnoteženja obremenitev', + modelHasBeenDeprecated: 'Ta model je zastarel', + providerManaged: 'Upravljano s strani ponudnika', + providerManagedDescription: 'Uporabite enoten sklop poverilnic, ki jih zagotovi ponudnik modela.', + defaultConfig: 'Privzeta konfiguracija', + apiKeyStatusNormal: 'Stanje API ključa je normalno', + apiKeyRateLimit: 'Omejitev hitrosti je dosežena, na voljo po {{seconds}} sekundah', + addConfig: 'Dodaj konfiguracijo', + editConfig: 'Uredi konfiguracijo', + loadBalancingLeastKeyWarning: 'Za omogočanje uravnoteženja obremenitev morata biti omogočena vsaj 2 ključa.', + loadBalancingInfo: 'Privzeto uravnoteženje obremenitev uporablja strategijo Round-robin. Če se sproži omejitev hitrosti, se uporabi 1-minutno obdobje ohlajanja.', + upgradeForLoadBalancing: 'Nadgradite svoj načrt, da omogočite uravnoteženje obremenitev.', + dataSource: { + add: 'Dodaj vir podatkov', + connect: 'Poveži', + configure: 'Konfiguriraj', + notion: { + title: 'Notion', + description: 'Uporaba Notiona kot vira podatkov za Znanost.', + connectedWorkspace: 'Povezano delovno okolje', + addWorkspace: 'Dodaj delovno okolje', + connected: 'Povezan', + disconnected: 'Prekinjen', + changeAuthorizedPages: 'Spremeni pooblaščene strani', + pagesAuthorized: 'Pooblaščene strani', + sync: 'Sinhroniziraj', + remove: 'Odstrani', + selector: { + pageSelected: 'Izbrane strani', + searchPages: 'Iskanje strani...', + noSearchResult: 'Ni rezultatov iskanja', + addPages: 'Dodaj strani', + preview: 'PREDOGLED', + }, + }, + website: { + title: 'Spletna stran', + description: 'Uvoz vsebine s spletnih strani z uporabo spletnega pajka.', + with: 'S', + configuredCrawlers: 'Konfigurirani pajki', + active: 'Aktiven', + inactive: 'Neaktiven', + }, + }, + plugin: { + serpapi: { + apiKey: 'API ključ', + apiKeyPlaceholder: 'Vnesite svoj API ključ', + keyFrom: 'Pridobite svoj SerpAPI ključ na strani računa SerpAPI', + }, + }, + apiBasedExtension: { + title: 'Razširitve API omogočajo centralizirano upravljanje API, kar poenostavi konfiguracijo za enostavno uporabo v aplikacijah Dify.', + link: 'Naučite se, kako razviti svojo API razširitev.', + linkUrl: 'https://docs.dify.ai/features/extension/api_based_extension', + add: 'Dodaj API razširitev', + selector: { + title: 'API razširitev', + placeholder: 'Prosimo, izberite API razširitev', + manage: 'Upravljaj API razširitev', + }, + modal: { + title: 'Dodaj API razširitev', + editTitle: 'Uredi API razširitev', + name: { + title: 'Ime', + placeholder: 'Vnesite ime', + }, + apiEndpoint: { + title: 'API konec', + placeholder: 'Vnesite API konec', + }, + apiKey: { + title: 'API ključ', + placeholder: 'Vnesite API ključ', + lengthError: 'Dolžina API ključa ne sme biti manjša od 5 znakov', + }, + }, + type: 'Tip', + }, + about: { + changeLog: 'Dnevnik sprememb', + updateNow: 'Posodobi zdaj', + nowAvailable: 'Dify {{version}} je zdaj na voljo.', + latestAvailable: 'Dify {{version}} je najnovejša različica na voljo.', + }, + appMenus: { + overview: 'Nadzor', + promptEng: 'Orkestriraj', + apiAccess: 'Dostop API', + logAndAnn: 'Dnevniki in objave', + logs: 'Dnevniki', + }, + environment: { + testing: 'TESTIRANJE', + development: 'RAZVOJ', + }, + appModes: { + completionApp: 'Generator besedila', + chatApp: 'Klepetalna aplikacija', + }, + datasetMenus: { + documents: 'Dokumenti', + hitTesting: 'Preizkušanje pridobivanja', + settings: 'Nastavitve', + emptyTip: 'Znanost še ni povezana, pojdite v aplikacijo ali vtičnik, da dokončate povezavo.', + viewDoc: 'Ogled dokumentacije', + relatedApp: 'povezane aplikacije', + }, + voiceInput: { + speaking: 'Govorite zdaj...', + converting: 'Pretvarjanje v besedilo...', + notAllow: 'mikrofon ni pooblaščen', + }, + modelName: { + 'gpt-3.5-turbo': 'GPT-3.5-Turbo', + 'gpt-3.5-turbo-16k': 'GPT-3.5-Turbo-16K', + 'gpt-4': 'GPT-4', + 'gpt-4-32k': 'GPT-4-32K', + 'text-davinci-003': 'Text-Davinci-003', + 'text-embedding-ada-002': 'Text-Embedding-Ada-002', + 'whisper-1': 'Whisper-1', + 'claude-instant-1': 'Claude-Instant', + 'claude-2': 'Claude-2', + }, + chat: { + renameConversation: 'Preimenuj pogovor', + conversationName: 'Ime pogovora', + conversationNamePlaceholder: 'Vnesite ime pogovora', + conversationNameCanNotEmpty: 'Ime pogovora je obvezno', + citation: { + title: 'CITATI', + linkToDataset: 'Povezava do znanja', + characters: 'Znakov:', + hitCount: 'Število zadetkov:', + vectorHash: 'Vektorski hash:', + hitScore: 'Ocena zadetka:', + }, + }, + promptEditor: { + placeholder: 'Tukaj napišite svoje pozivno besedilo, vnesite \'{\' za vstavljanje spremenljivke, vnesite \'/\' za vstavljanje vsebinskega bloka poziva', + context: { + item: { + title: 'Kontekst', + desc: 'Vstavi predlogo konteksta', + }, + modal: { + title: '{{num}} Znanost v kontekstu', + add: 'Dodaj kontekst ', + footer: 'Kontekste lahko upravljate v spodnjem razdelku Kontekst.', + }, + }, + history: { + item: { + title: 'Zgodovina pogovora', + desc: 'Vstavi predlogo zgodovinskega sporočila', + }, + modal: { + title: 'PRIMER', + user: 'Pozdravljeni', + assistant: 'Pozdravljeni! Kako vam lahko pomagam danes?', + edit: 'Uredi imena vlog pogovora', + }, + }, + variable: { + item: { + title: 'Spremenljivke in zunanji orodja', + desc: 'Vstavi spremenljivke in zunanja orodja', + }, + outputToolDisabledItem: { + title: 'Spremenljivke', + desc: 'Vstavi spremenljivke', + }, + modal: { + add: 'Nova spremenljivka', + addTool: 'Novo orodje', + }, + }, + query: { + item: { + title: 'Poizvedba', + desc: 'Vstavi predlogo uporabniške poizvedbe', + }, + }, + existed: 'Že obstaja v pozivu', + }, + imageUploader: { + uploadFromComputer: 'Naloži iz računalnika', + uploadFromComputerReadError: 'Branje slike ni uspelo, poskusite znova.', + uploadFromComputerUploadError: 'Nalaganje slike ni uspelo, poskusite znova.', + uploadFromComputerLimit: 'Nalaganje slik ne sme presegati {{size}} MB', + pasteImageLink: 'Prilepi povezavo do slike', + pasteImageLinkInputPlaceholder: 'Tukaj prilepite povezavo do slike', + pasteImageLinkInvalid: 'Neveljavna povezava slike', + imageUpload: 'Nalaganje slike', + }, + tag: { + placeholder: 'Vse oznake', + addNew: 'Dodaj novo oznako', + noTag: 'Ni oznak', + noTagYet: 'Še ni oznak', + addTag: 'Dodaj oznake', + editTag: 'Uredi oznake', + manageTags: 'Upravljaj oznake', + selectorPlaceholder: 'Vnesite za iskanje ali ustvarjanje', + create: 'Ustvari', + delete: 'Izbriši oznako', + deleteTip: 'Oznaka se uporablja, jo želite izbrisati?', + created: 'Oznaka uspešno ustvarjena', + failed: 'Ustvarjanje oznake ni uspelo', + }, + }, +} +export default translation diff --git a/web/i18n/sl-SI/custom.ts b/web/i18n/sl-SI/custom.ts new file mode 100644 index 0000000000..6c2f3f4f93 --- /dev/null +++ b/web/i18n/sl-SI/custom.ts @@ -0,0 +1,30 @@ +const translation = { + custom: 'Prilagoditev', + upgradeTip: { + prefix: 'Nadgradite svoj načrt za', + suffix: 'prilagoditev vaše blagovne znamke.', + }, + webapp: { + title: 'Prilagodi blagovno znamko spletne aplikacije', + removeBrand: 'Odstrani Powered by Dify', + changeLogo: 'Spremeni sliko Powered by Brand', + changeLogoTip: 'Format SVG ali PNG z minimalno velikostjo 40x40px', + }, + app: { + title: 'Prilagodi blagovno znamko glave aplikacije', + changeLogoTip: 'Format SVG ali PNG z minimalno velikostjo 80x80px', + }, + upload: 'Naloži', + uploading: 'Nalagam', + uploadedFail: 'Nalaganje slike ni uspelo, prosimo, poskusite znova.', + change: 'Spremeni', + apply: 'Uporabi', + restore: 'Obnovi privzete nastavitve', + customize: { + contactUs: ' kontaktirajte nas ', + prefix: 'Za prilagoditev logotipa blagovne znamke znotraj aplikacije, prosimo,', + suffix: 'za nadgradnjo na Enterprise izdajo.', + }, +} + +export default translation diff --git a/web/i18n/sl-SI/dataset-creation.ts b/web/i18n/sl-SI/dataset-creation.ts new file mode 100644 index 0000000000..1b24313045 --- /dev/null +++ b/web/i18n/sl-SI/dataset-creation.ts @@ -0,0 +1,175 @@ +const translation = { + steps: { + header: { + creation: 'Ustvari Znanje', + update: 'Dodaj podatke', + }, + one: 'Izberi vir podatkov', + two: 'Predobdelava in čiščenje besedila', + three: 'Izvedi in zaključi', + }, + error: { + unavailable: 'To Znanje ni na voljo', + }, + firecrawl: { + configFirecrawl: 'Nastavi 🔥Firecrawl', + apiKeyPlaceholder: 'API ključ od firecrawl.dev', + getApiKeyLinkText: 'Pridobi API ključ na firecrawl.dev', + }, + jinaReader: { + configJinaReader: 'Nastavi Jina Reader', + apiKeyPlaceholder: 'API ključ od jina.ai', + getApiKeyLinkText: 'Pridobi brezplačni API ključ na jina.ai', + }, + stepOne: { + filePreview: 'Predogled datoteke', + pagePreview: 'Predogled strani', + dataSourceType: { + file: 'Uvozi iz datoteke', + notion: 'Sinhroniziraj z Notion', + web: 'Sinhroniziraj s spletno stranjo', + }, + uploader: { + title: 'Naloži datoteko', + button: 'Povleci in spusti datoteko ali', + browse: 'Prebrskaj', + tip: 'Podprti tipi datotek: {{supportTypes}}. Največ {{size}}MB na datoteko.', + validation: { + typeError: 'Tip datoteke ni podprt', + size: 'Datoteka je prevelika. Največja dovoljena velikost je {{size}}MB', + count: 'Podprta je le ena datoteka', + filesNumber: 'Dosegli ste omejitev za pošiljanje {{filesNumber}} datotek.', + }, + cancel: 'Prekliči', + change: 'Zamenjaj', + failed: 'Nalaganje ni uspelo', + }, + notionSyncTitle: 'Notion ni povezan', + notionSyncTip: 'Za sinhronizacijo z Notion je najprej potrebno vzpostaviti povezavo.', + connect: 'Pojdi na povezavo', + button: 'Naprej', + emptyDatasetCreation: 'Želim ustvariti prazno Znanje', + modal: { + title: 'Ustvari prazno Znanje', + tip: 'Prazno Znanje ne bo vsebovalo dokumentov, dokumente pa lahko naložite kadarkoli.', + input: 'Ime Znanja', + placeholder: 'Vnesite ime', + nameNotEmpty: 'Ime ne sme biti prazno', + nameLengthInvalid: 'Ime mora imeti od 1 do 40 znakov', + cancelButton: 'Prekliči', + confirmButton: 'Ustvari', + failed: 'Ustvarjanje ni uspelo', + }, + website: { + chooseProvider: 'Izberi ponudnika', + fireCrawlNotConfigured: 'Firecrawl ni nastavljen', + fireCrawlNotConfiguredDescription: 'Nastavite Firecrawl z API ključem, da ga lahko uporabite.', + jinaReaderNotConfigured: 'Jina Reader ni nastavljen', + jinaReaderNotConfiguredDescription: 'Nastavite Jina Reader z vnosom brezplačnega API ključa.', + configure: 'Nastavi', + run: 'Zaženi', + firecrawlTitle: 'Izvleci spletno vsebino z 🔥Firecrawl', + firecrawlDoc: 'Firecrawl dokumentacija', + firecrawlDocLink: 'https://docs.dify.ai/guides/knowledge-base/sync-from-website', + jinaReaderTitle: 'Pretvori celotno stran v Markdown', + jinaReaderDoc: 'Več o Jina Reader', + jinaReaderDocLink: 'https://jina.ai/reader', + useSitemap: 'Uporabi sitemap', + useSitemapTooltip: 'Sledi zemljevidu spletne strani (sitemap) za iskanje strani. Če ne, bo Jina Reader iterativno iskal strani na podlagi pomembnosti, kar bo prineslo manj, a bolj relevantnih strani.', + options: 'Možnosti', + crawlSubPage: 'Išči podstrani', + limit: 'Omejitev', + maxDepth: 'Največja globina', + excludePaths: 'Izključi poti', + includeOnlyPaths: 'Vključi le poti', + extractOnlyMainContent: 'Izvleci le glavno vsebino (brez glav, navigacij, nog itd.)', + exceptionErrorTitle: 'Med izvajanjem iskanja je prišlo do izjeme:', + unknownError: 'Neznana napaka', + totalPageScraped: 'Skupaj preiskanih strani:', + selectAll: 'Izberi vse', + resetAll: 'Ponastavi vse', + scrapTimeInfo: 'Skupaj preiskanih {{total}} strani v {{time}}s', + preview: 'Predogled', + maxDepthTooltip: 'Največja globina iskanja glede na vneseni URL. Globina 0 bo iskala le stran z vnesenim URL-jem, globina 1 bo iskala URL in vse za tem, dodano z enim /, in tako naprej.', + }, + }, + stepTwo: { + segmentation: 'Nastavitve razdeljevanja', + auto: 'Samodejno', + autoDescription: 'Samodejno nastavi pravila za razdeljevanje in predobdelavo besedila. Neizkušenim uporabnikom priporočamo to možnost.', + custom: 'Po meri', + customDescription: 'Prilagodi pravila za razdeljevanje, dolžino delcev in pravila za predobdelavo besedila itd.', + separator: 'Ločilo', + separatorTip: 'Ločilo je znak, ki se uporablja za ločevanje besedila. \\n\\n in \\n sta pogosto uporabljeni ločili za ločevanje odstavkov in vrstic. V kombinaciji z vejicami (\\n\\n,\\n) bodo odstavki razdeljeni po vrsticah, ko bo presežena največja dolžina delcev. Uporabite lahko tudi posebna ločila, ki jih sami določite (npr. ***).', + separatorPlaceholder: '\\n\\n za ločevanje odstavkov; \\n za ločevanje vrstic', + maxLength: 'Največja dolžina delca', + overlap: 'Prekrivanje delcev', + overlapTip: 'Nastavitev prekrivanja delcev lahko ohrani semantično povezavo med njimi, kar izboljša učinkovitost iskanja. Priporočamo nastavitev 10%-25% največje dolžine delca.', + overlapCheck: 'Prekrivanje delca ne sme biti večje od največje dolžine delca', + rules: 'Pravila predobdelave besedila', + removeExtraSpaces: 'Zamenjaj zaporedne presledke, vrstice in zavihke', + removeUrlEmails: 'Izbriši vse URL-je in e-poštne naslove', + removeStopwords: 'Odstrani neuporabne besede kot so "a", "an", "the"', + preview: 'Potrdi in predogled', + reset: 'Ponastavi', + indexMode: 'Način indeksiranja', + qualified: 'Visoka kakovost', + recommend: 'Priporočeno', + qualifiedTip: 'Za obdelavo se uporabi privzeti sistemski vmesnik za vdelavo, ki zagotavlja višjo natančnost pri poizvedbah uporabnikov.', + warning: 'Najprej nastavite API ključ za model ponudnika.', + click: 'Pojdi na nastavitve', + economical: 'Ekonomsko', + economicalTip: 'Uporablja lokalne vektorske pogone, ključne besede, itd., kar zmanjša natančnost brez porabe žetonov.', + QATitle: 'Razdeljevanje v obliki Vprašanje & Odgovor', + QATip: 'Omogočanje te možnosti bo porabilo več žetonov', + QALanguage: 'Razdelitev z uporabo', + estimateCost: 'Ocena stroškov', + estimateSegment: 'Ocenjeno število delcev', + segmentCount: 'delci', + calculating: 'Izračunavam...', + fileSource: 'Predobdelava dokumentov', + notionSource: 'Predobdelava strani', + websiteSource: 'Predobdelava spletne strani', + other: 'in drugi ', + fileUnit: ' dokumenti', + notionUnit: ' strani', + webpageUnit: ' strani', + previousStep: 'Prejšnji korak', + nextStep: 'Shrani in obdela', + save: 'Shrani in obdela', + cancel: 'Prekliči', + sideTipTitle: 'Zakaj razdeljevanje in predobdelava?', + sideTipP1: 'Pri obdelavi besedilnih podatkov sta razdeljevanje in čiščenje dve pomembni fazi predobdelave.', + sideTipP2: 'Razdeljevanje dolga besedila na odstavke omogoča modelom boljše razumevanje. To izboljša kakovost in relevantnost rezultatov modela.', + sideTipP3: 'Čiščenje odstranjuje nepotrebne znake in formate, kar Znanje naredi bolj čisto in lažje obdeljivo.', + sideTipP4: 'Pravilno razdeljevanje in čiščenje izboljšata delovanje modela, kar zagotavlja bolj natančne in dragocene rezultate.', + previewTitle: 'Predogled', + previewTitleButton: 'Predogled', + previewButton: 'Preklop v obliko Vprašanje & Odgovor', + previewSwitchTipStart: 'Trenutni predogled delcev je v obliki besedila, preklop na predogled v obliki vprašanj in odgovorov bo', + previewSwitchTipEnd: ' porabil dodatne žetone', + characters: 'znaki', + indexSettingTip: 'Če želite spremeniti način indeksiranja in model vdelave, pojdite na ', + retrievalSettingTip: 'Če želite spremeniti nastavitve iskanja, pojdite na ', + datasetSettingLink: 'nastavitve Znanja.', + }, + stepThree: { + creationTitle: '🎉 Znanje ustvarjeno', + creationContent: 'Samodejno smo poimenovali Znanje, ime lahko kadarkoli spremenite.', + label: 'Ime Znanja', + additionTitle: '🎉 Dokument naložen', + additionP1: 'Dokument je bil naložen v Znanje', + additionP2: ', lahko ga najdete v seznamu dokumentov Znanja.', + stop: 'Ustavi obdelavo', + resume: 'Nadaljuj obdelavo', + navTo: 'Pojdi na dokument', + sideTipTitle: 'Kaj sledi', + sideTipContent: 'Ko je dokument končan z indeksiranjem, lahko Znanje vključite v aplikacijo kot kontekst, nastavitve konteksta najdete na strani za orkestracijo ukazov. Prav tako ga lahko ustvarite kot samostojni vtičnik za indeksiranje ChatGPT in ga izdate.', + modelTitle: 'Ali ste prepričani, da želite ustaviti vdelavo?', + modelContent: 'Če boste morali nadaljevati obdelavo kasneje, se bo ta nadaljevala tam, kjer ste končali.', + modelButtonConfirm: 'Potrdi', + modelButtonCancel: 'Prekliči', + }, +} + +export default translation diff --git a/web/i18n/sl-SI/dataset-documents.ts b/web/i18n/sl-SI/dataset-documents.ts new file mode 100644 index 0000000000..0ca8be9e95 --- /dev/null +++ b/web/i18n/sl-SI/dataset-documents.ts @@ -0,0 +1,352 @@ +const translation = { + list: { + title: 'Dokumenti', + desc: 'Vse datoteke znanja so prikazane tukaj, celotno znanje pa je mogoče povezati s citati Dify ali indeksirati prek vtičnika Chat.', + addFile: 'Dodaj datoteko', + addPages: 'Dodaj strani', + addUrl: 'Dodaj URL', + table: { + header: { + fileName: 'IME DATOTEKE', + words: 'BESEDE', + hitCount: 'ŠTEVILO PRIDOBITEV', + uploadTime: 'ČAS NALAGANJA', + status: 'STATUS', + action: 'DEJANJE', + }, + rename: 'Preimenuj', + name: 'Ime', + }, + action: { + uploadFile: 'Naloži novo datoteko', + settings: 'Nastavitve segmenta', + addButton: 'Dodaj del', + add: 'Dodaj del', + batchAdd: 'Serijsko dodajanje', + archive: 'Arhiviraj', + unarchive: 'Razveljavi arhiviranje', + delete: 'Izbriši', + enableWarning: 'Arhivirane datoteke ni mogoče omogočiti', + sync: 'Sinhroniziraj', + }, + index: { + enable: 'Omogoči', + disable: 'Onemogoči', + all: 'Vse', + enableTip: 'Datoteka je lahko indeksirana', + disableTip: 'Datoteka ne more biti indeksirana', + }, + status: { + queuing: 'V čakalni vrsti', + indexing: 'Indeksiranje', + paused: 'Zaustavljeno', + error: 'Napaka', + available: 'Na voljo', + enabled: 'Omogočeno', + disabled: 'Onemogočeno', + archived: 'Arhivirano', + }, + empty: { + title: 'Dokumentacije še ni', + upload: { + tip: 'Lahko naložite datoteke, sinhronizirate z spletno stranjo ali aplikacijami, kot so Notion, GitHub itd.', + }, + sync: { + tip: 'Dify bo občasno prenesel datoteke iz Notion in dokončal obdelavo.', + }, + }, + delete: { + title: 'Ali ste prepričani, da želite izbrisati?', + content: 'Če boste nadaljevali obdelavo kasneje, boste nadaljevali tam, kjer ste končali.', + }, + batchModal: { + title: 'Serijsko dodajanje delov', + csvUploadTitle: 'Povlecite in spustite svojo CSV datoteko tukaj ali ', + browse: 'brskajte', + tip: 'CSV datoteka mora ustrezati naslednji strukturi:', + question: 'vprašanje', + answer: 'odgovor', + contentTitle: 'vsebina dela', + content: 'vsebina', + template: 'Prenesite predlogo tukaj', + cancel: 'Prekliči', + run: 'Zaženi serijo', + runError: 'Serijsko dodajanje ni uspelo', + processing: 'V obdelavi serije', + completed: 'Uvoz zaključen', + error: 'Napaka pri uvozu', + ok: 'V redu', + }, + }, + metadata: { + title: 'Metapodatki', + desc: 'Označevanje metapodatkov za dokumente omogoča, da AI pravočasno dostopa do njih in prikaže vir referenc uporabnikom.', + dateTimeFormat: 'MMMM D, YYYY hh:mm A', + docTypeSelectTitle: 'Izberite vrsto dokumenta', + docTypeChangeTitle: 'Spremeni vrsto dokumenta', + docTypeSelectWarning: + 'Če se vrsta dokumenta spremeni, trenutni vneseni metapodatki ne bodo ohranjeni', + firstMetaAction: 'Začni', + placeholder: { + add: 'Dodaj ', + select: 'Izberi ', + }, + source: { + upload_file: 'Naloži datoteko', + notion: 'Sinhroniziraj iz Notion', + github: 'Sinhroniziraj iz Github', + }, + type: { + book: 'Knjiga', + webPage: 'Spletna stran', + paper: 'Znanstveni članek', + socialMediaPost: 'Objava na družbenih omrežjih', + personalDocument: 'Osebni dokument', + businessDocument: 'Poslovni dokument', + IMChat: 'Klepet', + wikipediaEntry: 'Vnos iz Wikipedije', + notion: 'Sinhroniziraj iz Notion', + github: 'Sinhroniziraj iz Github', + technicalParameters: 'Tehnični parametri', + }, + field: { + processRule: { + processDoc: 'Obdelaj dokument', + segmentRule: 'Pravilo segmenta', + segmentLength: 'Dolžina segmentov', + processClean: 'Čiščenje besedila', + }, + book: { + title: 'Naslov', + language: 'Jezik', + author: 'Avtor', + publisher: 'Založnik', + publicationDate: 'Datum objave', + ISBN: 'ISBN', + category: 'Kategorija', + }, + webPage: { + title: 'Naslov', + url: 'URL', + language: 'Jezik', + authorPublisher: 'Avtor/Založnik', + publishDate: 'Datum objave', + topicsKeywords: 'Teme/Ključne besede', + description: 'Opis', + }, + paper: { + title: 'Naslov', + language: 'Jezik', + author: 'Avtor', + publishDate: 'Datum objave', + journalConferenceName: 'Ime revije/konference', + volumeIssuePage: 'Letnik/Številka/Stran', + DOI: 'DOI', + topicsKeywords: 'Teme/Ključne besede', + abstract: 'Povzetek', + }, + socialMediaPost: { + platform: 'Platforma', + authorUsername: 'Avtor/Uporabniško ime', + publishDate: 'Datum objave', + postURL: 'URL objave', + topicsTags: 'Teme/Oznake', + }, + personalDocument: { + title: 'Naslov', + author: 'Avtor', + creationDate: 'Datum nastanka', + lastModifiedDate: 'Datum zadnje spremembe', + documentType: 'Vrsta dokumenta', + tagsCategory: 'Oznake/Kategorija', + }, + businessDocument: { + title: 'Naslov', + author: 'Avtor', + creationDate: 'Datum nastanka', + lastModifiedDate: 'Datum zadnje spremembe', + documentType: 'Vrsta dokumenta', + departmentTeam: 'Oddelek/Ekipa', + }, + IMChat: { + chatPlatform: 'Platforma za klepet', + chatPartiesGroupName: 'Udeleženci klepeta/Skupina', + participants: 'Udeleženci', + startDate: 'Datum začetka', + endDate: 'Datum konca', + topicsKeywords: 'Teme/Ključne besede', + fileType: 'Vrsta datoteke', + }, + wikipediaEntry: { + title: 'Naslov', + language: 'Jezik', + webpageURL: 'URL spletne strani', + editorContributor: 'Urednik/Sodelavec', + lastEditDate: 'Datum zadnje spremembe', + summaryIntroduction: 'Povzetek/Uvod', + }, + notion: { + title: 'Naslov', + language: 'Jezik', + author: 'Avtor', + createdTime: 'Čas nastanka', + lastModifiedTime: 'Čas zadnje spremembe', + url: 'URL', + tag: 'Oznaka', + description: 'Opis', + }, + github: { + repoName: 'Ime repozitorija', + repoDesc: 'Opis repozitorija', + repoOwner: 'Lastnik repozitorija', + fileName: 'Ime datoteke', + filePath: 'Pot do datoteke', + programmingLang: 'Programski jezik', + url: 'URL', + license: 'Licenca', + lastCommitTime: 'Čas zadnje spremembe', + lastCommitAuthor: 'Avtor zadnje spremembe', + }, + originInfo: { + originalFilename: 'Izvirno ime datoteke', + originalFileSize: 'Izvirna velikost datoteke', + uploadDate: 'Datum nalaganja', + lastUpdateDate: 'Datum zadnje spremembe', + source: 'Vir', + }, + technicalParameters: { + segmentSpecification: 'Specifikacija segmentov', + segmentLength: 'Dolžina segmentov', + avgParagraphLength: 'Povprečna dolžina odstavka', + paragraphs: 'Odstavki', + hitCount: 'Število pridobitev', + embeddingTime: 'Čas vdelave', + embeddedSpend: 'Stroški vdelave', + }, + }, + languageMap: { + zh: 'Kitajščina', + en: 'Angleščina', + es: 'Španščina', + fr: 'Francoščina', + de: 'Nemščina', + ja: 'Japonščina', + ko: 'Korejščina', + ru: 'Ruščina', + ar: 'Arabščina', + pt: 'Portugalščina', + it: 'Italijanščina', + nl: 'Nizozemščina', + pl: 'Poljščina', + sv: 'Švedščina', + tr: 'Turščina', + he: 'Hebrejščina', + hi: 'Hindujščina', + da: 'Danščina', + fi: 'Finščina', + no: 'Norveščina', + hu: 'Madžarščina', + el: 'Grščina', + cs: 'Češčina', + th: 'Tajščina', + id: 'Indonezijščina', + }, + categoryMap: { + book: { + fiction: 'Leposlovje', + biography: 'Biografija', + history: 'Zgodovina', + science: 'Znanost', + technology: 'Tehnologija', + education: 'Izobraževanje', + philosophy: 'Filozofija', + religion: 'Religija', + socialSciences: 'Družboslovje', + art: 'Umetnost', + travel: 'Potovanja', + health: 'Zdravje', + selfHelp: 'Samopomoč', + businessEconomics: 'Poslovanje in ekonomija', + cooking: 'Kuhanje', + childrenYoungAdults: 'Otroci in mladi odrasli', + comicsGraphicNovels: 'Stripi in grafični romani', + poetry: 'Poezija', + drama: 'Drama', + other: 'Drugo', + }, + personalDoc: { + notes: 'Zapiski', + blogDraft: 'Osnutek bloga', + diary: 'Dnevnik', + researchReport: 'Raziskovalno poročilo', + bookExcerpt: 'Odlomek iz knjige', + schedule: 'Urnik', + list: 'Seznam', + projectOverview: 'Pregled projekta', + photoCollection: 'Fotografska zbirka', + creativeWriting: 'Ustvarjalno pisanje', + codeSnippet: 'Koda', + designDraft: 'Oblikovalski osnutek', + personalResume: 'Osebni življenjepis', + other: 'Drugo', + }, + businessDoc: { + meetingMinutes: 'Zapisniki sestankov', + researchReport: 'Raziskovalno poročilo', + proposal: 'Predlog', + employeeHandbook: 'Priročnik za zaposlene', + trainingMaterials: 'Izobraževalni materiali', + requirementsDocument: 'Dokumentacija zahtev', + designDocument: 'Oblikovalska dokumentacija', + productSpecification: 'Specifikacija izdelka', + financialReport: 'Finančno poročilo', + marketAnalysis: 'Tržna analiza', + projectPlan: 'Načrt projekta', + teamStructure: 'Struktura ekipe', + policiesProcedures: 'Pravila in postopki', + contractsAgreements: 'Pogodbe in dogovori', + emailCorrespondence: 'E-poštna korespondenca', + other: 'Drugo', + }, + }, + }, + embedding: { + processing: 'Proces vdelave...', + paused: 'Vdelava zaustavljena', + completed: 'Vdelava zaključena', + error: 'Napaka pri vdelavi', + docName: 'Predobdelava dokumenta', + mode: 'Pravilo segmentacije', + segmentLength: 'Dolžina segmentov', + textCleaning: 'Predobdelava in čiščenje besedila', + segments: 'Odstavki', + highQuality: 'Način visoke kakovosti', + economy: 'Ekonomski način', + estimate: 'Ocenjena poraba', + stop: 'Ustavi obdelavo', + resume: 'Nadaljuj obdelavo', + automatic: 'Samodejno', + custom: 'Po meri', + previewTip: 'Predogled odstavkov bo na voljo po zaključku vdelave', + }, + segment: { + paragraphs: 'Odstavki', + keywords: 'Ključne besede', + addKeyWord: 'Dodaj ključno besedo', + keywordError: 'Največja dolžina ključne besede je 20', + characters: 'znakov', + hitCount: 'Število pridobitev', + vectorHash: 'Vektorski hash: ', + questionPlaceholder: 'dodajte vprašanje tukaj', + questionEmpty: 'Vprašanje ne sme biti prazno', + answerPlaceholder: 'dodajte odgovor tukaj', + answerEmpty: 'Odgovor ne sme biti prazen', + contentPlaceholder: 'dodajte vsebino tukaj', + contentEmpty: 'Vsebina ne sme biti prazna', + newTextSegment: 'Nov besedilni segment', + newQaSegment: 'Nov Q&A segment', + delete: 'Izbriši ta del?', + }, +} + +export default translation diff --git a/web/i18n/sl-SI/dataset-hit-testing.ts b/web/i18n/sl-SI/dataset-hit-testing.ts new file mode 100644 index 0000000000..5f5f895d6e --- /dev/null +++ b/web/i18n/sl-SI/dataset-hit-testing.ts @@ -0,0 +1,30 @@ +const translation = { + title: 'Testiranje pridobivanja', + settingTitle: 'Nastavitve pridobivanja', + desc: 'Preizkusite učinkovitost zadetkov znanja na podlagi podanega poizvedbenega besedila', + dateTimeFormat: 'DD/MM/YYYY hh:mm A', + recents: 'Nedavno', + table: { + header: { + source: 'Vir', + text: 'Besedilo', + time: 'Čas', + }, + }, + input: { + title: 'Izvorno besedilo', + placeholder: 'Prosimo, vnesite besedilo, priporočljivo je kratko izjavno poved.', + countWarning: 'Do 200 znakov.', + indexWarning: 'Samo znanje visoke kakovosti.', + testing: 'Testiranje', + }, + hit: { + title: 'PRIDOBLJENI ODSTAVKI', + emptyTip: 'Rezultati testiranja pridobivanja bodo prikazani tukaj', + }, + noRecentTip: 'Tukaj ni nedavnih rezultatov poizvedb', + viewChart: 'Prikaži VEKTORSKI GRAF', + viewDetail: 'Prikaži podrobnosti', +} + +export default translation diff --git a/web/i18n/sl-SI/dataset-settings.ts b/web/i18n/sl-SI/dataset-settings.ts new file mode 100644 index 0000000000..1e42fcd9b4 --- /dev/null +++ b/web/i18n/sl-SI/dataset-settings.ts @@ -0,0 +1,38 @@ +const translation = { + title: 'Nastavitve znanja', + desc: 'Tukaj lahko spremenite lastnosti in nastavitve pridobivanja tega znanja.', + form: { + name: 'Ime znanja', + namePlaceholder: 'Prosimo, vnesite ime znanja', + nameError: 'Ime ne sme biti prazno', + desc: 'Opis znanja', + descInfo: 'Prosimo, napišite jasen besedilni opis, ki bo povzel vsebino znanja. Ta opis bo uporabljen kot osnova za ujemanje pri izbiri med več znanji za sklepanje.', + descPlaceholder: 'Opišite, kaj je v tem znanju (neobvezno)', + descWrite: 'Naučite se, kako napisati dober opis znanja.', + permissions: 'Dovoljenja', + permissionsOnlyMe: 'Samo jaz', + permissionsAllMember: 'Vsi člani ekipe', + permissionsInvitedMembers: 'Določeni člani ekipe', + me: '(Vi)', + indexMethod: 'Metoda indeksiranja', + indexMethodHighQuality: 'Visoka kakovost', + indexMethodHighQualityTip: 'Za obdelavo pokličite vdelani model za zagotovitev večje natančnosti pri poizvedbah uporabnikov.', + indexMethodEconomy: 'Varčna', + indexMethodEconomyTip: 'Uporabite offline vektorske motorje, indeksiranje ključnih besed itd., da zmanjšate natančnost brez porabe žetonov', + embeddingModel: 'Vdelani model', + embeddingModelTip: 'Če želite spremeniti vdelani model, pojdite na ', + embeddingModelTipLink: 'Nastavitve', + retrievalSetting: { + title: 'Nastavitve pridobivanja', + learnMore: 'Izvedite več', + description: ' o metodi pridobivanja.', + longDescription: ' o metodi pridobivanja, to lahko kadar koli spremenite v nastavitvah znanja.', + }, + externalKnowledgeAPI: 'Zunanji API za znanje', + externalKnowledgeID: 'ID zunanjega znanja', + retrievalSettings: 'Nastavitve pridobivanja', + save: 'Shrani', + }, +} + +export default translation diff --git a/web/i18n/sl-SI/dataset.ts b/web/i18n/sl-SI/dataset.ts new file mode 100644 index 0000000000..482a3eab7e --- /dev/null +++ b/web/i18n/sl-SI/dataset.ts @@ -0,0 +1,151 @@ +const translation = { + knowledge: 'Znanje', + externalTag: 'Zunanje', + externalAPI: 'Zunanji API', + externalAPIPanelTitle: 'Zunanji API za znanje', + externalKnowledgeId: 'ID zunanjega znanja', + externalKnowledgeName: 'Ime zunanjega znanja', + externalKnowledgeDescription: 'Opis znanja', + externalKnowledgeIdPlaceholder: 'Prosimo, vnesite ID znanja', + externalKnowledgeNamePlaceholder: 'Prosimo, vnesite ime baze znanja', + externalKnowledgeDescriptionPlaceholder: 'Opišite, kaj je v tej bazi znanja (neobvezno)', + learnHowToWriteGoodKnowledgeDescription: 'Naučite se, kako napisati dober opis znanja', + externalAPIPanelDescription: 'Zunanji API za znanje se uporablja za povezovanje z bazo znanja izven Dify in pridobivanje znanja iz te baze.', + externalAPIPanelDocumentation: 'Naučite se, kako ustvariti zunanji API za znanje', + documentCount: ' dokumentov', + wordCount: ' tisoč besed', + appCount: ' povezanih aplikacij', + createDataset: 'Ustvari znanje', + createNewExternalAPI: 'Ustvari nov zunanji API za znanje', + noExternalKnowledge: 'Zunanjega API-ja za znanje še ni, kliknite tukaj za ustvarjanje', + createExternalAPI: 'Dodaj zunanji API za znanje', + editExternalAPIFormTitle: 'Uredi zunanji API za znanje', + editExternalAPITooltipTitle: 'POVEZANO ZNANJE', + editExternalAPIConfirmWarningContent: { + front: 'Ta zunanji API za znanje je povezan z', + end: 'zunanjim znanjem, in ta sprememba bo vplivala na vse njih. Ali ste prepričani, da želite shraniti to spremembo?', + }, + editExternalAPIFormWarning: { + front: 'Ta zunanji API je povezan z', + end: 'zunanjim znanjem', + }, + deleteExternalAPIConfirmWarningContent: { + title: { + front: 'Izbriši', + end: '?', + }, + content: { + front: 'Ta zunanji API za znanje je povezan z', + end: 'zunanjim znanjem. Brisanje tega API-ja bo onemogočilo vse povezane baze znanja. Ali ste prepričani, da želite izbrisati ta API?', + }, + noConnectionContent: 'Ali ste prepričani, da želite izbrisati ta API?', + }, + selectExternalKnowledgeAPI: { + placeholder: 'Izberite zunanji API za znanje', + }, + connectDataset: 'Povežite se z zunanjo bazo znanja', + connectDatasetIntro: { + title: 'Kako se povezati z zunanjo bazo znanja', + content: { + front: 'Za povezavo z zunanjo bazo znanja morate najprej ustvariti zunanji API. Prosimo, natančno preberite in se sklicujte na', + link: 'Naučite se, kako ustvariti zunanji API', + end: '. Nato poiščite ustrezni ID znanja in ga vnesite v obrazec na levi. Če so vse informacije pravilne, boste po kliku na gumb za povezavo samodejno preusmerjeni na testiranje pridobivanja v bazi znanja.', + }, + learnMore: 'Izvedite več', + }, + connectHelper: { + helper1: 'Povežite se z zunanjimi bazami znanja preko API-ja in ID-ja baze znanja. Trenutno je ', + helper2: 'podprta le funkcionalnost pridobivanja', + helper3: '. Močno priporočamo, da ', + helper4: 'natančno preberete dokumentacijo za pomoč', + helper5: ' pred uporabo te funkcije.', + }, + createDatasetIntro: 'Uvozite lastne podatke v besedilni obliki ali v realnem času pišite podatke prek Webhook-a za izboljšanje konteksta LLM.', + deleteDatasetConfirmTitle: 'Izbrisati to znanje?', + deleteDatasetConfirmContent: + 'Brisanje znanja je nepovratno. Uporabniki do vašega znanja ne bodo več imeli dostopa, vse nastavitve pozivov in dnevniki bodo trajno izbrisani.', + datasetUsedByApp: 'Znanje se uporablja v nekaterih aplikacijah. Aplikacije ne bodo več mogle uporabljati tega znanja, vse nastavitve pozivov in dnevniki bodo trajno izbrisani.', + datasetDeleted: 'Znanje izbrisano', + datasetDeleteFailed: 'Brisanje znanja ni uspelo', + didYouKnow: 'Ali ste vedeli?', + intro1: 'Znanje je mogoče integrirati v aplikacijo Dify ', + intro2: 'kot kontekst', + intro3: ',', + intro4: 'ali pa ', + intro5: 'se lahko ustvari', + intro6: ' kot samostojni vtičnik ChatGPT za objavo', + unavailable: 'Ni na voljo', + unavailableTip: 'Vdelani model ni na voljo, potrebno je konfigurirati privzeti vdelani model', + datasets: 'ZNANJE', + datasetsApi: 'API DOSTOP', + externalKnowledgeForm: { + connect: 'Poveži', + cancel: 'Prekliči', + }, + externalAPIForm: { + name: 'Ime', + endpoint: 'API Končna točka', + apiKey: 'API ključ', + save: 'Shrani', + cancel: 'Prekliči', + edit: 'Uredi', + encrypted: { + front: 'Vaš API žeton bo šifriran in shranjen z uporabo', + end: 'tehnologije.', + }, + }, + retrieval: { + semantic_search: { + title: 'Vektorsko iskanje', + description: 'Ustvari vdelke poizvedbe in poišči odstavke besedila, ki so najbolj podobni njegovi vektorski predstavitvi.', + }, + full_text_search: { + title: 'Iskanje celotnega besedila', + description: 'Indeksirajte vse izraze v dokumentu, kar uporabnikom omogoča iskanje katerega koli izraza in pridobitev ustreznega odstavka besedila, ki ga vsebuje.', + }, + hybrid_search: { + title: 'Hibridno iskanje', + description: 'Istočasno izvede iskanje celotnega besedila in vektorsko iskanje ter ponovno razvrsti zadetke, da izbere najboljše ujemanje za uporabnikovo poizvedbo. Uporabniki lahko določijo uteži ali konfigurirajo model za ponovno razvrščanje.', + recommend: 'Priporočamo', + }, + invertedIndex: { + title: 'Inverzni indeks', + description: 'Inverzni indeks je struktura, ki se uporablja za učinkovito pridobivanje. Organizirano po izrazih, vsak izraz kaže na dokumente ali spletne strani, ki ga vsebujejo.', + }, + change: 'Spremeni', + changeRetrievalMethod: 'Spremeni metodo pridobivanja', + }, + docsFailedNotice: 'dokumentov ni bilo mogoče indeksirati', + retry: 'Poskusi znova', + indexingTechnique: { + high_quality: 'HQ', + economy: 'ECO', + }, + indexingMethod: { + semantic_search: 'VEKTORSKO', + full_text_search: 'CELOTNO BESEDILO', + hybrid_search: 'HIBRIDNO', + invertedIndex: 'INVERZNO', + }, + defaultRetrievalTip: 'Privzeto se uporablja večpotno pridobivanje. Znanje se pridobiva iz več baz znanja in nato ponovno razvrsti.', + mixtureHighQualityAndEconomicTip: 'Model za ponovno razvrščanje je potreben za mešanico baz znanja visoke kakovosti in varčnih baz.', + inconsistentEmbeddingModelTip: 'Model za ponovno razvrščanje je potreben, če so vdelani modeli izbranih baz znanja neenotni.', + mixtureInternalAndExternalTip: 'Model za ponovno razvrščanje je potreben za mešanico notranjega in zunanjega znanja.', + allExternalTip: 'Pri uporabi samo zunanjega znanja lahko uporabnik izbere, ali želi omogočiti model za ponovno razvrščanje. Če ni omogočen, bodo pridobljeni deli razvrščeni glede na ocene. Če so strategije pridobivanja različnih baz znanja neenotne, bo to netočno.', + retrievalSettings: 'Nastavitve pridobivanja', + rerankSettings: 'Nastavitve za ponovno razvrščanje', + weightedScore: { + title: 'Utežena ocena', + description: 'Z nastavljanjem dodeljenih uteži ta strategija za ponovno razvrščanje določa, ali naj se daje prednost semantičnemu ali ključnemu ujemanju.', + semanticFirst: 'Semantično najprej', + keywordFirst: 'Ključne besede najprej', + customized: 'Prilagojeno', + semantic: 'Semantično', + keyword: 'Ključna beseda', + }, + nTo1RetrievalLegacy: 'N-to-1 pridobivanje bo uradno ukinjeno septembra. Priporočamo uporabo najnovejšega večpotnega pridobivanja za boljše rezultate.', + nTo1RetrievalLegacyLink: 'Izvedite več', + nTo1RetrievalLegacyLinkText: 'N-to-1 pridobivanje bo uradno ukinjeno septembra.', +} + +export default translation diff --git a/web/i18n/sl-SI/explore.ts b/web/i18n/sl-SI/explore.ts new file mode 100644 index 0000000000..ea3813b488 --- /dev/null +++ b/web/i18n/sl-SI/explore.ts @@ -0,0 +1,41 @@ +const translation = { + title: 'Razišči', + sidebar: { + discovery: 'Odkritja', + chat: 'Klepet', + workspace: 'Delovni prostor', + action: { + pin: 'Pripni', + unpin: 'Odpni', + rename: 'Preimenuj', + delete: 'Izbriši', + }, + delete: { + title: 'Izbriši aplikacijo', + content: 'Ali ste prepričani, da želite izbrisati to aplikacijo?', + }, + }, + apps: { + title: 'Razišči aplikacije Dify', + description: 'Uporabite te predloge aplikacij takoj ali prilagodite svoje aplikacije na podlagi predlog.', + allCategories: 'Priporočeno', + }, + appCard: { + addToWorkspace: 'Dodaj v delovni prostor', + customize: 'Prilagodi', + }, + appCustomize: { + title: 'Ustvari aplikacijo iz {{name}}', + subTitle: 'Ikona aplikacije & ime', + nameRequired: 'Ime aplikacije je obvezno', + }, + category: { + Assistant: 'Pomočnik', + Writing: 'Pisanje', + Translate: 'Prevajanje', + Programming: 'Programiranje', + HR: 'Kadri', + }, +} + +export default translation diff --git a/web/i18n/sl-SI/layout.ts b/web/i18n/sl-SI/layout.ts new file mode 100644 index 0000000000..928649474b --- /dev/null +++ b/web/i18n/sl-SI/layout.ts @@ -0,0 +1,4 @@ +const translation = { +} + +export default translation diff --git a/web/i18n/sl-SI/login.ts b/web/i18n/sl-SI/login.ts new file mode 100644 index 0000000000..13dfea984d --- /dev/null +++ b/web/i18n/sl-SI/login.ts @@ -0,0 +1,75 @@ +const translation = { + pageTitle: 'Hej, začnimo!👋', + welcome: 'Dobrodošli v Dify, prosimo, prijavite se za nadaljevanje.', + email: 'E-poštni naslov', + emailPlaceholder: 'Vaš e-poštni naslov', + password: 'Geslo', + passwordPlaceholder: 'Vaše geslo', + name: 'Uporabniško ime', + namePlaceholder: 'Vaše uporabniško ime', + forget: 'Ste pozabili geslo?', + signBtn: 'Prijava', + sso: 'Nadaljujte z SSO', + installBtn: 'Namesti', + setAdminAccount: 'Nastavitev administratorskega računa', + setAdminAccountDesc: 'Najvišje pravice za administratorski račun, ki se lahko uporablja za ustvarjanje aplikacij in upravljanje LLM ponudnikov itd.', + createAndSignIn: 'Ustvari in prijavi se', + oneMoreStep: 'Še en korak', + createSample: 'Na podlagi teh informacij bomo za vas ustvarili vzorčno aplikacijo', + invitationCode: 'Vabilna koda', + invitationCodePlaceholder: 'Vaša vabilna koda', + interfaceLanguage: 'Jezik vmesnika', + timezone: 'Časovni pas', + go: 'Pojdi na Dify', + sendUsMail: 'Pošljite nam uvod po e-pošti, mi pa bomo obravnavali vašo zahtevo za vabilo.', + acceptPP: 'Prebral sem in sprejemam politiko zasebnosti', + reset: 'Za ponastavitev gesla zaženite naslednji ukaz', + withGitHub: 'Nadaljujte z GitHub', + withGoogle: 'Nadaljujte z Google', + rightTitle: 'Odklenite polni potencial LLM', + rightDesc: 'Brez napora gradite vizualno privlačne, operabilne in izboljšljive AI aplikacije.', + tos: 'Pogoji storitve', + pp: 'Politika zasebnosti', + tosDesc: 'Z registracijo se strinjate z našimi', + goToInit: 'Če računa še niste inicializirali, pojdite na stran za inicializacijo', + dontHave: 'Nimate?', + invalidInvitationCode: 'Neveljavna vabilna koda', + accountAlreadyInited: 'Račun je že inicializiran', + forgotPassword: 'Ste pozabili geslo?', + resetLinkSent: 'Povezava za ponastavitev poslana', + sendResetLink: 'Pošlji povezavo za ponastavitev', + backToSignIn: 'Nazaj na prijavo', + forgotPasswordDesc: 'Prosimo, vnesite svoj e-poštni naslov za ponastavitev gesla. Poslali vam bomo e-pošto z navodili za ponastavitev gesla.', + checkEmailForResetLink: 'Preverite svojo e-pošto za povezavo za ponastavitev gesla. Če je ne prejmete v nekaj minutah, preverite tudi mapo z neželeno pošto.', + passwordChanged: 'Prijavite se zdaj', + changePassword: 'Spremeni geslo', + changePasswordTip: 'Prosimo, vnesite novo geslo za svoj račun', + invalidToken: 'Neveljaven ali potekel žeton', + confirmPassword: 'Potrdite geslo', + confirmPasswordPlaceholder: 'Potrdite svoje novo geslo', + passwordChangedTip: 'Vaše geslo je bilo uspešno spremenjeno', + error: { + emailEmpty: 'E-poštni naslov je obvezen', + emailInValid: 'Prosimo, vnesite veljaven e-poštni naslov', + nameEmpty: 'Ime je obvezno', + passwordEmpty: 'Geslo je obvezno', + passwordLengthInValid: 'Geslo mora vsebovati vsaj 8 znakov', + passwordInvalid: 'Geslo mora vsebovati črke in številke, dolžina pa mora biti več kot 8 znakov', + }, + license: { + tip: 'Preden začnete z Dify Community Edition, preberite GitHub', + link: 'Licenco odprte kode', + }, + join: 'Pridruži se', + joinTipStart: 'Vabimo vas, da se pridružite', + joinTipEnd: 'ekipi na Dify', + invalid: 'Povezava je potekla', + explore: 'Razišči Dify', + activatedTipStart: 'Pridružili ste se ekipi', + activatedTipEnd: 'ekipi', + activated: 'Prijavite se zdaj', + adminInitPassword: 'Geslo za inicializacijo administratorja', + validate: 'Potrdi', +} + +export default translation diff --git a/web/i18n/sl-SI/register.ts b/web/i18n/sl-SI/register.ts new file mode 100644 index 0000000000..928649474b --- /dev/null +++ b/web/i18n/sl-SI/register.ts @@ -0,0 +1,4 @@ +const translation = { +} + +export default translation diff --git a/web/i18n/sl-SI/run-log.ts b/web/i18n/sl-SI/run-log.ts new file mode 100644 index 0000000000..2ac026ac84 --- /dev/null +++ b/web/i18n/sl-SI/run-log.ts @@ -0,0 +1,29 @@ +const translation = { + input: 'VNOS', + result: 'REZULTAT', + detail: 'PODROBNOSTI', + tracing: 'SLEDENJE', + resultPanel: { + status: 'STATUS', + time: 'PRETEČENI ČAS', + tokens: 'SKUPNI ŽETONI', + }, + meta: { + title: 'METAPODATKI', + status: 'Status', + version: 'Različica', + executor: 'Izvajalec', + startTime: 'Čas začetka', + time: 'Pretečeni čas', + tokens: 'Skupni žetoni', + steps: 'Koraki izvajanja', + }, + resultEmpty: { + title: 'Ta zagon je izpisal samo format JSON,', + tipLeft: 'prosimo, pojdite na ', + link: 'panel podrobnosti', + tipRight: ' za ogled.', + }, +} + +export default translation diff --git a/web/i18n/sl-SI/share-app.ts b/web/i18n/sl-SI/share-app.ts new file mode 100644 index 0000000000..68ad6594fc --- /dev/null +++ b/web/i18n/sl-SI/share-app.ts @@ -0,0 +1,71 @@ +const translation = { + common: { + welcome: '', + appUnavailable: 'Aplikacija ni na voljo', + appUnknownError: 'Aplikacija ni na voljo', + }, + chat: { + newChat: 'Nov klepet', + pinnedTitle: 'Pripeto', + unpinnedTitle: 'Klepeti', + newChatDefaultName: 'Nova konverzacija', + resetChat: 'Ponastavi konverzacijo', + poweredBy: 'Poganja', + prompt: 'Poziv', + privatePromptConfigTitle: 'Nastavitve konverzacije', + publicPromptConfigTitle: 'Začetni poziv', + configStatusDes: 'Pred začetkom lahko spremenite nastavitve konverzacije', + configDisabled: + 'Nastavitve prejšnje seje so uporabljene za to sejo.', + startChat: 'Začni klepet', + privacyPolicyLeft: 'Prosimo, preberite ', + privacyPolicyMiddle: 'politiko zasebnosti', + privacyPolicyRight: ' ponudnika aplikacije.', + deleteConversation: { + title: 'Izbriši konverzacijo', + content: 'Ali ste prepričani, da želite izbrisati to konverzacijo?', + }, + tryToSolve: 'Poskusite rešiti', + temporarySystemIssue: 'Oprostite, začasna težava s sistemom.', + }, + generation: { + tabs: { + create: 'Zaženi enkrat', + batch: 'Zaženi serijo', + saved: 'Shranjeno', + }, + savedNoData: { + title: 'Še niste shranili rezultata!', + description: 'Začnite z ustvarjanjem vsebine in tukaj najdite svoje shranjene rezultate.', + startCreateContent: 'Začnite z ustvarjanjem vsebine', + }, + title: 'AI Zaključek', + queryTitle: 'Vsebina poizvedbe', + completionResult: 'Rezultat zaključka', + queryPlaceholder: 'Vnesite vsebino poizvedbe...', + run: 'Izvedi', + copy: 'Kopiraj', + resultTitle: 'AI Zaključek', + noData: 'AI vam bo tukaj zagotovil, kar želite.', + csvUploadTitle: 'Povlecite in spustite svojo CSV datoteko tukaj, ali ', + browse: 'brskajte', + csvStructureTitle: 'CSV datoteka mora ustrezati naslednji strukturi:', + downloadTemplate: 'Prenesite predlogo tukaj', + field: 'Polje', + batchFailed: { + info: '{{num}} neuspelih izvedb', + retry: 'Poskusite znova', + outputPlaceholder: 'Brez izhodne vsebine', + }, + errorMsg: { + empty: 'Prosimo, vnesite vsebino v naloženo datoteko.', + fileStructNotMatch: 'Naložena CSV datoteka ne ustreza strukturi.', + emptyLine: 'Vrstica {{rowIndex}} je prazna', + invalidLine: 'Vrstica {{rowIndex}}: vrednost {{varName}} ne sme biti prazna', + moreThanMaxLengthLine: 'Vrstica {{rowIndex}}: vrednost {{varName}} ne sme biti daljša od {{maxLength}} znakov', + atLeastOne: 'Prosimo, vnesite vsaj eno vrstico v naloženo datoteko.', + }, + }, +} + +export default translation diff --git a/web/i18n/sl-SI/tools.ts b/web/i18n/sl-SI/tools.ts new file mode 100644 index 0000000000..57160cfe62 --- /dev/null +++ b/web/i18n/sl-SI/tools.ts @@ -0,0 +1,153 @@ +const translation = { + title: 'Orodja', + createCustomTool: 'Ustvari prilagojeno orodje', + customToolTip: 'Izvedite več o prilagojenih orodjih Dify', + type: { + all: 'Vsa', + builtIn: 'Vgrajena', + custom: 'Prilagojena', + workflow: 'Potek dela', + }, + contribute: { + line1: 'Zanima me ', + line2: 'prispevanje orodij k Dify.', + viewGuide: 'Oglejte si vodič', + }, + author: 'Avtor', + auth: { + unauthorized: 'Za avtorizacijo', + authorized: 'Avtorizirano', + setup: 'Nastavite avtorizacijo za uporabo', + setupModalTitle: 'Nastavi avtorizacijo', + setupModalTitleDescription: 'Po konfiguraciji poverilnic bodo vsi člani znotraj delovnega prostora lahko uporabljali to orodje pri orkestraciji aplikacij.', + }, + includeToolNum: 'Vključeno {{num}} orodij', + addTool: 'Dodaj orodje', + addToolModal: { + type: 'tip', + category: 'kategorija', + add: 'dodaj', + added: 'dodano', + manageInTools: 'Upravljaj v Orodjih', + emptyTitle: 'Orodje za potek dela ni na voljo', + emptyTip: 'Pojdite na "Potek dela -> Objavi kot orodje"', + }, + createTool: { + title: 'Ustvari prilagojeno orodje', + editAction: 'Konfiguriraj', + editTitle: 'Uredi prilagojeno orodje', + name: 'Ime', + toolNamePlaceHolder: 'Vnesite ime orodja', + nameForToolCall: 'Ime za klic orodja', + nameForToolCallPlaceHolder: 'Uporablja se za strojno prepoznavo, na primer getCurrentWeather, list_pets', + nameForToolCallTip: 'Podprte so samo številke, črke in podčrtaji.', + description: 'Opis', + descriptionPlaceholder: 'Kratek opis namena orodja, npr. pridobi temperaturo za določeno lokacijo.', + schema: 'Shema', + schemaPlaceHolder: 'Vnesite svojo OpenAPI shemo tukaj', + viewSchemaSpec: 'Oglejte si OpenAPI-Swagger specifikacijo', + importFromUrl: 'Uvozi iz URL-ja', + importFromUrlPlaceHolder: 'https://...', + urlError: 'Prosimo, vnesite veljaven URL', + examples: 'Primeri', + exampleOptions: { + json: 'Vreme(JSON)', + yaml: 'Trgovina za domače ljubljenčke(YAML)', + blankTemplate: 'Prazna predloga', + }, + availableTools: { + title: 'Razpoložljiva orodja', + name: 'Ime', + description: 'Opis', + method: 'Metoda', + path: 'Pot', + action: 'Dejanja', + test: 'Testiraj', + }, + authMethod: { + title: 'Metoda avtorizacije', + type: 'Vrsta avtorizacije', + keyTooltip: 'Ključ HTTP glave, pustite kot "Authorization", če ne veste, kaj je to, ali pa nastavite na vrednost po meri', + types: { + none: 'Brez', + api_key: 'API ključ', + apiKeyPlaceholder: 'Ime HTTP glave za API ključ', + apiValuePlaceholder: 'Vnesite API ključ', + }, + key: 'Ključ', + value: 'Vrednost', + }, + authHeaderPrefix: { + title: 'Vrsta avtorizacije', + types: { + basic: 'Osnovna', + bearer: 'Imetnik', + custom: 'Prilagojena', + }, + }, + privacyPolicy: 'Politika zasebnosti', + privacyPolicyPlaceholder: 'Prosimo, vnesite politiko zasebnosti', + toolInput: { + title: 'Vnos orodja', + name: 'Ime', + required: 'Obvezno', + method: 'Metoda', + methodSetting: 'Nastavitve', + methodSettingTip: 'Uporabnik izpolni konfiguracijo orodja', + methodParameter: 'Parameter', + methodParameterTip: 'LLM izpolni med sklepanjem', + label: 'Oznake', + labelPlaceholder: 'Izberite oznake (neobvezno)', + description: 'Opis', + descriptionPlaceholder: 'Opis pomena parametra', + }, + customDisclaimer: 'Prilagojeno zavrnitev odgovornosti', + customDisclaimerPlaceholder: 'Prosimo, vnesite prilagojeno zavrnitev odgovornosti', + confirmTitle: 'Potrditi shranjevanje?', + confirmTip: 'Aplikacije, ki uporabljajo to orodje, bodo vplivane', + deleteToolConfirmTitle: 'Izbrisati to orodje?', + deleteToolConfirmContent: 'Brisanje orodja je nepovratno. Uporabniki ne bodo več imeli dostopa do vašega orodja.', + }, + test: { + title: 'Test', + parametersValue: 'Parametri in vrednosti', + parameters: 'Parametri', + value: 'Vrednost', + testResult: 'Rezultati testa', + testResultPlaceholder: 'Rezultati testa bodo prikazani tukaj', + }, + thought: { + using: 'Uporablja se', + used: 'Uporabljeno', + requestTitle: 'Zahteva za', + responseTitle: 'Odgovor iz', + }, + setBuiltInTools: { + info: 'Informacije', + setting: 'Nastavitve', + toolDescription: 'Opis orodja', + parameters: 'parametri', + string: 'niz', + number: 'številka', + required: 'Obvezno', + infoAndSetting: 'Informacije in nastavitve', + }, + noCustomTool: { + title: 'Ni prilagojenih orodij!', + content: 'Tukaj lahko dodate in upravljate svoja prilagojena orodja za gradnjo AI aplikacij.', + createTool: 'Ustvari orodje', + }, + noSearchRes: { + title: 'Oprostite, ni rezultatov!', + content: 'Nismo našli nobenih orodij, ki ustrezajo vašemu iskanju.', + reset: 'Ponastavi iskanje', + }, + builtInPromptTitle: 'Poziv', + toolRemoved: 'Orodje odstranjeno', + notAuthorized: 'Orodje ni avtorizirano', + howToGet: 'Kako pridobiti', + openInStudio: 'Odpri v Studiju', + toolNameUsageTip: 'Ime klica orodja za sklepanja in pozivanje agenta', +} + +export default translation diff --git a/web/i18n/sl-SI/workflow.ts b/web/i18n/sl-SI/workflow.ts new file mode 100644 index 0000000000..767139b741 --- /dev/null +++ b/web/i18n/sl-SI/workflow.ts @@ -0,0 +1,559 @@ +const translation = { + common: { + undo: 'Razveljavi', + redo: 'Uveljavi', + editing: 'Urejanje', + autoSaved: 'Samodejno shranjeno', + unpublished: 'Nepublicirano', + published: 'Objavljeno', + publish: 'Objavi', + update: 'Posodobi', + run: 'Zaženi', + running: 'V teku', + inRunMode: 'V načinu zagona', + inPreview: 'V predogledu', + inPreviewMode: 'V načinu predogleda', + preview: 'Predogled', + viewRunHistory: 'Ogled zgodovine zagona', + runHistory: 'Zgodovina zagona', + goBackToEdit: 'Nazaj na urejevalnik', + conversationLog: 'Zapisnik pogovora', + features: 'Značilnosti', + debugAndPreview: 'Predogled', + restart: 'Ponovni zagon', + currentDraft: 'Trenutni osnutek', + currentDraftUnpublished: 'Trenutni osnutek ni objavljen', + latestPublished: 'Zadnje objavljeno', + publishedAt: 'Objavljeno ob', + restore: 'Obnovi', + runApp: 'Zaženi aplikacijo', + batchRunApp: 'Serijski zagon aplikacije', + accessAPIReference: 'Dostop do API referenc', + embedIntoSite: 'Vdelaj v spletno stran', + addTitle: 'Dodaj naslov...', + addDescription: 'Dodaj opis...', + noVar: 'Ni spremenljivke', + searchVar: 'Išči spremenljivko', + variableNamePlaceholder: 'Ime spremenljivke', + setVarValuePlaceholder: 'Nastavi vrednost spremenljivke', + needConnectTip: 'Ta korak ni povezan z ničemer', + maxTreeDepth: 'Največja omejitev je {{depth}} vozlišč na vejo', + needEndNode: 'Dodati je treba zaključni blok', + needAnswerNode: 'Dodati je treba blok z odgovorom', + workflowProcess: 'Proces delovnega toka', + notRunning: 'Še ni v teku', + previewPlaceholder: 'Vnesite vsebino v spodnje polje za začetek odpravljanja napak klepetalnika', + effectVarConfirm: { + title: 'Odstrani spremenljivko', + content: 'Spremenljivka se uporablja v drugih vozliščih. Ali jo kljub temu želite odstraniti?', + }, + insertVarTip: 'Pritisnite tipko \'/\' za hitro vstavljanje', + processData: 'Obdelava podatkov', + input: 'Vnos', + output: 'Izhod', + jinjaEditorPlaceholder: 'Vnesite \'/\' ali \'{\' za vstavljanje spremenljivke', + viewOnly: 'Samo ogled', + showRunHistory: 'Prikaži zgodovino zagona', + enableJinja: 'Omogoči podporo za Jinja predloge', + learnMore: 'Več informacij', + copy: 'Kopiraj', + duplicate: 'Podvoji', + addBlock: 'Dodaj blok', + pasteHere: 'Prilepi tukaj', + pointerMode: 'Način kazalca', + handMode: 'Način roke', + model: 'Model', + workflowAsTool: 'Potek dela kot orodje', + configureRequired: 'Potrebna konfiguracija', + configure: 'Konfiguriraj', + manageInTools: 'Upravljaj v Orodjih', + workflowAsToolTip: 'Po posodobitvi poteka dela je potrebno ponovno konfigurirati orodje.', + viewDetailInTracingPanel: 'Oglejte si podrobnosti', + syncingData: 'Sinhronizacija podatkov, le nekaj sekund.', + importDSL: 'Uvozi DSL', + importDSLTip: 'Trenutni osnutek bo prepisan. Pred uvozom izvozite delovni tok kot varnostno kopijo.', + backupCurrentDraft: 'Varnostno kopiraj trenutni osnutek', + chooseDSL: 'Izberite DSL(yml) datoteko', + overwriteAndImport: 'Prepiši in uvozi', + importFailure: 'Uvoz ni uspel', + importSuccess: 'Uvoz uspešen', + parallelRun: 'Vzporedni zagon', + parallelTip: { + click: { + title: 'Klikni', + desc: ' za dodajanje', + }, + drag: { + title: 'Povleci', + desc: ' za povezavo', + }, + limit: 'Vzporednost je omejena na {{num}} vej.', + depthLimit: 'Omejitev gnezdenja vzporednih slojev na {{num}} slojev', + }, + disconnect: 'Prekini povezavo', + jumpToNode: 'Skoči na to vozlišče', + addParallelNode: 'Dodaj vzporedno vozlišče', + parallel: 'VZPOREDNO', + branch: 'VEJA', + }, + env: { + envPanelTitle: 'Spremenljivke okolja', + envDescription: 'Spremenljivke okolja se uporabljajo za shranjevanje zasebnih informacij in poverilnic. So samo za branje in jih je mogoče ločiti od DSL datoteke med izvozom.', + envPanelButton: 'Dodaj spremenljivko', + modal: { + title: 'Dodaj spremenljivko okolja', + editTitle: 'Uredi spremenljivko okolja', + type: 'Vrsta', + name: 'Ime', + namePlaceholder: 'ime okolja', + value: 'Vrednost', + valuePlaceholder: 'vrednost okolja', + secretTip: 'Uporablja se za definiranje občutljivih informacij ali podatkov, s konfiguriranimi nastavitvami DSL za preprečevanje uhajanja.', + }, + export: { + title: 'Izvoziti skrivne spremenljivke okolja?', + checkbox: 'Izvozi skrivne vrednosti', + ignore: 'Izvozi DSL', + export: 'Izvozi DSL z skrivnimi vrednostmi', + }, + chatVariable: { + panelTitle: 'Spremenljivke pogovora', + panelDescription: 'Spremenljivke pogovora se uporabljajo za shranjevanje interaktivnih informacij, ki jih mora LLM zapomniti, vključno z zgodovino pogovorov, naloženimi datotekami, uporabniškimi nastavitvami. So za branje in pisanje.', + docLink: 'Obiščite naše dokumente za več informacij.', + button: 'Dodaj spremenljivko', + modal: { + title: 'Dodaj spremenljivko pogovora', + editTitle: 'Uredi spremenljivko pogovora', + name: 'Ime', + namePlaceholder: 'Ime spremenljivke', + type: 'Vrsta', + value: 'Privzeta vrednost', + valuePlaceholder: 'Privzeta vrednost, pustite prazno, če je ne želite nastaviti', + description: 'Opis', + descriptionPlaceholder: 'Opišite spremenljivko', + editInJSON: 'Uredi v JSON', + oneByOne: 'Dodaj eno po eno', + editInForm: 'Uredi v obrazcu', + arrayValue: 'Vrednost', + addArrayValue: 'Dodaj vrednost', + objectKey: 'Ključ', + objectType: 'Vrsta', + objectValue: 'Privzeta vrednost', + }, + storedContent: 'Shranjena vsebina', + updatedAt: 'Posodobljeno ob', + }, + changeHistory: { + title: 'Zgodovina sprememb', + placeholder: 'Še niste ničesar spremenili', + clearHistory: 'Počisti zgodovino', + hint: 'Namig', + hintText: 'Vaša dejanja urejanja se spremljajo v zgodovini sprememb, ki se hrani na vaši napravi med trajanjem te seje. Ta zgodovina se bo izbrisala, ko zapustite urejevalnik.', + stepBackward_one: '{{count}} korak nazaj', + stepBackward_other: '{{count}} korakov nazaj', + stepForward_one: '{{count}} korak naprej', + stepForward_other: '{{count}} korakov naprej', + sessionStart: 'Začetek seje', + currentState: 'Trenutno stanje', + nodeTitleChange: 'Naslov bloka spremenjen', + nodeDescriptionChange: 'Opis bloka spremenjen', + nodeDragStop: 'Blok premaknjen', + nodeChange: 'Blok spremenjen', + nodeConnect: 'Blok povezan', + nodePaste: 'Blok prilepljen', + nodeDelete: 'Blok izbrisan', + nodeAdd: 'Blok dodan', + nodeResize: 'Velikost bloka spremenjena', + noteAdd: 'Opomba dodana', + noteChange: 'Opomba spremenjena', + noteDelete: 'Opomba izbrisana', + edgeDelete: 'Blok prekinjen', + }, + errorMsg: { + fieldRequired: '{{field}} je obvezen', + rerankModelRequired: 'Pred vklopom modela za ponovno razvrščanje, prosimo potrdite, da je bil model uspešno konfiguriran v nastavitvah.', + authRequired: 'Potrebna je avtorizacija', + invalidJson: '{{field}} je neveljaven JSON', + fields: { + variable: 'Ime spremenljivke', + variableValue: 'Vrednost spremenljivke', + code: 'Koda', + model: 'Model', + rerankModel: 'Model za ponovno razvrščanje', + }, + invalidVariable: 'Neveljavna spremenljivka', + }, + singleRun: { + testRun: 'Testni zagon', + startRun: 'Začni zagon', + running: 'V teku', + testRunIteration: 'Iteracija testnega zagona', + back: 'Nazaj', + iteration: 'Iteracija', + }, + tabs: { + 'searchBlock': 'Iskanje bloka', + 'blocks': 'Bloki', + 'searchTool': 'Iskanje orodja', + 'tools': 'Orodja', + 'allTool': 'Vsa', + 'builtInTool': 'Vgrajena', + 'customTool': 'Prilagojena', + 'workflowTool': 'Potek dela', + 'question-understand': 'Razumevanje vprašanja', + 'logic': 'Logika', + 'transform': 'Pretvorba', + 'utilities': 'Pripomočki', + 'noResult': 'Ni najdenih zadetkov', + }, + blocks: { + 'start': 'Začetek', + 'end': 'Konec', + 'answer': 'Odgovor', + 'llm': 'LLM', + 'knowledge-retrieval': 'Pridobivanje znanja', + 'question-classifier': 'Klasifikator vprašanj', + 'if-else': 'IF/ELSE', + 'code': 'Koda', + 'template-transform': 'Predloga', + 'http-request': 'HTTP zahteva', + 'variable-assigner': 'Dodeljevalec spremenljivk', + 'variable-aggregator': 'Zbiralnik spremenljivk', + 'assigner': 'Dodeljevalec spremenljivk', + 'iteration-start': 'Začetek iteracije', + 'iteration': 'Iteracija', + 'parameter-extractor': 'Izvleček parametrov', + }, + blocksAbout: { + 'start': 'Določite začetne parametre za zagon delovnega toka', + 'end': 'Določite konec in vrsto rezultata delovnega toka', + 'answer': 'Določite vsebino odgovora v klepetalni konverzaciji', + 'llm': 'Klicanje velikih jezikovnih modelov za odgovarjanje na vprašanja ali obdelavo naravnega jezika', + 'knowledge-retrieval': 'Omogoča iskanje vsebine, povezane z uporabnikovimi vprašanji, iz baze znanja', + 'question-classifier': 'Določite pogoje za klasifikacijo uporabniških vprašanj; LLM lahko določi, kako se bo konverzacija razvijala glede na klasifikacijski opis', + 'if-else': 'Omogoča razdelitev delovnega toka na dve veji glede na pogoje if/else', + 'code': 'Izvedite del kode Python ali NodeJS za implementacijo prilagojene logike', + 'template-transform': 'Pretvorite podatke v niz z uporabo Jinja predloge', + 'http-request': 'Omogoča pošiljanje strežniških zahtev preko HTTP protokola', + 'variable-assigner': 'Združi spremenljivke več vej v eno spremenljivko za enotno konfiguracijo vozlišč nižje v poteku.', + 'assigner': 'Vozlišče za dodelitev spremenljivk se uporablja za dodelitev vrednosti pisnim spremenljivkam (kot so spremenljivke konverzacije).', + 'variable-aggregator': 'Združi spremenljivke več vej v eno spremenljivko za enotno konfiguracijo vozlišč nižje v poteku.', + 'iteration': 'Izvedite več korakov na seznamu objektov, dokler niso vsi rezultati izpisani.', + 'parameter-extractor': 'Uporabite LLM za izvleček strukturiranih parametrov iz naravnega jezika za klice orodij ali HTTP zahteve.', + }, + operator: { + zoomIn: 'Povečaj', + zoomOut: 'Pomanjšaj', + zoomTo50: 'Povečaj na 50%', + zoomTo100: 'Povečaj na 100%', + zoomToFit: 'Prilagodi velikost', + }, + panel: { + userInputField: 'Vnosno polje uporabnika', + changeBlock: 'Spremeni blok', + helpLink: 'Povezava za pomoč', + about: 'O', + createdBy: 'Ustvaril ', + nextStep: 'Naslednji korak', + addNextStep: 'Dodaj naslednji blok v tem delovnem toku', + selectNextStep: 'Izberi naslednji blok', + runThisStep: 'Zaženi ta korak', + checklist: 'Kontrolni seznam', + checklistTip: 'Poskrbite, da so vsi problemi rešeni pred objavo', + checklistResolved: 'Vsi problemi so rešeni', + organizeBlocks: 'Organiziraj bloke', + change: 'Spremeni', + optional: '(neobvezno)', + }, + nodes: { + common: { + outputVars: 'Izhodne spremenljivke', + insertVarTip: 'Vstavi spremenljivko', + memory: { + memory: 'Pomnjenje', + memoryTip: 'Nastavitve pomnjenja klepeta', + windowSize: 'Velikost okna', + conversationRoleName: 'Ime vloge v konverzaciji', + user: 'Predpona uporabnika', + assistant: 'Predpona pomočnika', + }, + memories: { + title: 'Pomnjenje', + tip: 'Pomnjenje klepeta', + builtIn: 'Vgrajeno', + }, + }, + start: { + required: 'obvezno', + inputField: 'Vnosno polje', + builtInVar: 'Vgrajene spremenljivke', + outputVars: { + query: 'Uporabniški vnos', + memories: { + des: 'Zgodovina konverzacije', + type: 'vrsta sporočila', + content: 'vsebina sporočila', + }, + files: 'Seznam datotek', + }, + noVarTip: 'Nastavite vnose, ki jih lahko uporabite v delovnem toku', + }, + end: { + outputs: 'Izhodi', + output: { + type: 'vrsta izhoda', + variable: 'izhoda spremenljivka', + }, + type: { + 'none': 'Brez', + 'plain-text': 'Navadno besedilo', + 'structured': 'Strukturirano', + }, + }, + answer: { + answer: 'Odgovor', + outputVars: 'Izhodne spremenljivke', + }, + llm: { + model: 'model', + variables: 'spremenljivke', + context: 'kontekst', + contextTooltip: 'Kot kontekst lahko uvozite Znanje', + notSetContextInPromptTip: 'Za omogočanje funkcije konteksta, prosimo, izpolnite kontekstno spremenljivko v POZIVU.', + prompt: 'poziv', + roleDescription: { + system: 'Podajte splošna navodila za konverzacijo', + user: 'Podajte navodila, poizvedbe ali katero koli besedilno vsebino za model', + assistant: 'Odzivi modela na uporabniška sporočila', + }, + addMessage: 'Dodaj sporočilo', + vision: 'vizija', + files: 'Datoteke', + resolution: { + name: 'Ločljivost', + high: 'Visoka', + low: 'Nizka', + }, + outputVars: { + output: 'Generirana vsebina', + usage: 'Podatki o uporabi modela', + }, + singleRun: { + variable: 'Spremenljivka', + }, + sysQueryInUser: 'sys.query v uporabniškem sporočilu je obvezno', + }, + knowledgeRetrieval: { + queryVariable: 'Poizvedbena spremenljivka', + knowledge: 'Znanje', + outputVars: { + output: 'Pridobljeni segmentirani podatki', + content: 'Segmentirana vsebina', + title: 'Segmentirani naslov', + icon: 'Segmentirana ikona', + url: 'Segmentiran URL', + metadata: 'Drugi metapodatki', + }, + }, + http: { + inputVars: 'Vnosne spremenljivke', + api: 'API', + apiPlaceholder: 'Vnesite URL, vstavite spremenljivko z tipko ‘/’', + notStartWithHttp: 'API mora začeti z http:// ali https://', + key: 'Ključ', + value: 'Vrednost', + bulkEdit: 'Serijsko urejanje', + keyValueEdit: 'Urejanje ključ-vrednost', + headers: 'Glave', + params: 'Parametri', + body: 'Telo', + outputVars: { + body: 'Vsebina odgovora', + statusCode: 'Statusna koda odgovora', + headers: 'Seznam glave odgovora v JSON', + files: 'Seznam datotek', + }, + }, + authorization: { + 'authorization': 'Avtorizacija', + 'authorizationType': 'Vrsta avtorizacije', + 'no-auth': 'Brez', + 'api-key': 'API-ključ', + 'auth-type': 'Vrsta avtorizacije', + 'basic': 'Osnovna', + 'bearer': 'Imetnik', + 'custom': 'Prilagojena', + 'api-key-title': 'API ključ', + 'header': 'Glava', + }, + insertVarPlaceholder: 'vnesite \'/\' za vstavljanje spremenljivke', + timeout: { + title: 'Časovna omejitev', + connectLabel: 'Časovna omejitev povezave', + connectPlaceholder: 'Vnesite časovno omejitev povezave v sekundah', + readLabel: 'Časovna omejitev branja', + readPlaceholder: 'Vnesite časovno omejitev branja v sekundah', + writeLabel: 'Časovna omejitev pisanja', + writePlaceholder: 'Vnesite časovno omejitev pisanja v sekundah', + }, + }, + code: { + inputVars: 'Vhodne spremenljivke', + outputVars: 'Izhodne spremenljivke', + advancedDependencies: 'Napredne odvisnosti', + advancedDependenciesTip: 'Dodajte nekaj prednaloženih odvisnosti, ki potrebujejo več časa za nalaganje ali niso privzeto vgrajene', + searchDependencies: 'Išči odvisnosti', + }, + templateTransform: { + inputVars: 'Vhodne spremenljivke', + code: 'Koda', + codeSupportTip: 'Podpira samo Jinja2', + outputVars: { + output: 'Pretvorjena vsebina', + }, + }, + ifElse: { + if: 'Če', + else: 'Sicer', + elseDescription: 'Uporablja se za definiranje logike, ki naj se izvede, ko pogoj "če" ni izpolnjen.', + and: 'in', + or: 'ali', + operator: 'Operater', + notSetVariable: 'Najprej nastavite spremenljivko', + comparisonOperator: { + 'contains': 'vsebuje', + 'not contains': 'ne vsebuje', + 'start with': 'se začne z', + 'end with': 'se konča z', + 'is': 'je', + 'is not': 'ni', + 'empty': 'je prazna', + 'not empty': 'ni prazna', + 'null': 'je null', + 'not null': 'ni null', + }, + enterValue: 'Vnesite vrednost', + addCondition: 'Dodaj pogoj', + conditionNotSetup: 'Pogoj NI nastavljen', + selectVariable: 'Izberite spremenljivko...', + }, + variableAssigner: { + title: 'Dodeli spremenljivke', + outputType: 'Vrsta izhoda', + varNotSet: 'Spremenljivka ni nastavljena', + noVarTip: 'Dodajte spremenljivke, ki jih želite dodeliti', + type: { + string: 'Niz', + number: 'Število', + object: 'Objekt', + array: 'Polje', + }, + aggregationGroup: 'Skupina za združevanje', + aggregationGroupTip: 'Omogočanje te funkcije omogoča agregatorju spremenljivk združevanje več naborov spremenljivk.', + addGroup: 'Dodaj skupino', + outputVars: { + varDescribe: 'Izhod {{groupName}}', + }, + setAssignVariable: 'Nastavi dodeljeno spremenljivko', + }, + assigner: { + 'assignedVariable': 'Dodeljena spremenljivka', + 'writeMode': 'Način pisanja', + 'writeModeTip': 'Način dodajanja: Na voljo samo za spremenljivke vrste polje.', + 'over-write': 'Prepiši', + 'append': 'Dodaj', + 'plus': 'Plus', + 'clear': 'Počisti', + 'setVariable': 'Nastavi spremenljivko', + 'variable': 'Spremenljivka', + }, + tool: { + toAuthorize: 'Za avtorizacijo', + inputVars: 'Vhodne spremenljivke', + outputVars: { + text: 'orodje je ustvarilo vsebino', + files: { + title: 'orodje je ustvarilo datoteke', + type: 'Podprta vrsta. Trenutno podpira samo slike', + transfer_method: 'Način prenosa. Vrednosti so remote_url ali local_file', + url: 'URL slike', + upload_file_id: 'ID naložene datoteke', + }, + json: 'orodje je ustvarilo json', + }, + }, + questionClassifiers: { + model: 'model', + inputVars: 'Vhodne spremenljivke', + outputVars: { + className: 'Ime razreda', + }, + class: 'Razred', + classNamePlaceholder: 'Vnesite ime razreda', + advancedSetting: 'Napredna nastavitev', + topicName: 'Ime teme', + topicPlaceholder: 'Vnesite ime teme', + addClass: 'Dodaj razred', + instruction: 'Navodilo', + instructionTip: 'Vnesite dodatna navodila, da bo klasifikator vprašanj lažje razumel, kako kategorizirati vprašanja.', + instructionPlaceholder: 'Vnesite vaše navodilo', + }, + parameterExtractor: { + inputVar: 'Vhodna spremenljivka', + extractParameters: 'Izvleči parametre', + importFromTool: 'Uvozi iz orodij', + addExtractParameter: 'Dodaj izvlečen parameter', + addExtractParameterContent: { + name: 'Ime', + namePlaceholder: 'Ime izvlečenega parametra', + type: 'Vrsta', + typePlaceholder: 'Vrsta izvlečenega parametra', + description: 'Opis', + descriptionPlaceholder: 'Opis izvlečenega parametra', + required: 'Obvezno', + requiredContent: 'Obvezno je uporabljeno samo kot referenca za sklepanja modela in ne za obvezno preverjanje izhoda parametra.', + }, + extractParametersNotSet: 'Parametri za izvleček niso nastavljeni', + instruction: 'Navodilo', + instructionTip: 'Vnesite dodatna navodila, da parameter extractor lažje razume, kako izvleči parametre.', + advancedSetting: 'Napredna nastavitev', + reasoningMode: 'Način sklepanja', + reasoningModeTip: 'Lahko izberete ustrezen način sklepanja glede na sposobnost modela za odgovore na navodila za klice funkcij ali pozive.', + isSuccess: 'Je uspeh. Pri uspehu je vrednost 1, pri neuspehu pa 0.', + errorReason: 'Razlog za napako', + }, + iteration: { + deleteTitle: 'Izbrisati vozlišče iteracije?', + deleteDesc: 'Brisanje vozlišča iteracije bo izbrisalo vsa podrejena vozlišča', + input: 'Vhod', + output: 'Izhodne spremenljivke', + iteration_one: '{{count}} iteracija', + iteration_other: '{{count}} iteracij', + currentIteration: 'Trenutna iteracija', + }, + note: { + addNote: 'Dodaj opombo', + editor: { + placeholder: 'Zapišite opombo...', + small: 'Majhno', + medium: 'Srednje', + large: 'Veliko', + bold: 'Krepko', + italic: 'Poševno', + strikethrough: 'Prečrtano', + link: 'Povezava', + openLink: 'Odpri', + unlink: 'Odstrani povezavo', + enterUrl: 'Vnesite URL...', + invalidUrl: 'Neveljaven URL', + bulletList: 'Označen seznam', + showAuthor: 'Pokaži avtorja', + }, + }, + }, + tracing: { + stopBy: 'Ustavljeno s strani {{user}}', + }, +} + +export default translation diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 6d574bc6f5..4ebd9d4797 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -369,6 +369,7 @@ const translation = { inputVars: '输入变量', api: 'API', apiPlaceholder: '输入 URL,输入变量时请键入‘/’', + extractListPlaceholder: '输入提取列表编号,输入变量时请键入‘/’', notStartWithHttp: 'API 应该以 http:// 或 https:// 开头', key: '键', type: '类型', @@ -608,6 +609,7 @@ const translation = { filterConditionComparisonOperator: '过滤条件比较操作符', filterConditionComparisonValue: '过滤条件比较值', selectVariableKeyPlaceholder: '选择子变量的 Key', + extractsCondition: '取第 N 项', limit: '取前 N 项', orderBy: '排序', asc: '升序', diff --git a/web/package.json b/web/package.json index ba619d45f7..bb60bedd5c 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "dify-web", - "version": "0.11.0", + "version": "0.11.1", "private": true, "engines": { "node": ">=18.17.0" diff --git a/web/service/base.ts b/web/service/base.ts index e2dd3314a5..4519c5f006 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -343,7 +343,9 @@ export const ssePost = ( getAbortController?.(abortController) const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX - const urlWithPrefix = `${urlPrefix}${url.startsWith('/') ? url : `/${url}`}` + const urlWithPrefix = (url.startsWith('http://') || url.startsWith('https://')) + ? url + : `${urlPrefix}${url.startsWith('/') ? url : `/${url}`}` const { body } = options if (body) diff --git a/web/service/refresh-token.ts b/web/service/refresh-token.ts index 8bd2215041..b193779629 100644 --- a/web/service/refresh-token.ts +++ b/web/service/refresh-token.ts @@ -1,11 +1,13 @@ import { apiPrefix } from '@/config' import { fetchWithRetry } from '@/utils' +const LOCAL_STORAGE_KEY = 'is_other_tab_refreshing' + let isRefreshing = false function waitUntilTokenRefreshed() { return new Promise((resolve, reject) => { function _check() { - const isRefreshingSign = localStorage.getItem('is_refreshing') + const isRefreshingSign = globalThis.localStorage.getItem(LOCAL_STORAGE_KEY) if ((isRefreshingSign && isRefreshingSign === '1') || isRefreshing) { setTimeout(() => { _check() @@ -22,13 +24,14 @@ function waitUntilTokenRefreshed() { // only one request can send async function getNewAccessToken(): Promise { try { - const isRefreshingSign = localStorage.getItem('is_refreshing') + const isRefreshingSign = globalThis.localStorage.getItem(LOCAL_STORAGE_KEY) if ((isRefreshingSign && isRefreshingSign === '1') || isRefreshing) { await waitUntilTokenRefreshed() } else { - globalThis.localStorage.setItem('is_refreshing', '1') isRefreshing = true + globalThis.localStorage.setItem(LOCAL_STORAGE_KEY, '1') + globalThis.addEventListener('beforeunload', releaseRefreshLock) const refresh_token = globalThis.localStorage.getItem('refresh_token') // Do not use baseFetch to refresh tokens. @@ -61,15 +64,21 @@ async function getNewAccessToken(): Promise { return Promise.reject(error) } finally { + releaseRefreshLock() + } +} + +function releaseRefreshLock() { + if (isRefreshing) { isRefreshing = false - globalThis.localStorage.removeItem('is_refreshing') + globalThis.localStorage.removeItem(LOCAL_STORAGE_KEY) + globalThis.removeEventListener('beforeunload', releaseRefreshLock) } } export async function refreshAccessTokenOrRelogin(timeout: number) { return Promise.race([new Promise((resolve, reject) => setTimeout(() => { - isRefreshing = false - globalThis.localStorage.removeItem('is_refreshing') + releaseRefreshLock() reject(new Error('request timeout')) }, timeout)), getNewAccessToken()]) } diff --git a/web/types/workflow.ts b/web/types/workflow.ts index 8c0d81639d..34b08e878e 100644 --- a/web/types/workflow.ts +++ b/web/types/workflow.ts @@ -8,7 +8,7 @@ import type { } from '@/app/components/workflow/types' import type { TransferMethod } from '@/types/app' -export interface NodeTracing { +export type NodeTracing = { id: string index: number predecessor_node_id: string @@ -33,6 +33,7 @@ export interface NodeTracing { parent_parallel_id?: string parent_parallel_start_node_id?: string parallel_mode_run_id?: string + iteration_duration_map?: IterationDurationMap } metadata: { iterator_length: number @@ -44,6 +45,7 @@ export interface NodeTracing { name: string email: string } + iterDurationMap?: IterationDurationMap finished_at: number extras?: any expand?: boolean // for UI @@ -54,7 +56,7 @@ export interface NodeTracing { parent_parallel_start_node_id?: string } -export interface FetchWorkflowDraftResponse { +export type FetchWorkflowDraftResponse = { id: string graph: { nodes: Node[] @@ -75,11 +77,11 @@ export interface FetchWorkflowDraftResponse { conversation_variables?: ConversationVariable[] } -export interface NodeTracingListResponse { +export type NodeTracingListResponse = { data: NodeTracing[] } -export interface WorkflowStartedResponse { +export type WorkflowStartedResponse = { task_id: string workflow_run_id: string event: string @@ -91,7 +93,7 @@ export interface WorkflowStartedResponse { } } -export interface WorkflowFinishedResponse { +export type WorkflowFinishedResponse = { task_id: string workflow_run_id: string event: string @@ -115,7 +117,7 @@ export interface WorkflowFinishedResponse { } } -export interface NodeStartedResponse { +export type NodeStartedResponse = { task_id: string workflow_run_id: string event: string @@ -133,7 +135,7 @@ export interface NodeStartedResponse { } } -export interface FileResponse { +export type FileResponse = { related_id: string extension: string filename: string @@ -144,7 +146,7 @@ export interface FileResponse { url: string } -export interface NodeFinishedResponse { +export type NodeFinishedResponse = { task_id: string workflow_run_id: string event: string @@ -176,7 +178,7 @@ export interface NodeFinishedResponse { } } -export interface IterationStartedResponse { +export type IterationStartedResponse = { task_id: string workflow_run_id: string event: string @@ -193,7 +195,7 @@ export interface IterationStartedResponse { } } -export interface IterationNextResponse { +export type IterationNextResponse = { task_id: string workflow_run_id: string event: string @@ -207,11 +209,14 @@ export interface IterationNextResponse { parallel_mode_run_id: string execution_metadata: { parallel_id?: string + iteration_index: number + parallel_mode_run_id?: string } + duration?: number } } -export interface IterationFinishedResponse { +export type IterationFinishedResponse = { task_id: string workflow_run_id: string event: string @@ -229,7 +234,7 @@ export interface IterationFinishedResponse { } } -export interface ParallelBranchStartedResponse { +export type ParallelBranchStartedResponse = { task_id: string workflow_run_id: string event: string @@ -243,7 +248,7 @@ export interface ParallelBranchStartedResponse { } } -export interface ParallelBranchFinishedResponse { +export type ParallelBranchFinishedResponse = { task_id: string workflow_run_id: string event: string @@ -259,7 +264,7 @@ export interface ParallelBranchFinishedResponse { } } -export interface TextChunkResponse { +export type TextChunkResponse = { task_id: string workflow_run_id: string event: string @@ -268,7 +273,7 @@ export interface TextChunkResponse { } } -export interface TextReplaceResponse { +export type TextReplaceResponse = { task_id: string workflow_run_id: string event: string @@ -277,7 +282,7 @@ export interface TextReplaceResponse { } } -export interface WorkflowRunHistory { +export type WorkflowRunHistory = { id: string sequence_number: number version: string @@ -303,11 +308,11 @@ export interface WorkflowRunHistory { email: string } } -export interface WorkflowRunHistoryResponse { +export type WorkflowRunHistoryResponse = { data: WorkflowRunHistory[] } -export interface ChatRunHistoryResponse { +export type ChatRunHistoryResponse = { data: WorkflowRunHistory[] } @@ -316,10 +321,12 @@ export type NodesDefaultConfigsResponse = { config: any }[] -export interface ConversationVariableResponse { +export type ConversationVariableResponse = { data: (ConversationVariable & { updated_at: number; created_at: number })[] has_more: boolean limit: number total: number page: number } + +export type IterationDurationMap = Record diff --git a/web/yarn.lock b/web/yarn.lock index f9ce5ed375..88a57dba70 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -6450,9 +6450,9 @@ elkjs@^0.9.0: integrity sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ== elliptic@^6.5.3, elliptic@^6.5.5: - version "6.5.7" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" - integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== + version "6.6.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.0.tgz#5919ec723286c1edf28685aa89261d4761afa210" + integrity sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA== dependencies: bn.js "^4.11.9" brorand "^1.1.0"